Merged PR 14: Style: C# 12 Primary constructor
Ce pull request change les constructeurs pour utiliser les constructeur primaires qui ont été ajouté dans C# 12 en Novembre 2023 Ceci nous aideras a garder une codebase propre et non verbose
This commit is contained in:
@@ -5,19 +5,12 @@ using Hutopy.Application.Common.Security;
|
|||||||
|
|
||||||
namespace Hutopy.Application.Common.Behaviours;
|
namespace Hutopy.Application.Common.Behaviours;
|
||||||
|
|
||||||
public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
|
public class AuthorizationBehaviour<TRequest, TResponse>(
|
||||||
|
IUser user,
|
||||||
|
IIdentityService identityService)
|
||||||
|
: IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
private readonly IUser _user;
|
|
||||||
private readonly IIdentityService _identityService;
|
|
||||||
|
|
||||||
public AuthorizationBehaviour(
|
|
||||||
IUser user,
|
|
||||||
IIdentityService identityService)
|
|
||||||
{
|
|
||||||
_user = user;
|
|
||||||
_identityService = identityService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var authorizeAttributes = request.GetType().GetCustomAttributes<AuthorizeAttribute>();
|
var authorizeAttributes = request.GetType().GetCustomAttributes<AuthorizeAttribute>();
|
||||||
@@ -25,7 +18,7 @@ public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRe
|
|||||||
if (authorizeAttributes.Any())
|
if (authorizeAttributes.Any())
|
||||||
{
|
{
|
||||||
// Must be authenticated user
|
// Must be authenticated user
|
||||||
if (_user.Id == null)
|
if (user.Id == null)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new UnauthorizedAccessException();
|
||||||
}
|
}
|
||||||
@@ -41,7 +34,7 @@ public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRe
|
|||||||
{
|
{
|
||||||
foreach (var role in roles)
|
foreach (var role in roles)
|
||||||
{
|
{
|
||||||
var isInRole = await _identityService.IsInRoleAsync(_user.Id, role.Trim());
|
var isInRole = await identityService.IsInRoleAsync(user.Id, role.Trim());
|
||||||
if (isInRole)
|
if (isInRole)
|
||||||
{
|
{
|
||||||
authorized = true;
|
authorized = true;
|
||||||
@@ -63,7 +56,7 @@ public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRe
|
|||||||
{
|
{
|
||||||
foreach (var policy in authorizeAttributesWithPolicies.Select(a => a.Policy))
|
foreach (var policy in authorizeAttributesWithPolicies.Select(a => a.Policy))
|
||||||
{
|
{
|
||||||
var authorized = await _identityService.AuthorizeAsync(_user.Id, policy);
|
var authorized = await identityService.AuthorizeAsync(user.Id, policy);
|
||||||
|
|
||||||
if (!authorized)
|
if (!authorized)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,28 +4,24 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Hutopy.Application.Common.Behaviours;
|
namespace Hutopy.Application.Common.Behaviours;
|
||||||
|
|
||||||
public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest> where TRequest : notnull
|
public class LoggingBehaviour<TRequest>(
|
||||||
|
ILogger<TRequest> logger,
|
||||||
|
IUser user,
|
||||||
|
IIdentityService identityService)
|
||||||
|
: IRequestPreProcessor<TRequest>
|
||||||
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger = logger;
|
||||||
private readonly IUser _user;
|
|
||||||
private readonly IIdentityService _identityService;
|
|
||||||
|
|
||||||
public LoggingBehaviour(ILogger<TRequest> logger, IUser user, IIdentityService identityService)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_user = user;
|
|
||||||
_identityService = identityService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Process(TRequest request, CancellationToken cancellationToken)
|
public async Task Process(TRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var requestName = typeof(TRequest).Name;
|
var requestName = typeof(TRequest).Name;
|
||||||
var userId = _user.Id ?? string.Empty;
|
var userId = user.Id ?? string.Empty;
|
||||||
string? userName = string.Empty;
|
string? userName = string.Empty;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(userId))
|
if (!string.IsNullOrEmpty(userId))
|
||||||
{
|
{
|
||||||
userName = await _identityService.GetUserNameAsync(userId);
|
userName = await identityService.GetUserNameAsync(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Hutopy Request: {Name} {@UserId} {@UserName} {@Request}",
|
_logger.LogInformation("Hutopy Request: {Name} {@UserId} {@UserName} {@Request}",
|
||||||
|
|||||||
@@ -4,24 +4,14 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Hutopy.Application.Common.Behaviours;
|
namespace Hutopy.Application.Common.Behaviours;
|
||||||
|
|
||||||
public class PerformanceBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
|
public class PerformanceBehaviour<TRequest, TResponse>(
|
||||||
|
ILogger<TRequest> logger,
|
||||||
|
IUser user,
|
||||||
|
IIdentityService identityService)
|
||||||
|
: IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
private readonly Stopwatch _timer;
|
private readonly Stopwatch _timer = new();
|
||||||
private readonly ILogger<TRequest> _logger;
|
|
||||||
private readonly IUser _user;
|
|
||||||
private readonly IIdentityService _identityService;
|
|
||||||
|
|
||||||
public PerformanceBehaviour(
|
|
||||||
ILogger<TRequest> logger,
|
|
||||||
IUser user,
|
|
||||||
IIdentityService identityService)
|
|
||||||
{
|
|
||||||
_timer = new Stopwatch();
|
|
||||||
|
|
||||||
_logger = logger;
|
|
||||||
_user = user;
|
|
||||||
_identityService = identityService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -33,20 +23,16 @@ public class PerformanceBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequ
|
|||||||
|
|
||||||
var elapsedMilliseconds = _timer.ElapsedMilliseconds;
|
var elapsedMilliseconds = _timer.ElapsedMilliseconds;
|
||||||
|
|
||||||
if (elapsedMilliseconds > 500)
|
if (elapsedMilliseconds <= 500) return response;
|
||||||
{
|
|
||||||
var requestName = typeof(TRequest).Name;
|
|
||||||
var userId = _user.Id ?? string.Empty;
|
|
||||||
var userName = string.Empty;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(userId))
|
var requestName = typeof(TRequest).Name;
|
||||||
{
|
var userId = user.Id ?? string.Empty;
|
||||||
userName = await _identityService.GetUserNameAsync(userId);
|
var userName = string.Empty;
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogWarning("Hutopy Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {@UserId} {@UserName} {@Request}",
|
if (!string.IsNullOrEmpty(userId)) userName = await identityService.GetUserNameAsync(userId);
|
||||||
requestName, elapsedMilliseconds, userId, userName, request);
|
|
||||||
}
|
logger.LogWarning("Hutopy Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {@UserId} {@UserName} {@Request}",
|
||||||
|
requestName, elapsedMilliseconds, userId, userName, request);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,11 @@
|
|||||||
|
|
||||||
namespace Hutopy.Application.Common.Behaviours;
|
namespace Hutopy.Application.Common.Behaviours;
|
||||||
|
|
||||||
public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
|
public class UnhandledExceptionBehaviour<TRequest, TResponse>(
|
||||||
|
ILogger<TRequest> logger)
|
||||||
|
: IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
private readonly ILogger<TRequest> _logger;
|
|
||||||
|
|
||||||
public UnhandledExceptionBehaviour(ILogger<TRequest> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -21,7 +17,7 @@ public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelineBehavio
|
|||||||
{
|
{
|
||||||
var requestName = typeof(TRequest).Name;
|
var requestName = typeof(TRequest).Name;
|
||||||
|
|
||||||
_logger.LogError(ex, "Hutopy Request: Unhandled Exception for Request {Name} {@Request}", requestName, request);
|
logger.LogError(ex, "Hutopy Request: Unhandled Exception for Request {Name} {@Request}", requestName, request);
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,34 +2,27 @@
|
|||||||
|
|
||||||
namespace Hutopy.Application.Common.Behaviours;
|
namespace Hutopy.Application.Common.Behaviours;
|
||||||
|
|
||||||
public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
public class ValidationBehaviour<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators)
|
||||||
where TRequest : notnull
|
: IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull
|
||||||
{
|
{
|
||||||
private readonly IEnumerable<IValidator<TRequest>> _validators;
|
|
||||||
|
|
||||||
public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
|
|
||||||
{
|
|
||||||
_validators = validators;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_validators.Any())
|
if (!validators.Any()) return await next();
|
||||||
{
|
|
||||||
var context = new ValidationContext<TRequest>(request);
|
|
||||||
|
|
||||||
var validationResults = await Task.WhenAll(
|
var context = new ValidationContext<TRequest>(request);
|
||||||
_validators.Select(v =>
|
|
||||||
v.ValidateAsync(context, cancellationToken)));
|
|
||||||
|
|
||||||
var failures = validationResults
|
var validationResults = await Task.WhenAll(
|
||||||
.Where(r => r.Errors.Any())
|
validators.Select(v =>
|
||||||
.SelectMany(r => r.Errors)
|
v.ValidateAsync(context, cancellationToken)));
|
||||||
.ToList();
|
|
||||||
|
var failures = validationResults
|
||||||
|
.Where(r => r.Errors.Any())
|
||||||
|
.SelectMany(r => r.Errors)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (failures.Count != 0) throw new ValidationException(failures);
|
||||||
|
|
||||||
if (failures.Any())
|
|
||||||
throw new ValidationException(failures);
|
|
||||||
}
|
|
||||||
return await next();
|
return await next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,9 @@
|
|||||||
|
|
||||||
namespace Hutopy.Application.Common.Exceptions;
|
namespace Hutopy.Application.Common.Exceptions;
|
||||||
|
|
||||||
public class ValidationException : Exception
|
public class ValidationException()
|
||||||
|
: Exception("One or more validation failures have occurred.")
|
||||||
{
|
{
|
||||||
public ValidationException()
|
|
||||||
: base("One or more validation failures have occurred.")
|
|
||||||
{
|
|
||||||
Errors = new Dictionary<string, string[]>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValidationException(IEnumerable<ValidationFailure> failures)
|
public ValidationException(IEnumerable<ValidationFailure> failures)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
@@ -18,5 +13,5 @@ public class ValidationException : Exception
|
|||||||
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
|
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<string, string[]> Errors { get; }
|
public IDictionary<string, string[]> Errors { get; } = new Dictionary<string, string[]>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
namespace Hutopy.Application.Common.Models;
|
namespace Hutopy.Application.Common.Models;
|
||||||
|
|
||||||
public class PaginatedList<T>
|
public class PaginatedList<T>(
|
||||||
|
IReadOnlyCollection<T> items,
|
||||||
|
int count,
|
||||||
|
int pageNumber,
|
||||||
|
int pageSize)
|
||||||
{
|
{
|
||||||
public IReadOnlyCollection<T> Items { get; }
|
public IReadOnlyCollection<T> Items { get; } = items;
|
||||||
public int PageNumber { get; }
|
public int PageNumber { get; } = pageNumber;
|
||||||
public int TotalPages { get; }
|
public int TotalPages { get; } = (int)Math.Ceiling(count / (double)pageSize);
|
||||||
public int TotalCount { get; }
|
public int TotalCount { get; } = count;
|
||||||
|
|
||||||
public PaginatedList(IReadOnlyCollection<T> items, int count, int pageNumber, int pageSize)
|
|
||||||
{
|
|
||||||
PageNumber = pageNumber;
|
|
||||||
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
|
|
||||||
TotalCount = count;
|
|
||||||
Items = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasPreviousPage => PageNumber > 1;
|
public bool HasPreviousPage => PageNumber > 1;
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
namespace Hutopy.Application.Common.Models;
|
namespace Hutopy.Application.Common.Models;
|
||||||
|
|
||||||
public class Result
|
public class Result(
|
||||||
|
bool succeeded,
|
||||||
|
IEnumerable<string> errors)
|
||||||
{
|
{
|
||||||
internal Result(bool succeeded, IEnumerable<string> errors)
|
public bool Succeeded { get; init; } = succeeded;
|
||||||
{
|
|
||||||
Succeeded = succeeded;
|
|
||||||
Errors = errors.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Succeeded { get; init; }
|
public string[] Errors { get; init; } = errors.ToArray();
|
||||||
|
|
||||||
public string[] Errors { get; init; }
|
|
||||||
|
|
||||||
public static Result Success()
|
public static Result Success()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the class this attribute is applied to requires authorization.
|
/// Specifies the class this attribute is applied to requires authorization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||||
public class AuthorizeAttribute : Attribute
|
public class AuthorizeAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Hutopy.Application;
|
||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Hutopy.Domain.Entities;
|
|||||||
|
|
||||||
namespace Hutopy.Application.FutureCreators.Commands;
|
namespace Hutopy.Application.FutureCreators.Commands;
|
||||||
|
|
||||||
public record CreateFutureCreatorCommand : IRequest<int>
|
public abstract record CreateFutureCreatorCommand : IRequest<int>
|
||||||
{
|
{
|
||||||
public required string FirstName { get; init; }
|
public required string FirstName { get; init; }
|
||||||
public required string LastName { get; init; }
|
public required string LastName { get; init; }
|
||||||
@@ -13,16 +13,10 @@ public record CreateFutureCreatorCommand : IRequest<int>
|
|||||||
public required string ReasonToJoin { get; init; }
|
public required string ReasonToJoin { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateFuturCreatorCommandHandler : IRequestHandler<CreateFutureCreatorCommand, int>
|
public class CreateFutureCreatorCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<CreateFutureCreatorCommand, int>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
|
|
||||||
public CreateFuturCreatorCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> Handle(CreateFutureCreatorCommand request, CancellationToken cancellationToken)
|
public async Task<int> Handle(CreateFutureCreatorCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = new FutureCreator
|
var entity = new FutureCreator
|
||||||
@@ -35,9 +29,9 @@ public class CreateFuturCreatorCommandHandler : IRequestHandler<CreateFutureCrea
|
|||||||
ReasonToJoin = request.ReasonToJoin,
|
ReasonToJoin = request.ReasonToJoin,
|
||||||
};
|
};
|
||||||
|
|
||||||
_context.FutureCreators.Add(entity);
|
context.FutureCreators.Add(entity);
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
return entity.Id;
|
return entity.Id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
namespace Hutopy.Application.Stripe.Commands;
|
namespace Hutopy.Application.Stripe.Commands;
|
||||||
public record CreateSessionCheckoutCommand : IRequest<string>
|
public abstract record CreateSessionCheckoutCommand : IRequest<string>
|
||||||
{
|
{
|
||||||
public required int Price { get; init; }
|
public required int Price { get; init; }
|
||||||
|
|
||||||
@@ -10,21 +10,16 @@ public record CreateSessionCheckoutCommand : IRequest<string>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class CreateSessionCheckoutCommandHandler : IRequestHandler<CreateSessionCheckoutCommand, string>
|
public class CreateSessionCheckoutCommandHandler(
|
||||||
|
IApplicationDbContext context,
|
||||||
|
IStripeService stripeService)
|
||||||
|
: IRequestHandler<CreateSessionCheckoutCommand, string>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
private readonly IApplicationDbContext _context = context;
|
||||||
private readonly IStripeService _stripeService;
|
|
||||||
|
|
||||||
|
|
||||||
public CreateSessionCheckoutCommandHandler(IApplicationDbContext context, IStripeService stripeService)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_stripeService = stripeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> Handle(CreateSessionCheckoutCommand request, CancellationToken cancellationToken)
|
public async Task<string> Handle(CreateSessionCheckoutCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var stripeSecret = await _stripeService.CreateCheckoutSession(request.Price, request.Currency);
|
var stripeSecret = await stripeService.CreateCheckoutSession(request.Price, request.Currency);
|
||||||
|
|
||||||
return stripeSecret;
|
return stripeSecret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,10 @@ public record CreateTodoItemCommand : IRequest<int>
|
|||||||
public string? Title { get; init; }
|
public string? Title { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateTodoItemCommandHandler : IRequestHandler<CreateTodoItemCommand, int>
|
public class CreateTodoItemCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<CreateTodoItemCommand, int>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public CreateTodoItemCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> Handle(CreateTodoItemCommand request, CancellationToken cancellationToken)
|
public async Task<int> Handle(CreateTodoItemCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = new TodoItem
|
var entity = new TodoItem
|
||||||
@@ -31,9 +26,9 @@ public class CreateTodoItemCommandHandler : IRequestHandler<CreateTodoItemComman
|
|||||||
|
|
||||||
entity.AddDomainEvent(new TodoItemCreatedEvent(entity));
|
entity.AddDomainEvent(new TodoItemCreatedEvent(entity));
|
||||||
|
|
||||||
_context.TodoItems.Add(entity);
|
context.TodoItems.Add(entity);
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
return entity.Id;
|
return entity.Id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,27 +9,22 @@ namespace Hutopy.Application.TodoItems.Commands.DeleteTodoItem;
|
|||||||
[Authorize(Policy = Policies.CanDelete)]
|
[Authorize(Policy = Policies.CanDelete)]
|
||||||
public record DeleteTodoItemCommand(int Id) : IRequest;
|
public record DeleteTodoItemCommand(int Id) : IRequest;
|
||||||
|
|
||||||
public class DeleteTodoItemCommandHandler : IRequestHandler<DeleteTodoItemCommand>
|
public class DeleteTodoItemCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<DeleteTodoItemCommand>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public DeleteTodoItemCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(DeleteTodoItemCommand request, CancellationToken cancellationToken)
|
public async Task Handle(DeleteTodoItemCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = await _context.TodoItems
|
var entity = await context.TodoItems
|
||||||
.FindAsync(new object[] { request.Id }, cancellationToken);
|
.FindAsync(new object[] { request.Id }, cancellationToken);
|
||||||
|
|
||||||
Guard.Against.NotFound(request.Id, entity);
|
Guard.Against.NotFound(request.Id, entity);
|
||||||
|
|
||||||
_context.TodoItems.Remove(entity);
|
context.TodoItems.Remove(entity);
|
||||||
|
|
||||||
entity.AddDomainEvent(new TodoItemDeletedEvent(entity));
|
entity.AddDomainEvent(new TodoItemDeletedEvent(entity));
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,18 +11,13 @@ public record UpdateTodoItemCommand : IRequest
|
|||||||
public bool Done { get; init; }
|
public bool Done { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateTodoItemCommandHandler : IRequestHandler<UpdateTodoItemCommand>
|
public class UpdateTodoItemCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<UpdateTodoItemCommand>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public UpdateTodoItemCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(UpdateTodoItemCommand request, CancellationToken cancellationToken)
|
public async Task Handle(UpdateTodoItemCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = await _context.TodoItems
|
var entity = await context.TodoItems
|
||||||
.FindAsync(new object[] { request.Id }, cancellationToken);
|
.FindAsync(new object[] { request.Id }, cancellationToken);
|
||||||
|
|
||||||
Guard.Against.NotFound(request.Id, entity);
|
Guard.Against.NotFound(request.Id, entity);
|
||||||
@@ -30,6 +25,6 @@ public class UpdateTodoItemCommandHandler : IRequestHandler<UpdateTodoItemComman
|
|||||||
entity.Title = request.Title;
|
entity.Title = request.Title;
|
||||||
entity.Done = request.Done;
|
entity.Done = request.Done;
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,18 +14,13 @@ public record UpdateTodoItemDetailCommand : IRequest
|
|||||||
public string? Note { get; init; }
|
public string? Note { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateTodoItemDetailCommandHandler : IRequestHandler<UpdateTodoItemDetailCommand>
|
public class UpdateTodoItemDetailCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<UpdateTodoItemDetailCommand>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public UpdateTodoItemDetailCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(UpdateTodoItemDetailCommand request, CancellationToken cancellationToken)
|
public async Task Handle(UpdateTodoItemDetailCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = await _context.TodoItems
|
var entity = await context.TodoItems
|
||||||
.FindAsync(new object[] { request.Id }, cancellationToken);
|
.FindAsync(new object[] { request.Id }, cancellationToken);
|
||||||
|
|
||||||
Guard.Against.NotFound(request.Id, entity);
|
Guard.Against.NotFound(request.Id, entity);
|
||||||
@@ -34,6 +29,6 @@ public class UpdateTodoItemDetailCommandHandler : IRequestHandler<UpdateTodoItem
|
|||||||
entity.Priority = request.Priority;
|
entity.Priority = request.Priority;
|
||||||
entity.Note = request.Note;
|
entity.Note = request.Note;
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,13 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Hutopy.Application.TodoItems.EventHandlers;
|
namespace Hutopy.Application.TodoItems.EventHandlers;
|
||||||
|
|
||||||
public class TodoItemCompletedEventHandler : INotificationHandler<TodoItemCompletedEvent>
|
public class TodoItemCompletedEventHandler(
|
||||||
|
ILogger<TodoItemCompletedEventHandler> logger)
|
||||||
|
: INotificationHandler<TodoItemCompletedEvent>
|
||||||
{
|
{
|
||||||
private readonly ILogger<TodoItemCompletedEventHandler> _logger;
|
|
||||||
|
|
||||||
public TodoItemCompletedEventHandler(ILogger<TodoItemCompletedEventHandler> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Handle(TodoItemCompletedEvent notification, CancellationToken cancellationToken)
|
public Task Handle(TodoItemCompletedEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Hutopy Domain Event: {DomainEvent}", notification.GetType().Name);
|
logger.LogInformation("Hutopy Domain Event: {DomainEvent}", notification.GetType().Name);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,13 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Hutopy.Application.TodoItems.EventHandlers;
|
namespace Hutopy.Application.TodoItems.EventHandlers;
|
||||||
|
|
||||||
public class TodoItemCreatedEventHandler : INotificationHandler<TodoItemCreatedEvent>
|
public class TodoItemCreatedEventHandler(
|
||||||
|
ILogger<TodoItemCreatedEventHandler> logger)
|
||||||
|
: INotificationHandler<TodoItemCreatedEvent>
|
||||||
{
|
{
|
||||||
private readonly ILogger<TodoItemCreatedEventHandler> _logger;
|
|
||||||
|
|
||||||
public TodoItemCreatedEventHandler(ILogger<TodoItemCreatedEventHandler> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Handle(TodoItemCreatedEvent notification, CancellationToken cancellationToken)
|
public Task Handle(TodoItemCreatedEvent notification, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Hutopy Domain Event: {DomainEvent}", notification.GetType().Name);
|
logger.LogInformation("Hutopy Domain Event: {DomainEvent}", notification.GetType().Name);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,31 +4,25 @@ using Hutopy.Application.Common.Models;
|
|||||||
|
|
||||||
namespace Hutopy.Application.TodoItems.Queries.GetTodoItemsWithPagination;
|
namespace Hutopy.Application.TodoItems.Queries.GetTodoItemsWithPagination;
|
||||||
|
|
||||||
public record GetTodoItemsWithPaginationQuery : IRequest<PaginatedList<TodoItemBriefDto>>
|
public abstract record GetTodoItemsWithPaginationQuery : IRequest<PaginatedList<TodoItemBriefDto>>
|
||||||
{
|
{
|
||||||
public int ListId { get; init; }
|
public int ListId { get; init; }
|
||||||
public int PageNumber { get; init; } = 1;
|
public int PageNumber { get; init; } = 1;
|
||||||
public int PageSize { get; init; } = 10;
|
public int PageSize { get; init; } = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GetTodoItemsWithPaginationQueryHandler : IRequestHandler<GetTodoItemsWithPaginationQuery, PaginatedList<TodoItemBriefDto>>
|
public class GetTodoItemsWithPaginationQueryHandler(
|
||||||
|
IApplicationDbContext context,
|
||||||
|
IMapper mapper)
|
||||||
|
: IRequestHandler<GetTodoItemsWithPaginationQuery, PaginatedList<TodoItemBriefDto>>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
public GetTodoItemsWithPaginationQueryHandler(IApplicationDbContext context, IMapper mapper)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PaginatedList<TodoItemBriefDto>> Handle(GetTodoItemsWithPaginationQuery request, CancellationToken cancellationToken)
|
public async Task<PaginatedList<TodoItemBriefDto>> Handle(GetTodoItemsWithPaginationQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Console.WriteLine(request);
|
Console.WriteLine(request);
|
||||||
return await _context.TodoItems
|
return await context.TodoItems
|
||||||
.Where(x => x.ListId == request.ListId)
|
.Where(x => x.ListId == request.ListId)
|
||||||
.OrderBy(x => x.Title)
|
.OrderBy(x => x.Title)
|
||||||
.ProjectTo<TodoItemBriefDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<TodoItemBriefDto>(mapper.ConfigurationProvider)
|
||||||
.PaginatedListAsync(request.PageNumber, request.PageSize);
|
.PaginatedListAsync(request.PageNumber, request.PageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,17 @@ public record CreateTodoListCommand : IRequest<int>
|
|||||||
public string? Title { get; init; }
|
public string? Title { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateTodoListCommandHandler : IRequestHandler<CreateTodoListCommand, int>
|
public class CreateTodoListCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<CreateTodoListCommand, int>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public CreateTodoListCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> Handle(CreateTodoListCommand request, CancellationToken cancellationToken)
|
public async Task<int> Handle(CreateTodoListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = new TodoList();
|
var entity = new TodoList { Title = request.Title };
|
||||||
|
|
||||||
entity.Title = request.Title;
|
context.TodoLists.Add(entity);
|
||||||
|
|
||||||
_context.TodoLists.Add(entity);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return entity.Id;
|
return entity.Id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,20 @@ namespace Hutopy.Application.TodoLists.Commands.DeleteTodoList;
|
|||||||
|
|
||||||
public record DeleteTodoListCommand(int Id) : IRequest;
|
public record DeleteTodoListCommand(int Id) : IRequest;
|
||||||
|
|
||||||
public class DeleteTodoListCommandHandler : IRequestHandler<DeleteTodoListCommand>
|
public class DeleteTodoListCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<DeleteTodoListCommand>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public DeleteTodoListCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(DeleteTodoListCommand request, CancellationToken cancellationToken)
|
public async Task Handle(DeleteTodoListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = await _context.TodoLists
|
var entity = await context.TodoLists
|
||||||
.Where(l => l.Id == request.Id)
|
.Where(l => l.Id == request.Id)
|
||||||
.SingleOrDefaultAsync(cancellationToken);
|
.SingleOrDefaultAsync(cancellationToken);
|
||||||
|
|
||||||
Guard.Against.NotFound(request.Id, entity);
|
Guard.Against.NotFound(request.Id, entity);
|
||||||
|
|
||||||
_context.TodoLists.Remove(entity);
|
context.TodoLists.Remove(entity);
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,19 +8,14 @@ namespace Hutopy.Application.TodoLists.Commands.PurgeTodoLists;
|
|||||||
[Authorize(Policy = Policies.CanPurge)]
|
[Authorize(Policy = Policies.CanPurge)]
|
||||||
public record PurgeTodoListsCommand : IRequest;
|
public record PurgeTodoListsCommand : IRequest;
|
||||||
|
|
||||||
public class PurgeTodoListsCommandHandler : IRequestHandler<PurgeTodoListsCommand>
|
public class PurgeTodoListsCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<PurgeTodoListsCommand>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public PurgeTodoListsCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(PurgeTodoListsCommand request, CancellationToken cancellationToken)
|
public async Task Handle(PurgeTodoListsCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_context.TodoLists.RemoveRange(_context.TodoLists);
|
context.TodoLists.RemoveRange(context.TodoLists);
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,25 +9,20 @@ public record UpdateTodoListCommand : IRequest
|
|||||||
public string? Title { get; init; }
|
public string? Title { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateTodoListCommandHandler : IRequestHandler<UpdateTodoListCommand>
|
public class UpdateTodoListCommandHandler(
|
||||||
|
IApplicationDbContext context)
|
||||||
|
: IRequestHandler<UpdateTodoListCommand>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
|
|
||||||
public UpdateTodoListCommandHandler(IApplicationDbContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(UpdateTodoListCommand request, CancellationToken cancellationToken)
|
public async Task Handle(UpdateTodoListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var entity = await _context.TodoLists
|
var entity = await context.TodoLists
|
||||||
.FindAsync(new object[] { request.Id }, cancellationToken);
|
.FindAsync(new object[] { request.Id }, cancellationToken);
|
||||||
|
|
||||||
Guard.Against.NotFound(request.Id, entity);
|
Guard.Against.NotFound(request.Id, entity);
|
||||||
|
|
||||||
entity.Title = request.Title;
|
entity.Title = request.Title;
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class UpdateTodoListCommandValidator : AbstractValidator<UpdateTodoListCo
|
|||||||
.WithErrorCode("Unique");
|
.WithErrorCode("Unique");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> BeUniqueTitle(UpdateTodoListCommand model, string title, CancellationToken cancellationToken)
|
private async Task<bool> BeUniqueTitle(UpdateTodoListCommand model, string title, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await _context.TodoLists
|
return await _context.TodoLists
|
||||||
.Where(l => l.Id != model.Id)
|
.Where(l => l.Id != model.Id)
|
||||||
|
|||||||
@@ -8,17 +8,11 @@ namespace Hutopy.Application.TodoLists.Queries.GetTodos;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public record GetTodosQuery : IRequest<TodosVm>;
|
public record GetTodosQuery : IRequest<TodosVm>;
|
||||||
|
|
||||||
public class GetTodosQueryHandler : IRequestHandler<GetTodosQuery, TodosVm>
|
public class GetTodosQueryHandler(
|
||||||
|
IApplicationDbContext context,
|
||||||
|
IMapper mapper)
|
||||||
|
: IRequestHandler<GetTodosQuery, TodosVm>
|
||||||
{
|
{
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
public GetTodosQueryHandler(IApplicationDbContext context, IMapper mapper)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TodosVm> Handle(GetTodosQuery request, CancellationToken cancellationToken)
|
public async Task<TodosVm> Handle(GetTodosQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return new TodosVm
|
return new TodosVm
|
||||||
@@ -28,9 +22,9 @@ public class GetTodosQueryHandler : IRequestHandler<GetTodosQuery, TodosVm>
|
|||||||
.Select(p => new LookupDto { Id = (int)p, Title = p.ToString() })
|
.Select(p => new LookupDto { Id = (int)p, Title = p.ToString() })
|
||||||
.ToList(),
|
.ToList(),
|
||||||
|
|
||||||
Lists = await _context.TodoLists
|
Lists = await context.TodoLists
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.ProjectTo<TodoListDto>(_mapper.ConfigurationProvider)
|
.ProjectTo<TodoListDto>(mapper.ConfigurationProvider)
|
||||||
.OrderBy(t => t.Title)
|
.OrderBy(t => t.Title)
|
||||||
.ToListAsync(cancellationToken)
|
.ToListAsync(cancellationToken)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,18 +4,13 @@ namespace Hutopy.Application.TodoLists.Queries.GetTodos;
|
|||||||
|
|
||||||
public class TodoListDto
|
public class TodoListDto
|
||||||
{
|
{
|
||||||
public TodoListDto()
|
|
||||||
{
|
|
||||||
Items = Array.Empty<TodoItemDto>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Id { get; init; }
|
public int Id { get; init; }
|
||||||
|
|
||||||
public string? Title { get; init; }
|
public string? Title { get; init; }
|
||||||
|
|
||||||
public string? Colour { get; init; }
|
public string? Colour { get; init; }
|
||||||
|
|
||||||
public IReadOnlyCollection<TodoItemDto> Items { get; init; }
|
public IReadOnlyCollection<TodoItemDto> Items { get; init; } = Array.Empty<TodoItemDto>();
|
||||||
|
|
||||||
private class Mapping : Profile
|
private class Mapping : Profile
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ public abstract class BaseEntity
|
|||||||
// Using non-generic integer types for simplicity
|
// Using non-generic integer types for simplicity
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
private readonly List<BaseEvent> _domainEvents = new();
|
private readonly List<BaseEvent> _domainEvents = [];
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public IReadOnlyCollection<BaseEvent> DomainEvents => _domainEvents.AsReadOnly();
|
public IEnumerable<BaseEvent> DomainEvents => _domainEvents.AsReadOnly();
|
||||||
|
|
||||||
public void AddDomainEvent(BaseEvent domainEvent)
|
public void AddDomainEvent(BaseEvent domainEvent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// Learn more: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/implement-value-objects
|
// Learn more: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/implement-value-objects
|
||||||
public abstract class ValueObject
|
public abstract class ValueObject
|
||||||
{
|
{
|
||||||
protected static bool EqualOperator(ValueObject left, ValueObject right)
|
private static bool EqualOperator(ValueObject left, ValueObject right)
|
||||||
{
|
{
|
||||||
if (left is null ^ right is null)
|
if (left is null ^ right is null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
namespace Hutopy.Domain.Events;
|
namespace Hutopy.Domain.Events;
|
||||||
|
|
||||||
public class TodoItemCompletedEvent : BaseEvent
|
public class TodoItemCompletedEvent(
|
||||||
|
TodoItem item)
|
||||||
|
: BaseEvent
|
||||||
{
|
{
|
||||||
public TodoItemCompletedEvent(TodoItem item)
|
public TodoItem Item { get; } = item;
|
||||||
{
|
|
||||||
Item = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TodoItem Item { get; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
namespace Hutopy.Domain.Events;
|
namespace Hutopy.Domain.Events;
|
||||||
|
|
||||||
public class TodoItemCreatedEvent : BaseEvent
|
public class TodoItemCreatedEvent(
|
||||||
|
TodoItem item)
|
||||||
|
: BaseEvent
|
||||||
{
|
{
|
||||||
public TodoItemCreatedEvent(TodoItem item)
|
public TodoItem Item { get; } = item;
|
||||||
{
|
|
||||||
Item = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TodoItem Item { get; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
namespace Hutopy.Domain.Events;
|
namespace Hutopy.Domain.Events;
|
||||||
|
|
||||||
public class TodoItemDeletedEvent : BaseEvent
|
public class TodoItemDeletedEvent(
|
||||||
|
TodoItem item)
|
||||||
|
: BaseEvent
|
||||||
{
|
{
|
||||||
public TodoItemDeletedEvent(TodoItem item)
|
public TodoItem Item { get; } = item;
|
||||||
{
|
|
||||||
Item = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TodoItem Item { get; }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
namespace Hutopy.Domain.Exceptions;
|
namespace Hutopy.Domain.Exceptions;
|
||||||
|
|
||||||
public class UnsupportedColourException : Exception
|
public class UnsupportedColourException(
|
||||||
{
|
string code) : Exception($"Colour \"{code}\" is unsupported.");
|
||||||
public UnsupportedColourException(string code)
|
|
||||||
: base($"Colour \"{code}\" is unsupported.")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class Colour(string code) : ValueObject
|
|||||||
return Code;
|
return Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static IEnumerable<Colour> SupportedColours
|
private static IEnumerable<Colour> SupportedColours
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace Hutopy.Infrastructure.Data;
|
namespace Hutopy.Infrastructure.Data;
|
||||||
|
|
||||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext
|
public class ApplicationDbContext(
|
||||||
|
DbContextOptions<ApplicationDbContext> options)
|
||||||
|
: IdentityDbContext<ApplicationUser>(options), IApplicationDbContext
|
||||||
{
|
{
|
||||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
|
||||||
|
|
||||||
public DbSet<TodoList> TodoLists => Set<TodoList>();
|
public DbSet<TodoList> TodoLists => Set<TodoList>();
|
||||||
|
|
||||||
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
|
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Hutopy.Domain.Constants;
|
||||||
using Hutopy.Domain.Constants;
|
|
||||||
using Hutopy.Domain.Entities;
|
using Hutopy.Domain.Entities;
|
||||||
using Hutopy.Infrastructure.Identity;
|
using Hutopy.Infrastructure.Identity;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
@@ -10,44 +9,35 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Hutopy.Infrastructure.Data;
|
namespace Hutopy.Infrastructure.Data;
|
||||||
|
|
||||||
public static class InitialiserExtensions
|
public static class InitializerExtensions
|
||||||
{
|
{
|
||||||
public static async Task InitialiseDatabaseAsync(this WebApplication app)
|
public static async Task InitialiseDatabaseAsync(this WebApplication app)
|
||||||
{
|
{
|
||||||
using var scope = app.Services.CreateScope();
|
using var scope = app.Services.CreateScope();
|
||||||
|
|
||||||
var initialiser = scope.ServiceProvider.GetRequiredService<ApplicationDbContextInitialiser>();
|
var initializer = scope.ServiceProvider.GetRequiredService<ApplicationDbContextInitializer>();
|
||||||
|
|
||||||
await initialiser.InitialiseAsync();
|
await initializer.InitialiseAsync();
|
||||||
|
|
||||||
await initialiser.SeedAsync();
|
await initializer.SeedAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ApplicationDbContextInitialiser
|
public class ApplicationDbContextInitializer(
|
||||||
|
ILogger<ApplicationDbContextInitializer> logger,
|
||||||
|
ApplicationDbContext context,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
RoleManager<IdentityRole> roleManager)
|
||||||
{
|
{
|
||||||
private readonly ILogger<ApplicationDbContextInitialiser> _logger;
|
|
||||||
private readonly ApplicationDbContext _context;
|
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
|
||||||
private readonly RoleManager<IdentityRole> _roleManager;
|
|
||||||
|
|
||||||
public ApplicationDbContextInitialiser(ILogger<ApplicationDbContextInitialiser> logger, ApplicationDbContext context, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_context = context;
|
|
||||||
_userManager = userManager;
|
|
||||||
_roleManager = roleManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task InitialiseAsync()
|
public async Task InitialiseAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _context.Database.MigrateAsync();
|
await context.Database.MigrateAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "An error occurred while initialising the database.");
|
logger.LogError(ex, "An error occurred while initialising the database.");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,38 +50,38 @@ public class ApplicationDbContextInitialiser
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "An error occurred while seeding the database.");
|
logger.LogError(ex, "An error occurred while seeding the database.");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TrySeedAsync()
|
private async Task TrySeedAsync()
|
||||||
{
|
{
|
||||||
// Default roles
|
// Default roles
|
||||||
var administratorRole = new IdentityRole(Roles.Administrator);
|
var administratorRole = new IdentityRole(Roles.Administrator);
|
||||||
|
|
||||||
if (_roleManager.Roles.All(r => r.Name != administratorRole.Name))
|
if (roleManager.Roles.All(r => r.Name != administratorRole.Name))
|
||||||
{
|
{
|
||||||
await _roleManager.CreateAsync(administratorRole);
|
await roleManager.CreateAsync(administratorRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default users
|
// Default users
|
||||||
var administrator = new ApplicationUser { UserName = "administrator@localhost", Email = "administrator@localhost" };
|
var administrator = new ApplicationUser { UserName = "administrator@localhost", Email = "administrator@localhost" };
|
||||||
|
|
||||||
if (_userManager.Users.All(u => u.UserName != administrator.UserName))
|
if (userManager.Users.All(u => u.UserName != administrator.UserName))
|
||||||
{
|
{
|
||||||
await _userManager.CreateAsync(administrator, "Administrator1!");
|
await userManager.CreateAsync(administrator, "Administrator1!");
|
||||||
if (!string.IsNullOrWhiteSpace(administratorRole.Name))
|
if (!string.IsNullOrWhiteSpace(administratorRole.Name))
|
||||||
{
|
{
|
||||||
await _userManager.AddToRolesAsync(administrator, new [] { administratorRole.Name });
|
await userManager.AddToRolesAsync(administrator, new [] { administratorRole.Name });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default data
|
// Default data
|
||||||
// Seed, if necessary
|
// Seed, if necessary
|
||||||
if (!_context.TodoLists.Any())
|
if (!context.TodoLists.Any())
|
||||||
{
|
{
|
||||||
_context.TodoLists.Add(new TodoList
|
context.TodoLists.Add(new TodoList
|
||||||
{
|
{
|
||||||
Title = "Todo List",
|
Title = "Todo List",
|
||||||
Items =
|
Items =
|
||||||
@@ -103,7 +93,7 @@ public class ApplicationDbContextInitialiser
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,19 +6,10 @@ using Microsoft.EntityFrameworkCore.Diagnostics;
|
|||||||
|
|
||||||
namespace Hutopy.Infrastructure.Data.Interceptors;
|
namespace Hutopy.Infrastructure.Data.Interceptors;
|
||||||
|
|
||||||
public class AuditableEntityInterceptor : SaveChangesInterceptor
|
public class AuditableEntityInterceptor(
|
||||||
|
IUser user,
|
||||||
|
TimeProvider dateTime) : SaveChangesInterceptor
|
||||||
{
|
{
|
||||||
private readonly IUser _user;
|
|
||||||
private readonly TimeProvider _dateTime;
|
|
||||||
|
|
||||||
public AuditableEntityInterceptor(
|
|
||||||
IUser user,
|
|
||||||
TimeProvider dateTime)
|
|
||||||
{
|
|
||||||
_user = user;
|
|
||||||
_dateTime = dateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
|
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
|
||||||
{
|
{
|
||||||
UpdateEntities(eventData.Context);
|
UpdateEntities(eventData.Context);
|
||||||
@@ -41,13 +32,13 @@ public class AuditableEntityInterceptor : SaveChangesInterceptor
|
|||||||
{
|
{
|
||||||
if (entry.State is EntityState.Added or EntityState.Modified || entry.HasChangedOwnedEntities())
|
if (entry.State is EntityState.Added or EntityState.Modified || entry.HasChangedOwnedEntities())
|
||||||
{
|
{
|
||||||
var utcNow = _dateTime.GetUtcNow();
|
var utcNow = dateTime.GetUtcNow();
|
||||||
if (entry.State == EntityState.Added)
|
if (entry.State == EntityState.Added)
|
||||||
{
|
{
|
||||||
entry.Entity.CreatedBy = _user.Id;
|
entry.Entity.CreatedBy = user.Id;
|
||||||
entry.Entity.Created = utcNow;
|
entry.Entity.Created = utcNow;
|
||||||
}
|
}
|
||||||
entry.Entity.LastModifiedBy = _user.Id;
|
entry.Entity.LastModifiedBy = user.Id;
|
||||||
entry.Entity.LastModified = utcNow;
|
entry.Entity.LastModified = utcNow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,10 @@ using Microsoft.EntityFrameworkCore.Diagnostics;
|
|||||||
|
|
||||||
namespace Hutopy.Infrastructure.Data.Interceptors;
|
namespace Hutopy.Infrastructure.Data.Interceptors;
|
||||||
|
|
||||||
public class DispatchDomainEventsInterceptor : SaveChangesInterceptor
|
public class DispatchDomainEventsInterceptor(
|
||||||
|
IPublisher mediator)
|
||||||
|
: SaveChangesInterceptor
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public DispatchDomainEventsInterceptor(IMediator mediator)
|
|
||||||
{
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
|
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
|
||||||
{
|
{
|
||||||
DispatchDomainEvents(eventData.Context).GetAwaiter().GetResult();
|
DispatchDomainEvents(eventData.Context).GetAwaiter().GetResult();
|
||||||
@@ -45,6 +40,6 @@ public class DispatchDomainEventsInterceptor : SaveChangesInterceptor
|
|||||||
entities.ToList().ForEach(e => e.ClearDomainEvents());
|
entities.ToList().ForEach(e => e.ClearDomainEvents());
|
||||||
|
|
||||||
foreach (var domainEvent in domainEvents)
|
foreach (var domainEvent in domainEvents)
|
||||||
await _mediator.Publish(domainEvent);
|
await mediator.Publish(domainEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Hutopy.Infrastructure;
|
||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
@@ -34,7 +35,7 @@ public static class DependencyInjection
|
|||||||
|
|
||||||
services.AddScoped<IApplicationDbContext>(provider => provider.GetRequiredService<ApplicationDbContext>());
|
services.AddScoped<IApplicationDbContext>(provider => provider.GetRequiredService<ApplicationDbContext>());
|
||||||
|
|
||||||
services.AddScoped<ApplicationDbContextInitialiser>();
|
services.AddScoped<ApplicationDbContextInitializer>();
|
||||||
|
|
||||||
services.AddAuthentication()
|
services.AddAuthentication()
|
||||||
.AddBearerToken(IdentityConstants.BearerScheme);
|
.AddBearerToken(IdentityConstants.BearerScheme);
|
||||||
|
|||||||
@@ -5,25 +5,15 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
|
|
||||||
namespace Hutopy.Infrastructure.Identity;
|
namespace Hutopy.Infrastructure.Identity;
|
||||||
|
|
||||||
public class IdentityService : IIdentityService
|
public class IdentityService(
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory,
|
||||||
|
IAuthorizationService authorizationService)
|
||||||
|
: IIdentityService
|
||||||
{
|
{
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
|
||||||
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _userClaimsPrincipalFactory;
|
|
||||||
private readonly IAuthorizationService _authorizationService;
|
|
||||||
|
|
||||||
public IdentityService(
|
|
||||||
UserManager<ApplicationUser> userManager,
|
|
||||||
IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory,
|
|
||||||
IAuthorizationService authorizationService)
|
|
||||||
{
|
|
||||||
_userManager = userManager;
|
|
||||||
_userClaimsPrincipalFactory = userClaimsPrincipalFactory;
|
|
||||||
_authorizationService = authorizationService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string?> GetUserNameAsync(string userId)
|
public async Task<string?> GetUserNameAsync(string userId)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByIdAsync(userId);
|
var user = await userManager.FindByIdAsync(userId);
|
||||||
|
|
||||||
return user?.UserName;
|
return user?.UserName;
|
||||||
}
|
}
|
||||||
@@ -36,44 +26,44 @@ public class IdentityService : IIdentityService
|
|||||||
Email = userName,
|
Email = userName,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await _userManager.CreateAsync(user, password);
|
var result = await userManager.CreateAsync(user, password);
|
||||||
|
|
||||||
return (result.ToApplicationResult(), user.Id);
|
return (result.ToApplicationResult(), user.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsInRoleAsync(string userId, string role)
|
public async Task<bool> IsInRoleAsync(string userId, string role)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByIdAsync(userId);
|
var user = await userManager.FindByIdAsync(userId);
|
||||||
|
|
||||||
return user != null && await _userManager.IsInRoleAsync(user, role);
|
return user != null && await userManager.IsInRoleAsync(user, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> AuthorizeAsync(string userId, string policyName)
|
public async Task<bool> AuthorizeAsync(string userId, string policyName)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByIdAsync(userId);
|
var user = await userManager.FindByIdAsync(userId);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var principal = await _userClaimsPrincipalFactory.CreateAsync(user);
|
var principal = await userClaimsPrincipalFactory.CreateAsync(user);
|
||||||
|
|
||||||
var result = await _authorizationService.AuthorizeAsync(principal, policyName);
|
var result = await authorizationService.AuthorizeAsync(principal, policyName);
|
||||||
|
|
||||||
return result.Succeeded;
|
return result.Succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> DeleteUserAsync(string userId)
|
public async Task<Result> DeleteUserAsync(string userId)
|
||||||
{
|
{
|
||||||
var user = await _userManager.FindByIdAsync(userId);
|
var user = await userManager.FindByIdAsync(userId);
|
||||||
|
|
||||||
return user != null ? await DeleteUserAsync(user) : Result.Success();
|
return user != null ? await DeleteUserAsync(user) : Result.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> DeleteUserAsync(ApplicationUser user)
|
public async Task<Result> DeleteUserAsync(ApplicationUser user)
|
||||||
{
|
{
|
||||||
var result = await _userManager.DeleteAsync(user);
|
var result = await userManager.DeleteAsync(user);
|
||||||
|
|
||||||
return result.ToApplicationResult();
|
return result.ToApplicationResult();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,22 +16,20 @@ public class StripeService : IStripeService
|
|||||||
{
|
{
|
||||||
var options = new SessionCreateOptions
|
var options = new SessionCreateOptions
|
||||||
{
|
{
|
||||||
LineItems = new List<SessionLineItemOptions>
|
LineItems =
|
||||||
{
|
[
|
||||||
new SessionLineItemOptions
|
new SessionLineItemOptions
|
||||||
{
|
{
|
||||||
PriceData = new SessionLineItemPriceDataOptions
|
PriceData = new SessionLineItemPriceDataOptions
|
||||||
{
|
|
||||||
UnitAmount = price,
|
|
||||||
Currency = currency,
|
|
||||||
ProductData = new SessionLineItemPriceDataProductDataOptions
|
|
||||||
{
|
{
|
||||||
Name = "Tip",
|
UnitAmount = price,
|
||||||
|
Currency = currency,
|
||||||
|
ProductData = new SessionLineItemPriceDataProductDataOptions { Name = "Tip", },
|
||||||
},
|
},
|
||||||
},
|
Quantity = 1,
|
||||||
Quantity = 1,
|
}
|
||||||
},
|
|
||||||
},
|
],
|
||||||
Mode = "payment",
|
Mode = "payment",
|
||||||
UiMode = "embedded",
|
UiMode = "embedded",
|
||||||
ReturnUrl = $"http://localhost:5173/creatorfolio",
|
ReturnUrl = $"http://localhost:5173/creatorfolio",
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ using Hutopy.Application.Common.Interfaces;
|
|||||||
using Hutopy.Infrastructure.Data;
|
using Hutopy.Infrastructure.Data;
|
||||||
using Hutopy.Web.Services;
|
using Hutopy.Web.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
using NSwag;
|
using NSwag;
|
||||||
using NSwag.Generation.Processors.Security;
|
using NSwag.Generation.Processors.Security;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Hutopy.Web;
|
||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class JoinUs : EndpointGroupBase
|
|||||||
.MapPost(CreateFutureCreator);
|
.MapPost(CreateFutureCreator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> CreateFutureCreator(ISender sender, CreateFutureCreatorCommand command)
|
private static Task<int> CreateFutureCreator(ISender sender, CreateFutureCreatorCommand command)
|
||||||
{
|
{
|
||||||
return sender.Send(command);
|
return sender.Send(command);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class Stripe : EndpointGroupBase
|
|||||||
.MapPost(CreateSessionCheckout);
|
.MapPost(CreateSessionCheckout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> CreateSessionCheckout(ISender sender, CreateSessionCheckoutCommand command)
|
private static Task<string> CreateSessionCheckout(ISender sender, CreateSessionCheckoutCommand command)
|
||||||
{
|
{
|
||||||
return sender.Send(command);
|
return sender.Send(command);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,31 +20,31 @@ public class TodoItems : EndpointGroupBase
|
|||||||
.MapDelete(DeleteTodoItem, "{id}");
|
.MapDelete(DeleteTodoItem, "{id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<PaginatedList<TodoItemBriefDto>> GetTodoItemsWithPagination(ISender sender, [AsParameters] GetTodoItemsWithPaginationQuery query)
|
private static Task<PaginatedList<TodoItemBriefDto>> GetTodoItemsWithPagination(ISender sender, [AsParameters] GetTodoItemsWithPaginationQuery query)
|
||||||
{
|
{
|
||||||
return sender.Send(query);
|
return sender.Send(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> CreateTodoItem(ISender sender, CreateTodoItemCommand command)
|
private static Task<int> CreateTodoItem(ISender sender, CreateTodoItemCommand command)
|
||||||
{
|
{
|
||||||
return sender.Send(command);
|
return sender.Send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IResult> UpdateTodoItem(ISender sender, int id, UpdateTodoItemCommand command)
|
private static async Task<IResult> UpdateTodoItem(ISender sender, int id, UpdateTodoItemCommand command)
|
||||||
{
|
{
|
||||||
if (id != command.Id) return Results.BadRequest();
|
if (id != command.Id) return Results.BadRequest();
|
||||||
await sender.Send(command);
|
await sender.Send(command);
|
||||||
return Results.NoContent();
|
return Results.NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IResult> UpdateTodoItemDetail(ISender sender, int id, UpdateTodoItemDetailCommand command)
|
private static async Task<IResult> UpdateTodoItemDetail(ISender sender, int id, UpdateTodoItemDetailCommand command)
|
||||||
{
|
{
|
||||||
if (id != command.Id) return Results.BadRequest();
|
if (id != command.Id) return Results.BadRequest();
|
||||||
await sender.Send(command);
|
await sender.Send(command);
|
||||||
return Results.NoContent();
|
return Results.NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IResult> DeleteTodoItem(ISender sender, int id)
|
private static async Task<IResult> DeleteTodoItem(ISender sender, int id)
|
||||||
{
|
{
|
||||||
await sender.Send(new DeleteTodoItemCommand(id));
|
await sender.Send(new DeleteTodoItemCommand(id));
|
||||||
return Results.NoContent();
|
return Results.NoContent();
|
||||||
|
|||||||
@@ -17,24 +17,24 @@ public class TodoLists : EndpointGroupBase
|
|||||||
.MapDelete(DeleteTodoList, "{id}");
|
.MapDelete(DeleteTodoList, "{id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TodosVm> GetTodoLists(ISender sender)
|
private static Task<TodosVm> GetTodoLists(ISender sender)
|
||||||
{
|
{
|
||||||
return sender.Send(new GetTodosQuery());
|
return sender.Send(new GetTodosQuery());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> CreateTodoList(ISender sender, CreateTodoListCommand command)
|
private static Task<int> CreateTodoList(ISender sender, CreateTodoListCommand command)
|
||||||
{
|
{
|
||||||
return sender.Send(command);
|
return sender.Send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IResult> UpdateTodoList(ISender sender, int id, UpdateTodoListCommand command)
|
private static async Task<IResult> UpdateTodoList(ISender sender, int id, UpdateTodoListCommand command)
|
||||||
{
|
{
|
||||||
if (id != command.Id) return Results.BadRequest();
|
if (id != command.Id) return Results.BadRequest();
|
||||||
await sender.Send(command);
|
await sender.Send(command);
|
||||||
return Results.NoContent();
|
return Results.NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IResult> DeleteTodoList(ISender sender, int id)
|
private static async Task<IResult> DeleteTodoList(ISender sender, int id)
|
||||||
{
|
{
|
||||||
await sender.Send(new DeleteTodoListCommand(id));
|
await sender.Send(new DeleteTodoListCommand(id));
|
||||||
return Results.NoContent();
|
return Results.NoContent();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class WeatherForecasts : EndpointGroupBase
|
|||||||
.MapGet(GetWeatherForecasts);
|
.MapGet(GetWeatherForecasts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<WeatherForecast>> GetWeatherForecasts(ISender sender)
|
private static async Task<IEnumerable<WeatherForecast>> GetWeatherForecasts(ISender sender)
|
||||||
{
|
{
|
||||||
return await sender.Send(new GetWeatherForecastsQuery());
|
return await sender.Send(new GetWeatherForecastsQuery());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ public class CustomExceptionHandler : IExceptionHandler
|
|||||||
public CustomExceptionHandler()
|
public CustomExceptionHandler()
|
||||||
{
|
{
|
||||||
// Register known exception types and handlers.
|
// Register known exception types and handlers.
|
||||||
_exceptionHandlers = new()
|
_exceptionHandlers = new Dictionary<Type, Func<HttpContext, Exception, Task>>
|
||||||
{
|
{
|
||||||
{ typeof(ValidationException), HandleValidationException },
|
{ typeof(ValidationException), HandleValidationException },
|
||||||
{ typeof(NotFoundException), HandleNotFoundException },
|
{ typeof(NotFoundException), HandleNotFoundException },
|
||||||
{ typeof(UnauthorizedAccessException), HandleUnauthorizedAccessException },
|
{ typeof(UnauthorizedAccessException), HandleUnauthorizedAccessException },
|
||||||
@@ -24,16 +24,14 @@ public class CustomExceptionHandler : IExceptionHandler
|
|||||||
{
|
{
|
||||||
var exceptionType = exception.GetType();
|
var exceptionType = exception.GetType();
|
||||||
|
|
||||||
if (_exceptionHandlers.ContainsKey(exceptionType))
|
if (!_exceptionHandlers.TryGetValue(exceptionType, out Func<HttpContext, Exception, Task>? value)) return false;
|
||||||
{
|
|
||||||
await _exceptionHandlers[exceptionType].Invoke(httpContext, exception);
|
await value.Invoke(httpContext, exception);
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleValidationException(HttpContext httpContext, Exception ex)
|
private static async Task HandleValidationException(HttpContext httpContext, Exception ex)
|
||||||
{
|
{
|
||||||
var exception = (ValidationException)ex;
|
var exception = (ValidationException)ex;
|
||||||
|
|
||||||
@@ -46,7 +44,7 @@ public class CustomExceptionHandler : IExceptionHandler
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleNotFoundException(HttpContext httpContext, Exception ex)
|
private static async Task HandleNotFoundException(HttpContext httpContext, Exception ex)
|
||||||
{
|
{
|
||||||
var exception = (NotFoundException)ex;
|
var exception = (NotFoundException)ex;
|
||||||
|
|
||||||
@@ -61,7 +59,7 @@ public class CustomExceptionHandler : IExceptionHandler
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleUnauthorizedAccessException(HttpContext httpContext, Exception ex)
|
private static async Task HandleUnauthorizedAccessException(HttpContext httpContext, Exception ex)
|
||||||
{
|
{
|
||||||
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
|
|
||||||
@@ -73,7 +71,7 @@ public class CustomExceptionHandler : IExceptionHandler
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleForbiddenAccessException(HttpContext httpContext, Exception ex)
|
private static async Task HandleForbiddenAccessException(HttpContext httpContext, Exception ex)
|
||||||
{
|
{
|
||||||
httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Hutopy.Web.Infrastructure;
|
|||||||
|
|
||||||
public static class MethodInfoExtensions
|
public static class MethodInfoExtensions
|
||||||
{
|
{
|
||||||
public static bool IsAnonymous(this MethodInfo method)
|
private static bool IsAnonymous(this MethodInfo method)
|
||||||
{
|
{
|
||||||
var invalidChars = new[] { '<', '>' };
|
var invalidChars = new[] { '<', '>' };
|
||||||
return method.Name.Any(invalidChars.Contains);
|
return method.Name.Any(invalidChars.Contains);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
using Hutopy.Application;
|
||||||
|
using Hutopy.Infrastructure;
|
||||||
using Hutopy.Infrastructure.Data;
|
using Hutopy.Infrastructure.Data;
|
||||||
|
using Hutopy.Web;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -61,4 +64,4 @@ app.MapEndpoints();
|
|||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
public partial class Program { }
|
public abstract partial class Program { }
|
||||||
|
|||||||
@@ -4,14 +4,9 @@ using Hutopy.Application.Common.Interfaces;
|
|||||||
|
|
||||||
namespace Hutopy.Web.Services;
|
namespace Hutopy.Web.Services;
|
||||||
|
|
||||||
public class CurrentUser : IUser
|
public class CurrentUser(
|
||||||
|
IHttpContextAccessor httpContextAccessor)
|
||||||
|
: IUser
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
public string? Id => httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
|
||||||
public CurrentUser(IHttpContextAccessor httpContextAccessor)
|
|
||||||
{
|
|
||||||
_httpContextAccessor = httpContextAccessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string? Id => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user