mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-10-14 17:54:42 +00:00
Refactor comments and update documentation for backend and pipeline (#246)
* Remove restating comments in backend * Remove restating comments in frontend stories * Remove restating comments in pipeline * Remove restating comments in pipeline scripts * Restore location-clear comment in BreweryRepository * Remove stale/restating comments found in second pass * Restore mock curated-data branch comment in main.cc * Remove more restating/filler comments in backend * Document account-management endpoints, compose files, and env generator * Squashed commit of the following: commit a8a5b8abd9969859c695092fae6990d03b2135ed Author: Aaron Po <aaron.po97@gmail.com> Date: Sun Aug 23 23:53:56 2026 -0400 Add reverse geocoding for brewery street addresses (#245) * Add geocoding to seed application, remove postal codes from generator Update comments * Update pipeline class diagram for OpenAI generator * Update pipeline activity diagram for brewery coordinates and OpenAI backend * Add mailpit, production, db-only, and minimal environments to deployment diagram * Fix authentication flow diagram for Identity migration * Add account-management flows to authentication diagram * Fix database schema diagram note referencing removed AuthRepository
This commit is contained in:
@@ -39,7 +39,6 @@ public class JwtAuthenticationHandler(
|
||||
if (string.IsNullOrWhiteSpace(secret))
|
||||
return AuthenticateResult.Fail("JWT secret is not configured");
|
||||
|
||||
// Check if Authorization header exists
|
||||
if (!Request.Headers.TryGetValue("Authorization", out StringValues authHeaderValue))
|
||||
return AuthenticateResult.Fail("Authorization header is missing");
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ namespace API.Core.Controllers;
|
||||
/// </remarks>
|
||||
[ApiController]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
[Route("error")] // required
|
||||
[Route("error")]
|
||||
public class NotFoundController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a 404 response for any request that did not match a route.
|
||||
/// </summary>
|
||||
[HttpGet("404")] //required
|
||||
[HttpGet("404")]
|
||||
public IActionResult Handle404()
|
||||
{
|
||||
return NotFound(new { message = "Route not found." });
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// API.Core/Filters/GlobalExceptionFilter.cs
|
||||
|
||||
using Domain.Exceptions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
@@ -61,7 +61,6 @@ builder.Services.AddSwaggerGen(options =>
|
||||
});
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
// Add FluentValidation
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
|
||||
builder.Services.AddValidatorsFromAssembly(typeof(BreweryController).Assembly);
|
||||
builder.Services.AddValidatorsFromAssembly(typeof(UserController).Assembly);
|
||||
@@ -80,7 +79,6 @@ builder.Services.AddMediatR(cfg =>
|
||||
cfg.AddOpenBehavior(typeof(ValidationBehavior<,>));
|
||||
});
|
||||
|
||||
// Add health checks
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
// Configure logging for container output
|
||||
@@ -89,8 +87,6 @@ builder.Logging.AddConsole();
|
||||
if (!builder.Environment.IsProduction())
|
||||
builder.Logging.AddDebug();
|
||||
|
||||
// Configure Dependency Injection -------------------------------------------------------------------------------------
|
||||
|
||||
builder.Services.AddInfrastructureSql();
|
||||
|
||||
builder.Services.AddFeaturesBreweries();
|
||||
@@ -101,10 +97,8 @@ builder.Services.AddFeaturesEmails();
|
||||
// ITokenInfrastructure is registered here because JwtAuthenticationHandler depends on it directly.
|
||||
builder.Services.AddScoped<ITokenInfrastructure, JwtInfrastructure>();
|
||||
|
||||
// Register the exception filter
|
||||
builder.Services.AddScoped<GlobalExceptionFilter>();
|
||||
|
||||
// Configure JWT Authentication
|
||||
builder
|
||||
.Services.AddAuthentication("JWT")
|
||||
.AddScheme<JwtAuthenticationOptions, JwtAuthenticationHandler>("JWT", options => { });
|
||||
@@ -128,7 +122,6 @@ app.MapHealthChecks("/health");
|
||||
app.MapControllers();
|
||||
app.MapFallbackToController("Handle404", "NotFound");
|
||||
|
||||
// Graceful shutdown handling
|
||||
IHostApplicationLifetime lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
|
||||
lifetime.ApplicationStopping.Register(() =>
|
||||
{
|
||||
|
||||
@@ -264,7 +264,6 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
public async Task WhenISubmitALoginRequestUsingAgetRequest()
|
||||
{
|
||||
HttpClient client = GetClient();
|
||||
// testing GET
|
||||
HttpRequestMessage requestMessage = new(HttpMethod.Get, "/api/auth/login")
|
||||
{
|
||||
Content = new StringContent("{}", Encoding.UTF8, "application/json"),
|
||||
@@ -703,7 +702,6 @@ public class AuthSteps(ScenarioContext scenario)
|
||||
[Given("I have an access token signed with the wrong secret")]
|
||||
public void GivenIHaveAnAccessTokenSignedWithTheWrongSecret()
|
||||
{
|
||||
// Create a token with a different secret
|
||||
scenario["accessToken"] =
|
||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ public class TestApiFactory : WebApplicationFactory<Program>
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
// Replace the real email provider with mock for testing
|
||||
ServiceDescriptor? emailProviderDescriptor = services.SingleOrDefault(d =>
|
||||
d.ServiceType == typeof(IEmailProvider)
|
||||
);
|
||||
@@ -25,7 +24,6 @@ public class TestApiFactory : WebApplicationFactory<Program>
|
||||
|
||||
services.AddScoped<IEmailProvider, MockEmailProvider>();
|
||||
|
||||
// Replace the real email dispatcher with mock for testing
|
||||
ServiceDescriptor? emailDispatcherDescriptor = services.SingleOrDefault(d =>
|
||||
d.ServiceType == typeof(IEmailDispatcher)
|
||||
);
|
||||
|
||||
@@ -11,8 +11,6 @@ public class ConflictException(string message) : Exception(message);
|
||||
/// </summary>
|
||||
public class NotFoundException(string message) : Exception(message);
|
||||
|
||||
// Domain.Exceptions/UnauthorizedException.cs
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when authentication fails or is required. Maps to HTTP 401 Unauthorized.
|
||||
/// </summary>
|
||||
|
||||
@@ -62,12 +62,10 @@ public class TokenServiceRefreshTests
|
||||
|
||||
ApplicationUser user = new() { Id = userId, UserName = username };
|
||||
|
||||
// Mock the validation of refresh token
|
||||
_tokenInfraMock
|
||||
.Setup(x => x.ValidateJwtAsync(refreshToken, It.IsAny<string>()))
|
||||
.ReturnsAsync(principal);
|
||||
|
||||
// Mock the generation of new tokens
|
||||
_tokenInfraMock
|
||||
.Setup(x => x.GenerateJwt(userId, username, It.IsAny<DateTime>(), It.IsAny<string>()))
|
||||
.Returns(
|
||||
|
||||
@@ -66,7 +66,6 @@ public class RegisterUserHandler(
|
||||
{
|
||||
await Console.Error.WriteLineAsync(ex.Message);
|
||||
Console.WriteLine("Could not send email.");
|
||||
// ignored
|
||||
}
|
||||
|
||||
return new RegistrationPayload(user.Id, user.UserName, refreshToken, accessToken, emailSent);
|
||||
|
||||
@@ -20,7 +20,6 @@ public class TokenService : ITokenService
|
||||
private readonly ITokenInfrastructure _tokenInfrastructure;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="TokenService" /> class.</summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when any of the <c>ACCESS_TOKEN_SECRET</c>, <c>REFRESH_TOKEN_SECRET</c>, or
|
||||
/// <c>CONFIRMATION_TOKEN_SECRET</c> configuration values are not set.
|
||||
|
||||
@@ -3,9 +3,6 @@ using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Commands.UpdateUser;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="UpdateUserCommand" />.
|
||||
/// </summary>
|
||||
public class UpdateUserHandler(IUserAccountRepository repository)
|
||||
: IRequestHandler<UpdateUserCommand>
|
||||
{
|
||||
|
||||
@@ -4,9 +4,6 @@ using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Queries.GetAllUsers;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="GetAllUsersQuery" />.
|
||||
/// </summary>
|
||||
public class GetAllUsersHandler(IUserAccountRepository repository)
|
||||
: IRequestHandler<GetAllUsersQuery, IEnumerable<UserAccount>>
|
||||
{
|
||||
|
||||
@@ -5,9 +5,6 @@ using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Queries.GetUserById;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="GetUserByIdQuery" />.
|
||||
/// </summary>
|
||||
public class GetUserByIdHandler(IUserAccountRepository repository)
|
||||
: IRequestHandler<GetUserByIdQuery, UserAccount>
|
||||
{
|
||||
|
||||
@@ -3,7 +3,4 @@ using MediatR;
|
||||
|
||||
namespace Features.UserManagement.Queries.GetUserById;
|
||||
|
||||
/// <summary>
|
||||
/// Requests the user account with the given ID.
|
||||
/// </summary>
|
||||
public record GetUserByIdQuery(Guid UserAccountId) : IRequest<UserAccount>;
|
||||
|
||||
@@ -93,14 +93,12 @@ public class SmtpEmailProvider : IEmailProvider
|
||||
|
||||
try
|
||||
{
|
||||
// Determine the SecureSocketOptions based on SSL setting
|
||||
SecureSocketOptions secureSocketOptions = _useSsl
|
||||
? SecureSocketOptions.StartTls
|
||||
: SecureSocketOptions.None;
|
||||
|
||||
await client.ConnectAsync(_host, _port, secureSocketOptions);
|
||||
|
||||
// Authenticate if credentials are provided
|
||||
if (!string.IsNullOrEmpty(_username) && !string.IsNullOrEmpty(_password))
|
||||
await client.AuthenticateAsync(_username, _password);
|
||||
|
||||
|
||||
@@ -12,9 +12,6 @@ public interface ITokenInfrastructure
|
||||
/// </summary>
|
||||
string GenerateJwt(Guid userId, string username, DateTime expiry, string secret);
|
||||
|
||||
/// <summary>
|
||||
/// Validates a JWT and returns the resulting claims principal.
|
||||
/// </summary>
|
||||
/// <exception cref="Domain.Exceptions.UnauthorizedException">Thrown when the token is invalid, expired, or fails validation.</exception>
|
||||
Task<ClaimsPrincipal> ValidateJwtAsync(string token, string secret);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ public class DefaultSqlConnectionFactory(IConfiguration configuration) : ISqlCon
|
||||
/// <exception cref="InvalidOperationException">Thrown when no connection string can be resolved from any source.</exception>
|
||||
private static string GetConnectionString(IConfiguration configuration)
|
||||
{
|
||||
// Check for full connection string first
|
||||
string? fullConnectionString = configuration["DB_CONNECTION_STRING"];
|
||||
if (!string.IsNullOrEmpty(fullConnectionString))
|
||||
return fullConnectionString;
|
||||
|
||||
@@ -38,9 +38,6 @@ public static class SqlConnectionStringHelper
|
||||
return builder.ConnectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a connection string to the master database using configuration.
|
||||
/// </summary>
|
||||
public static string BuildMasterConnectionString(IConfiguration configuration)
|
||||
{
|
||||
return BuildConnectionString(configuration, "master");
|
||||
|
||||
@@ -5,10 +5,8 @@ namespace Shared.Contracts;
|
||||
/// </summary>
|
||||
public record ResponseBody<T>
|
||||
{
|
||||
/// <summary>Gets the human-readable message describing the response.</summary>
|
||||
public required string Message { get; init; }
|
||||
|
||||
/// <summary>Gets the response's data payload.</summary>
|
||||
public required T Payload { get; init; }
|
||||
}
|
||||
|
||||
@@ -17,6 +15,5 @@ public record ResponseBody<T>
|
||||
/// </summary>
|
||||
public record ResponseBody
|
||||
{
|
||||
/// <summary>Gets the human-readable message describing the response.</summary>
|
||||
public required string Message { get; init; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user