Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements on application architecture and code design #30

Merged
merged 1 commit into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ services:
container_name: sqlserver
networks:
- backend-network

mssqltools:
image: mcr.microsoft.com/mssql-tools
depends_on:
- sqlserver
volumes:
- ./scripts/initial-script.sql:/tmp/initial-script.sql
- ./scripts/init-sqlserver.sh:/tmp/init-sqlserver.sh
command: /bin/bash ./tmp/init-sqlserver.sh
networks:
- backend-network

elasticsearch:
container_name: elasticsearch
Expand Down
1 change: 1 addition & 0 deletions scripts/init-sqlserver.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/opt/mssql-tools/bin/sqlcmd -S sqlserver -U sa -P SqlServer2019! -d master -i /tmp/initial-script.sql
2 changes: 2 additions & 0 deletions scripts/initial-script.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--script used to create the database used in integration tests, if the database name changes, it is necessary to update appsettings.Test.json
CREATE DATABASE OrionTests;
34 changes: 19 additions & 15 deletions src/Orion.Api/Configuration/HealthCheckConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ namespace Orion.Api.Configuration;

public static class HealthCheckConfiguration
{
private static readonly string[] DatabaseTags =
[
"database"
];
private static readonly string[] ElasticSearchTags =
[
"elasticsearch",
"kibana"
];

public static void AddApplicationHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks().AddSqlServer(configuration["ConnectionStrings:OrionDatabase"],
tags: DatabaseTags);

services.AddHealthChecks().AddElasticsearch(configuration["ElasticConfiguration:Uri"],
tags: ElasticSearchTags);
}

public static void ConfigureHealthCheck(this IApplicationBuilder app)
{
app.UseHealthChecks("/health", new HealthCheckOptions
Expand All @@ -13,19 +32,4 @@ public static void ConfigureHealthCheck(this IApplicationBuilder app)
});
}

public static void AddApplicationHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
services.AddHealthChecks().AddSqlServer(configuration["ConnectionStrings:OrionDatabase"],
tags: new[]
{
"database"
});

services.AddHealthChecks().AddElasticsearch(configuration["ElasticConfiguration:Uri"],
tags: new[]
{
"elasticsearch",
"kibana"
});
}
}
8 changes: 2 additions & 6 deletions src/Orion.Api/Controllers/Base/ApiController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@
namespace Orion.Api.Controllers.Base;

