From f07688a96f9948aa7c6c271246a9056f6486f4d1 Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Fri, 1 Aug 2025 04:03:50 +0300 Subject: [PATCH 1/6] refactor: removed MediatR --- .../CreateArticleCommandHandlerTests.cs | 21 ++-- Application/Application.csproj | 1 - .../Common/Behaviours/QueryCachingBehavior.cs | 32 +++--- .../Common/Behaviours/ValidationBehavior.cs | 104 +++++++++--------- Application/Common/Messaging/ICachedQuery.cs | 9 +- Application/Common/Messaging/IQuery.cs | 5 +- .../Extensions/ServiceCollectionExt.cs | 52 ++++++++- .../CreateArticle/ArticleCreatedEvent.cs | 16 ++- .../CreateArticle/CreateArticleCommand.cs | 3 +- .../CreateArticleCommandHandler.cs | 22 ++-- .../ICreateArticleCommandHandler.cs | 8 ++ .../DeleteArticle/ArticleDeletedEvent.cs | 10 +- .../DeleteArticle/DeleteArticleCommand.cs | 7 +- .../DeleteArticleCommandHandler.cs | 10 +- .../IDeleteArticleCommandHandler.cs | 8 ++ .../EditArticle/ArticleEditedEvent.cs | 16 ++- .../EditArticle/EditArticleCommand.cs | 7 +- .../EditArticle/EditArticleCommandHandler.cs | 22 ++-- .../EditArticle/IEditArticleCommandHandler.cs | 8 ++ .../CacheInvalidationArticleHandler.cs | 66 +++++------ .../GetArticle/GetArticleQueryHandler.cs | 3 +- .../GetArticle/IGetArticleQueryHandler.cs | 8 ++ .../GetPendingRevisionsQuery.cs | 2 +- .../GetPendingRevisionsQueryHandler.cs | 8 +- .../IGetPendingRevisionsQueryHandler.cs | 8 ++ .../GetPendingRevisionsQuery.cs | 2 +- .../GetPendingRevisionsQueryHandler.cs | 4 +- .../IGetPendingRevisionsCountQueryHandler.cs | 9 ++ .../GetRevisionHistoryQuery.cs | 2 +- .../GetRevisionHistoryQueryHandler.cs | 8 +- .../IGetRevisionHistoryQueryHandler.cs | 9 ++ .../GetRevisionReviewHistoryQuery.cs | 2 +- .../GetRevisionReviewHistoryQueryHandler.cs | 5 +- .../IGetRevisionReviewHistoryQueryHandler.cs | 9 ++ .../ArticleChangedRevisionEvent.cs | 18 ++- .../IReviewRevisionCommandHandler.cs | 9 ++ .../ReviewRevision/ReviewRevisionCommand.cs | 11 +- .../ReviewRevisionCommandHandler.cs | 60 +++++----- .../ReviewRevision/RevisionReviewedEvent.cs | 15 ++- .../ISearchArticlesQueryHandler.cs | 8 ++ .../SearchArticles/SearchArticlesQuery.cs | 2 +- .../SearchArticlesQueryHandler.cs | 4 +- .../SetRedirect/ISetRedirectCommandHandler.cs | 9 ++ .../Articles/SetRedirect/RedirectSetEvent.cs | 12 +- .../SetRedirect/SetRedirectCommand.cs | 6 +- .../SetRedirect/SetRedirectCommandHandler.cs | 20 ++-- .../CreateAuthor/AuthorCreatedEvent.cs | 12 +- .../CreateAuthor/CreateAuthorCommand.cs | 7 +- .../CreateAuthorCommandHandler.cs | 18 ++- .../ICreateAuthorCommandHandler.cs | 8 ++ .../Authors/EditAuthor/AuthorEditedEvent.cs | 12 +- .../Authors/EditAuthor/EditAuthorCommand.cs | 7 +- .../EditAuthor/EditAuthorCommandHandler.cs | 18 ++- .../EditAuthor/IEditAuthorCommandHandler.cs | 8 ++ .../CreateCategory/CategoryCreatedEvent.cs | 14 +-- .../CreateCategory/CreateCategoryCommand.cs | 5 +- .../CreateCategoryCommandHandler.cs | 20 ++-- .../ICreateCategoryCommandHandler.cs | 8 ++ .../DeleteCategory/CategoryDeletedEvent.cs | 10 +- .../DeleteCategory/DeleteCategoryCommand.cs | 5 +- .../DeleteCategoryCommandHandler.cs | 10 +- .../IDeleteCategoryCommandHandler.cs | 9 ++ .../CacheInvalidationCategoriesHandler.cs | 36 +++--- .../GetCategoriesQueryHandler.cs | 4 +- .../IGetCategoriesQueryHandler.cs | 9 ++ .../GetCategoriesTreeQueryHandler.cs | 6 +- .../IGetCategoriesTreeQueryHandler.cs | 9 ++ .../GetCategoryArticlesQueryHandler.cs | 4 +- .../IGetCategoryArticlesQueryHandler.cs | 9 ++ .../CacheInvalidationNavigationsHandler.cs | 18 +-- .../GetNavigationsTreeQueryHandler.cs | 4 +- .../IGetNavigationsTreeQueryHandler.cs | 9 ++ .../IUpdateNavigationsTreeCommandHandler.cs | 9 ++ .../NavigationsTreeUpdatedEvent.cs | 4 +- .../UpdateNavigationsTreeCommand.cs | 6 +- .../UpdateNavigationsTreeCommandHandler.cs | 10 +- .../Extensions/ServiceCollectionExt.cs | 23 ++-- WebApi/Features/Articles/ArticlesModule.cs | 88 ++++++++++----- .../Features/Categories/CategoriesModule.cs | 35 ++++-- .../Features/Navigations/NavigationsModule.cs | 9 +- 80 files changed, 650 insertions(+), 493 deletions(-) create mode 100644 Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs create mode 100644 Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs create mode 100644 Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs create mode 100644 Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs create mode 100644 Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs create mode 100644 Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs create mode 100644 Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs create mode 100644 Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs create mode 100644 Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs create mode 100644 Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs create mode 100644 Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs create mode 100644 Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs create mode 100644 Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs create mode 100644 Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs create mode 100644 Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs create mode 100644 Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs create mode 100644 Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs create mode 100644 Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs create mode 100644 Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs create mode 100644 Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs diff --git a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs index 817f7bc..7292334 100644 --- a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs +++ b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs @@ -2,7 +2,6 @@ using Application.Data; using Application.Features.Articles.CreateArticle; using Domain.Entities; -using MediatR; using MockQueryable.Moq; using Slugify; @@ -13,7 +12,6 @@ public class CreateArticleCommandHandlerTests private readonly Mock _mockDbContext = new(); private readonly Mock _mockSlugHelper = new(); private readonly Mock _mockCurrentUserService = new(); - private readonly Mock _mockPublisher = new(); private readonly CreateArticleCommandHandler _handler; public CreateArticleCommandHandlerTests() @@ -21,8 +19,7 @@ public CreateArticleCommandHandlerTests() _handler = new CreateArticleCommandHandler( _mockDbContext.Object, _mockSlugHelper.Object, - _mockCurrentUserService.Object, - _mockPublisher.Object + _mockCurrentUserService.Object ); } @@ -242,12 +239,14 @@ public async void Handle_Should_PublishArticleCreatedEvent_WhenGeneratedIdIsUniq var result = await _handler.Handle(command, default); //Assert - _mockPublisher.Verify( - x => x.Publish( - It.Is(e => e.Id == result.Value.Id), - It.IsAny() - ), - Times.Once - ); + //TODO: Reimplement without MediatR + // _mockPublisher.Verify( + // x => x.Publish( + // It.Is(e => e.Id == result.Value.Id), + // It.IsAny() + // ), + // Times.Once + // ); + throw new Exception("TODO"); } } \ No newline at end of file diff --git a/Application/Application.csproj b/Application/Application.csproj index 7ad2828..7b41381 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -12,7 +12,6 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Application/Common/Behaviours/QueryCachingBehavior.cs b/Application/Common/Behaviours/QueryCachingBehavior.cs index 26754cb..7b658f4 100644 --- a/Application/Common/Behaviours/QueryCachingBehavior.cs +++ b/Application/Common/Behaviours/QueryCachingBehavior.cs @@ -1,16 +1,16 @@ -using Application.Common.Caching; -using Application.Common.Messaging; -using MediatR; - -namespace Application.Common.Behaviours; - -public class QueryCachingBehavior(ICacheService cacheService) : IPipelineBehavior - where TRequest : ICachedQuery -{ - public async Task Handle(TRequest request, RequestHandlerDelegate next, - CancellationToken token) - { - if (request.IgnoreCaching) return await next(); - return await cacheService.GetOrCreateAsync(request.Key, _ => next(), request.Expiration, token); - } -} \ No newline at end of file +// using Application.Common.Caching; +// using Application.Common.Messaging; +// using MediatR; +// +// namespace Application.Common.Behaviours; +// +// public class QueryCachingBehavior(ICacheService cacheService) : IPipelineBehavior +// where TRequest : ICachedQuery +// { +// public async Task Handle(TRequest request, RequestHandlerDelegate next, +// CancellationToken token) +// { +// if (request.IgnoreCaching) return await next(); +// return await cacheService.GetOrCreateAsync(request.Key, _ => next(), request.Expiration, token); +// } +// } \ No newline at end of file diff --git a/Application/Common/Behaviours/ValidationBehavior.cs b/Application/Common/Behaviours/ValidationBehavior.cs index da38cdf..57de890 100644 --- a/Application/Common/Behaviours/ValidationBehavior.cs +++ b/Application/Common/Behaviours/ValidationBehavior.cs @@ -1,52 +1,52 @@ -using System.Reflection; -using ErrorOr; -using FluentValidation; -using FluentValidation.Results; -using MediatR; -using ValidationException = Application.Common.Exceptions.ValidationException; - -namespace Application.Common.Behaviours; - -public class ValidationBehavior : IPipelineBehavior - where TRequest : IRequest - where TResponse : IErrorOr -{ - private readonly IValidator? _validator; - - public ValidationBehavior(IValidator? validator = null) - { - _validator = validator; - } - - public async Task Handle( - TRequest request, - RequestHandlerDelegate next, - CancellationToken token) - { - if (_validator == null) return await next(); - - var validationResult = await _validator.ValidateAsync(request, token); - - if (validationResult.IsValid) return await next(); - - return TryCreateResponseFromErrors(validationResult.Errors, out var response) - ? response - : throw new ValidationException(validationResult.Errors); - } - - private static bool TryCreateResponseFromErrors(List validationFailures, out TResponse response) - { - var errors = validationFailures.ConvertAll(x => Error.Validation( - x.PropertyName, - x.ErrorMessage)); - - response = (TResponse?) typeof(TResponse) - .GetMethod( - nameof(ErrorOr.From), - BindingFlags.Static | BindingFlags.Public, - new[] {typeof(List)})? - .Invoke(null, new[] {errors})!; - - return response is not null; - } -} \ No newline at end of file +// using System.Reflection; +// using ErrorOr; +// using FluentValidation; +// using FluentValidation.Results; +// using MediatR; +// using ValidationException = Application.Common.Exceptions.ValidationException; +// +// namespace Application.Common.Behaviours; +// +// public class ValidationBehavior : IPipelineBehavior +// where TRequest : IRequest +// where TResponse : IErrorOr +// { +// private readonly IValidator? _validator; +// +// public ValidationBehavior(IValidator? validator = null) +// { +// _validator = validator; +// } +// +// public async Task Handle( +// TRequest request, +// RequestHandlerDelegate next, +// CancellationToken token) +// { +// if (_validator == null) return await next(); +// +// var validationResult = await _validator.ValidateAsync(request, token); +// +// if (validationResult.IsValid) return await next(); +// +// return TryCreateResponseFromErrors(validationResult.Errors, out var response) +// ? response +// : throw new ValidationException(validationResult.Errors); +// } +// +// private static bool TryCreateResponseFromErrors(List validationFailures, out TResponse response) +// { +// var errors = validationFailures.ConvertAll(x => Error.Validation( +// x.PropertyName, +// x.ErrorMessage)); +// +// response = (TResponse?) typeof(TResponse) +// .GetMethod( +// nameof(ErrorOr.From), +// BindingFlags.Static | BindingFlags.Public, +// new[] {typeof(List)})? +// .Invoke(null, new[] {errors})!; +// +// return response is not null; +// } +// } \ No newline at end of file diff --git a/Application/Common/Messaging/ICachedQuery.cs b/Application/Common/Messaging/ICachedQuery.cs index 0222386..6f4eecb 100644 --- a/Application/Common/Messaging/ICachedQuery.cs +++ b/Application/Common/Messaging/ICachedQuery.cs @@ -1,12 +1,13 @@ namespace Application.Common.Messaging; -public interface ICachedQuery : IQuery, ICachedQuery; +//TODO: Redo without MediatR +public interface ICachedQuery : IQuery, ICachedQuery; public interface ICachedQuery -{ +{ string Key { get; } - + TimeSpan? Expiration { get; } - + bool IgnoreCaching { get; } } \ No newline at end of file diff --git a/Application/Common/Messaging/IQuery.cs b/Application/Common/Messaging/IQuery.cs index 1a935f3..de720e2 100644 --- a/Application/Common/Messaging/IQuery.cs +++ b/Application/Common/Messaging/IQuery.cs @@ -1,6 +1,3 @@ -using ErrorOr; -using MediatR; - namespace Application.Common.Messaging; -public interface IQuery : IRequest>; \ No newline at end of file +public interface IQuery; \ No newline at end of file diff --git a/Application/Extensions/ServiceCollectionExt.cs b/Application/Extensions/ServiceCollectionExt.cs index d462950..cc38b7f 100644 --- a/Application/Extensions/ServiceCollectionExt.cs +++ b/Application/Extensions/ServiceCollectionExt.cs @@ -1,7 +1,25 @@ using System.Reflection; -using Application.Common.Behaviours; +using Application.Features.Articles.CreateArticle; +using Application.Features.Articles.DeleteArticle; +using Application.Features.Articles.EditArticle; +using Application.Features.Articles.GetArticle; +using Application.Features.Articles.GetPendingRevisions; +using Application.Features.Articles.GetPendingRevisionsCount; +using Application.Features.Articles.GetRevisionHistory; +using Application.Features.Articles.GetRevisionReviewHistory; +using Application.Features.Articles.ReviewRevision; +using Application.Features.Articles.SearchArticles; +using Application.Features.Articles.SetRedirect; +using Application.Features.Authors.CreateAuthor; +using Application.Features.Authors.EditAuthor; +using Application.Features.Categories.CreateCategory; +using Application.Features.Categories.DeleteCategory; +using Application.Features.Categories.GetCategories; +using Application.Features.Categories.GetCategoriesTree; +using Application.Features.Categories.GetCategoryArticles; +using Application.Features.Navigations.GetNavigationTree; +using Application.Features.Navigations.UpdateNavigationsTree; using FluentValidation; -using MediatR; using Microsoft.Extensions.DependencyInjection; using Slugify; @@ -11,12 +29,36 @@ public static class ServiceCollectionExtensions { public static IServiceCollection AddApplicationServices(this IServiceCollection services) { - services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); - services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); - services.AddTransient(typeof(IPipelineBehavior<,>), typeof(QueryCachingBehavior<,>)); + //TODO: Reimplement without MediatR + // services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); + // services.AddTransient(typeof(IPipelineBehavior<,>), typeof(QueryCachingBehavior<,>)); services.AddTransient(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + return services; } } \ No newline at end of file diff --git a/Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs b/Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs index 3cc84fb..48e8a3c 100644 --- a/Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs +++ b/Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs @@ -1,11 +1,9 @@ -using MediatR; - namespace Application.Features.Articles.CreateArticle; -public class ArticleCreatedEvent : INotification -{ - public required string Id { get; init; } - public required string Title { get; init; } - public required string Content { get; init; } - public required List CategoryIds { get; init; } -} \ No newline at end of file +// public class ArticleCreatedEvent : INotification +// { +// public required string Id { get; init; } +// public required string Title { get; init; } +// public required string Content { get; init; } +// public required List CategoryIds { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs b/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs index b24d8c0..7b08e27 100644 --- a/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs +++ b/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs @@ -1,5 +1,4 @@ using ErrorOr; -using MediatR; namespace Application.Features.Articles.CreateArticle; @@ -8,4 +7,4 @@ public record CreateArticleCommand( string Content, string AuthorsNote, List CategoryIds -) : IRequest>; \ No newline at end of file +); \ No newline at end of file diff --git a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs index 0cc6586..37a03ca 100644 --- a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs +++ b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs @@ -2,7 +2,6 @@ using Application.Data; using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; using Slugify; @@ -11,9 +10,8 @@ namespace Application.Features.Articles.CreateArticle; public class CreateArticleCommandHandler( IApplicationDbContext dbContext, ISlugHelper slugHelper, - ICurrentUserService identityService, - IPublisher publisher -) : IRequestHandler> + ICurrentUserService identityService +) : ICreateArticleCommandHandler { public async Task> Handle(CreateArticleCommand command, CancellationToken token) { @@ -52,14 +50,14 @@ public async Task> Handle(CreateArticleCommand co await dbContext.SaveChangesAsync(token); - var articleCreatedEvent = new ArticleCreatedEvent - { - Id = article.Id, - Title = article.Title, - Content = revision.Content, - CategoryIds = categories.Select(e => e.Id).ToList() - }; - await publisher.Publish(articleCreatedEvent, token); + // var articleCreatedEvent = new ArticleCreatedEvent + // { + // Id = article.Id, + // Title = article.Title, + // Content = revision.Content, + // CategoryIds = categories.Select(e => e.Id).ToList() + // }; + // await publisher.Publish(articleCreatedEvent, token); return new CreateArticleResponse(article.Id); } diff --git a/Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs b/Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs new file mode 100644 index 0000000..a0bb7f5 --- /dev/null +++ b/Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Articles.CreateArticle; + +public interface ICreateArticleCommandHandler +{ + Task> Handle(CreateArticleCommand command, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs b/Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs index 88a466a..9c84c38 100644 --- a/Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs +++ b/Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs @@ -1,8 +1,6 @@ -using MediatR; - namespace Application.Features.Articles.DeleteArticle; -public class ArticleDeletedEvent : INotification -{ - public required string Id { get; init; } -} \ No newline at end of file +// public class ArticleDeletedEvent : INotification +// { +// public required string Id { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs b/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs index aae45ca..7ed27d6 100644 --- a/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs +++ b/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs @@ -1,6 +1,3 @@ -using ErrorOr; -using MediatR; +namespace Application.Features.Articles.DeleteArticle; -namespace Application.Features.Articles.DeleteArticle; - -public record DeleteArticleCommand(string Id) : IRequest>; \ No newline at end of file +public record DeleteArticleCommand(string Id); \ No newline at end of file diff --git a/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs b/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs index 1403eb7..e51a23b 100644 --- a/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs +++ b/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs @@ -1,13 +1,9 @@ using Application.Data; using ErrorOr; -using MediatR; namespace Application.Features.Articles.DeleteArticle; -public class DeleteArticleCommandHandler( - IApplicationDbContext dbContext, - IPublisher publisher -) : IRequestHandler> +public class DeleteArticleCommandHandler(IApplicationDbContext dbContext) : IDeleteArticleCommandHandler { public async Task> Handle(DeleteArticleCommand request, CancellationToken token) { @@ -16,8 +12,8 @@ public async Task> Handle(DeleteArticleCommand re dbContext.Articles.Remove(article); await dbContext.SaveChangesAsync(token); - var articleDeletedEvent = new ArticleDeletedEvent {Id = article.Id}; - await publisher.Publish(articleDeletedEvent, token); + // var articleDeletedEvent = new ArticleDeletedEvent {Id = article.Id}; + // await publisher.Publish(articleDeletedEvent, token); return new DeleteArticleResponse(article.Id); } diff --git a/Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs b/Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs new file mode 100644 index 0000000..29d96d4 --- /dev/null +++ b/Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Articles.DeleteArticle; + +public interface IDeleteArticleCommandHandler +{ + Task> Handle(DeleteArticleCommand request, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/EditArticle/ArticleEditedEvent.cs b/Application/Features/Articles/EditArticle/ArticleEditedEvent.cs index 806c4ab..8b206a4 100644 --- a/Application/Features/Articles/EditArticle/ArticleEditedEvent.cs +++ b/Application/Features/Articles/EditArticle/ArticleEditedEvent.cs @@ -1,11 +1,9 @@ -using MediatR; - namespace Application.Features.Articles.EditArticle; -public class ArticleEditedEvent : INotification -{ - public required string Id { get; init; } - public required string Content { get; init; } - public required string AuthorsNote { get; init; } - public required List CategoryIds { get; init; } -} \ No newline at end of file +// public class ArticleEditedEvent : INotification +// { +// public required string Id { get; init; } +// public required string Content { get; init; } +// public required string AuthorsNote { get; init; } +// public required List CategoryIds { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Articles/EditArticle/EditArticleCommand.cs b/Application/Features/Articles/EditArticle/EditArticleCommand.cs index 0da2309..8ba1cff 100644 --- a/Application/Features/Articles/EditArticle/EditArticleCommand.cs +++ b/Application/Features/Articles/EditArticle/EditArticleCommand.cs @@ -1,11 +1,8 @@ -using ErrorOr; -using MediatR; - -namespace Application.Features.Articles.EditArticle; +namespace Application.Features.Articles.EditArticle; public record EditArticleCommand( string Id, string Content, string AuthorsNote, List CategoryIds -) : IRequest>; \ No newline at end of file +); \ No newline at end of file diff --git a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs index ed2ff4f..5b834bc 100644 --- a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs +++ b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs @@ -2,16 +2,14 @@ using Application.Data; using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.EditArticle; public class EditArticleCommandHandler( IApplicationDbContext dbContext, - ICurrentUserService identityService, - IPublisher publisher -) : IRequestHandler> + ICurrentUserService identityService +) : IEditArticleCommandHandler { public async Task> Handle(EditArticleCommand request, CancellationToken token) { @@ -41,14 +39,14 @@ public async Task> Handle(EditArticleCommand reques await dbContext.Revisions.AddAsync(revision, token); await dbContext.SaveChangesAsync(token); - var articleEditedEvent = new ArticleEditedEvent - { - Id = article.Id, - Content = revision.Content, - AuthorsNote = request.AuthorsNote, - CategoryIds = requestCategories.Select(e => e.Id).ToList() - }; - await publisher.Publish(articleEditedEvent, token); + // var articleEditedEvent = new ArticleEditedEvent + // { + // Id = article.Id, + // Content = revision.Content, + // AuthorsNote = request.AuthorsNote, + // CategoryIds = requestCategories.Select(e => e.Id).ToList() + // }; + // await publisher.Publish(articleEditedEvent, token); return new EditArticleResponse(article.Id); } diff --git a/Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs b/Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs new file mode 100644 index 0000000..7872d4c --- /dev/null +++ b/Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Articles.EditArticle; + +public interface IEditArticleCommandHandler +{ + Task> Handle(EditArticleCommand request, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs b/Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs index b5b1b30..af1b0f0 100644 --- a/Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs +++ b/Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs @@ -3,39 +3,39 @@ using Application.Features.Articles.DeleteArticle; using Application.Features.Articles.ReviewRevision; using Application.Features.Articles.SetRedirect; -using MediatR; namespace Application.Features.Articles.EventHandlers; -public class CacheInvalidationArticleHandler(ICacheService cacheService) : - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler -{ - public async Task Handle(ArticleDeletedEvent notification, CancellationToken token) - { - await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.Id), token); - } - - public async Task Handle(RevisionReviewedEvent notification, CancellationToken token) - { - await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.ArticleId), token); - } - - public async Task Handle(RedirectSetEvent notification, CancellationToken token) - { - await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.ArticleId), token); - await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.RedirectId), token); - } - - public async Task Handle(ArticleChangedRevisionEvent notification, CancellationToken token) - { - var difference = notification.PreviousRevisionCategoryIds.Except(notification.CurrentRevisionCategoryIds) - .Concat(notification.CurrentRevisionCategoryIds.Except(notification.PreviousRevisionCategoryIds)); - foreach (var s in difference) - { - await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(s), token); - } - } -} \ No newline at end of file +//TODO: Reimplement without MediatR +// public class CacheInvalidationArticleHandler(ICacheService cacheService) : +// INotificationHandler, +// INotificationHandler, +// INotificationHandler, +// INotificationHandler +// { +// public async Task Handle(ArticleDeletedEvent notification, CancellationToken token) +// { +// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.Id), token); +// } +// +// public async Task Handle(RevisionReviewedEvent notification, CancellationToken token) +// { +// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.ArticleId), token); +// } +// +// public async Task Handle(RedirectSetEvent notification, CancellationToken token) +// { +// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.ArticleId), token); +// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.RedirectId), token); +// } +// +// public async Task Handle(ArticleChangedRevisionEvent notification, CancellationToken token) +// { +// var difference = notification.PreviousRevisionCategoryIds.Except(notification.CurrentRevisionCategoryIds) +// .Concat(notification.CurrentRevisionCategoryIds.Except(notification.PreviousRevisionCategoryIds)); +// foreach (var s in difference) +// { +// await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(s), token); +// } +// } +// } \ No newline at end of file diff --git a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs index 77236d5..e1393f1 100644 --- a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs +++ b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs @@ -3,7 +3,6 @@ using Application.Data; using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetArticle; @@ -11,7 +10,7 @@ namespace Application.Features.Articles.GetArticle; public class GetArticleQueryHandler( IApplicationDbContext dbContext, IIdentityService identityService -) : IRequestHandler> +) : IGetArticleQueryHandler { public async Task> Handle(GetArticleQuery query, CancellationToken token) { diff --git a/Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs b/Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs new file mode 100644 index 0000000..1495972 --- /dev/null +++ b/Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Articles.GetArticle; + +public interface IGetArticleQueryHandler +{ + Task> Handle(GetArticleQuery query, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs index 852d64b..f23dc03 100644 --- a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs +++ b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetPendingRevisions; -public record GetPendingRevisionsQuery : IQuery; \ No newline at end of file +public record GetPendingRevisionsQuery : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs index 173c01e..2faacb9 100644 --- a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs +++ b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs @@ -1,16 +1,12 @@ using Application.Data; -using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetPendingRevisions; -public class GetPendingRevisionsQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetPendingRevisionsQueryHandler(IApplicationDbContext dbContext) : IGetPendingRevisionsQueryHandler { - public async Task> Handle(GetPendingRevisionsQuery query, - CancellationToken token) + public async Task> Handle(GetPendingRevisionsQuery query, CancellationToken token) { var pendingRevisions = await dbContext.Revisions .Where(e => e.LatestReviewId == null) diff --git a/Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs b/Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs new file mode 100644 index 0000000..a63df51 --- /dev/null +++ b/Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Articles.GetPendingRevisions; + +public interface IGetPendingRevisionsQueryHandler +{ + Task> Handle(GetPendingRevisionsQuery query, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs index 90c5337..ad8d4b7 100644 --- a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs +++ b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetPendingRevisionsCount; -public record GetPendingRevisionsCountQuery : IQuery; \ No newline at end of file +public record GetPendingRevisionsCountQuery : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs index 98d2671..4be43c6 100644 --- a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs +++ b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs @@ -1,12 +1,10 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetPendingRevisionsCount; -public class GetPendingRevisionsCountQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetPendingRevisionsCountQueryHandler(IApplicationDbContext dbContext) : IGetPendingRevisionsCountQueryHandler { public async Task> Handle(GetPendingRevisionsCountQuery query, CancellationToken token) diff --git a/Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs b/Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs new file mode 100644 index 0000000..7464672 --- /dev/null +++ b/Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Articles.GetPendingRevisionsCount; + +public interface IGetPendingRevisionsCountQueryHandler +{ + Task> Handle(GetPendingRevisionsCountQuery query, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs index fcbf8d0..4a6240c 100644 --- a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs +++ b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetRevisionHistory; -public record GetRevisionHistoryQuery(string Id) : IQuery; \ No newline at end of file +public record GetRevisionHistoryQuery(string Id) : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs index dbbd068..fc3cd2d 100644 --- a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs +++ b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs @@ -1,12 +1,10 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetRevisionHistory; -public class GetRevisionHistoryQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetRevisionHistoryQueryHandler(IApplicationDbContext dbContext) : IGetRevisionHistoryQueryHandler { public async Task> Handle(GetRevisionHistoryQuery query, CancellationToken token) @@ -18,12 +16,12 @@ public async Task> Handle(GetRevisionHistory if (article == null) return Errors.Article.NotFound; - if (article.RedirectArticle != null) + if (article.RedirectArticle != null) article = article.RedirectArticle; var revisions = await dbContext.Revisions .Where(e => e.ArticleId == article.Id) - .OrderByDescending(e=>e.Timestamp) + .OrderByDescending(e => e.Timestamp) .Select(e => new GetRevisionHistoryResponse.Element( e.Id, new GetRevisionHistoryResponse.Author(e.Author.Id, e.Author.Name), diff --git a/Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs new file mode 100644 index 0000000..e8790d0 --- /dev/null +++ b/Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Articles.GetRevisionHistory; + +public interface IGetRevisionHistoryQueryHandler +{ + Task> Handle(GetRevisionHistoryQuery query, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs index 62635e9..008a51f 100644 --- a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs +++ b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetRevisionReviewHistory; -public record GetRevisionReviewHistoryQuery(Guid Id) : IQuery; \ No newline at end of file +public record GetRevisionReviewHistoryQuery(Guid Id) : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs index aa0e19e..b2b35b1 100644 --- a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs +++ b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs @@ -1,13 +1,10 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetRevisionReviewHistory; -public class GetRevisionReviewHistoryQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetRevisionReviewHistoryQueryHandler(IApplicationDbContext dbContext) : IGetRevisionReviewHistoryQueryHandler { public async Task> Handle(GetRevisionReviewHistoryQuery query, CancellationToken token) diff --git a/Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs new file mode 100644 index 0000000..d290e46 --- /dev/null +++ b/Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Articles.GetRevisionReviewHistory; + +public interface IGetRevisionReviewHistoryQueryHandler +{ + Task> Handle(GetRevisionReviewHistoryQuery query, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs b/Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs index ef07de2..32c8e6e 100644 --- a/Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs +++ b/Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs @@ -1,12 +1,10 @@ -using MediatR; - namespace Application.Features.Articles.ReviewRevision; -public class ArticleChangedRevisionEvent : INotification -{ - public required string ArticleId { get; init; } - public required Guid? PreviousRevisionId { get; init; } - public required Guid? CurrentRevisionId { get; init; } - public required List PreviousRevisionCategoryIds { get; init; } - public required List CurrentRevisionCategoryIds { get; init; } -} \ No newline at end of file +// public class ArticleChangedRevisionEvent : INotification +// { +// public required string ArticleId { get; init; } +// public required Guid? PreviousRevisionId { get; init; } +// public required Guid? CurrentRevisionId { get; init; } +// public required List PreviousRevisionCategoryIds { get; init; } +// public required List CurrentRevisionCategoryIds { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs b/Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs new file mode 100644 index 0000000..3ac00a3 --- /dev/null +++ b/Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Articles.ReviewRevision; + +public interface IReviewRevisionCommandHandler +{ + Task> Handle(ReviewRevisionCommand command, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs index 75fd35a..2ee2542 100644 --- a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs +++ b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs @@ -1,12 +1,9 @@ using Domain.Entities; -using ErrorOr; -using MediatR; namespace Application.Features.Articles.ReviewRevision; public record ReviewRevisionCommand( - Guid RevisionId, - ReviewStatus Status, - string Review - ) - : IRequest>; \ No newline at end of file + Guid RevisionId, + ReviewStatus Status, + string Review +); \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs index 7da7b22..5ace5cf 100644 --- a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs +++ b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs @@ -2,16 +2,14 @@ using Application.Data; using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.ReviewRevision; public class ReviewRevisionCommandHandler( IApplicationDbContext dbContext, - ICurrentUserService identityService, - IPublisher publisher -) : IRequestHandler> + ICurrentUserService identityService +) : IReviewRevisionCommandHandler { public async Task> Handle(ReviewRevisionCommand command, CancellationToken token) @@ -40,7 +38,7 @@ public async Task> Handle(ReviewRevisionCommand Revision = revision }; - ArticleChangedRevisionEvent? articleChangedRevisionEvent = null; + // ArticleChangedRevisionEvent? articleChangedRevisionEvent = null; revision.LatestReview = review; @@ -60,42 +58,42 @@ public async Task> Handle(ReviewRevisionCommand .OrderByDescending(e => e.Timestamp) .FirstOrDefaultAsync(token); - ChangeArticleRevision(article, rollbackRevision, out articleChangedRevisionEvent); + // ChangeArticleRevision(article, rollbackRevision, out articleChangedRevisionEvent); } if (command is {Status: ReviewStatus.Accepted}) { - ChangeArticleRevision(article, revision, out articleChangedRevisionEvent); + // ChangeArticleRevision(article, revision, out articleChangedRevisionEvent); } await dbContext.SaveChangesAsync(token); - var revisionReviewedEvent = new RevisionReviewedEvent - { - ArticleId = article.Id, - RevisionId = revision.Id, - Status = command.Status, - Review = command.Review, - }; - await publisher.Publish(revisionReviewedEvent, token); - - if (articleChangedRevisionEvent != null) await publisher.Publish(articleChangedRevisionEvent, token); + // var revisionReviewedEvent = new RevisionReviewedEvent + // { + // ArticleId = article.Id, + // RevisionId = revision.Id, + // Status = command.Status, + // Review = command.Review, + // }; + // await publisher.Publish(revisionReviewedEvent, token); + // + // if (articleChangedRevisionEvent != null) await publisher.Publish(articleChangedRevisionEvent, token); return new ReviewRevisionResponse(review.Id); } - private static void ChangeArticleRevision(Article article, Revision? revision, out ArticleChangedRevisionEvent articleChangedRevisionEvent) - { - var previousRevisionId = article.CurrentRevision?.Id; - var previousRevisionCategoriesIds = article.CurrentRevision?.Categories.Select(e => e.Id).ToList() ?? new List(); - article.CurrentRevision = revision; - articleChangedRevisionEvent = new ArticleChangedRevisionEvent - { - ArticleId = article.Id, - PreviousRevisionId = previousRevisionId, - CurrentRevisionId = article.CurrentRevision?.Id, - PreviousRevisionCategoryIds = previousRevisionCategoriesIds, - CurrentRevisionCategoryIds = revision?.Categories.Select(e => e.Id).ToList() ?? new List() - }; - } + // private static void ChangeArticleRevision(Article article, Revision? revision, out ArticleChangedRevisionEvent articleChangedRevisionEvent) + // { + // var previousRevisionId = article.CurrentRevision?.Id; + // var previousRevisionCategoriesIds = article.CurrentRevision?.Categories.Select(e => e.Id).ToList() ?? new List(); + // article.CurrentRevision = revision; + // articleChangedRevisionEvent = new ArticleChangedRevisionEvent + // { + // ArticleId = article.Id, + // PreviousRevisionId = previousRevisionId, + // CurrentRevisionId = article.CurrentRevision?.Id, + // PreviousRevisionCategoryIds = previousRevisionCategoriesIds, + // CurrentRevisionCategoryIds = revision?.Categories.Select(e => e.Id).ToList() ?? new List() + // }; + // } } \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs b/Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs index 1dc3d38..06b6227 100644 --- a/Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs +++ b/Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs @@ -1,12 +1,11 @@ using Domain.Entities; -using MediatR; namespace Application.Features.Articles.ReviewRevision; -public class RevisionReviewedEvent : INotification -{ - public required string ArticleId { get; init; } - public required Guid RevisionId { get; init; } - public required ReviewStatus Status { get; init; } - public required string Review { get; init; } -} \ No newline at end of file +// public class RevisionReviewedEvent : INotification +// { +// public required string ArticleId { get; init; } +// public required Guid RevisionId { get; init; } +// public required ReviewStatus Status { get; init; } +// public required string Review { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs b/Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs new file mode 100644 index 0000000..bf1f6ba --- /dev/null +++ b/Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Articles.SearchArticles; + +public interface ISearchArticlesQueryHandler +{ + Task> Handle(SearchArticlesQuery query, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs b/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs index 6fa2d01..2523afa 100644 --- a/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs +++ b/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs @@ -2,7 +2,7 @@ namespace Application.Features.Articles.SearchArticles; -public record SearchArticlesQuery : IQuery +public record SearchArticlesQuery : IQuery { public string? SearchTerm { get; } public int Page { get; } diff --git a/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs b/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs index bc624a8..b4e27b1 100644 --- a/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs +++ b/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs @@ -1,12 +1,10 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.SearchArticles; -public class SearchArticlesQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class SearchArticlesQueryHandler(IApplicationDbContext dbContext) : ISearchArticlesQueryHandler { public async Task> Handle(SearchArticlesQuery query, CancellationToken token) { diff --git a/Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs b/Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs new file mode 100644 index 0000000..ed7e7de --- /dev/null +++ b/Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Articles.SetRedirect; + +public interface ISetRedirectCommandHandler +{ + Task> Handle(SetRedirectCommand command, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/RedirectSetEvent.cs b/Application/Features/Articles/SetRedirect/RedirectSetEvent.cs index a6beedd..d46bc58 100644 --- a/Application/Features/Articles/SetRedirect/RedirectSetEvent.cs +++ b/Application/Features/Articles/SetRedirect/RedirectSetEvent.cs @@ -1,9 +1,7 @@ -using MediatR; - namespace Application.Features.Articles.SetRedirect; -public class RedirectSetEvent : INotification -{ - public required string ArticleId { get; init; } - public required string RedirectId { get; init; } -} \ No newline at end of file +// public class RedirectSetEvent : INotification +// { +// public required string ArticleId { get; init; } +// public required string RedirectId { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs b/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs index f877d67..359946d 100644 --- a/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs +++ b/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs @@ -1,7 +1,3 @@ -using Domain.Entities; -using ErrorOr; -using MediatR; - namespace Application.Features.Articles.SetRedirect; -public record SetRedirectCommand(string ArticleId, string RedirectId) : IRequest>; \ No newline at end of file +public record SetRedirectCommand(string ArticleId, string RedirectId); \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs b/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs index ea84f46..266e882 100644 --- a/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs +++ b/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs @@ -1,16 +1,10 @@ -using Application.Authorization.Abstractions; using Application.Data; -using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.SetRedirect; -public class SetRedirectCommandHandler( - IApplicationDbContext dbContext, - IPublisher publisher -) : IRequestHandler> +public class SetRedirectCommandHandler(IApplicationDbContext dbContext) : ISetRedirectCommandHandler { public async Task> Handle(SetRedirectCommand command, CancellationToken token) @@ -37,12 +31,12 @@ await dbContext.Revisions await dbContext.SaveChangesAsync(token); - var revisionReviewedEvent = new RedirectSetEvent - { - ArticleId = article.Id, - RedirectId = redirectArticle.Id, - }; - await publisher.Publish(revisionReviewedEvent, token); + // var revisionReviewedEvent = new RedirectSetEvent + // { + // ArticleId = article.Id, + // RedirectId = redirectArticle.Id, + // }; + // await publisher.Publish(revisionReviewedEvent, token); return new SetRedirectResponse(redirectArticle.Id); } diff --git a/Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs b/Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs index 4f7ec71..cf55b80 100644 --- a/Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs +++ b/Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs @@ -1,9 +1,7 @@ -using MediatR; - namespace Application.Features.Authors.CreateAuthor; -public class AuthorCreatedEvent : INotification -{ - public required string Id { get; init; } - public required string Name { get; init; } -} \ No newline at end of file +// public class AuthorCreatedEvent : INotification +// { +// public required string Id { get; init; } +// public required string Name { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs index be92681..bdc9c4a 100644 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs +++ b/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs @@ -1,6 +1,3 @@ -using ErrorOr; -using MediatR; +namespace Application.Features.Authors.CreateAuthor; -namespace Application.Features.Authors.CreateAuthor; - -public record CreateAuthorCommand(string Id, string Name) : IRequest>; \ No newline at end of file +public record CreateAuthorCommand(string Id, string Name); \ No newline at end of file diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs index 11f271c..cfc285c 100644 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs +++ b/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs @@ -1,14 +1,12 @@ using Application.Data; using Domain.Entities; using ErrorOr; -using MediatR; namespace Application.Features.Authors.CreateAuthor; public class CreateAuthorCommandHandler( - IApplicationDbContext dbContext, - IPublisher publisher -) : IRequestHandler> + IApplicationDbContext dbContext +) : ICreateAuthorCommandHandler { public async Task> Handle(CreateAuthorCommand command, CancellationToken token) { @@ -20,12 +18,12 @@ public async Task> Handle(CreateAuthorCommand comm dbContext.Authors.Add(author); await dbContext.SaveChangesAsync(token); - var authorCreatedEvent = new AuthorCreatedEvent - { - Id = author.Id, - Name = author.Name - }; - await publisher.Publish(authorCreatedEvent, token); + // var authorCreatedEvent = new AuthorCreatedEvent + // { + // Id = author.Id, + // Name = author.Name + // }; + // await publisher.Publish(authorCreatedEvent, token); return new CreateAuthorResponse(author.Id); } diff --git a/Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs b/Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs new file mode 100644 index 0000000..749bbe7 --- /dev/null +++ b/Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Authors.CreateAuthor; + +public interface ICreateAuthorCommandHandler +{ + Task> Handle(CreateAuthorCommand command, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs b/Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs index af61e18..e8ddfac 100644 --- a/Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs +++ b/Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs @@ -1,9 +1,7 @@ -using MediatR; - namespace Application.Features.Authors.EditAuthor; -public class AuthorEditedEvent : INotification -{ - public required string Id { get; init; } - public required string Name { get; init; } -} \ No newline at end of file +// public class AuthorEditedEvent : INotification +// { +// public required string Id { get; init; } +// public required string Name { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs b/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs index d1059d4..5ac9c6b 100644 --- a/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs +++ b/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs @@ -1,6 +1,3 @@ -using ErrorOr; -using MediatR; +namespace Application.Features.Authors.EditAuthor; -namespace Application.Features.Authors.EditAuthor; - -public record EditAuthorCommand(string Id, string Name) : IRequest>; \ No newline at end of file +public record EditAuthorCommand(string Id, string Name); \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs b/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs index ed0e35c..d0a6ecd 100644 --- a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs +++ b/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs @@ -1,14 +1,12 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Authors.EditAuthor; public class EditAuthorCommandHandler( - IApplicationDbContext dbContext, - IPublisher publisher -) : IRequestHandler> + IApplicationDbContext dbContext +) : IEditAuthorCommandHandler { public async Task> Handle(EditAuthorCommand command, CancellationToken token) { @@ -20,12 +18,12 @@ public async Task> Handle(EditAuthorCommand command, await dbContext.SaveChangesAsync(token); - var authorEditedEvent = new AuthorEditedEvent - { - Id = author.Id, - Name = author.Name - }; - await publisher.Publish(authorEditedEvent, token); + // var authorEditedEvent = new AuthorEditedEvent + // { + // Id = author.Id, + // Name = author.Name + // }; + // await publisher.Publish(authorEditedEvent, token); return new EditAuthorResponse(author.Id); } diff --git a/Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs b/Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs new file mode 100644 index 0000000..d87b8b5 --- /dev/null +++ b/Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Authors.EditAuthor; + +public interface IEditAuthorCommandHandler +{ + Task> Handle(EditAuthorCommand command, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs b/Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs index f113523..dc915cb 100644 --- a/Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs +++ b/Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs @@ -1,10 +1,8 @@ -using MediatR; - namespace Application.Features.Categories.CreateCategory; -public class CategoryCreatedEvent : INotification -{ - public required string Id { get; init; } - public required string Name { get; init; } - public string? ParentId { get; init; } -} \ No newline at end of file +// public class CategoryCreatedEvent : INotification +// { +// public required string Id { get; init; } +// public required string Name { get; init; } +// public string? ParentId { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs b/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs index 55a873b..dd4a356 100644 --- a/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs +++ b/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs @@ -1,6 +1,3 @@ -using ErrorOr; -using MediatR; - namespace Application.Features.Categories.CreateCategory; -public record CreateCategoryCommand(string Name, string? ParentId) : IRequest>; \ No newline at end of file +public record CreateCategoryCommand(string Name, string? ParentId); \ No newline at end of file diff --git a/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs b/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs index 6879880..1b8800d 100644 --- a/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs +++ b/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs @@ -1,7 +1,6 @@ using Application.Data; using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; using Slugify; @@ -9,9 +8,8 @@ namespace Application.Features.Categories.CreateCategory; public class CreateCategoryCommandHandler( IApplicationDbContext dbContext, - ISlugHelper slugHelper, - IPublisher publisher -) : IRequestHandler> + ISlugHelper slugHelper +) : ICreateCategoryCommandHandler { public async Task> Handle(CreateCategoryCommand command, CancellationToken token) { @@ -49,13 +47,13 @@ public async Task> Handle(CreateCategoryCommand dbContext.Categories.Add(entity); await dbContext.SaveChangesAsync(token); - var categoryCreatedEvent = new CategoryCreatedEvent - { - Id = entity.Id, - Name = entity.Name, - ParentId = parent?.Id - }; - await publisher.Publish(categoryCreatedEvent, token); + // var categoryCreatedEvent = new CategoryCreatedEvent + // { + // Id = entity.Id, + // Name = entity.Name, + // ParentId = parent?.Id + // }; + // await publisher.Publish(categoryCreatedEvent, token); return new CreateCategoryResponse(entity.Id); } diff --git a/Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs b/Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs new file mode 100644 index 0000000..d05b0ad --- /dev/null +++ b/Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs @@ -0,0 +1,8 @@ +using ErrorOr; + +namespace Application.Features.Categories.CreateCategory; + +public interface ICreateCategoryCommandHandler +{ + Task> Handle(CreateCategoryCommand command, CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs b/Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs index f0d6880..71c1916 100644 --- a/Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs +++ b/Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs @@ -1,8 +1,6 @@ -using MediatR; - namespace Application.Features.Categories.DeleteCategory; -public class CategoryDeletedEvent : INotification -{ - public required string Id { get; init; } -} \ No newline at end of file +// public class CategoryDeletedEvent : INotification +// { +// public required string Id { get; init; } +// } \ No newline at end of file diff --git a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs index 207ee69..2ea7b7c 100644 --- a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs +++ b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs @@ -1,6 +1,3 @@ -using ErrorOr; -using MediatR; - namespace Application.Features.Categories.DeleteCategory; -public record DeleteCategoryCommand(string Id) : IRequest>; \ No newline at end of file +public record DeleteCategoryCommand(string Id); \ No newline at end of file diff --git a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs index 0595c81..3505680 100644 --- a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs +++ b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs @@ -1,13 +1,11 @@ using Application.Data; using ErrorOr; -using MediatR; namespace Application.Features.Categories.DeleteCategory; public class DeleteCategoryCommandHandler( - IApplicationDbContext dbContext, - IPublisher publisher -) : IRequestHandler> + IApplicationDbContext dbContext +) : IDeleteCategoryCommandHandler { public async Task> Handle(DeleteCategoryCommand deleteCategoryCommand, CancellationToken token) @@ -17,8 +15,8 @@ public async Task> Handle(DeleteCategoryCommand dbContext.Categories.Remove(category); await dbContext.SaveChangesAsync(token); - var categoryDeletedEvent = new CategoryDeletedEvent {Id = category.Id}; - await publisher.Publish(categoryDeletedEvent, token); + // var categoryDeletedEvent = new CategoryDeletedEvent {Id = category.Id}; + // await publisher.Publish(categoryDeletedEvent, token); return new DeleteCategoryResponse(category.Id); } diff --git a/Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs b/Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs new file mode 100644 index 0000000..4948025 --- /dev/null +++ b/Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Categories.DeleteCategory; + +public interface IDeleteCategoryCommandHandler +{ + Task> Handle(DeleteCategoryCommand deleteCategoryCommand, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs b/Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs index 6c6fd3d..67025cd 100644 --- a/Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs +++ b/Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs @@ -2,24 +2,24 @@ using Application.Common.Constants; using Application.Features.Categories.CreateCategory; using Application.Features.Categories.DeleteCategory; -using MediatR; namespace Application.Features.Categories.EventHandlers; -public class CacheInvalidationCategoriesHandler(ICacheService cacheService) : - INotificationHandler, - INotificationHandler -{ - public async Task Handle(CategoryCreatedEvent notification, CancellationToken token) - { - await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); - await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); - } - - public async Task Handle(CategoryDeletedEvent notification, CancellationToken token) - { - await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); - await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); - await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(notification.Id), token); - } -} \ No newline at end of file +//TODO: Reimplement without MediatR +// public class CacheInvalidationCategoriesHandler(ICacheService cacheService) : +// INotificationHandler, +// INotificationHandler +// { +// public async Task Handle(CategoryCreatedEvent notification, CancellationToken token) +// { +// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); +// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); +// } +// +// public async Task Handle(CategoryDeletedEvent notification, CancellationToken token) +// { +// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); +// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); +// await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(notification.Id), token); +// } +// } \ No newline at end of file diff --git a/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs b/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs index dd5b2fe..30715fe 100644 --- a/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs +++ b/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs @@ -1,12 +1,10 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Categories.GetCategories; -public class GetCategoriesQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetCategoriesQueryHandler(IApplicationDbContext dbContext) : IGetCategoriesQueryHandler { public async Task> Handle(GetCategoriesQuery request, CancellationToken token) diff --git a/Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs b/Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs new file mode 100644 index 0000000..2f8bcad --- /dev/null +++ b/Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Categories.GetCategories; + +public interface IGetCategoriesQueryHandler +{ + Task> Handle(GetCategoriesQuery request, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs index 62113a8..a3ab039 100644 --- a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs +++ b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs @@ -1,14 +1,10 @@ using Application.Data; -using Application.Features.Articles.GetPendingRevisions; -using Domain.Entities; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Categories.GetCategoriesTree; -public class GetCategoriesTreeQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetCategoriesTreeQueryHandler(IApplicationDbContext dbContext) : IGetCategoriesTreeQueryHandler { public async Task> Handle(GetCategoriesTreeQuery request, CancellationToken token) diff --git a/Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs b/Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs new file mode 100644 index 0000000..a970449 --- /dev/null +++ b/Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Categories.GetCategoriesTree; + +public interface IGetCategoriesTreeQueryHandler +{ + Task> Handle(GetCategoriesTreeQuery request, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs index cd02083..bc82c7d 100644 --- a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs +++ b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs @@ -1,12 +1,10 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Categories.GetCategoryArticles; -public class GetCategoryArticlesQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetCategoryArticlesQueryHandler(IApplicationDbContext dbContext) : IGetCategoryArticlesQueryHandler { public async Task> Handle(GetCategoryArticlesQuery request, CancellationToken token) diff --git a/Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs b/Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs new file mode 100644 index 0000000..7851496 --- /dev/null +++ b/Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Categories.GetCategoryArticles; + +public interface IGetCategoryArticlesQueryHandler +{ + Task> Handle(GetCategoryArticlesQuery request, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs b/Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs index 117179d..e8b64b8 100644 --- a/Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs +++ b/Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs @@ -1,15 +1,15 @@ using Application.Common.Caching; using Application.Common.Constants; using Application.Features.Navigations.UpdateNavigationsTree; -using MediatR; namespace Application.Features.Navigations.EventHandlers; -public class CacheInvalidationNavigationsHandler(ICacheService cacheService) : - INotificationHandler -{ - public async Task Handle(NavigationsTreeUpdatedEvent notification, CancellationToken token) - { - await cacheService.RemoveAsync(CachingKeys.Navigation.NavigationsTree, token); - } -} \ No newline at end of file +//TODO: Reimplement without MediatR +// public class CacheInvalidationNavigationsHandler(ICacheService cacheService) : +// INotificationHandler +// { +// public async Task Handle(NavigationsTreeUpdatedEvent notification, CancellationToken token) +// { +// await cacheService.RemoveAsync(CachingKeys.Navigation.NavigationsTree, token); +// } +// } \ No newline at end of file diff --git a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs index c18502b..c58af37 100644 --- a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs +++ b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs @@ -1,12 +1,10 @@ using Application.Data; using ErrorOr; -using MediatR; using Microsoft.EntityFrameworkCore; namespace Application.Features.Navigations.GetNavigationTree; -public class GetNavigationsTreeQueryHandler - (IApplicationDbContext dbContext) : IRequestHandler> +public class GetNavigationsTreeQueryHandler(IApplicationDbContext dbContext) : IGetNavigationsTreeQueryHandler { public async Task> Handle(GetNavigationsTreeQuery request, CancellationToken token) diff --git a/Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs b/Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs new file mode 100644 index 0000000..059a37f --- /dev/null +++ b/Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Navigations.GetNavigationTree; + +public interface IGetNavigationsTreeQueryHandler +{ + Task> Handle(GetNavigationsTreeQuery request, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs b/Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs new file mode 100644 index 0000000..daab520 --- /dev/null +++ b/Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs @@ -0,0 +1,9 @@ +using ErrorOr; + +namespace Application.Features.Navigations.UpdateNavigationsTree; + +public interface IUpdateNavigationsTreeCommandHandler +{ + Task> Handle(UpdateNavigationsTreeCommand request, + CancellationToken token); +} \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs b/Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs index 2dbbe3d..c780139 100644 --- a/Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs +++ b/Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs @@ -1,5 +1,3 @@ -using MediatR; - namespace Application.Features.Navigations.UpdateNavigationsTree; -public class NavigationsTreeUpdatedEvent : INotification; \ No newline at end of file +// public class NavigationsTreeUpdatedEvent : INotification; \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs index 6e5f9ac..3a35eb6 100644 --- a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs +++ b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs @@ -1,10 +1,6 @@ -using Application.Features.Navigations.GetNavigationTree; -using ErrorOr; -using MediatR; - namespace Application.Features.Navigations.UpdateNavigationsTree; -public record UpdateNavigationsTreeCommand(List Data) : IRequest> +public record UpdateNavigationsTreeCommand(List Data) { public record Element(string Name, string? Uri, string? Icon, List Children); } \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs index 1656c6d..6074fda 100644 --- a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs +++ b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs @@ -1,14 +1,12 @@ using Application.Data; using Domain.Entities; using ErrorOr; -using MediatR; namespace Application.Features.Navigations.UpdateNavigationsTree; public class UpdateNavigationsTreeCommandHandler( - IApplicationDbContext dbContext, - IPublisher publisher -) : IRequestHandler> + IApplicationDbContext dbContext +) : IUpdateNavigationsTreeCommandHandler { public async Task> Handle(UpdateNavigationsTreeCommand request, CancellationToken token) @@ -47,8 +45,8 @@ public async Task> Handle(UpdateNavigatio await dbContext.Navigations.AddRangeAsync(navigationsToAdd, token); await dbContext.SaveChangesAsync(token); - var navigationsTreeUpdatedEvent = new NavigationsTreeUpdatedEvent(); - await publisher.Publish(navigationsTreeUpdatedEvent, token); + // var navigationsTreeUpdatedEvent = new NavigationsTreeUpdatedEvent(); + // await publisher.Publish(navigationsTreeUpdatedEvent, token); return new UpdateNavigationsTreeResponse(); } diff --git a/Infrastructure/Extensions/ServiceCollectionExt.cs b/Infrastructure/Extensions/ServiceCollectionExt.cs index 1f717a5..7f79540 100644 --- a/Infrastructure/Extensions/ServiceCollectionExt.cs +++ b/Infrastructure/Extensions/ServiceCollectionExt.cs @@ -9,7 +9,6 @@ using Infrastructure.Data; using Keycloak.AuthServices.Authentication; using Keycloak.AuthServices.Authorization; -using MediatR; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -57,16 +56,18 @@ private static async Task CreateOrUpdateAuthorOnTokenValidated(TokenValidatedCon if (principal?.Identity?.Name == null) return; if (principal.Identity.IsAuthenticated == false) return; - var mediator = context.HttpContext.RequestServices.GetService(); - if (mediator == null) return; + //TODO: Reimplement without MediatR - var createAuthorCommand = new CreateAuthorCommand(idClaim.Value, principal.Identity.Name); - - var createAuthorResult = await mediator.Send(createAuthorCommand); - if (createAuthorResult.IsError && createAuthorResult.FirstError == DuplicateId) - { - var editAuthorCommand = new EditAuthorCommand(idClaim.Value, principal.Identity.Name); - await mediator.Send(editAuthorCommand); - } + // var mediator = context.HttpContext.RequestServices.GetService(); + // if (mediator == null) return; + // + // var createAuthorCommand = new CreateAuthorCommand(idClaim.Value, principal.Identity.Name); + // + // var createAuthorResult = await mediator.Send(createAuthorCommand); + // if (createAuthorResult.IsError && createAuthorResult.FirstError == DuplicateId) + // { + // var editAuthorCommand = new EditAuthorCommand(idClaim.Value, principal.Identity.Name); + // await mediator.Send(editAuthorCommand); + // } } } \ No newline at end of file diff --git a/WebApi/Features/Articles/ArticlesModule.cs b/WebApi/Features/Articles/ArticlesModule.cs index e043a0b..739ff23 100644 --- a/WebApi/Features/Articles/ArticlesModule.cs +++ b/WebApi/Features/Articles/ArticlesModule.cs @@ -9,7 +9,6 @@ using Application.Features.Articles.ReviewRevision; using Application.Features.Articles.SearchArticles; using Application.Features.Articles.SetRedirect; -using MediatR; using Microsoft.AspNetCore.Authorization; using WebApi.Extensions; using WebApi.Features.Articles.Requests; @@ -22,10 +21,13 @@ public static class ArticlesModule public static void AddArticlesEndpoints(this IEndpointRouteBuilder app) { app.MapPost("/api/articles", - async Task (IMediator mediator, CreateArticleRequest request) => + async Task ( + ICreateArticleCommandHandler createArticleCommandHandler, + CreateArticleRequest request, + CancellationToken cancellationToken) => { var command = new CreateArticleCommand(request.Title, request.Content, request.AuthorsNote, request.CategoryIds); - var result = await mediator.Send(command); + var result = await createArticleCommandHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/{value.Id}", value), error => error.ToIResult() @@ -42,10 +44,15 @@ async Task (IMediator mediator, CreateArticleRequest request) => .WithOpenApi(); app.MapGet("/api/articles", - async Task (IMediator mediator, string? searchTerm, int page = 1, int pageSize = 50) => + async Task ( + ISearchArticlesQueryHandler searchArticlesQueryHandler, + CancellationToken cancellationToken, + string? searchTerm, + int page = 1, + int pageSize = 50) => { var query = new SearchArticlesQuery(searchTerm, page, pageSize); - var result = await mediator.Send(query); + var result = await searchArticlesQueryHandler.Handle(query, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -57,9 +64,12 @@ async Task (IMediator mediator, string? searchTerm, int page = 1, int p .WithOpenApi(); app.MapGet("/api/articles/{id}", - async Task (string id, IMediator mediator) => + async Task ( + string id, + IGetArticleQueryHandler getArticleQueryHandler, + CancellationToken cancellationToken) => { - var result = await mediator.Send(new GetArticleQuery(id)); + var result = await getArticleQueryHandler.Handle(new GetArticleQuery(id), cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -71,11 +81,14 @@ async Task (string id, IMediator mediator) => .ProducesProblem(StatusCodes.Status400BadRequest) .ProducesProblem(StatusCodes.Status404NotFound) .WithOpenApi(); - + app.MapGet("/api/articles/revision:{id:guid}", - async Task (Guid id, IMediator mediator) => + async Task ( + Guid id, + IGetArticleQueryHandler getArticleQueryHandler, + CancellationToken cancellationToken) => { - var result = await mediator.Send(new GetArticleQuery(null, id)); + var result = await getArticleQueryHandler.Handle(new GetArticleQuery(null, id), cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -89,10 +102,14 @@ async Task (Guid id, IMediator mediator) => .WithOpenApi(); app.MapPut("/api/articles/{id}", - async Task (string id, IMediator mediator, EditArticleRequest request) => + async Task ( + string id, + IEditArticleCommandHandler editArticleCommandHandler, + EditArticleRequest request, + CancellationToken cancellationToken) => { var command = new EditArticleCommand(id, request.Content, request.AuthorsNote, request.CategoryIds); - var result = await mediator.Send(command); + var result = await editArticleCommandHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/{value.Id}", value), error => error.ToIResult() @@ -110,10 +127,14 @@ async Task (string id, IMediator mediator, EditArticleRequest request) .WithOpenApi(); app.MapPut("/api/articles/{id}/redirect", - async Task (string id, IMediator mediator, SerArticleRedirectRequest request) => + async Task ( + string id, + ISetRedirectCommandHandler setRedirectCommandHandler, + SerArticleRedirectRequest request, + CancellationToken cancellationToken) => { var command = new SetRedirectCommand(id, request.RedirectArticleId); - var result = await mediator.Send(command); + var result = await setRedirectCommandHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/{value.Id}", true), error => error.ToIResult() @@ -131,10 +152,10 @@ async Task (string id, IMediator mediator, SerArticleRedirectRequest re .WithOpenApi(); app.MapGet("/api/articles/revisions/pending", - async Task (IMediator mediator) => + async Task (IGetPendingRevisionsQueryHandler getPendingRevisionsQueryHandler, CancellationToken cancellationToken) => { var command = new GetPendingRevisionsQuery(); - var result = await mediator.Send(command); + var result = await getPendingRevisionsQueryHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -149,10 +170,12 @@ async Task (IMediator mediator) => .WithOpenApi(); app.MapGet("/api/articles/revisions/pending/count", - async Task (IMediator mediator) => + async Task ( + IGetPendingRevisionsCountQueryHandler getPendingRevisionsCountQueryHandler, + CancellationToken cancellationToken) => { var command = new GetPendingRevisionsCountQuery(); - var result = await mediator.Send(command); + var result = await getPendingRevisionsCountQueryHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -168,10 +191,13 @@ async Task (IMediator mediator) => .WithOpenApi(); app.MapGet("/api/articles/{id}/revisions", - async Task (string id, IMediator mediator) => + async Task ( + string id, + IGetRevisionHistoryQueryHandler getRevisionHistoryQueryHandler, + CancellationToken cancellationToken) => { var command = new GetRevisionHistoryQuery(id); - var result = await mediator.Send(command); + var result = await getRevisionHistoryQueryHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -184,10 +210,13 @@ async Task (string id, IMediator mediator) => .WithOpenApi(); app.MapGet("/api/articles/revisions/{id}/reviews", - async Task (Guid id, IMediator mediator) => + async Task ( + Guid id, + IGetRevisionReviewHistoryQueryHandler getRevisionHistoryQueryHandler, + CancellationToken cancellationToken) => { var command = new GetRevisionReviewHistoryQuery(id); - var result = await mediator.Send(command); + var result = await getRevisionHistoryQueryHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -200,10 +229,14 @@ async Task (Guid id, IMediator mediator) => .WithOpenApi(); app.MapPost("/api/articles/revisions/{id}/reviews", - async Task (Guid id, IMediator mediator, ReviewArticleRevisionRequest request) => + async Task ( + Guid id, + IReviewRevisionCommandHandler reviewRevisionCommandHandler, + ReviewArticleRevisionRequest request, + CancellationToken cancellationToken) => { var command = new ReviewRevisionCommand(id, request.Status, request.Review); - var result = await mediator.Send(command); + var result = await reviewRevisionCommandHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/revisions/{id}/reviews/{value.Id}", value), error => error.ToIResult() @@ -220,10 +253,13 @@ async Task (Guid id, IMediator mediator, ReviewArticleRevisionRequest r .WithOpenApi(); app.MapDelete("/api/articles/{id}", - async (string id, IMediator mediator) => + async ( + string id, + IDeleteArticleCommandHandler deleteArticleCommandHandler, + CancellationToken cancellationToken) => { var command = new DeleteArticleCommand(id); - var result = await mediator.Send(command); + var result = await deleteArticleCommandHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() diff --git a/WebApi/Features/Categories/CategoriesModule.cs b/WebApi/Features/Categories/CategoriesModule.cs index 42ccc37..e4cd65b 100644 --- a/WebApi/Features/Categories/CategoriesModule.cs +++ b/WebApi/Features/Categories/CategoriesModule.cs @@ -3,7 +3,6 @@ using Application.Features.Categories.GetCategories; using Application.Features.Categories.GetCategoriesTree; using Application.Features.Categories.GetCategoryArticles; -using MediatR; using Microsoft.AspNetCore.Authorization; using WebApi.Extensions; using WebApi.Features.Categories.Requests; @@ -15,10 +14,14 @@ public static class CategoriesModule { public static void AddCategoriesEndpoints(this IEndpointRouteBuilder app) { - app.MapPost("/api/categories", async (IMediator mediator, CreateCategoryRequest request) => + app.MapPost("/api/categories", + async ( + ICreateCategoryCommandHandler createCategoryCommandHandler, + CreateCategoryRequest request, + CancellationToken cancellationToken) => { var command = new CreateCategoryCommand(request.Name, request.ParentId); - var result = await mediator.Send(command); + var result = await createCategoryCommandHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/categories/{value.Id}", value), error => error.ToIResult() @@ -36,9 +39,11 @@ public static void AddCategoriesEndpoints(this IEndpointRouteBuilder app) .WithOpenApi(); app.MapGet("/api/categories", - async Task (IMediator mediator) => + async Task ( + IGetCategoriesQueryHandler getCategoriesQueryHandler, + CancellationToken cancellationToken) => { - var response = await mediator.Send(new GetCategoriesQuery()); + var response = await getCategoriesQueryHandler.Handle(new GetCategoriesQuery(), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -50,9 +55,11 @@ async Task (IMediator mediator) => .WithOpenApi(); app.MapGet("/api/categories/tree", - async Task (IMediator mediator) => + async Task ( + IGetCategoriesTreeQueryHandler getCategoriesTreeQueryHandler, + CancellationToken cancellationToken) => { - var response = await mediator.Send(new GetCategoriesTreeQuery()); + var response = await getCategoriesTreeQueryHandler.Handle(new GetCategoriesTreeQuery(), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -64,10 +71,13 @@ async Task (IMediator mediator) => .WithOpenApi(); app.MapGet("/api/categories/{id}/articles", - async Task (string id, IMediator mediator) => + async Task ( + string id, + IGetCategoryArticlesQueryHandler getCategoryArticlesQuery, + CancellationToken cancellationToken) => { var query = new GetCategoryArticlesQuery(id); - var response = await mediator.Send(query); + var response = await getCategoryArticlesQuery.Handle(query, cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -80,10 +90,13 @@ async Task (string id, IMediator mediator) => .WithOpenApi(); app.MapDelete("/api/categories/{id}", - async (string id, IMediator mediator) => + async ( + string id, + IDeleteCategoryCommandHandler deleteCategoryCommandHandler, + CancellationToken cancellationToken) => { var command = new DeleteCategoryCommand(id); - var result = await mediator.Send(command); + var result = await deleteCategoryCommandHandler.Handle(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() diff --git a/WebApi/Features/Navigations/NavigationsModule.cs b/WebApi/Features/Navigations/NavigationsModule.cs index c1bd2e4..4c3e7fb 100644 --- a/WebApi/Features/Navigations/NavigationsModule.cs +++ b/WebApi/Features/Navigations/NavigationsModule.cs @@ -1,6 +1,5 @@ using Application.Features.Navigations.GetNavigationTree; using Application.Features.Navigations.UpdateNavigationsTree; -using MediatR; using Microsoft.AspNetCore.Authorization; using WebApi.Extensions; using WebApi.Features.Navigations.Requests; @@ -13,9 +12,9 @@ public static class NavigationsModule public static void AddNavigationsEndpoints(this IEndpointRouteBuilder app) { app.MapGet("/api/navigations/tree", - async Task (IMediator mediator) => + async Task (IGetNavigationsTreeQueryHandler getNavigationsTreeQueryHandler, CancellationToken cancellationToken) => { - var response = await mediator.Send(new GetNavigationsTreeQuery()); + var response = await getNavigationsTreeQueryHandler.Handle(new GetNavigationsTreeQuery(), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -27,10 +26,10 @@ async Task (IMediator mediator) => .WithOpenApi(); app.MapPut("/api/navigations/tree", - async Task (IMediator mediator, UpdateNavigationsTreeRequest request) => + async Task (IUpdateNavigationsTreeCommandHandler updateNavigationsTreeCommandHandler, UpdateNavigationsTreeRequest request, CancellationToken cancellationToken) => { var command = new UpdateNavigationsTreeCommand(request.Data); - var response = await mediator.Send(command); + var response = await updateNavigationsTreeCommandHandler.Handle(command, cancellationToken); return response.MatchFirst( value => Results.Ok(), error => error.ToIResult() From a4832ef134e62f13b2a92df02a33d6c9f8976eaa Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:53:00 +0300 Subject: [PATCH 2/6] refactor: replaced pipeline behaviours with helpers --- .../CreateArticleCommandHandlerTests.cs | 42 ++---------- .../Common/Behaviours/QueryCachingBehavior.cs | 16 ----- .../Common/Behaviours/ValidationBehavior.cs | 52 --------------- Application/Common/Utils/CachingHelper.cs | 24 +++++++ Application/Common/Utils/ValidatorHelper.cs | 16 +++++ .../CreateArticle/ArticleCreatedEvent.cs | 9 --- .../CreateArticleCommandHandler.cs | 20 +++--- .../DeleteArticle/ArticleDeletedEvent.cs | 6 -- .../DeleteArticleCommandHandler.cs | 9 +-- .../EditArticle/ArticleEditedEvent.cs | 9 --- .../EditArticle/EditArticleCommandHandler.cs | 20 +++--- .../CacheInvalidationArticleHandler.cs | 41 ------------ .../Articles/GetArticle/GetArticleQuery.cs | 3 +- .../GetArticle/GetArticleQueryHandler.cs | 24 +++++-- .../ArticleChangedRevisionEvent.cs | 10 --- .../ReviewRevisionCommandHandler.cs | 64 ++++++++++--------- .../ReviewRevision/RevisionReviewedEvent.cs | 11 ---- .../Articles/SetRedirect/RedirectSetEvent.cs | 7 -- .../SetRedirect/SetRedirectCommandHandler.cs | 27 +++++--- .../CreateAuthor/AuthorCreatedEvent.cs | 7 -- .../CreateAuthorCommandHandler.cs | 7 -- .../Authors/EditAuthor/AuthorEditedEvent.cs | 7 -- .../EditAuthor/EditAuthorCommandHandler.cs | 7 -- .../CreateCategory/CategoryCreatedEvent.cs | 8 --- .../CreateCategoryCommandHandler.cs | 25 +++++--- .../DeleteCategory/CategoryDeletedEvent.cs | 6 -- .../DeleteCategoryCommandHandler.cs | 10 ++- .../CacheInvalidationCategoriesHandler.cs | 25 -------- .../GetCategories/GetCategoriesQuery.cs | 3 +- .../GetCategoriesQueryHandler.cs | 20 +++--- .../GetCategoriesTreeQuery.cs | 3 +- .../GetCategoriesTreeQueryHandler.cs | 40 ++++++------ .../GetCategoryArticlesQuery.cs | 3 +- .../GetCategoryArticlesQueryHandler.cs | 27 ++++---- .../CacheInvalidationNavigationsHandler.cs | 15 ----- .../GetNavigationsTreeQuery.cs | 3 +- .../GetNavigationsTreeQueryHandler.cs | 45 +++++++------ .../NavigationsTreeUpdatedEvent.cs | 3 - .../UpdateNavigationsTreeCommandHandler.cs | 17 ++++- Infrastructure/Caching/CacheService.cs | 2 +- 40 files changed, 263 insertions(+), 430 deletions(-) delete mode 100644 Application/Common/Behaviours/QueryCachingBehavior.cs delete mode 100644 Application/Common/Behaviours/ValidationBehavior.cs create mode 100644 Application/Common/Utils/CachingHelper.cs create mode 100644 Application/Common/Utils/ValidatorHelper.cs delete mode 100644 Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs delete mode 100644 Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs delete mode 100644 Application/Features/Articles/EditArticle/ArticleEditedEvent.cs delete mode 100644 Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs delete mode 100644 Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs delete mode 100644 Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs delete mode 100644 Application/Features/Articles/SetRedirect/RedirectSetEvent.cs delete mode 100644 Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs delete mode 100644 Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs delete mode 100644 Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs delete mode 100644 Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs delete mode 100644 Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs delete mode 100644 Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs delete mode 100644 Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs diff --git a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs index 7292334..9ee9a92 100644 --- a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs +++ b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs @@ -2,6 +2,7 @@ using Application.Data; using Application.Features.Articles.CreateArticle; using Domain.Entities; +using FluentValidation; using MockQueryable.Moq; using Slugify; @@ -12,6 +13,7 @@ public class CreateArticleCommandHandlerTests private readonly Mock _mockDbContext = new(); private readonly Mock _mockSlugHelper = new(); private readonly Mock _mockCurrentUserService = new(); + private readonly Mock> _mockValidator = new(); private readonly CreateArticleCommandHandler _handler; public CreateArticleCommandHandlerTests() @@ -19,7 +21,8 @@ public CreateArticleCommandHandlerTests() _handler = new CreateArticleCommandHandler( _mockDbContext.Object, _mockSlugHelper.Object, - _mockCurrentUserService.Object + _mockCurrentUserService.Object, + _mockValidator.Object ); } @@ -212,41 +215,4 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu Times.Once ); } - - [Fact] - public async void Handle_Should_PublishArticleCreatedEvent_WhenGeneratedIdIsUnique() - { - //Arrange - var mockArticlesDbSet = new List
{new() {Id = "something-cool", Title = "something-cool"}} - .AsQueryable() - .BuildMockDbSet(); - var mockCategoriesDbSet = new List() - .AsQueryable() - .BuildMockDbSet(); - var mockRevisionsDbSet = new List() - .AsQueryable() - .BuildMockDbSet(); - - _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); - _mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object); - _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); - _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); - _mockCurrentUserService.Setup(x => x.UserId).Returns("test-user-id"); - var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []); - var expectedCategoryIds = new List {"category1", "category2"}; - - //Act - var result = await _handler.Handle(command, default); - - //Assert - //TODO: Reimplement without MediatR - // _mockPublisher.Verify( - // x => x.Publish( - // It.Is(e => e.Id == result.Value.Id), - // It.IsAny() - // ), - // Times.Once - // ); - throw new Exception("TODO"); - } } \ No newline at end of file diff --git a/Application/Common/Behaviours/QueryCachingBehavior.cs b/Application/Common/Behaviours/QueryCachingBehavior.cs deleted file mode 100644 index 7b658f4..0000000 --- a/Application/Common/Behaviours/QueryCachingBehavior.cs +++ /dev/null @@ -1,16 +0,0 @@ -// using Application.Common.Caching; -// using Application.Common.Messaging; -// using MediatR; -// -// namespace Application.Common.Behaviours; -// -// public class QueryCachingBehavior(ICacheService cacheService) : IPipelineBehavior -// where TRequest : ICachedQuery -// { -// public async Task Handle(TRequest request, RequestHandlerDelegate next, -// CancellationToken token) -// { -// if (request.IgnoreCaching) return await next(); -// return await cacheService.GetOrCreateAsync(request.Key, _ => next(), request.Expiration, token); -// } -// } \ No newline at end of file diff --git a/Application/Common/Behaviours/ValidationBehavior.cs b/Application/Common/Behaviours/ValidationBehavior.cs deleted file mode 100644 index 57de890..0000000 --- a/Application/Common/Behaviours/ValidationBehavior.cs +++ /dev/null @@ -1,52 +0,0 @@ -// using System.Reflection; -// using ErrorOr; -// using FluentValidation; -// using FluentValidation.Results; -// using MediatR; -// using ValidationException = Application.Common.Exceptions.ValidationException; -// -// namespace Application.Common.Behaviours; -// -// public class ValidationBehavior : IPipelineBehavior -// where TRequest : IRequest -// where TResponse : IErrorOr -// { -// private readonly IValidator? _validator; -// -// public ValidationBehavior(IValidator? validator = null) -// { -// _validator = validator; -// } -// -// public async Task Handle( -// TRequest request, -// RequestHandlerDelegate next, -// CancellationToken token) -// { -// if (_validator == null) return await next(); -// -// var validationResult = await _validator.ValidateAsync(request, token); -// -// if (validationResult.IsValid) return await next(); -// -// return TryCreateResponseFromErrors(validationResult.Errors, out var response) -// ? response -// : throw new ValidationException(validationResult.Errors); -// } -// -// private static bool TryCreateResponseFromErrors(List validationFailures, out TResponse response) -// { -// var errors = validationFailures.ConvertAll(x => Error.Validation( -// x.PropertyName, -// x.ErrorMessage)); -// -// response = (TResponse?) typeof(TResponse) -// .GetMethod( -// nameof(ErrorOr.From), -// BindingFlags.Static | BindingFlags.Public, -// new[] {typeof(List)})? -// .Invoke(null, new[] {errors})!; -// -// return response is not null; -// } -// } \ No newline at end of file diff --git a/Application/Common/Utils/CachingHelper.cs b/Application/Common/Utils/CachingHelper.cs new file mode 100644 index 0000000..9978ffb --- /dev/null +++ b/Application/Common/Utils/CachingHelper.cs @@ -0,0 +1,24 @@ +using Application.Common.Caching; +using Application.Common.Messaging; + +namespace Application.Common.Utils; + +public static class CachingHelper +{ + public static async Task GetOrCacheAsync( + ICacheService cacheService, + TRequest request, + Func> dataRetriever, + CancellationToken token) + where TRequest : ICachedQuery + { + if (request.IgnoreCaching) + return await dataRetriever(); + + return await cacheService.GetOrCreateAsync( + request.Key, + _ => dataRetriever(), + request.Expiration, + token); + } +} \ No newline at end of file diff --git a/Application/Common/Utils/ValidatorHelper.cs b/Application/Common/Utils/ValidatorHelper.cs new file mode 100644 index 0000000..65a4a99 --- /dev/null +++ b/Application/Common/Utils/ValidatorHelper.cs @@ -0,0 +1,16 @@ +using ErrorOr; +using FluentValidation; + +namespace Application.Common.Utils; + +public static class ValidatorHelper +{ + public static ErrorOr Validate(IValidator validator, TRequest request) + { + var validationResult = validator.Validate(request); + + return validationResult.IsValid + ? Result.Success + : validationResult.Errors.ConvertAll(e => Error.Validation(e.PropertyName, e.ErrorMessage)); + } +} \ No newline at end of file diff --git a/Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs b/Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs deleted file mode 100644 index 48e8a3c..0000000 --- a/Application/Features/Articles/CreateArticle/ArticleCreatedEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Application.Features.Articles.CreateArticle; - -// public class ArticleCreatedEvent : INotification -// { -// public required string Id { get; init; } -// public required string Title { get; init; } -// public required string Content { get; init; } -// public required List CategoryIds { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs index 37a03ca..b5f6561 100644 --- a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs +++ b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs @@ -1,7 +1,9 @@ using Application.Authorization.Abstractions; +using Application.Common.Utils; using Application.Data; using Domain.Entities; using ErrorOr; +using FluentValidation; using Microsoft.EntityFrameworkCore; using Slugify; @@ -10,11 +12,18 @@ namespace Application.Features.Articles.CreateArticle; public class CreateArticleCommandHandler( IApplicationDbContext dbContext, ISlugHelper slugHelper, - ICurrentUserService identityService + ICurrentUserService identityService, + IValidator validator ) : ICreateArticleCommandHandler { public async Task> Handle(CreateArticleCommand command, CancellationToken token) { + var validationResult = ValidatorHelper.Validate(validator, command); + if (validationResult.IsError) + { + return validationResult.Errors; + } + var id = slugHelper.GenerateSlug(command.Title); if (string.IsNullOrEmpty(id)) return Errors.Article.EmptyId; @@ -50,15 +59,6 @@ public async Task> Handle(CreateArticleCommand co await dbContext.SaveChangesAsync(token); - // var articleCreatedEvent = new ArticleCreatedEvent - // { - // Id = article.Id, - // Title = article.Title, - // Content = revision.Content, - // CategoryIds = categories.Select(e => e.Id).ToList() - // }; - // await publisher.Publish(articleCreatedEvent, token); - return new CreateArticleResponse(article.Id); } } \ No newline at end of file diff --git a/Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs b/Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs deleted file mode 100644 index 9c84c38..0000000 --- a/Application/Features/Articles/DeleteArticle/ArticleDeletedEvent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Application.Features.Articles.DeleteArticle; - -// public class ArticleDeletedEvent : INotification -// { -// public required string Id { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs b/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs index e51a23b..762df3c 100644 --- a/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs +++ b/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs @@ -1,9 +1,11 @@ -using Application.Data; +using Application.Common.Caching; +using Application.Common.Constants; +using Application.Data; using ErrorOr; namespace Application.Features.Articles.DeleteArticle; -public class DeleteArticleCommandHandler(IApplicationDbContext dbContext) : IDeleteArticleCommandHandler +public class DeleteArticleCommandHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IDeleteArticleCommandHandler { public async Task> Handle(DeleteArticleCommand request, CancellationToken token) { @@ -12,8 +14,7 @@ public async Task> Handle(DeleteArticleCommand re dbContext.Articles.Remove(article); await dbContext.SaveChangesAsync(token); - // var articleDeletedEvent = new ArticleDeletedEvent {Id = article.Id}; - // await publisher.Publish(articleDeletedEvent, token); + await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(article.Id), token); return new DeleteArticleResponse(article.Id); } diff --git a/Application/Features/Articles/EditArticle/ArticleEditedEvent.cs b/Application/Features/Articles/EditArticle/ArticleEditedEvent.cs deleted file mode 100644 index 8b206a4..0000000 --- a/Application/Features/Articles/EditArticle/ArticleEditedEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Application.Features.Articles.EditArticle; - -// public class ArticleEditedEvent : INotification -// { -// public required string Id { get; init; } -// public required string Content { get; init; } -// public required string AuthorsNote { get; init; } -// public required List CategoryIds { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs index 5b834bc..1f9752d 100644 --- a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs +++ b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs @@ -1,18 +1,27 @@ using Application.Authorization.Abstractions; +using Application.Common.Utils; using Application.Data; using Domain.Entities; using ErrorOr; +using FluentValidation; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.EditArticle; public class EditArticleCommandHandler( IApplicationDbContext dbContext, - ICurrentUserService identityService + ICurrentUserService identityService, + IValidator validator ) : IEditArticleCommandHandler { public async Task> Handle(EditArticleCommand request, CancellationToken token) { + var validationResult = ValidatorHelper.Validate(validator, request); + if (validationResult.IsError) + { + return validationResult.Errors; + } + var article = await dbContext.Articles.Include(e => e.RedirectArticle) .FirstOrDefaultAsync(e => e.Id == request.Id, token); if (article == null) return Errors.Article.NotFound; @@ -39,15 +48,6 @@ public async Task> Handle(EditArticleCommand reques await dbContext.Revisions.AddAsync(revision, token); await dbContext.SaveChangesAsync(token); - // var articleEditedEvent = new ArticleEditedEvent - // { - // Id = article.Id, - // Content = revision.Content, - // AuthorsNote = request.AuthorsNote, - // CategoryIds = requestCategories.Select(e => e.Id).ToList() - // }; - // await publisher.Publish(articleEditedEvent, token); - return new EditArticleResponse(article.Id); } } \ No newline at end of file diff --git a/Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs b/Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs deleted file mode 100644 index af1b0f0..0000000 --- a/Application/Features/Articles/EventHandlers/CacheInvalidationArticleHandler.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Application.Common.Caching; -using Application.Common.Constants; -using Application.Features.Articles.DeleteArticle; -using Application.Features.Articles.ReviewRevision; -using Application.Features.Articles.SetRedirect; - -namespace Application.Features.Articles.EventHandlers; - -//TODO: Reimplement without MediatR -// public class CacheInvalidationArticleHandler(ICacheService cacheService) : -// INotificationHandler, -// INotificationHandler, -// INotificationHandler, -// INotificationHandler -// { -// public async Task Handle(ArticleDeletedEvent notification, CancellationToken token) -// { -// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.Id), token); -// } -// -// public async Task Handle(RevisionReviewedEvent notification, CancellationToken token) -// { -// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.ArticleId), token); -// } -// -// public async Task Handle(RedirectSetEvent notification, CancellationToken token) -// { -// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.ArticleId), token); -// await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(notification.RedirectId), token); -// } -// -// public async Task Handle(ArticleChangedRevisionEvent notification, CancellationToken token) -// { -// var difference = notification.PreviousRevisionCategoryIds.Except(notification.CurrentRevisionCategoryIds) -// .Concat(notification.CurrentRevisionCategoryIds.Except(notification.PreviousRevisionCategoryIds)); -// foreach (var s in difference) -// { -// await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(s), token); -// } -// } -// } \ No newline at end of file diff --git a/Application/Features/Articles/GetArticle/GetArticleQuery.cs b/Application/Features/Articles/GetArticle/GetArticleQuery.cs index 25ad530..be91794 100644 --- a/Application/Features/Articles/GetArticle/GetArticleQuery.cs +++ b/Application/Features/Articles/GetArticle/GetArticleQuery.cs @@ -1,9 +1,10 @@ using Application.Common.Constants; using Application.Common.Messaging; +using ErrorOr; namespace Application.Features.Articles.GetArticle; -public record GetArticleQuery(string? Id, Guid? RevisionId = null) : ICachedQuery +public record GetArticleQuery(string? Id, Guid? RevisionId = null) : ICachedQuery> { public string Key => CachingKeys.Articles.ArticleById(Id); public TimeSpan? Expiration => null; diff --git a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs index e1393f1..bafec6a 100644 --- a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs +++ b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs @@ -1,23 +1,37 @@ using Application.Authorization.Abstractions; +using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Utils; using Application.Data; using Domain.Entities; using ErrorOr; +using FluentValidation; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetArticle; public class GetArticleQueryHandler( IApplicationDbContext dbContext, - IIdentityService identityService + IIdentityService identityService, + IValidator validator, + ICacheService cacheService ) : IGetArticleQueryHandler { public async Task> Handle(GetArticleQuery query, CancellationToken token) { - if (!string.IsNullOrWhiteSpace(query.Id)) - return await GetByArticleId(query.Id, token); - - return await GetByRevisionId(query.RevisionId.GetValueOrDefault(), token); + var validationResult = ValidatorHelper.Validate(validator, query); + if (validationResult.IsError) + { + return validationResult.Errors; + } + + return await CachingHelper.GetOrCacheAsync(cacheService, query, async () => + { + if (!string.IsNullOrWhiteSpace(query.Id)) + return await GetByArticleId(query.Id, token); + + return await GetByRevisionId(query.RevisionId.GetValueOrDefault(), token); + }, token); } private async Task> GetByArticleId(string id, CancellationToken token) diff --git a/Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs b/Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs deleted file mode 100644 index 32c8e6e..0000000 --- a/Application/Features/Articles/ReviewRevision/ArticleChangedRevisionEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Application.Features.Articles.ReviewRevision; - -// public class ArticleChangedRevisionEvent : INotification -// { -// public required string ArticleId { get; init; } -// public required Guid? PreviousRevisionId { get; init; } -// public required Guid? CurrentRevisionId { get; init; } -// public required List PreviousRevisionCategoryIds { get; init; } -// public required List CurrentRevisionCategoryIds { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs index 5ace5cf..45a695b 100644 --- a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs +++ b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs @@ -1,19 +1,31 @@ using Application.Authorization.Abstractions; +using Application.Common.Caching; +using Application.Common.Constants; +using Application.Common.Utils; using Application.Data; using Domain.Entities; using ErrorOr; +using FluentValidation; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.ReviewRevision; public class ReviewRevisionCommandHandler( IApplicationDbContext dbContext, - ICurrentUserService identityService + ICurrentUserService identityService, + ICacheService cacheService, + IValidator validator ) : IReviewRevisionCommandHandler { public async Task> Handle(ReviewRevisionCommand command, CancellationToken token) { + var validationResult = ValidatorHelper.Validate(validator, command); + if (validationResult.IsError) + { + return validationResult.Errors; + } + var revision = await dbContext.Revisions .Include(e => e.Article) .ThenInclude(e => e.CurrentRevision) @@ -38,7 +50,7 @@ public async Task> Handle(ReviewRevisionCommand Revision = revision }; - // ArticleChangedRevisionEvent? articleChangedRevisionEvent = null; + HashSet affectedCategories = []; revision.LatestReview = review; @@ -47,7 +59,7 @@ public async Task> Handle(ReviewRevisionCommand revision.Content = "[REDACTED]"; revision.AuthorsNote = "[REDACTED]"; } - + if (article.CurrentRevision == revision && command is {Status: ReviewStatus.Removed or ReviewStatus.Rejected}) { var rollbackRevision = await dbContext.Revisions @@ -58,42 +70,34 @@ public async Task> Handle(ReviewRevisionCommand .OrderByDescending(e => e.Timestamp) .FirstOrDefaultAsync(token); - // ChangeArticleRevision(article, rollbackRevision, out articleChangedRevisionEvent); + ChangeArticleRevision(article, rollbackRevision, out affectedCategories); } if (command is {Status: ReviewStatus.Accepted}) { - // ChangeArticleRevision(article, revision, out articleChangedRevisionEvent); + ChangeArticleRevision(article, revision, out affectedCategories); } await dbContext.SaveChangesAsync(token); - - // var revisionReviewedEvent = new RevisionReviewedEvent - // { - // ArticleId = article.Id, - // RevisionId = revision.Id, - // Status = command.Status, - // Review = command.Review, - // }; - // await publisher.Publish(revisionReviewedEvent, token); - // - // if (articleChangedRevisionEvent != null) await publisher.Publish(articleChangedRevisionEvent, token); + + await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(article.Id), token); + foreach (var id in affectedCategories) + { + await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(id), token); + } return new ReviewRevisionResponse(review.Id); } - // private static void ChangeArticleRevision(Article article, Revision? revision, out ArticleChangedRevisionEvent articleChangedRevisionEvent) - // { - // var previousRevisionId = article.CurrentRevision?.Id; - // var previousRevisionCategoriesIds = article.CurrentRevision?.Categories.Select(e => e.Id).ToList() ?? new List(); - // article.CurrentRevision = revision; - // articleChangedRevisionEvent = new ArticleChangedRevisionEvent - // { - // ArticleId = article.Id, - // PreviousRevisionId = previousRevisionId, - // CurrentRevisionId = article.CurrentRevision?.Id, - // PreviousRevisionCategoryIds = previousRevisionCategoriesIds, - // CurrentRevisionCategoryIds = revision?.Categories.Select(e => e.Id).ToList() ?? new List() - // }; - // } + private static void ChangeArticleRevision(Article article, Revision? revision, out HashSet affectedCategories) + { + var previousRevisionCategoriesIds = article.CurrentRevision?.Categories.Select(e => e.Id).ToHashSet() ?? []; + + article.CurrentRevision = revision; + + var currentRevisionCategoryIds = revision?.Categories.Select(e => e.Id).ToHashSet() ?? []; + + previousRevisionCategoriesIds.SymmetricExceptWith(currentRevisionCategoryIds); + affectedCategories = previousRevisionCategoriesIds; + } } \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs b/Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs deleted file mode 100644 index 06b6227..0000000 --- a/Application/Features/Articles/ReviewRevision/RevisionReviewedEvent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Domain.Entities; - -namespace Application.Features.Articles.ReviewRevision; - -// public class RevisionReviewedEvent : INotification -// { -// public required string ArticleId { get; init; } -// public required Guid RevisionId { get; init; } -// public required ReviewStatus Status { get; init; } -// public required string Review { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/RedirectSetEvent.cs b/Application/Features/Articles/SetRedirect/RedirectSetEvent.cs deleted file mode 100644 index d46bc58..0000000 --- a/Application/Features/Articles/SetRedirect/RedirectSetEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Application.Features.Articles.SetRedirect; - -// public class RedirectSetEvent : INotification -// { -// public required string ArticleId { get; init; } -// public required string RedirectId { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs b/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs index 266e882..6fe4313 100644 --- a/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs +++ b/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs @@ -1,14 +1,27 @@ +using Application.Common.Caching; +using Application.Common.Constants; +using Application.Common.Utils; using Application.Data; using ErrorOr; +using FluentValidation; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.SetRedirect; -public class SetRedirectCommandHandler(IApplicationDbContext dbContext) : ISetRedirectCommandHandler +public class SetRedirectCommandHandler( + IApplicationDbContext dbContext, + ICacheService cacheService, + IValidator validator + ) : ISetRedirectCommandHandler { - public async Task> Handle(SetRedirectCommand command, - CancellationToken token) + public async Task> Handle(SetRedirectCommand command, CancellationToken token) { + var validationResult = ValidatorHelper.Validate(validator, command); + if (validationResult.IsError) + { + return validationResult.Errors; + } + var article = await dbContext.Articles.FirstOrDefaultAsync(e => e.Id == command.ArticleId, token); if (article == null) return Errors.Article.NotFound; if (article.RedirectArticleId != null) return Errors.Article.RedirectExists; @@ -31,12 +44,8 @@ await dbContext.Revisions await dbContext.SaveChangesAsync(token); - // var revisionReviewedEvent = new RedirectSetEvent - // { - // ArticleId = article.Id, - // RedirectId = redirectArticle.Id, - // }; - // await publisher.Publish(revisionReviewedEvent, token); + await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(article.Id), token); + await cacheService.RemoveAsync(CachingKeys.Articles.ArticleById(redirectArticle.Id), token); return new SetRedirectResponse(redirectArticle.Id); } diff --git a/Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs b/Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs deleted file mode 100644 index cf55b80..0000000 --- a/Application/Features/Authors/CreateAuthor/AuthorCreatedEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Application.Features.Authors.CreateAuthor; - -// public class AuthorCreatedEvent : INotification -// { -// public required string Id { get; init; } -// public required string Name { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs index cfc285c..54dc0ac 100644 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs +++ b/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs @@ -18,13 +18,6 @@ public async Task> Handle(CreateAuthorCommand comm dbContext.Authors.Add(author); await dbContext.SaveChangesAsync(token); - // var authorCreatedEvent = new AuthorCreatedEvent - // { - // Id = author.Id, - // Name = author.Name - // }; - // await publisher.Publish(authorCreatedEvent, token); - return new CreateAuthorResponse(author.Id); } } \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs b/Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs deleted file mode 100644 index e8ddfac..0000000 --- a/Application/Features/Authors/EditAuthor/AuthorEditedEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Application.Features.Authors.EditAuthor; - -// public class AuthorEditedEvent : INotification -// { -// public required string Id { get; init; } -// public required string Name { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs b/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs index d0a6ecd..f2100b6 100644 --- a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs +++ b/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs @@ -18,13 +18,6 @@ public async Task> Handle(EditAuthorCommand command, await dbContext.SaveChangesAsync(token); - // var authorEditedEvent = new AuthorEditedEvent - // { - // Id = author.Id, - // Name = author.Name - // }; - // await publisher.Publish(authorEditedEvent, token); - return new EditAuthorResponse(author.Id); } } \ No newline at end of file diff --git a/Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs b/Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs deleted file mode 100644 index dc915cb..0000000 --- a/Application/Features/Categories/CreateCategory/CategoryCreatedEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Application.Features.Categories.CreateCategory; - -// public class CategoryCreatedEvent : INotification -// { -// public required string Id { get; init; } -// public required string Name { get; init; } -// public string? ParentId { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs b/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs index 1b8800d..cad7945 100644 --- a/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs +++ b/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs @@ -1,6 +1,10 @@ -using Application.Data; +using Application.Common.Caching; +using Application.Common.Constants; +using Application.Common.Utils; +using Application.Data; using Domain.Entities; using ErrorOr; +using FluentValidation; using Microsoft.EntityFrameworkCore; using Slugify; @@ -8,11 +12,19 @@ namespace Application.Features.Categories.CreateCategory; public class CreateCategoryCommandHandler( IApplicationDbContext dbContext, - ISlugHelper slugHelper + ISlugHelper slugHelper, + ICacheService cacheService, + IValidator validator ) : ICreateCategoryCommandHandler { public async Task> Handle(CreateCategoryCommand command, CancellationToken token) { + var validationResult = ValidatorHelper.Validate(validator, command); + if (validationResult.IsError) + { + return validationResult.Errors; + } + var id = slugHelper.GenerateSlug(command.Name); if (string.IsNullOrEmpty(id)) return Errors.Category.EmptyId; @@ -47,13 +59,8 @@ public async Task> Handle(CreateCategoryCommand dbContext.Categories.Add(entity); await dbContext.SaveChangesAsync(token); - // var categoryCreatedEvent = new CategoryCreatedEvent - // { - // Id = entity.Id, - // Name = entity.Name, - // ParentId = parent?.Id - // }; - // await publisher.Publish(categoryCreatedEvent, token); + await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); + await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); return new CreateCategoryResponse(entity.Id); } diff --git a/Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs b/Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs deleted file mode 100644 index 71c1916..0000000 --- a/Application/Features/Categories/DeleteCategory/CategoryDeletedEvent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Application.Features.Categories.DeleteCategory; - -// public class CategoryDeletedEvent : INotification -// { -// public required string Id { get; init; } -// } \ No newline at end of file diff --git a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs index 3505680..8df7638 100644 --- a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs +++ b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs @@ -1,10 +1,13 @@ +using Application.Common.Caching; +using Application.Common.Constants; using Application.Data; using ErrorOr; namespace Application.Features.Categories.DeleteCategory; public class DeleteCategoryCommandHandler( - IApplicationDbContext dbContext + IApplicationDbContext dbContext, + ICacheService cacheService ) : IDeleteCategoryCommandHandler { public async Task> Handle(DeleteCategoryCommand deleteCategoryCommand, @@ -15,8 +18,9 @@ public async Task> Handle(DeleteCategoryCommand dbContext.Categories.Remove(category); await dbContext.SaveChangesAsync(token); - // var categoryDeletedEvent = new CategoryDeletedEvent {Id = category.Id}; - // await publisher.Publish(categoryDeletedEvent, token); + await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); + await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); + await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(category.Id), token); return new DeleteCategoryResponse(category.Id); } diff --git a/Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs b/Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs deleted file mode 100644 index 67025cd..0000000 --- a/Application/Features/Categories/EventHandlers/CacheInvalidationCategoriesHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Application.Common.Caching; -using Application.Common.Constants; -using Application.Features.Categories.CreateCategory; -using Application.Features.Categories.DeleteCategory; - -namespace Application.Features.Categories.EventHandlers; - -//TODO: Reimplement without MediatR -// public class CacheInvalidationCategoriesHandler(ICacheService cacheService) : -// INotificationHandler, -// INotificationHandler -// { -// public async Task Handle(CategoryCreatedEvent notification, CancellationToken token) -// { -// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); -// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); -// } -// -// public async Task Handle(CategoryDeletedEvent notification, CancellationToken token) -// { -// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesAll, token); -// await cacheService.RemoveAsync(CachingKeys.Categories.CategoriesTree, token); -// await cacheService.RemoveAsync(CachingKeys.Categories.CategoryArticlesById(notification.Id), token); -// } -// } \ No newline at end of file diff --git a/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs b/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs index 6e4a0c5..13e8276 100644 --- a/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs +++ b/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs @@ -1,9 +1,10 @@ using Application.Common.Constants; using Application.Common.Messaging; +using ErrorOr; namespace Application.Features.Categories.GetCategories; -public record GetCategoriesQuery : ICachedQuery +public record GetCategoriesQuery : ICachedQuery> { public string Key => CachingKeys.Categories.CategoriesAll; public TimeSpan? Expiration => null; diff --git a/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs b/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs index 30715fe..ba2b5f8 100644 --- a/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs +++ b/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs @@ -1,19 +1,23 @@ +using Application.Common.Caching; +using Application.Common.Utils; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Categories.GetCategories; -public class GetCategoriesQueryHandler(IApplicationDbContext dbContext) : IGetCategoriesQueryHandler +public class GetCategoriesQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetCategoriesQueryHandler { - public async Task> Handle(GetCategoriesQuery request, - CancellationToken token) + public async Task> Handle(GetCategoriesQuery request, CancellationToken token) { - var list = await dbContext.Categories - .Select(e => new GetCategoriesResponse.Element(e.Id, e.Name, e.ParentId)) - .AsNoTracking() - .ToListAsync(token); + return await CachingHelper.GetOrCacheAsync(cacheService, request, async () => + { + var list = await dbContext.Categories + .Select(e => new GetCategoriesResponse.Element(e.Id, e.Name, e.ParentId)) + .AsNoTracking() + .ToListAsync(token); - return new GetCategoriesResponse(list); + return new GetCategoriesResponse(list); + }, token); } } \ No newline at end of file diff --git a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs index e69650c..46fd112 100644 --- a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs +++ b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs @@ -1,9 +1,10 @@ using Application.Common.Constants; using Application.Common.Messaging; +using ErrorOr; namespace Application.Features.Categories.GetCategoriesTree; -public record GetCategoriesTreeQuery : ICachedQuery +public record GetCategoriesTreeQuery : ICachedQuery> { public string Key => CachingKeys.Categories.CategoriesTree; public TimeSpan? Expiration => null; diff --git a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs index a3ab039..46846de 100644 --- a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs +++ b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs @@ -1,30 +1,34 @@ +using Application.Common.Caching; +using Application.Common.Utils; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Categories.GetCategoriesTree; -public class GetCategoriesTreeQueryHandler(IApplicationDbContext dbContext) : IGetCategoriesTreeQueryHandler +public class GetCategoriesTreeQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetCategoriesTreeQueryHandler { - public async Task> Handle(GetCategoriesTreeQuery request, - CancellationToken token) + public async Task> Handle(GetCategoriesTreeQuery request, CancellationToken token) { - var list = await dbContext.Categories - .Include(e => e.SubCategories) - .ToListAsync(token); + return await CachingHelper.GetOrCacheAsync(cacheService, request, async () => + { + var list = await dbContext.Categories + .Include(e => e.SubCategories) + .ToListAsync(token); - var lookup = list.ToLookup(e => e.ParentId, e => new GetCategoriesTreeResponse.Element( - e.Id, - e.Name, - new List() - ) - ); - - foreach (var node in lookup.SelectMany(e => e)) - node.Children.AddRange(lookup[node.Id]); - - var rootNodes = lookup[null].ToList(); + var lookup = list.ToLookup(e => e.ParentId, e => new GetCategoriesTreeResponse.Element( + e.Id, + e.Name, + new List() + ) + ); - return new GetCategoriesTreeResponse(rootNodes); + foreach (var node in lookup.SelectMany(e => e)) + node.Children.AddRange(lookup[node.Id]); + + var rootNodes = lookup[null].ToList(); + + return new GetCategoriesTreeResponse(rootNodes); + }, token); } } \ No newline at end of file diff --git a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs index b9630c2..78ea48d 100644 --- a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs +++ b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs @@ -1,9 +1,10 @@ using Application.Common.Constants; using Application.Common.Messaging; +using ErrorOr; namespace Application.Features.Categories.GetCategoryArticles; -public record GetCategoryArticlesQuery(string Id) : ICachedQuery +public record GetCategoryArticlesQuery(string Id) : ICachedQuery> { public string Key => CachingKeys.Categories.CategoryArticlesById(Id); public TimeSpan? Expiration => TimeSpan.FromHours(12); diff --git a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs index bc82c7d..aa5408d 100644 --- a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs +++ b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs @@ -1,25 +1,30 @@ +using Application.Common.Caching; +using Application.Common.Utils; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Categories.GetCategoryArticles; -public class GetCategoryArticlesQueryHandler(IApplicationDbContext dbContext) : IGetCategoryArticlesQueryHandler +public class GetCategoryArticlesQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetCategoryArticlesQueryHandler { public async Task> Handle(GetCategoryArticlesQuery request, CancellationToken token) { - var category = await dbContext.Categories.AsNoTracking() - .FirstOrDefaultAsync(e => e.Id == request.Id, token); + return await CachingHelper.GetOrCacheAsync>(cacheService, request, async () => + { + var category = await dbContext.Categories.AsNoTracking() + .FirstOrDefaultAsync(e => e.Id == request.Id, token); - if (category == null) return Errors.Category.NotFound; + if (category == null) return Errors.Category.NotFound; - var articlesList = await dbContext.Articles - .Where(e => e.RedirectArticleId == null && e.CurrentRevision.Categories.Any(x => x.Id == request.Id)) - .Select(e=> new GetCategoryArticlesResponse.Element(e.Id, e.Title)) - .AsNoTracking() - .ToListAsync(token); - - return new GetCategoryArticlesResponse(articlesList); + var articlesList = await dbContext.Articles + .Where(e => e.RedirectArticleId == null && e.CurrentRevision.Categories.Any(x => x.Id == request.Id)) + .Select(e=> new GetCategoryArticlesResponse.Element(e.Id, e.Title)) + .AsNoTracking() + .ToListAsync(token); + + return new GetCategoryArticlesResponse(articlesList); + }, token); } } \ No newline at end of file diff --git a/Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs b/Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs deleted file mode 100644 index e8b64b8..0000000 --- a/Application/Features/Navigations/EventHandlers/CacheInvalidationNavigationsHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Application.Common.Caching; -using Application.Common.Constants; -using Application.Features.Navigations.UpdateNavigationsTree; - -namespace Application.Features.Navigations.EventHandlers; - -//TODO: Reimplement without MediatR -// public class CacheInvalidationNavigationsHandler(ICacheService cacheService) : -// INotificationHandler -// { -// public async Task Handle(NavigationsTreeUpdatedEvent notification, CancellationToken token) -// { -// await cacheService.RemoveAsync(CachingKeys.Navigation.NavigationsTree, token); -// } -// } \ No newline at end of file diff --git a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs index a322699..ba16bb1 100644 --- a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs +++ b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs @@ -1,9 +1,10 @@ using Application.Common.Constants; using Application.Common.Messaging; +using ErrorOr; namespace Application.Features.Navigations.GetNavigationTree; -public record GetNavigationsTreeQuery : ICachedQuery +public record GetNavigationsTreeQuery : ICachedQuery> { public string Key => CachingKeys.Navigation.NavigationsTree; public TimeSpan? Expiration => TimeSpan.FromHours(12); diff --git a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs index c58af37..15fe716 100644 --- a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs +++ b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs @@ -1,34 +1,39 @@ +using Application.Common.Caching; +using Application.Common.Utils; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Navigations.GetNavigationTree; -public class GetNavigationsTreeQueryHandler(IApplicationDbContext dbContext) : IGetNavigationsTreeQueryHandler +public class GetNavigationsTreeQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetNavigationsTreeQueryHandler { public async Task> Handle(GetNavigationsTreeQuery request, CancellationToken token) { - var navigations = await dbContext.Navigations - .Include(e => e.Children) - .AsNoTracking() - .ToListAsync(token); + return await CachingHelper.GetOrCacheAsync(cacheService, request, async () => + { + var navigations = await dbContext.Navigations + .Include(e => e.Children) + .AsNoTracking() + .ToListAsync(token); - var lookup = navigations.ToLookup(e => e.ParentId, e => new GetNavigationsTreeResponse.Element( - e.Id, - e.Weight, - e.Name, - e.Uri, - e.Icon, - new List() - ) - ); - - foreach (var node in lookup.SelectMany(e => e)) - node.Children.AddRange(lookup[node.Id].OrderByDescending(e=>e.Weight)); - - var rootNodes = lookup[null].OrderByDescending(e=>e.Weight).ToList(); + var lookup = navigations.ToLookup(e => e.ParentId, e => new GetNavigationsTreeResponse.Element( + e.Id, + e.Weight, + e.Name, + e.Uri, + e.Icon, + [] + ) + ); - return new GetNavigationsTreeResponse(rootNodes); + foreach (var node in lookup.SelectMany(e => e)) + node.Children.AddRange(lookup[node.Id].OrderByDescending(e=>e.Weight)); + + var rootNodes = lookup[null].OrderByDescending(e=>e.Weight).ToList(); + + return new GetNavigationsTreeResponse(rootNodes); + }, token); } } \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs b/Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs deleted file mode 100644 index c780139..0000000 --- a/Application/Features/Navigations/UpdateNavigationsTree/NavigationsTreeUpdatedEvent.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Application.Features.Navigations.UpdateNavigationsTree; - -// public class NavigationsTreeUpdatedEvent : INotification; \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs index 6074fda..942ea3e 100644 --- a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs +++ b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs @@ -1,16 +1,28 @@ +using Application.Common.Caching; +using Application.Common.Constants; +using Application.Common.Utils; using Application.Data; using Domain.Entities; using ErrorOr; +using FluentValidation; namespace Application.Features.Navigations.UpdateNavigationsTree; public class UpdateNavigationsTreeCommandHandler( - IApplicationDbContext dbContext + IApplicationDbContext dbContext, + ICacheService cacheService, + IValidator validator ) : IUpdateNavigationsTreeCommandHandler { public async Task> Handle(UpdateNavigationsTreeCommand request, CancellationToken token) { + var validationResult = ValidatorHelper.Validate(validator, request); + if (validationResult.IsError) + { + return validationResult.Errors; + } + foreach (var item in dbContext.Navigations) dbContext.Navigations.Remove(item); @@ -45,8 +57,7 @@ public async Task> Handle(UpdateNavigatio await dbContext.Navigations.AddRangeAsync(navigationsToAdd, token); await dbContext.SaveChangesAsync(token); - // var navigationsTreeUpdatedEvent = new NavigationsTreeUpdatedEvent(); - // await publisher.Publish(navigationsTreeUpdatedEvent, token); + await cacheService.RemoveAsync(CachingKeys.Navigation.NavigationsTree, token); return new UpdateNavigationsTreeResponse(); } diff --git a/Infrastructure/Caching/CacheService.cs b/Infrastructure/Caching/CacheService.cs index 7a429d3..41ee429 100644 --- a/Infrastructure/Caching/CacheService.cs +++ b/Infrastructure/Caching/CacheService.cs @@ -17,7 +17,7 @@ public async Task GetOrCreateAsync(string key, Func Date: Mon, 4 Aug 2025 01:43:42 +0300 Subject: [PATCH 3/6] test: updated CreateArticleCommandHandlerTests --- .../Articles/CreateArticleCommandHandlerTests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs index 9ee9a92..8c7b1f6 100644 --- a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs +++ b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs @@ -5,6 +5,7 @@ using FluentValidation; using MockQueryable.Moq; using Slugify; +using FluentValidation.Results; namespace Application.Tests.Unit.Articles; @@ -36,6 +37,8 @@ public async void Handle_Should_ReturnFailureResult_WhenGeneratedIdIsEmpty() _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug(" ")).Returns(""); + _mockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new ValidationResult()); var command = new CreateArticleCommand(" ", "Lorem ipsum", "", []); @@ -57,6 +60,8 @@ public async void Handle_Should_NotCallSaveChangesAsync_WhenGeneratedIdIsEmpty() _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug(" ")).Returns(""); + _mockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new ValidationResult()); var command = new CreateArticleCommand(" ", "Lorem ipsum", "", []); @@ -77,6 +82,8 @@ public async void Handle_Should_ReturnFailureResult_WhenGeneratedIdIsNotUnique() _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug("something cool")).Returns("something-cool"); + _mockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new ValidationResult()); var command = new CreateArticleCommand("something cool", "Lorem ipsum", "", []); @@ -98,6 +105,8 @@ public async void Handle_Should_NotCallSaveChangesAsync_WhenGeneratedIdIsNotUniq _mockDbContext.Setup(x => x.Articles).Returns(mockArticlesDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug("something cool")).Returns("something-cool"); + _mockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new ValidationResult()); var command = new CreateArticleCommand("something cool", "Lorem ipsum", "", []); @@ -126,6 +135,8 @@ public async void Handle_Should_CallSaveChangesAsync_WhenGeneratedIdIsUnique() _mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object); _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); + _mockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new ValidationResult()); var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []); //Act @@ -153,6 +164,8 @@ public async void Handle_Should_ReturnSuccessResultWithArticleId_WhenGeneratedId _mockDbContext.Setup(x => x.Categories).Returns(mockCategoriesDbSet.Object); _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); + _mockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new ValidationResult()); var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []); @@ -194,6 +207,8 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu _mockDbContext.Setup(x => x.Revisions).Returns(mockRevisionsDbSet.Object); _mockSlugHelper.Setup(x => x.GenerateSlug("something extra cool")).Returns("something-extra-cool"); _mockCurrentUserService.Setup(x => x.UserId).Returns("test-user-id"); + _mockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new ValidationResult()); var expectedCategoryIds = new List {"category1", "category2"}; var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", ["category1", "category2", "category3"]); From 970b6a1321e46314d71a387c3ce111b38df4d4bd Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Fri, 8 Aug 2025 18:45:50 +0300 Subject: [PATCH 4/6] chore: removed old comments --- Application/Extensions/ServiceCollectionExt.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Application/Extensions/ServiceCollectionExt.cs b/Application/Extensions/ServiceCollectionExt.cs index cc38b7f..ec86e42 100644 --- a/Application/Extensions/ServiceCollectionExt.cs +++ b/Application/Extensions/ServiceCollectionExt.cs @@ -30,9 +30,6 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddApplicationServices(this IServiceCollection services) { services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); - //TODO: Reimplement without MediatR - // services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); - // services.AddTransient(typeof(IPipelineBehavior<,>), typeof(QueryCachingBehavior<,>)); services.AddTransient(); services.AddScoped(); From 3d3784c18733ecba2aae0f856c16863420280ee7 Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:05:03 +0300 Subject: [PATCH 5/6] feat: IQuery, ICommand, IQueryHandler, ICommandHandler --- .../CreateArticleCommandHandlerTests.cs | 14 ++--- Application/Common/Messaging/ICachedQuery.cs | 3 +- Application/Common/Messaging/ICommand.cs | 3 + .../Common/Messaging/ICommandHandler.cs | 8 +++ Application/Common/Messaging/IQuery.cs | 2 +- Application/Common/Messaging/IQueryHandler.cs | 8 +++ .../Extensions/ServiceCollectionExt.cs | 41 +++++++------ .../CreateArticle/CreateArticleCommand.cs | 4 +- .../CreateArticleCommandHandler.cs | 5 +- .../ICreateArticleCommandHandler.cs | 8 --- .../DeleteArticle/DeleteArticleCommand.cs | 6 +- .../DeleteArticleCommandHandler.cs | 6 +- .../IDeleteArticleCommandHandler.cs | 8 --- .../EditArticle/EditArticleCommand.cs | 6 +- .../EditArticle/EditArticleCommandHandler.cs | 5 +- .../EditArticle/IEditArticleCommandHandler.cs | 8 --- .../Articles/GetArticle/GetArticleQuery.cs | 3 +- .../GetArticle/GetArticleQueryHandler.cs | 5 +- .../GetArticle/IGetArticleQueryHandler.cs | 8 --- .../GetPendingRevisionsQuery.cs | 2 +- .../GetPendingRevisionsQueryHandler.cs | 6 +- .../IGetPendingRevisionsQueryHandler.cs | 8 --- .../GetPendingRevisionsQuery.cs | 2 +- .../GetPendingRevisionsQueryHandler.cs | 6 +- .../IGetPendingRevisionsCountQueryHandler.cs | 9 --- .../GetRevisionHistoryQuery.cs | 2 +- .../GetRevisionHistoryQueryHandler.cs | 6 +- .../IGetRevisionHistoryQueryHandler.cs | 9 --- .../GetRevisionReviewHistoryQuery.cs | 2 +- .../GetRevisionReviewHistoryQueryHandler.cs | 6 +- .../IGetRevisionReviewHistoryQueryHandler.cs | 9 --- .../IReviewRevisionCommandHandler.cs | 9 --- .../ReviewRevision/ReviewRevisionCommand.cs | 3 +- .../ReviewRevisionCommandHandler.cs | 5 +- .../ISearchArticlesQueryHandler.cs | 8 --- .../SearchArticles/SearchArticlesQuery.cs | 2 +- .../SearchArticlesQueryHandler.cs | 18 +++--- .../SetRedirect/ISetRedirectCommandHandler.cs | 9 --- .../SetRedirect/SetRedirectCommand.cs | 4 +- .../SetRedirect/SetRedirectCommandHandler.cs | 5 +- .../CreateAuthor/CreateAuthorCommand.cs | 6 +- .../CreateAuthorCommandHandler.cs | 7 ++- .../ICreateAuthorCommandHandler.cs | 8 --- .../Authors/EditAuthor/EditAuthorCommand.cs | 6 +- .../EditAuthor/EditAuthorCommandHandler.cs | 7 ++- .../EditAuthor/IEditAuthorCommandHandler.cs | 8 --- .../CreateCategory/CreateCategoryCommand.cs | 4 +- .../CreateCategoryCommandHandler.cs | 5 +- .../ICreateCategoryCommandHandler.cs | 8 --- .../DeleteCategory/DeleteCategoryCommand.cs | 4 +- .../DeleteCategoryCommandHandler.cs | 6 +- .../IDeleteCategoryCommandHandler.cs | 9 --- .../GetCategories/GetCategoriesQuery.cs | 3 +- .../GetCategoriesQueryHandler.cs | 6 +- .../IGetCategoriesQueryHandler.cs | 9 --- .../GetCategoriesTreeQuery.cs | 3 +- .../GetCategoriesTreeQueryHandler.cs | 6 +- .../IGetCategoriesTreeQueryHandler.cs | 9 --- .../GetCategoryArticlesQuery.cs | 3 +- .../GetCategoryArticlesQueryHandler.cs | 6 +- .../IGetCategoryArticlesQueryHandler.cs | 9 --- .../GetNavigationsTreeQuery.cs | 3 +- .../GetNavigationsTreeQueryHandler.cs | 6 +- .../IGetNavigationsTreeQueryHandler.cs | 9 --- .../IUpdateNavigationsTreeCommandHandler.cs | 9 --- .../UpdateNavigationsTreeCommand.cs | 4 +- .../UpdateNavigationsTreeCommandHandler.cs | 5 +- WebApi/Features/Articles/ArticlesModule.cs | 61 ++++++++++--------- .../Features/Categories/CategoriesModule.cs | 23 +++---- .../Features/Navigations/NavigationsModule.cs | 13 ++-- 70 files changed, 221 insertions(+), 325 deletions(-) create mode 100644 Application/Common/Messaging/ICommand.cs create mode 100644 Application/Common/Messaging/ICommandHandler.cs create mode 100644 Application/Common/Messaging/IQueryHandler.cs delete mode 100644 Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs delete mode 100644 Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs delete mode 100644 Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs delete mode 100644 Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs delete mode 100644 Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs delete mode 100644 Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs delete mode 100644 Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs delete mode 100644 Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs delete mode 100644 Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs delete mode 100644 Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs delete mode 100644 Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs delete mode 100644 Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs delete mode 100644 Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs delete mode 100644 Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs delete mode 100644 Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs delete mode 100644 Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs delete mode 100644 Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs delete mode 100644 Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs delete mode 100644 Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs delete mode 100644 Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs diff --git a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs index 8c7b1f6..9ef78d6 100644 --- a/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs +++ b/Application.Tests.Unit/Articles/CreateArticleCommandHandlerTests.cs @@ -43,7 +43,7 @@ public async void Handle_Should_ReturnFailureResult_WhenGeneratedIdIsEmpty() var command = new CreateArticleCommand(" ", "Lorem ipsum", "", []); //Act - var result = await _handler.Handle(command, default); + var result = await _handler.HandleAsync(command, default); //Assert result.IsError.Should().BeTrue(); @@ -66,7 +66,7 @@ public async void Handle_Should_NotCallSaveChangesAsync_WhenGeneratedIdIsEmpty() var command = new CreateArticleCommand(" ", "Lorem ipsum", "", []); //Act - await _handler.Handle(command, default); + await _handler.HandleAsync(command, default); //Assert _mockDbContext.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never); @@ -88,7 +88,7 @@ public async void Handle_Should_ReturnFailureResult_WhenGeneratedIdIsNotUnique() var command = new CreateArticleCommand("something cool", "Lorem ipsum", "", []); //Act - var result = await _handler.Handle(command, default); + var result = await _handler.HandleAsync(command, default); //Assert result.IsError.Should().BeTrue(); @@ -111,7 +111,7 @@ public async void Handle_Should_NotCallSaveChangesAsync_WhenGeneratedIdIsNotUniq var command = new CreateArticleCommand("something cool", "Lorem ipsum", "", []); //Act - var result = await _handler.Handle(command, default); + var result = await _handler.HandleAsync(command, default); //Assert _mockDbContext.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never); @@ -140,7 +140,7 @@ public async void Handle_Should_CallSaveChangesAsync_WhenGeneratedIdIsUnique() var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []); //Act - await _handler.Handle(command, default); + await _handler.HandleAsync(command, default); //Assert _mockDbContext.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Once); @@ -170,7 +170,7 @@ public async void Handle_Should_ReturnSuccessResultWithArticleId_WhenGeneratedId var command = new CreateArticleCommand("something extra cool", "Lorem ipsum", "", []); //Act - var result = await _handler.Handle(command, default); + var result = await _handler.HandleAsync(command, default); //Assert _mockDbContext.Verify(x => x.Articles.AddAsync( @@ -214,7 +214,7 @@ public async void Handle_Should_CreateRevisionWithContent_WhenGeneratedIdIsUniqu ["category1", "category2", "category3"]); //Act - var result = await _handler.Handle(command, default); + var result = await _handler.HandleAsync(command, default); //Assert _mockDbContext.Verify(x => x.Revisions.AddAsync( diff --git a/Application/Common/Messaging/ICachedQuery.cs b/Application/Common/Messaging/ICachedQuery.cs index 6f4eecb..7efef32 100644 --- a/Application/Common/Messaging/ICachedQuery.cs +++ b/Application/Common/Messaging/ICachedQuery.cs @@ -1,7 +1,6 @@ namespace Application.Common.Messaging; -//TODO: Redo without MediatR -public interface ICachedQuery : IQuery, ICachedQuery; +public interface ICachedQuery : IQuery, ICachedQuery; public interface ICachedQuery { diff --git a/Application/Common/Messaging/ICommand.cs b/Application/Common/Messaging/ICommand.cs new file mode 100644 index 0000000..01d217d --- /dev/null +++ b/Application/Common/Messaging/ICommand.cs @@ -0,0 +1,3 @@ +namespace Application.Common.Messaging; + +public interface ICommand; \ No newline at end of file diff --git a/Application/Common/Messaging/ICommandHandler.cs b/Application/Common/Messaging/ICommandHandler.cs new file mode 100644 index 0000000..be0c025 --- /dev/null +++ b/Application/Common/Messaging/ICommandHandler.cs @@ -0,0 +1,8 @@ +namespace Application.Common.Messaging; + +using ErrorOr; + +public interface ICommandHandler where TCommand : ICommand +{ + Task> HandleAsync(TCommand command, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Application/Common/Messaging/IQuery.cs b/Application/Common/Messaging/IQuery.cs index de720e2..9b559b1 100644 --- a/Application/Common/Messaging/IQuery.cs +++ b/Application/Common/Messaging/IQuery.cs @@ -1,3 +1,3 @@ namespace Application.Common.Messaging; -public interface IQuery; \ No newline at end of file +public interface IQuery; \ No newline at end of file diff --git a/Application/Common/Messaging/IQueryHandler.cs b/Application/Common/Messaging/IQueryHandler.cs new file mode 100644 index 0000000..61f85a4 --- /dev/null +++ b/Application/Common/Messaging/IQueryHandler.cs @@ -0,0 +1,8 @@ +namespace Application.Common.Messaging; + +using ErrorOr; + +public interface IQueryHandler where TQuery : IQuery +{ + Task> HandleAsync(TQuery query, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Application/Extensions/ServiceCollectionExt.cs b/Application/Extensions/ServiceCollectionExt.cs index ec86e42..13d3339 100644 --- a/Application/Extensions/ServiceCollectionExt.cs +++ b/Application/Extensions/ServiceCollectionExt.cs @@ -1,4 +1,5 @@ using System.Reflection; +using Application.Common.Messaging; using Application.Features.Articles.CreateArticle; using Application.Features.Articles.DeleteArticle; using Application.Features.Articles.EditArticle; @@ -32,29 +33,29 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); services.AddTransient(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped, CreateArticleCommandHandler>(); + services.AddScoped, DeleteArticleCommandHandler>(); + services.AddScoped, EditArticleCommandHandler>(); + services.AddScoped, GetArticleQueryHandler>(); + services.AddScoped, GetPendingRevisionsQueryHandler>(); + services.AddScoped, GetPendingRevisionsCountQueryHandler>(); + services.AddScoped, GetRevisionHistoryQueryHandler>(); + services.AddScoped, GetRevisionReviewHistoryQueryHandler>(); + services.AddScoped, ReviewRevisionCommandHandler>(); + services.AddScoped, SearchArticlesQueryHandler>(); + services.AddScoped, SetRedirectCommandHandler>(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped, CreateAuthorCommandHandler>(); + services.AddScoped, EditAuthorCommandHandler>(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped, CreateCategoryCommandHandler>(); + services.AddScoped, DeleteCategoryCommandHandler>(); + services.AddScoped, GetCategoriesQueryHandler>(); + services.AddScoped, GetCategoriesTreeQueryHandler>(); + services.AddScoped, GetCategoryArticlesQueryHandler>(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped, GetNavigationsTreeQueryHandler>(); + services.AddScoped, UpdateNavigationsTreeCommandHandler>(); return services; } diff --git a/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs b/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs index 7b08e27..5a1505b 100644 --- a/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs +++ b/Application/Features/Articles/CreateArticle/CreateArticleCommand.cs @@ -1,4 +1,4 @@ -using ErrorOr; +using Application.Common.Messaging; namespace Application.Features.Articles.CreateArticle; @@ -7,4 +7,4 @@ public record CreateArticleCommand( string Content, string AuthorsNote, List CategoryIds -); \ No newline at end of file +) : ICommand; \ No newline at end of file diff --git a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs index b5f6561..bbb2574 100644 --- a/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs +++ b/Application/Features/Articles/CreateArticle/CreateArticleCommandHandler.cs @@ -1,4 +1,5 @@ using Application.Authorization.Abstractions; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using Domain.Entities; @@ -14,9 +15,9 @@ public class CreateArticleCommandHandler( ISlugHelper slugHelper, ICurrentUserService identityService, IValidator validator -) : ICreateArticleCommandHandler +) : ICommandHandler { - public async Task> Handle(CreateArticleCommand command, CancellationToken token) + public async Task> HandleAsync(CreateArticleCommand command, CancellationToken token) { var validationResult = ValidatorHelper.Validate(validator, command); if (validationResult.IsError) diff --git a/Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs b/Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs deleted file mode 100644 index a0bb7f5..0000000 --- a/Application/Features/Articles/CreateArticle/ICreateArticleCommandHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.CreateArticle; - -public interface ICreateArticleCommandHandler -{ - Task> Handle(CreateArticleCommand command, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs b/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs index 7ed27d6..52285da 100644 --- a/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs +++ b/Application/Features/Articles/DeleteArticle/DeleteArticleCommand.cs @@ -1,3 +1,5 @@ -namespace Application.Features.Articles.DeleteArticle; +using Application.Common.Messaging; -public record DeleteArticleCommand(string Id); \ No newline at end of file +namespace Application.Features.Articles.DeleteArticle; + +public record DeleteArticleCommand(string Id) : ICommand; \ No newline at end of file diff --git a/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs b/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs index 762df3c..4e5e532 100644 --- a/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs +++ b/Application/Features/Articles/DeleteArticle/DeleteArticleCommandHandler.cs @@ -1,13 +1,15 @@ using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Messaging; using Application.Data; using ErrorOr; namespace Application.Features.Articles.DeleteArticle; -public class DeleteArticleCommandHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IDeleteArticleCommandHandler +public class DeleteArticleCommandHandler(IApplicationDbContext dbContext, ICacheService cacheService) + : ICommandHandler { - public async Task> Handle(DeleteArticleCommand request, CancellationToken token) + public async Task> HandleAsync(DeleteArticleCommand request, CancellationToken token) { var article = await dbContext.Articles.FindAsync(new object[] {request.Id}, token); if (article == null) return Errors.Article.NotFound; diff --git a/Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs b/Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs deleted file mode 100644 index 29d96d4..0000000 --- a/Application/Features/Articles/DeleteArticle/IDeleteArticleCommandHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.DeleteArticle; - -public interface IDeleteArticleCommandHandler -{ - Task> Handle(DeleteArticleCommand request, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/EditArticle/EditArticleCommand.cs b/Application/Features/Articles/EditArticle/EditArticleCommand.cs index 8ba1cff..b345d7f 100644 --- a/Application/Features/Articles/EditArticle/EditArticleCommand.cs +++ b/Application/Features/Articles/EditArticle/EditArticleCommand.cs @@ -1,8 +1,10 @@ -namespace Application.Features.Articles.EditArticle; +using Application.Common.Messaging; + +namespace Application.Features.Articles.EditArticle; public record EditArticleCommand( string Id, string Content, string AuthorsNote, List CategoryIds -); \ No newline at end of file +): ICommand; \ No newline at end of file diff --git a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs index 1f9752d..e2fd8c9 100644 --- a/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs +++ b/Application/Features/Articles/EditArticle/EditArticleCommandHandler.cs @@ -1,4 +1,5 @@ using Application.Authorization.Abstractions; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using Domain.Entities; @@ -12,9 +13,9 @@ public class EditArticleCommandHandler( IApplicationDbContext dbContext, ICurrentUserService identityService, IValidator validator -) : IEditArticleCommandHandler +) : ICommandHandler { - public async Task> Handle(EditArticleCommand request, CancellationToken token) + public async Task> HandleAsync(EditArticleCommand request, CancellationToken token) { var validationResult = ValidatorHelper.Validate(validator, request); if (validationResult.IsError) diff --git a/Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs b/Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs deleted file mode 100644 index 7872d4c..0000000 --- a/Application/Features/Articles/EditArticle/IEditArticleCommandHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.EditArticle; - -public interface IEditArticleCommandHandler -{ - Task> Handle(EditArticleCommand request, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/GetArticle/GetArticleQuery.cs b/Application/Features/Articles/GetArticle/GetArticleQuery.cs index be91794..25ad530 100644 --- a/Application/Features/Articles/GetArticle/GetArticleQuery.cs +++ b/Application/Features/Articles/GetArticle/GetArticleQuery.cs @@ -1,10 +1,9 @@ using Application.Common.Constants; using Application.Common.Messaging; -using ErrorOr; namespace Application.Features.Articles.GetArticle; -public record GetArticleQuery(string? Id, Guid? RevisionId = null) : ICachedQuery> +public record GetArticleQuery(string? Id, Guid? RevisionId = null) : ICachedQuery { public string Key => CachingKeys.Articles.ArticleById(Id); public TimeSpan? Expiration => null; diff --git a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs index bafec6a..7251b71 100644 --- a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs +++ b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs @@ -1,6 +1,7 @@ using Application.Authorization.Abstractions; using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using Domain.Entities; @@ -15,9 +16,9 @@ public class GetArticleQueryHandler( IIdentityService identityService, IValidator validator, ICacheService cacheService -) : IGetArticleQueryHandler +) : IQueryHandler { - public async Task> Handle(GetArticleQuery query, CancellationToken token) + public async Task> HandleAsync(GetArticleQuery query, CancellationToken token) { var validationResult = ValidatorHelper.Validate(validator, query); if (validationResult.IsError) diff --git a/Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs b/Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs deleted file mode 100644 index 1495972..0000000 --- a/Application/Features/Articles/GetArticle/IGetArticleQueryHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.GetArticle; - -public interface IGetArticleQueryHandler -{ - Task> Handle(GetArticleQuery query, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs index f23dc03..852d64b 100644 --- a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs +++ b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetPendingRevisions; -public record GetPendingRevisionsQuery : IQuery; \ No newline at end of file +public record GetPendingRevisionsQuery : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs index 2faacb9..9ccd6d3 100644 --- a/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs +++ b/Application/Features/Articles/GetPendingRevisions/GetPendingRevisionsQueryHandler.cs @@ -1,12 +1,14 @@ +using Application.Common.Messaging; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetPendingRevisions; -public class GetPendingRevisionsQueryHandler(IApplicationDbContext dbContext) : IGetPendingRevisionsQueryHandler +public class GetPendingRevisionsQueryHandler(IApplicationDbContext dbContext) + : IQueryHandler { - public async Task> Handle(GetPendingRevisionsQuery query, CancellationToken token) + public async Task> HandleAsync(GetPendingRevisionsQuery query, CancellationToken token) { var pendingRevisions = await dbContext.Revisions .Where(e => e.LatestReviewId == null) diff --git a/Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs b/Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs deleted file mode 100644 index a63df51..0000000 --- a/Application/Features/Articles/GetPendingRevisions/IGetPendingRevisionsQueryHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.GetPendingRevisions; - -public interface IGetPendingRevisionsQueryHandler -{ - Task> Handle(GetPendingRevisionsQuery query, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs index ad8d4b7..90c5337 100644 --- a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs +++ b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetPendingRevisionsCount; -public record GetPendingRevisionsCountQuery : IQuery; \ No newline at end of file +public record GetPendingRevisionsCountQuery : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs index 4be43c6..26be007 100644 --- a/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs +++ b/Application/Features/Articles/GetPendingRevisionsCount/GetPendingRevisionsQueryHandler.cs @@ -1,12 +1,14 @@ +using Application.Common.Messaging; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetPendingRevisionsCount; -public class GetPendingRevisionsCountQueryHandler(IApplicationDbContext dbContext) : IGetPendingRevisionsCountQueryHandler +public class GetPendingRevisionsCountQueryHandler(IApplicationDbContext dbContext) + : IQueryHandler { - public async Task> Handle(GetPendingRevisionsCountQuery query, + public async Task> HandleAsync(GetPendingRevisionsCountQuery query, CancellationToken token) { var pendingRevisionsCount = await dbContext.Revisions diff --git a/Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs b/Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs deleted file mode 100644 index 7464672..0000000 --- a/Application/Features/Articles/GetPendingRevisionsCount/IGetPendingRevisionsCountQueryHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.GetPendingRevisionsCount; - -public interface IGetPendingRevisionsCountQueryHandler -{ - Task> Handle(GetPendingRevisionsCountQuery query, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs index 4a6240c..fcbf8d0 100644 --- a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs +++ b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetRevisionHistory; -public record GetRevisionHistoryQuery(string Id) : IQuery; \ No newline at end of file +public record GetRevisionHistoryQuery(string Id) : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs index fc3cd2d..3d009fd 100644 --- a/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs +++ b/Application/Features/Articles/GetRevisionHistory/GetRevisionHistoryQueryHandler.cs @@ -1,12 +1,14 @@ +using Application.Common.Messaging; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetRevisionHistory; -public class GetRevisionHistoryQueryHandler(IApplicationDbContext dbContext) : IGetRevisionHistoryQueryHandler +public class GetRevisionHistoryQueryHandler(IApplicationDbContext dbContext) + : IQueryHandler { - public async Task> Handle(GetRevisionHistoryQuery query, + public async Task> HandleAsync(GetRevisionHistoryQuery query, CancellationToken token) { var article = await dbContext.Articles diff --git a/Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs deleted file mode 100644 index e8790d0..0000000 --- a/Application/Features/Articles/GetRevisionHistory/IGetRevisionHistoryQueryHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.GetRevisionHistory; - -public interface IGetRevisionHistoryQueryHandler -{ - Task> Handle(GetRevisionHistoryQuery query, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs index 008a51f..62635e9 100644 --- a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs +++ b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQuery.cs @@ -2,4 +2,4 @@ namespace Application.Features.Articles.GetRevisionReviewHistory; -public record GetRevisionReviewHistoryQuery(Guid Id) : IQuery; \ No newline at end of file +public record GetRevisionReviewHistoryQuery(Guid Id) : IQuery; \ No newline at end of file diff --git a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs index b2b35b1..11af175 100644 --- a/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs +++ b/Application/Features/Articles/GetRevisionReviewHistory/GetRevisionReviewHistoryQueryHandler.cs @@ -1,12 +1,14 @@ +using Application.Common.Messaging; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.GetRevisionReviewHistory; -public class GetRevisionReviewHistoryQueryHandler(IApplicationDbContext dbContext) : IGetRevisionReviewHistoryQueryHandler +public class GetRevisionReviewHistoryQueryHandler(IApplicationDbContext dbContext) + : IQueryHandler { - public async Task> Handle(GetRevisionReviewHistoryQuery query, + public async Task> HandleAsync(GetRevisionReviewHistoryQuery query, CancellationToken token) { if (!await dbContext.Revisions.AnyAsync(e => e.Id == query.Id, token)) return Errors.Revision.NotFound; diff --git a/Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs b/Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs deleted file mode 100644 index d290e46..0000000 --- a/Application/Features/Articles/GetRevisionReviewHistory/IGetRevisionReviewHistoryQueryHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.GetRevisionReviewHistory; - -public interface IGetRevisionReviewHistoryQueryHandler -{ - Task> Handle(GetRevisionReviewHistoryQuery query, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs b/Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs deleted file mode 100644 index 3ac00a3..0000000 --- a/Application/Features/Articles/ReviewRevision/IReviewRevisionCommandHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.ReviewRevision; - -public interface IReviewRevisionCommandHandler -{ - Task> Handle(ReviewRevisionCommand command, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs index 2ee2542..b6a2527 100644 --- a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs +++ b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommand.cs @@ -1,3 +1,4 @@ +using Application.Common.Messaging; using Domain.Entities; namespace Application.Features.Articles.ReviewRevision; @@ -6,4 +7,4 @@ public record ReviewRevisionCommand( Guid RevisionId, ReviewStatus Status, string Review -); \ No newline at end of file +): ICommand; \ No newline at end of file diff --git a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs index 45a695b..080c655 100644 --- a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs +++ b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs @@ -1,6 +1,7 @@ using Application.Authorization.Abstractions; using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using Domain.Entities; @@ -15,9 +16,9 @@ public class ReviewRevisionCommandHandler( ICurrentUserService identityService, ICacheService cacheService, IValidator validator -) : IReviewRevisionCommandHandler +) : ICommandHandler { - public async Task> Handle(ReviewRevisionCommand command, + public async Task> HandleAsync(ReviewRevisionCommand command, CancellationToken token) { var validationResult = ValidatorHelper.Validate(validator, command); diff --git a/Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs b/Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs deleted file mode 100644 index bf1f6ba..0000000 --- a/Application/Features/Articles/SearchArticles/ISearchArticlesQueryHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.SearchArticles; - -public interface ISearchArticlesQueryHandler -{ - Task> Handle(SearchArticlesQuery query, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs b/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs index 2523afa..6fa2d01 100644 --- a/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs +++ b/Application/Features/Articles/SearchArticles/SearchArticlesQuery.cs @@ -2,7 +2,7 @@ namespace Application.Features.Articles.SearchArticles; -public record SearchArticlesQuery : IQuery +public record SearchArticlesQuery : IQuery { public string? SearchTerm { get; } public int Page { get; } diff --git a/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs b/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs index b4e27b1..ec28160 100644 --- a/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs +++ b/Application/Features/Articles/SearchArticles/SearchArticlesQueryHandler.cs @@ -1,12 +1,14 @@ +using Application.Common.Messaging; using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; namespace Application.Features.Articles.SearchArticles; -public class SearchArticlesQueryHandler(IApplicationDbContext dbContext) : ISearchArticlesQueryHandler +public class SearchArticlesQueryHandler(IApplicationDbContext dbContext) + : IQueryHandler { - public async Task> Handle(SearchArticlesQuery query, CancellationToken token) + public async Task> HandleAsync(SearchArticlesQuery query, CancellationToken token) { var articles = dbContext.Articles .Where(e => e.RedirectArticleId == null && e.CurrentRevisionId != null); @@ -14,18 +16,18 @@ public async Task> Handle(SearchArticlesQuery qu if (!string.IsNullOrWhiteSpace(query.SearchTerm)) { articles = articles.Where(e => EF.Functions.ILike(e.Title, $"%{query.SearchTerm}%")) - .OrderByDescending(e => EF.Functions.TrigramsSimilarity(e.Title, query.SearchTerm)); + .OrderByDescending(e => EF.Functions.TrigramsSimilarity(e.Title, query.SearchTerm)); } - + var totalCount = await articles.AsNoTracking().CountAsync(token); - + var pagedArticles = await articles - .Skip((query.Page-1) * query.PageSize) + .Skip((query.Page - 1) * query.PageSize) .Take(query.PageSize) - .Select(e=> new SearchArticlesResponse.Element(e.Id, e.Title)) + .Select(e => new SearchArticlesResponse.Element(e.Id, e.Title)) .AsNoTracking() .ToListAsync(token); - + return new SearchArticlesResponse(query.Page, pagedArticles.Count, totalCount, pagedArticles); } } \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs b/Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs deleted file mode 100644 index ed7e7de..0000000 --- a/Application/Features/Articles/SetRedirect/ISetRedirectCommandHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Articles.SetRedirect; - -public interface ISetRedirectCommandHandler -{ - Task> Handle(SetRedirectCommand command, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs b/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs index 359946d..9635341 100644 --- a/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs +++ b/Application/Features/Articles/SetRedirect/SetRedirectCommand.cs @@ -1,3 +1,5 @@ +using Application.Common.Messaging; + namespace Application.Features.Articles.SetRedirect; -public record SetRedirectCommand(string ArticleId, string RedirectId); \ No newline at end of file +public record SetRedirectCommand(string ArticleId, string RedirectId) : ICommand; \ No newline at end of file diff --git a/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs b/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs index 6fe4313..23c1ca4 100644 --- a/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs +++ b/Application/Features/Articles/SetRedirect/SetRedirectCommandHandler.cs @@ -1,5 +1,6 @@ using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using ErrorOr; @@ -12,9 +13,9 @@ public class SetRedirectCommandHandler( IApplicationDbContext dbContext, ICacheService cacheService, IValidator validator - ) : ISetRedirectCommandHandler +) : ICommandHandler { - public async Task> Handle(SetRedirectCommand command, CancellationToken token) + public async Task> HandleAsync(SetRedirectCommand command, CancellationToken token) { var validationResult = ValidatorHelper.Validate(validator, command); if (validationResult.IsError) diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs index bdc9c4a..c856b17 100644 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs +++ b/Application/Features/Authors/CreateAuthor/CreateAuthorCommand.cs @@ -1,3 +1,5 @@ -namespace Application.Features.Authors.CreateAuthor; +using Application.Common.Messaging; -public record CreateAuthorCommand(string Id, string Name); \ No newline at end of file +namespace Application.Features.Authors.CreateAuthor; + +public record CreateAuthorCommand(string Id, string Name) : ICommand; \ No newline at end of file diff --git a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs b/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs index 54dc0ac..db373b2 100644 --- a/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs +++ b/Application/Features/Authors/CreateAuthor/CreateAuthorCommandHandler.cs @@ -1,4 +1,5 @@ -using Application.Data; +using Application.Common.Messaging; +using Application.Data; using Domain.Entities; using ErrorOr; @@ -6,9 +7,9 @@ namespace Application.Features.Authors.CreateAuthor; public class CreateAuthorCommandHandler( IApplicationDbContext dbContext -) : ICreateAuthorCommandHandler +) : ICommandHandler { - public async Task> Handle(CreateAuthorCommand command, CancellationToken token) + public async Task> HandleAsync(CreateAuthorCommand command, CancellationToken token) { var author = new Author {Id = command.Id, Name = command.Name}; diff --git a/Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs b/Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs deleted file mode 100644 index 749bbe7..0000000 --- a/Application/Features/Authors/CreateAuthor/ICreateAuthorCommandHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Authors.CreateAuthor; - -public interface ICreateAuthorCommandHandler -{ - Task> Handle(CreateAuthorCommand command, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs b/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs index 5ac9c6b..025a41c 100644 --- a/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs +++ b/Application/Features/Authors/EditAuthor/EditAuthorCommand.cs @@ -1,3 +1,5 @@ -namespace Application.Features.Authors.EditAuthor; +using Application.Common.Messaging; -public record EditAuthorCommand(string Id, string Name); \ No newline at end of file +namespace Application.Features.Authors.EditAuthor; + +public record EditAuthorCommand(string Id, string Name) : ICommand; \ No newline at end of file diff --git a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs b/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs index f2100b6..2cffbcd 100644 --- a/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs +++ b/Application/Features/Authors/EditAuthor/EditAuthorCommandHandler.cs @@ -1,4 +1,5 @@ -using Application.Data; +using Application.Common.Messaging; +using Application.Data; using ErrorOr; using Microsoft.EntityFrameworkCore; @@ -6,9 +7,9 @@ namespace Application.Features.Authors.EditAuthor; public class EditAuthorCommandHandler( IApplicationDbContext dbContext -) : IEditAuthorCommandHandler +) : ICommandHandler { - public async Task> Handle(EditAuthorCommand command, CancellationToken token) + public async Task> HandleAsync(EditAuthorCommand command, CancellationToken token) { var author = await dbContext.Authors.FirstOrDefaultAsync(e => e.Id == command.Id, token); diff --git a/Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs b/Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs deleted file mode 100644 index d87b8b5..0000000 --- a/Application/Features/Authors/EditAuthor/IEditAuthorCommandHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Authors.EditAuthor; - -public interface IEditAuthorCommandHandler -{ - Task> Handle(EditAuthorCommand command, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs b/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs index dd4a356..9c35bb6 100644 --- a/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs +++ b/Application/Features/Categories/CreateCategory/CreateCategoryCommand.cs @@ -1,3 +1,5 @@ +using Application.Common.Messaging; + namespace Application.Features.Categories.CreateCategory; -public record CreateCategoryCommand(string Name, string? ParentId); \ No newline at end of file +public record CreateCategoryCommand(string Name, string? ParentId) : ICommand; \ No newline at end of file diff --git a/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs b/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs index cad7945..69a1d62 100644 --- a/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs +++ b/Application/Features/Categories/CreateCategory/CreateCategoryCommandHandler.cs @@ -1,5 +1,6 @@ using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using Domain.Entities; @@ -15,9 +16,9 @@ public class CreateCategoryCommandHandler( ISlugHelper slugHelper, ICacheService cacheService, IValidator validator -) : ICreateCategoryCommandHandler +) : ICommandHandler { - public async Task> Handle(CreateCategoryCommand command, CancellationToken token) + public async Task> HandleAsync(CreateCategoryCommand command, CancellationToken token) { var validationResult = ValidatorHelper.Validate(validator, command); if (validationResult.IsError) diff --git a/Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs b/Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs deleted file mode 100644 index d05b0ad..0000000 --- a/Application/Features/Categories/CreateCategory/ICreateCategoryCommandHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Categories.CreateCategory; - -public interface ICreateCategoryCommandHandler -{ - Task> Handle(CreateCategoryCommand command, CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs index 2ea7b7c..d120de9 100644 --- a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs +++ b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommand.cs @@ -1,3 +1,5 @@ +using Application.Common.Messaging; + namespace Application.Features.Categories.DeleteCategory; -public record DeleteCategoryCommand(string Id); \ No newline at end of file +public record DeleteCategoryCommand(string Id) : ICommand; \ No newline at end of file diff --git a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs index 8df7638..07e3ca7 100644 --- a/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs +++ b/Application/Features/Categories/DeleteCategory/DeleteCategoryCommandHandler.cs @@ -1,6 +1,8 @@ using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Messaging; using Application.Data; +using Application.Features.Articles.DeleteArticle; using ErrorOr; namespace Application.Features.Categories.DeleteCategory; @@ -8,9 +10,9 @@ namespace Application.Features.Categories.DeleteCategory; public class DeleteCategoryCommandHandler( IApplicationDbContext dbContext, ICacheService cacheService -) : IDeleteCategoryCommandHandler +) : ICommandHandler { - public async Task> Handle(DeleteCategoryCommand deleteCategoryCommand, + public async Task> HandleAsync(DeleteCategoryCommand deleteCategoryCommand, CancellationToken token) { var category = await dbContext.Categories.FindAsync(new object[] {deleteCategoryCommand.Id}, token); diff --git a/Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs b/Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs deleted file mode 100644 index 4948025..0000000 --- a/Application/Features/Categories/DeleteCategory/IDeleteCategoryCommandHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Categories.DeleteCategory; - -public interface IDeleteCategoryCommandHandler -{ - Task> Handle(DeleteCategoryCommand deleteCategoryCommand, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs b/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs index 13e8276..6e4a0c5 100644 --- a/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs +++ b/Application/Features/Categories/GetCategories/GetCategoriesQuery.cs @@ -1,10 +1,9 @@ using Application.Common.Constants; using Application.Common.Messaging; -using ErrorOr; namespace Application.Features.Categories.GetCategories; -public record GetCategoriesQuery : ICachedQuery> +public record GetCategoriesQuery : ICachedQuery { public string Key => CachingKeys.Categories.CategoriesAll; public TimeSpan? Expiration => null; diff --git a/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs b/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs index ba2b5f8..40d548e 100644 --- a/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs +++ b/Application/Features/Categories/GetCategories/GetCategoriesQueryHandler.cs @@ -1,4 +1,5 @@ using Application.Common.Caching; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using ErrorOr; @@ -6,9 +7,10 @@ namespace Application.Features.Categories.GetCategories; -public class GetCategoriesQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetCategoriesQueryHandler +public class GetCategoriesQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) + : IQueryHandler { - public async Task> Handle(GetCategoriesQuery request, CancellationToken token) + public async Task> HandleAsync(GetCategoriesQuery request, CancellationToken token) { return await CachingHelper.GetOrCacheAsync(cacheService, request, async () => { diff --git a/Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs b/Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs deleted file mode 100644 index 2f8bcad..0000000 --- a/Application/Features/Categories/GetCategories/IGetCategoriesQueryHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Categories.GetCategories; - -public interface IGetCategoriesQueryHandler -{ - Task> Handle(GetCategoriesQuery request, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs index 46fd112..e69650c 100644 --- a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs +++ b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQuery.cs @@ -1,10 +1,9 @@ using Application.Common.Constants; using Application.Common.Messaging; -using ErrorOr; namespace Application.Features.Categories.GetCategoriesTree; -public record GetCategoriesTreeQuery : ICachedQuery> +public record GetCategoriesTreeQuery : ICachedQuery { public string Key => CachingKeys.Categories.CategoriesTree; public TimeSpan? Expiration => null; diff --git a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs index 46846de..abd416a 100644 --- a/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs +++ b/Application/Features/Categories/GetCategoriesTree/GetCategoriesTreeQueryHandler.cs @@ -1,4 +1,5 @@ using Application.Common.Caching; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using ErrorOr; @@ -6,9 +7,10 @@ namespace Application.Features.Categories.GetCategoriesTree; -public class GetCategoriesTreeQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetCategoriesTreeQueryHandler +public class GetCategoriesTreeQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) + : IQueryHandler { - public async Task> Handle(GetCategoriesTreeQuery request, CancellationToken token) + public async Task> HandleAsync(GetCategoriesTreeQuery request, CancellationToken token) { return await CachingHelper.GetOrCacheAsync(cacheService, request, async () => { diff --git a/Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs b/Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs deleted file mode 100644 index a970449..0000000 --- a/Application/Features/Categories/GetCategoriesTree/IGetCategoriesTreeQueryHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Categories.GetCategoriesTree; - -public interface IGetCategoriesTreeQueryHandler -{ - Task> Handle(GetCategoriesTreeQuery request, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs index 78ea48d..b9630c2 100644 --- a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs +++ b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQuery.cs @@ -1,10 +1,9 @@ using Application.Common.Constants; using Application.Common.Messaging; -using ErrorOr; namespace Application.Features.Categories.GetCategoryArticles; -public record GetCategoryArticlesQuery(string Id) : ICachedQuery> +public record GetCategoryArticlesQuery(string Id) : ICachedQuery { public string Key => CachingKeys.Categories.CategoryArticlesById(Id); public TimeSpan? Expiration => TimeSpan.FromHours(12); diff --git a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs index aa5408d..49ad565 100644 --- a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs +++ b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs @@ -1,4 +1,5 @@ using Application.Common.Caching; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using ErrorOr; @@ -6,9 +7,10 @@ namespace Application.Features.Categories.GetCategoryArticles; -public class GetCategoryArticlesQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetCategoryArticlesQueryHandler +public class GetCategoryArticlesQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) + : IQueryHandler { - public async Task> Handle(GetCategoryArticlesQuery request, + public async Task> HandleAsync(GetCategoryArticlesQuery request, CancellationToken token) { return await CachingHelper.GetOrCacheAsync>(cacheService, request, async () => diff --git a/Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs b/Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs deleted file mode 100644 index 7851496..0000000 --- a/Application/Features/Categories/GetCategoryArticles/IGetCategoryArticlesQueryHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Categories.GetCategoryArticles; - -public interface IGetCategoryArticlesQueryHandler -{ - Task> Handle(GetCategoryArticlesQuery request, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs index ba16bb1..a322699 100644 --- a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs +++ b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQuery.cs @@ -1,10 +1,9 @@ using Application.Common.Constants; using Application.Common.Messaging; -using ErrorOr; namespace Application.Features.Navigations.GetNavigationTree; -public record GetNavigationsTreeQuery : ICachedQuery> +public record GetNavigationsTreeQuery : ICachedQuery { public string Key => CachingKeys.Navigation.NavigationsTree; public TimeSpan? Expiration => TimeSpan.FromHours(12); diff --git a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs index 15fe716..a70170e 100644 --- a/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs +++ b/Application/Features/Navigations/GetNavigationTree/GetNavigationsTreeQueryHandler.cs @@ -1,4 +1,5 @@ using Application.Common.Caching; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using ErrorOr; @@ -6,9 +7,10 @@ namespace Application.Features.Navigations.GetNavigationTree; -public class GetNavigationsTreeQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) : IGetNavigationsTreeQueryHandler +public class GetNavigationsTreeQueryHandler(IApplicationDbContext dbContext, ICacheService cacheService) + : IQueryHandler { - public async Task> Handle(GetNavigationsTreeQuery request, + public async Task> HandleAsync(GetNavigationsTreeQuery request, CancellationToken token) { return await CachingHelper.GetOrCacheAsync(cacheService, request, async () => diff --git a/Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs b/Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs deleted file mode 100644 index 059a37f..0000000 --- a/Application/Features/Navigations/GetNavigationTree/IGetNavigationsTreeQueryHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Navigations.GetNavigationTree; - -public interface IGetNavigationsTreeQueryHandler -{ - Task> Handle(GetNavigationsTreeQuery request, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs b/Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs deleted file mode 100644 index daab520..0000000 --- a/Application/Features/Navigations/UpdateNavigationsTree/IUpdateNavigationsTreeCommandHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using ErrorOr; - -namespace Application.Features.Navigations.UpdateNavigationsTree; - -public interface IUpdateNavigationsTreeCommandHandler -{ - Task> Handle(UpdateNavigationsTreeCommand request, - CancellationToken token); -} \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs index 3a35eb6..384478a 100644 --- a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs +++ b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommand.cs @@ -1,6 +1,8 @@ +using Application.Common.Messaging; + namespace Application.Features.Navigations.UpdateNavigationsTree; -public record UpdateNavigationsTreeCommand(List Data) +public record UpdateNavigationsTreeCommand(List Data) : ICommand { public record Element(string Name, string? Uri, string? Icon, List Children); } \ No newline at end of file diff --git a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs index 942ea3e..da80ca4 100644 --- a/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs +++ b/Application/Features/Navigations/UpdateNavigationsTree/UpdateNavigationsTreeCommandHandler.cs @@ -1,5 +1,6 @@ using Application.Common.Caching; using Application.Common.Constants; +using Application.Common.Messaging; using Application.Common.Utils; using Application.Data; using Domain.Entities; @@ -12,9 +13,9 @@ public class UpdateNavigationsTreeCommandHandler( IApplicationDbContext dbContext, ICacheService cacheService, IValidator validator -) : IUpdateNavigationsTreeCommandHandler +) : ICommandHandler { - public async Task> Handle(UpdateNavigationsTreeCommand request, + public async Task> HandleAsync(UpdateNavigationsTreeCommand request, CancellationToken token) { var validationResult = ValidatorHelper.Validate(validator, request); diff --git a/WebApi/Features/Articles/ArticlesModule.cs b/WebApi/Features/Articles/ArticlesModule.cs index 739ff23..79d231b 100644 --- a/WebApi/Features/Articles/ArticlesModule.cs +++ b/WebApi/Features/Articles/ArticlesModule.cs @@ -1,4 +1,5 @@ -using Application.Features.Articles.CreateArticle; +using Application.Common.Messaging; +using Application.Features.Articles.CreateArticle; using Application.Features.Articles.DeleteArticle; using Application.Features.Articles.EditArticle; using Application.Features.Articles.GetArticle; @@ -22,12 +23,12 @@ public static void AddArticlesEndpoints(this IEndpointRouteBuilder app) { app.MapPost("/api/articles", async Task ( - ICreateArticleCommandHandler createArticleCommandHandler, + ICommandHandler createArticleCommandHandler, CreateArticleRequest request, CancellationToken cancellationToken) => { var command = new CreateArticleCommand(request.Title, request.Content, request.AuthorsNote, request.CategoryIds); - var result = await createArticleCommandHandler.Handle(command, cancellationToken); + var result = await createArticleCommandHandler.HandleAsync(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/{value.Id}", value), error => error.ToIResult() @@ -45,14 +46,14 @@ async Task ( app.MapGet("/api/articles", async Task ( - ISearchArticlesQueryHandler searchArticlesQueryHandler, + IQueryHandler searchArticlesQueryHandler, CancellationToken cancellationToken, string? searchTerm, int page = 1, int pageSize = 50) => { var query = new SearchArticlesQuery(searchTerm, page, pageSize); - var result = await searchArticlesQueryHandler.Handle(query, cancellationToken); + var result = await searchArticlesQueryHandler.HandleAsync(query, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -66,10 +67,10 @@ async Task ( app.MapGet("/api/articles/{id}", async Task ( string id, - IGetArticleQueryHandler getArticleQueryHandler, + IQueryHandler getArticleQueryHandler, CancellationToken cancellationToken) => { - var result = await getArticleQueryHandler.Handle(new GetArticleQuery(id), cancellationToken); + var result = await getArticleQueryHandler.HandleAsync(new GetArticleQuery(id), cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -85,10 +86,10 @@ async Task ( app.MapGet("/api/articles/revision:{id:guid}", async Task ( Guid id, - IGetArticleQueryHandler getArticleQueryHandler, + IQueryHandler getArticleQueryHandler, CancellationToken cancellationToken) => { - var result = await getArticleQueryHandler.Handle(new GetArticleQuery(null, id), cancellationToken); + var result = await getArticleQueryHandler.HandleAsync(new GetArticleQuery(null, id), cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -104,12 +105,12 @@ async Task ( app.MapPut("/api/articles/{id}", async Task ( string id, - IEditArticleCommandHandler editArticleCommandHandler, + ICommandHandler editArticleCommandHandler, EditArticleRequest request, CancellationToken cancellationToken) => { var command = new EditArticleCommand(id, request.Content, request.AuthorsNote, request.CategoryIds); - var result = await editArticleCommandHandler.Handle(command, cancellationToken); + var result = await editArticleCommandHandler.HandleAsync(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/{value.Id}", value), error => error.ToIResult() @@ -129,12 +130,12 @@ async Task ( app.MapPut("/api/articles/{id}/redirect", async Task ( string id, - ISetRedirectCommandHandler setRedirectCommandHandler, + ICommandHandler setRedirectCommandHandler, SerArticleRedirectRequest request, CancellationToken cancellationToken) => { var command = new SetRedirectCommand(id, request.RedirectArticleId); - var result = await setRedirectCommandHandler.Handle(command, cancellationToken); + var result = await setRedirectCommandHandler.HandleAsync(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/{value.Id}", true), error => error.ToIResult() @@ -152,10 +153,12 @@ async Task ( .WithOpenApi(); app.MapGet("/api/articles/revisions/pending", - async Task (IGetPendingRevisionsQueryHandler getPendingRevisionsQueryHandler, CancellationToken cancellationToken) => + async Task ( + IQueryHandler getPendingRevisionsQueryHandler, + CancellationToken cancellationToken) => { - var command = new GetPendingRevisionsQuery(); - var result = await getPendingRevisionsQueryHandler.Handle(command, cancellationToken); + var query = new GetPendingRevisionsQuery(); + var result = await getPendingRevisionsQueryHandler.HandleAsync(query, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -171,11 +174,11 @@ async Task (IGetPendingRevisionsQueryHandler getPendingRevisionsQueryHa app.MapGet("/api/articles/revisions/pending/count", async Task ( - IGetPendingRevisionsCountQueryHandler getPendingRevisionsCountQueryHandler, + IQueryHandler getPendingRevisionsCountQueryHandler, CancellationToken cancellationToken) => { - var command = new GetPendingRevisionsCountQuery(); - var result = await getPendingRevisionsCountQueryHandler.Handle(command, cancellationToken); + var query = new GetPendingRevisionsCountQuery(); + var result = await getPendingRevisionsCountQueryHandler.HandleAsync(query, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -193,11 +196,11 @@ async Task ( app.MapGet("/api/articles/{id}/revisions", async Task ( string id, - IGetRevisionHistoryQueryHandler getRevisionHistoryQueryHandler, + IQueryHandler getRevisionHistoryQueryHandler, CancellationToken cancellationToken) => { - var command = new GetRevisionHistoryQuery(id); - var result = await getRevisionHistoryQueryHandler.Handle(command, cancellationToken); + var query = new GetRevisionHistoryQuery(id); + var result = await getRevisionHistoryQueryHandler.HandleAsync(query, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -212,11 +215,11 @@ async Task ( app.MapGet("/api/articles/revisions/{id}/reviews", async Task ( Guid id, - IGetRevisionReviewHistoryQueryHandler getRevisionHistoryQueryHandler, + IQueryHandler getRevisionHistoryQueryHandler, CancellationToken cancellationToken) => { - var command = new GetRevisionReviewHistoryQuery(id); - var result = await getRevisionHistoryQueryHandler.Handle(command, cancellationToken); + var query = new GetRevisionReviewHistoryQuery(id); + var result = await getRevisionHistoryQueryHandler.HandleAsync(query, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -231,12 +234,12 @@ async Task ( app.MapPost("/api/articles/revisions/{id}/reviews", async Task ( Guid id, - IReviewRevisionCommandHandler reviewRevisionCommandHandler, + ICommandHandler reviewRevisionCommandHandler, ReviewArticleRevisionRequest request, CancellationToken cancellationToken) => { var command = new ReviewRevisionCommand(id, request.Status, request.Review); - var result = await reviewRevisionCommandHandler.Handle(command, cancellationToken); + var result = await reviewRevisionCommandHandler.HandleAsync(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/articles/revisions/{id}/reviews/{value.Id}", value), error => error.ToIResult() @@ -255,11 +258,11 @@ async Task ( app.MapDelete("/api/articles/{id}", async ( string id, - IDeleteArticleCommandHandler deleteArticleCommandHandler, + ICommandHandler deleteArticleCommandHandler, CancellationToken cancellationToken) => { var command = new DeleteArticleCommand(id); - var result = await deleteArticleCommandHandler.Handle(command, cancellationToken); + var result = await deleteArticleCommandHandler.HandleAsync(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() diff --git a/WebApi/Features/Categories/CategoriesModule.cs b/WebApi/Features/Categories/CategoriesModule.cs index e4cd65b..d3afa46 100644 --- a/WebApi/Features/Categories/CategoriesModule.cs +++ b/WebApi/Features/Categories/CategoriesModule.cs @@ -1,4 +1,5 @@ -using Application.Features.Categories.CreateCategory; +using Application.Common.Messaging; +using Application.Features.Categories.CreateCategory; using Application.Features.Categories.DeleteCategory; using Application.Features.Categories.GetCategories; using Application.Features.Categories.GetCategoriesTree; @@ -16,12 +17,12 @@ public static void AddCategoriesEndpoints(this IEndpointRouteBuilder app) { app.MapPost("/api/categories", async ( - ICreateCategoryCommandHandler createCategoryCommandHandler, + ICommandHandler createCategoryCommandHandler, CreateCategoryRequest request, CancellationToken cancellationToken) => { var command = new CreateCategoryCommand(request.Name, request.ParentId); - var result = await createCategoryCommandHandler.Handle(command, cancellationToken); + var result = await createCategoryCommandHandler.HandleAsync(command, cancellationToken); return result.MatchFirst( value => Results.Created($"/api/categories/{value.Id}", value), error => error.ToIResult() @@ -40,10 +41,10 @@ public static void AddCategoriesEndpoints(this IEndpointRouteBuilder app) app.MapGet("/api/categories", async Task ( - IGetCategoriesQueryHandler getCategoriesQueryHandler, + IQueryHandler getCategoriesQueryHandler, CancellationToken cancellationToken) => { - var response = await getCategoriesQueryHandler.Handle(new GetCategoriesQuery(), cancellationToken); + var response = await getCategoriesQueryHandler.HandleAsync(new GetCategoriesQuery(), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -56,10 +57,10 @@ async Task ( app.MapGet("/api/categories/tree", async Task ( - IGetCategoriesTreeQueryHandler getCategoriesTreeQueryHandler, + IQueryHandler getCategoriesTreeQueryHandler, CancellationToken cancellationToken) => { - var response = await getCategoriesTreeQueryHandler.Handle(new GetCategoriesTreeQuery(), cancellationToken); + var response = await getCategoriesTreeQueryHandler.HandleAsync(new GetCategoriesTreeQuery(), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -73,11 +74,11 @@ async Task ( app.MapGet("/api/categories/{id}/articles", async Task ( string id, - IGetCategoryArticlesQueryHandler getCategoryArticlesQuery, + IQueryHandler getCategoryArticlesQuery, CancellationToken cancellationToken) => { var query = new GetCategoryArticlesQuery(id); - var response = await getCategoryArticlesQuery.Handle(query, cancellationToken); + var response = await getCategoryArticlesQuery.HandleAsync(query, cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -92,11 +93,11 @@ async Task ( app.MapDelete("/api/categories/{id}", async ( string id, - IDeleteCategoryCommandHandler deleteCategoryCommandHandler, + ICommandHandler deleteCategoryCommandHandler, CancellationToken cancellationToken) => { var command = new DeleteCategoryCommand(id); - var result = await deleteCategoryCommandHandler.Handle(command, cancellationToken); + var result = await deleteCategoryCommandHandler.HandleAsync(command, cancellationToken); return result.MatchFirst( value => Results.Ok(value), error => error.ToIResult() diff --git a/WebApi/Features/Navigations/NavigationsModule.cs b/WebApi/Features/Navigations/NavigationsModule.cs index 4c3e7fb..3368bd6 100644 --- a/WebApi/Features/Navigations/NavigationsModule.cs +++ b/WebApi/Features/Navigations/NavigationsModule.cs @@ -1,4 +1,5 @@ -using Application.Features.Navigations.GetNavigationTree; +using Application.Common.Messaging; +using Application.Features.Navigations.GetNavigationTree; using Application.Features.Navigations.UpdateNavigationsTree; using Microsoft.AspNetCore.Authorization; using WebApi.Extensions; @@ -12,9 +13,9 @@ public static class NavigationsModule public static void AddNavigationsEndpoints(this IEndpointRouteBuilder app) { app.MapGet("/api/navigations/tree", - async Task (IGetNavigationsTreeQueryHandler getNavigationsTreeQueryHandler, CancellationToken cancellationToken) => + async Task (IQueryHandler getNavigationsTreeQueryHandler, CancellationToken cancellationToken) => { - var response = await getNavigationsTreeQueryHandler.Handle(new GetNavigationsTreeQuery(), cancellationToken); + var response = await getNavigationsTreeQueryHandler.HandleAsync(new GetNavigationsTreeQuery(), cancellationToken); return response.MatchFirst( value => Results.Ok(value), error => error.ToIResult() @@ -26,10 +27,10 @@ async Task (IGetNavigationsTreeQueryHandler getNavigationsTreeQueryHand .WithOpenApi(); app.MapPut("/api/navigations/tree", - async Task (IUpdateNavigationsTreeCommandHandler updateNavigationsTreeCommandHandler, UpdateNavigationsTreeRequest request, CancellationToken cancellationToken) => + async Task (ICommandHandler updateNavigationsTreeCommandHandler, UpdateNavigationsTreeRequest request, CancellationToken cancellationToken) => { var command = new UpdateNavigationsTreeCommand(request.Data); - var response = await updateNavigationsTreeCommandHandler.Handle(command, cancellationToken); + var response = await updateNavigationsTreeCommandHandler.HandleAsync(command, cancellationToken); return response.MatchFirst( value => Results.Ok(), error => error.ToIResult() @@ -37,7 +38,7 @@ async Task (IUpdateNavigationsTreeCommandHandler updateNavigationsTreeC }) .WithName("UpdateNavigationsTree") .WithTags("Navigations") - .Produces() + .Produces() .ProducesProblem(StatusCodes.Status400BadRequest) .ProducesProblem(StatusCodes.Status401Unauthorized) .ProducesProblem(StatusCodes.Status403Forbidden) From 96c4a3e09074f1bd9fcd31878e12e690f2cb457c Mon Sep 17 00:00:00 2001 From: VitSun666 <61446535+VitSun666@users.noreply.github.com> Date: Sat, 9 Aug 2025 00:13:56 +0300 Subject: [PATCH 6/6] chore: supress null warning in entity framework queries --- .../Features/Articles/GetArticle/GetArticleQueryHandler.cs | 2 +- .../Articles/ReviewRevision/ReviewRevisionCommandHandler.cs | 2 +- .../GetCategoryArticles/GetCategoryArticlesQueryHandler.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs index 7251b71..507767f 100644 --- a/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs +++ b/Application/Features/Articles/GetArticle/GetArticleQueryHandler.cs @@ -40,7 +40,7 @@ private async Task> GetByArticleId(string id, Cancel var article = await dbContext.Articles .Include(e => e.CurrentRevision) .Include(e => e.RedirectArticle) - .ThenInclude(e => e.CurrentRevision) + .ThenInclude(e => e!.CurrentRevision) .AsNoTracking() .FirstOrDefaultAsync(e => e.Id == id, token); diff --git a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs index 080c655..9f44fa1 100644 --- a/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs +++ b/Application/Features/Articles/ReviewRevision/ReviewRevisionCommandHandler.cs @@ -30,7 +30,7 @@ public async Task> HandleAsync(ReviewRevisionCom var revision = await dbContext.Revisions .Include(e => e.Article) .ThenInclude(e => e.CurrentRevision) - .ThenInclude(e => e.Categories) + .ThenInclude(e => e!.Categories) .Include(e => e.Categories) .Include(e => e.LatestReview) .FirstOrDefaultAsync(e => e.Id == command.RevisionId, token); diff --git a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs index 49ad565..64ba9fe 100644 --- a/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs +++ b/Application/Features/Categories/GetCategoryArticles/GetCategoryArticlesQueryHandler.cs @@ -21,7 +21,7 @@ public async Task> HandleAsync(GetCategoryA if (category == null) return Errors.Category.NotFound; var articlesList = await dbContext.Articles - .Where(e => e.RedirectArticleId == null && e.CurrentRevision.Categories.Any(x => x.Id == request.Id)) + .Where(e => e.RedirectArticleId == null && e.CurrentRevision!.Categories.Any(x => x.Id == request.Id)) .Select(e=> new GetCategoryArticlesResponse.Element(e.Id, e.Title)) .AsNoTracking() .ToListAsync(token);