Skip to content
1 change: 1 addition & 0 deletions OneBus.API/Controllers/EmployeeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace OneBus.API.Controllers
{
[NonController]
[Route("api/v1/employees")]
[ApiController]
[Produces("application/json")]
Expand Down
1 change: 1 addition & 0 deletions OneBus.API/Controllers/EmployeeWorkdayController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace OneBus.API.Controllers
{
[NonController]
[Route("api/v1/employeesWorkdays")]
[ApiController]
[Produces("application/json")]
Expand Down
1 change: 1 addition & 0 deletions OneBus.API/Controllers/LineController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace OneBus.API.Controllers
{
[NonController]
[Route("api/v1/lines")]
[ApiController]
[Produces("application/json")]
Expand Down
1 change: 1 addition & 0 deletions OneBus.API/Controllers/LineTimeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace OneBus.API.Controllers
{
[NonController]
[Route("api/v1/linesTimes")]
[ApiController]
[Produces("application/json")]
Expand Down
1 change: 1 addition & 0 deletions OneBus.API/Controllers/MaintenanceController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace OneBus.API.Controllers
{
[NonController]
[Route("api/v1/maintenances")]
[ApiController]
[Produces("application/json")]
Expand Down
18 changes: 15 additions & 3 deletions OneBus.API/Controllers/UserController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OneBus.API.Extensions;
using OneBus.Application.DTOs.Login;
using OneBus.Application.Interfaces.Services;
using OneBus.Domain.Commons.Result;
using OneBus.Domain.Models;
using Swashbuckle.AspNetCore.Annotations;

namespace OneBus.API.Controllers
Expand All @@ -24,16 +27,25 @@ public UserController(IUserService userService)
/// Efetuar login
/// </summary>
/// <remarks>
/// POST de Login
/// Example:
///
/// POST /logins
/// {
/// "email": "helloworld@gmail.com",
/// "password": "dasdfsf35346353tsd"
/// }
/// </remarks>
/// <param name="login"></param>
/// <param name="cancellationToken"></param>
/// <returns>Usuário autenticado</returns>
[AllowAnonymous]
[HttpPost("logins")]
public Task<IActionResult> LoginAsync([FromBody] LoginDTO login, CancellationToken cancellationToken = default)
[ProducesResponseType(typeof(SuccessResult<TokenModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(InvalidResult<TokenModel>), StatusCodes.Status422UnprocessableEntity)]
[ProducesResponseType(typeof(NotFoundResult<TokenModel>), StatusCodes.Status404NotFound)]
public async Task<IActionResult> LoginAsync([FromBody] LoginDTO login, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
return (await _userService.LoginAsync(login, cancellationToken)).ToActionResult();
}
}
}
1 change: 1 addition & 0 deletions OneBus.API/Controllers/VehicleController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace OneBus.API.Controllers
{
[NonController]
[Route("api/v1/vehicles")]
[ApiController]
[Produces("application/json")]
Expand Down
1 change: 1 addition & 0 deletions OneBus.API/Controllers/VehicleOperationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace OneBus.API.Controllers
{
[NonController]
[Route("api/v1/vehiclesOperations")]
[ApiController]
[Produces("application/json")]
Expand Down
5 changes: 2 additions & 3 deletions OneBus.API/Extensions/ResultExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using OneBus.Domain.Commons.Result;

namespace OneBus.API.Extensions
Expand All @@ -12,7 +11,7 @@ public static IActionResult ToActionResult<T>(this Result<T> result)
{
SuccessResult<T> => new OkObjectResult(result),
ConflictResult<T> => new ConflictObjectResult(result),
NotFound<T> => new NotFoundObjectResult(result),
NotFoundResult<T> => new NotFoundObjectResult(result),
ErrorResult<T> => new BadRequestObjectResult(result),
InvalidResult<T> => new UnprocessableEntityObjectResult(result),
_ => new StatusCodeResult(StatusCodes.Status500InternalServerError)
Expand Down
8 changes: 7 additions & 1 deletion OneBus.Application/Interfaces/Services/IUserService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
using OneBus.Application.DTOs.User;
using OneBus.Application.DTOs.Login;
using OneBus.Application.DTOs.User;
using OneBus.Domain.Commons.Result;
using OneBus.Domain.Entities;
using OneBus.Domain.Filters;
using OneBus.Domain.Models;

namespace OneBus.Application.Interfaces.Services
{
public interface IUserService :
IBaseService<User, CreateUserDTO, ReadUserDTO, UpdateUserDTO, BaseFilter>
{
public Task<Result<TokenModel>> LoginAsync(
LoginDTO loginDTO,
CancellationToken cancellationToken = default);
}
}
19 changes: 17 additions & 2 deletions OneBus.Application/Services/TokenService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,26 @@ public TokenModel Generate(ReadUserDTO user)
DateTime createdAt = DateTime.UtcNow;
DateTime expiresAt = createdAt.AddDays(_tokenSettings.DaysUntilExpires);

return new TokenModel(Create(
string token = Create(
key: Encoding.ASCII.GetBytes(_tokenSettings.Key),
identity,
createdAt,
expiresAt)
expiresAt);

string refreshToken = Create(
key: Encoding.ASCII.GetBytes(_tokenSettings.Key),
identity,
createdAt,
createdAt.AddDays(_tokenSettings.DaysUntilExpires * 2));

return new TokenModel(
user.Id!.Value,
user.Name,
user.Email,
token,
createdAt,
expiresAt,
refreshToken
);
}

Expand Down
39 changes: 35 additions & 4 deletions OneBus.Application/Services/UserService.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
using FluentValidation;
using Mapster;
using OneBus.Application.DTOs.Login;
using OneBus.Application.DTOs.User;
using OneBus.Application.Extensions;
using OneBus.Application.Interfaces.Services;
using OneBus.Domain.Commons;
using OneBus.Domain.Commons.Result;
using OneBus.Domain.Entities;
using OneBus.Domain.Filters;
using OneBus.Domain.Interfaces.Repositories;
using OneBus.Domain.Models;

namespace OneBus.Application.Services
{
public class UserService : BaseService<User, CreateUserDTO, ReadUserDTO, UpdateUserDTO, BaseFilter>,
IUserService
{
private readonly ITokenService _tokenService;

public UserService(
IBaseRepository<User, BaseFilter> baseRepository,
IValidator<CreateUserDTO> createValidator,
IValidator<UpdateUserDTO> updateValidator)
: base(baseRepository, createValidator, updateValidator)
IUserRepository userRepository,
ITokenService tokenService,
IValidator<CreateUserDTO> createValidator,
IValidator<UpdateUserDTO> updateValidator)
: base(userRepository, createValidator, updateValidator)
{
_tokenService = tokenService;
}

public async Task<Result<TokenModel>> LoginAsync(
LoginDTO loginDTO, CancellationToken cancellationToken = default)
{
if (loginDTO is null)
return ErrorResult<TokenModel>.Create(ErrorUtils.InvalidParameter("Login"));

var user = await _baseReadOnlyRepository.GetOneAsync(c => c.Email.ToLower() == loginDTO.Email.ToLower(),
cancellationToken: cancellationToken);

if (user is null)
return NotFoundResult<TokenModel>.Create(ErrorUtils.EntityNotFound(nameof(loginDTO.Email)));

if (!user.Password.Verify(loginDTO.Password, user.Salt))
return InvalidResult<TokenModel>.Create(ErrorUtils.InvalidParameter("Senha"));

var readUserDTO = user.Adapt<ReadUserDTO>();

var tokenModel = _tokenService.Generate(readUserDTO);
return SuccessResult<TokenModel>.Create(tokenModel);
}

protected override void UpdateFields(User entity, UpdateUserDTO updateDTO)
Expand Down
13 changes: 11 additions & 2 deletions OneBus.Domain/Commons/ErrorUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,18 @@ public static class ErrorUtils
/// Registro não encontrado.
/// </summary>
/// <returns></returns>
public static Error EntityNotFound()
public static Error EntityNotFound(string field = "id")
{
return new Error("Registro não encontrado.", "id");
return new Error("Registro não encontrado.", field);
}

/// <summary>
/// Valor inválido.
/// </summary>
/// <returns></returns>
public static Error InvalidParameter(string field = "id")
{
return new Error("Valor inválido.", field);
}

/// <summary>
Expand Down
30 changes: 23 additions & 7 deletions OneBus.Domain/Interfaces/Repositories/IBaseReadOnlyRepository.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using OneBus.Domain.Commons;
using OneBus.Domain.Entities;
using OneBus.Domain.Filters;
using System.Linq.Expressions;

namespace OneBus.Domain.Interfaces.Repositories
{
Expand All @@ -9,23 +10,38 @@ public interface IBaseReadOnlyRepository<TEntity, TFilter>
where TFilter : BaseFilter
{
Task<TEntity?> GetOneAsync(
Predicate<TEntity> predicate,
DbQueryOptions? dbQueryOptions = null,
Expression<Func<TEntity, bool>> expression,
DbQueryOptions? dbQueryOptions = null,
CancellationToken cancellationToken = default);

Task<IEnumerable<TEntity>> GetManyAsync(
Predicate<TEntity> predicate,
DbQueryOptions? dbQueryOptions = null,
Expression<Func<TEntity, bool>> expression,
DbQueryOptions? dbQueryOptions = null,
CancellationToken cancellationToken = default);

Task<IEnumerable<TEntity>> GetManyAsync(
TFilter filter,
DbQueryOptions? dbQueryOptions = null,
CancellationToken cancellationToken = default);

Task<IEnumerable<TEntity>> GetPaginedAsync(
TFilter filter,
DbQueryOptions? dbQueryOptions = null,
DbQueryOptions? dbQueryOptions = null,
CancellationToken cancellationToken = default);

Task<long> LongCountAsync(
TFilter filter,
DbQueryOptions? dbQueryOptions = null,
CancellationToken cancellationToken = default);

Task<long> LongCountAsync(
TFilter filter,
DbQueryOptions? dbQueryOptions = null,
Expression<Func<TEntity, bool>> expression,
DbQueryOptions? dbQueryOptions = null,
CancellationToken cancellationToken = default);

Task<bool> AnyAsync(
Expression<Func<TEntity, bool>> expression,
DbQueryOptions? dbQueryOptions = null,
CancellationToken cancellationToken = default);
}
}
42 changes: 40 additions & 2 deletions OneBus.Domain/Models/TokenModel.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,50 @@
namespace OneBus.Domain.Models
using System.Reflection;

namespace OneBus.Domain.Models
{
public class TokenModel
{
public TokenModel(string token)
public TokenModel(
long userId,
string name,
string email,
string token,
DateTime created,
DateTime expiration,
string refreshToken)
{
Name = name;
Email = email;
Token = token;
UserId = userId;
Created = created;
Expiration = expiration;
RefreshToken = refreshToken;
}

public long UserId { get; }

public string Email { get; }

public string Name { get; }

public string Token { get; }

public string RefreshToken { get; }

public string Version
{
get
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
return $"{version?.Major}.{version?.Minor}.{version?.Build}";
}
}

public DateTime Created { get; }

public DateTime Expiration { get; }

public double DurationInSeconds { get { return (Expiration - Created).TotalSeconds; } }
}
}
Loading