[ApiController]
public abstract class ApiController : ControllerBase
public abstract class ApiController(IMediator mediator) : ControllerBase
{
protected readonly IMediator Mediator;
protected ApiController(IMediator mediator)
{
Mediator = mediator;
}
protected readonly IMediator Mediator = mediator;

protected CreatedResult Created(object entity)
{
Expand Down
11 changes: 2 additions & 9 deletions src/Orion.Api/Controllers/V1/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,8 @@ namespace Orion.Api.Controllers.V1;
[ApiVersion(1.0)]
[Route("api/[controller]")]
[AllowAnonymous]
public class AuthController : ApiController
public class AuthController(IMediator mediator, IConfiguration configuration) : ApiController(mediator)
{
private readonly IConfiguration _configuration;

public AuthController(IMediator mediator, IConfiguration configuration) : base(mediator)
{
_configuration = configuration;
}

[HttpPost("Login")]
[SwaggerResponse((int)HttpStatusCode.OK,"A success reponse with a Token, Refresh Token and expiration date" ,typeof(LoginWithCredentialsResponse))]
[SwaggerResponse((int)HttpStatusCode.BadRequest,"A error response with the error description", typeof(ExceptionResponse))]
Expand All @@ -51,7 +44,7 @@ private IActionResult AuthorizeUser(LoginWithCredentialsResponse loginWithCreden
{
if (loginWithCredentialsResponse != null)
{
var (token, validTo) = AuthenticationConfiguration.CreateToken(loginWithCredentialsResponse, _configuration);
var (token, validTo) = AuthenticationConfiguration.CreateToken(loginWithCredentialsResponse, configuration);

return Ok(
new UserApiTokenModel
Expand Down
7 changes: 1 addition & 6 deletions src/Orion.Api/Controllers/V1/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,8 @@ namespace Orion.Api.Controllers.V1;
[ApiVersion(1.0)]
[Route("api/[controller]")]
[AuthorizeFor(Roles.Admin)]
public class UsersController : ApiController
public class UsersController(IMediator mediator) : ApiController(mediator)
{
public UsersController(IMediator mediator) : base(mediator)
{

}

[HttpGet]
[SwaggerResponse((int)HttpStatusCode.OK,"A success response with a list of Users paginated", typeof(PagedList<UserGetPaginatedResponse>))]
[SwaggerResponse((int)HttpStatusCode.Unauthorized)]
Expand Down
21 changes: 5 additions & 16 deletions src/Orion.Api/Middleware/OrionMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,15 @@

namespace Orion.Api.Middleware;

public class OrionMiddleware
public class OrionMiddleware(RequestDelegate next, ILogger<OrionMiddleware> logger, IHostEnvironment env)
{
private readonly RequestDelegate _next;
private readonly ILogger<OrionMiddleware> _logger;
private readonly IHostEnvironment _env;

public OrionMiddleware(RequestDelegate next, ILogger<OrionMiddleware> logger, IHostEnvironment env)
{
_env = env;
_next = next;
_logger = logger;
}

public async Task Invoke(HttpContext context)
{
try
{
if (_next != null)
if (next != null)
{
await _next(context);
await next(context);
}
}
catch (Exception ex)
Expand All @@ -38,7 +27,7 @@ private async Task HandleExceptionAsync(HttpContext context, Exception exception

var errorResponse = new ExceptionResponse(exception.Message, NotificationType.Error);

if (exception is not BusinessException && _env.IsDevelopment())
if (exception is not BusinessException && env.IsDevelopment())
{
errorResponse = new ExceptionResponse(exception.Message, NotificationType.Error);
}
Expand Down Expand Up @@ -73,7 +62,7 @@ private async Task ProccessResponseAsync(HttpContext context, HttpStatusCode sta

if (statusCode == HttpStatusCode.InternalServerError)
foreach (var error in errorResponse.Errors)
_logger.LogError(exception, "Internal Server Error: {message}", error);
logger.LogError(exception, "Internal Server Error: {message}", error);

context.Response.StatusCode = (int)statusCode;
context.Response.ContentType = "application/json";
Expand Down
8 changes: 4 additions & 4 deletions src/Orion.Api/Orion.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="7.1.0" />
<PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="7.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="7.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="7.1.0" />
<PackageReference Include="Asp.Versioning.Mvc" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}
},
"ConnectionStrings": {
"OrionDatabase": "Data Source=localhost,1433;Initial Catalog=Orion;User ID=Orion;Password=123;TrustServerCertificate=True"
"OrionDatabase": "Data Source=localhost,1434;Initial Catalog=OrionDev;User ID=sa;Password=SqlServer2019!;TrustServerCertificate=True"
},
"JwtOptions": {
"SymmetricSecurityKey": "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p",
Expand Down
2 changes: 1 addition & 1 deletion src/Orion.Api/appsettings.Production.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}
},
"ConnectionStrings": {
"OrionDatabase": "Data Source=10.0.0.90,1433;Initial Catalog=Orion;User ID=Orion;Password=123;TrustServerCertificate=True"
"OrionDatabase": "Data Source=sqlserver;Initial Catalog=OrionDev;User ID=sa;Password=SqlServer2019!;TrustServerCertificate=True"
},
"JwtOptions": {
"SymmetricSecurityKey": "5cCI6IkpXVCJ9.eyJlbWFpbCI6InZhbmRlcmxhbi5nc0BnbWFpbC5jb20iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJhZG1p",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,11 @@

namespace Orion.Application.Core.Commands.LoginWithCredentials;

public class LoginWithCredentialsRequestHandler : IRequestHandler<LoginWithCredentialsRequest, LoginWithCredentialsResponse>
public class LoginWithCredentialsRequestHandler(IUserService userService) : IRequestHandler<LoginWithCredentialsRequest, LoginWithCredentialsResponse>
{
private readonly IUserService _userService;

public LoginWithCredentialsRequestHandler(IUserService userService)
{
_userService = userService;
}

public async Task<LoginWithCredentialsResponse> Handle(LoginWithCredentialsRequest request, CancellationToken cancellationToken)
{
var (user, refreshToken) = await _userService.SignInWithCredentialsAsync(request.Email, request.Password);
var (user, refreshToken) = await userService.SignInWithCredentialsAsync(request.Email, request.Password);

return new LoginWithCredentialsResponse
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,12 @@

namespace Orion.Application.Core.Commands.LoginWithRefreshToken;

public class LoginWithRefreshTokenRequestHandler : IRequestHandler<LoginWithRefreshTokenRequest, LoginWithRefreshTokenResponse>
public class LoginWithRefreshTokenRequestHandler(IUserService userService)
: IRequestHandler<LoginWithRefreshTokenRequest, LoginWithRefreshTokenResponse>
{
private readonly IUserService _userService;

public LoginWithRefreshTokenRequestHandler(IUserService userService)
{
_userService = userService;
}

public async Task<LoginWithRefreshTokenResponse> Handle(LoginWithRefreshTokenRequest request, CancellationToken cancellationToken)
{
var (user, refreshToken) = await _userService.SignInWithRefreshTokenAsync(request.RefreshToken, request.Token);
var (user, refreshToken) = await userService.SignInWithRefreshTokenAsync(request.RefreshToken, request.Token);

return new LoginWithRefreshTokenResponse
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,17 @@

namespace Orion.Application.Core.Commands.UserChangePassword
{
public class UserUpdatePasswordRequestHandler : IRequestHandler<UserChangePasswordRequest, Unit>
public class UserUpdatePasswordRequestHandler(
IUserService userService,
ICurrentUser currentUser,
IMediator mediator)
: IRequestHandler<UserChangePasswordRequest, Unit>
{
private readonly IUserService _userService;
private readonly ICurrentUser _currentUser;
private readonly IMediator _mediator;

public UserUpdatePasswordRequestHandler(IUserService userService, ICurrentUser currentUser, IMediator mediator)
{
_userService = userService;
_currentUser = currentUser;
_mediator = mediator;
}

public async Task<Unit> Handle(UserChangePasswordRequest request, CancellationToken cancellationToken)
{
await _userService.ChangePasswordAsync(_currentUser.Id, request.CurrentPassword, request.NewPassword);
await userService.ChangePasswordAsync(currentUser.Id, request.CurrentPassword, request.NewPassword);

await _mediator.Publish(new UserPasswordChangedNotification(), cancellationToken);
await mediator.Publish(new UserPasswordChangedNotification(), cancellationToken);

return Unit.Value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,14 @@

namespace Orion.Application.Core.Commands.UserCreate;

public class UserCreateRequestHandler : IRequestHandler<UserCreateRequest, UserCreateResponse>
public class UserCreateRequestHandler(IUserService userService, IMediator mediator)
: IRequestHandler<UserCreateRequest, UserCreateResponse>
{
private readonly IUserService _userService;
private readonly IMediator _mediator;

public UserCreateRequestHandler(IUserService userService, IMediator mediator)
{
_userService = userService;
_mediator = mediator;
}

public async Task<UserCreateResponse> Handle(UserCreateRequest request, CancellationToken cancellationToken)
{
var userCreated = await _userService.AddAsync(request);
var userCreated = await userService.AddAsync(request);

await _mediator.Publish(new UserCreatedNotification(userCreated), cancellationToken);
await mediator.Publish(new UserCreatedNotification(userCreated), cancellationToken);

return (UserCreateResponse)userCreated;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@

namespace Orion.Application.Core.Commands.UserDelete;

public class UserDeleteRequest : IRequest <Unit>
public class UserDeleteRequest(string publicId) : IRequest<Unit>
{
public UserDeleteRequest(string publicId)
{
PublicId = publicId;
}

public string PublicId { get; private set; }
public string PublicId { get; private set; } = publicId;
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,11 @@

namespace Orion.Application.Core.Commands.UserDelete;

public class UserDeleteRequestHandler : IRequestHandler<UserDeleteRequest, Unit>
public class UserDeleteRequestHandler(IUserService userService) : IRequestHandler<UserDeleteRequest, Unit>
{
private readonly IUserService _userService;

public UserDeleteRequestHandler(IUserService userService)
{
_userService = userService;
}

public async Task<Unit> Handle(UserDeleteRequest request, CancellationToken cancellationToken)
{
await _userService.DeleteAsync(request.PublicId);
await userService.DeleteAsync(request.PublicId);

return Unit.Value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,11 @@

namespace Orion.Application.Core.Commands.UserUpdate;

public class UserUpdateRequestHandler : IRequestHandler<UserUpdateRequest, Unit>
public class UserUpdateRequestHandler(IUserService userService) : IRequestHandler<UserUpdateRequest, Unit>
{
private readonly IUserService _userService;

public UserUpdateRequestHandler(IUserService userService)
{
_userService = userService;
}

public async Task<Unit> Handle(UserUpdateRequest request, CancellationToken cancellationToken)
{
await _userService.UpdateAsync(request);
await userService.UpdateAsync(request);

return Unit.Value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,8 @@

namespace Orion.Application.Core.Notifications.UserCreated
{
public class UserCreatedNotification : INotification
public class UserCreatedNotification(User user) : INotification
{
public UserCreatedNotification(User user)
{
Entity = user;
}

public User Entity { get; set; }
public User Entity { get; set; } = user;
}
}
Loading
Loading