From bb6cba1ed919b9bd32813452d763c24558fde093 Mon Sep 17 00:00:00 2001 From: "Amir.H Khademi" Date: Wed, 29 May 2024 19:17:41 +0330 Subject: [PATCH] feat(Notfication),feat(NotificationCQRS).feat(NotificationController) - Add notification marten entity - Add notification service by CQRS and mediatR - Add notification controller --- .../Controllers/NotificationController.cs | 21 +++++++++ Brizco.Api/Controllers/RecipeController.cs | 3 +- Brizco.Common/Models/Entity/IMartenEntity.cs | 8 ++-- Brizco.Common/Models/Entity/MartenEntity.cs | 21 ++++----- Brizco.Core/Brizco.Core.csproj | 3 ++ .../EntityServices/Abstracts/IBrewService.cs | 8 ---- .../MartenServices/Abstracts/IBrewService.cs | 8 ++++ .../BrewService.cs | 13 +++--- .../Commands/NotificationCommands.cs | 4 ++ .../Queries/NotificationQueries.cs | 5 +++ .../MartenEntities/Brews/BaseBrew.cs | 1 - .../Notifications/Notification.cs | 9 ++++ .../Marten/MartenRepository.cs | 6 +++ Brizco.Repository/Brizco.Repository.csproj | 2 + .../CreateNotificationHandlerCommand.cs | 33 ++++++++++++++ .../GetNotificationsQueryHandler.cs | 44 +++++++++++++++++++ .../ReadNotificationCommandHandler.cs | 28 ++++++++++++ .../Repositories/Marten/IMartenRepository.cs | 1 + 18 files changed, 187 insertions(+), 31 deletions(-) create mode 100644 Brizco.Api/Controllers/NotificationController.cs delete mode 100644 Brizco.Core/EntityServices/Abstracts/IBrewService.cs create mode 100644 Brizco.Core/MartenServices/Abstracts/IBrewService.cs rename Brizco.Core/{EntityServices => MartenServices}/BrewService.cs (93%) create mode 100644 Brizco.Domain/CommandQueries/Commands/NotificationCommands.cs create mode 100644 Brizco.Domain/CommandQueries/Queries/NotificationQueries.cs create mode 100644 Brizco.Domain/MartenEntities/Notifications/Notification.cs create mode 100644 Brizco.Repository/MartenHandlers/Notifications/CreateNotificationHandlerCommand.cs create mode 100644 Brizco.Repository/MartenHandlers/Notifications/GetNotificationsQueryHandler.cs create mode 100644 Brizco.Repository/MartenHandlers/Notifications/ReadNotificationCommandHandler.cs diff --git a/Brizco.Api/Controllers/NotificationController.cs b/Brizco.Api/Controllers/NotificationController.cs new file mode 100644 index 0000000..8bb138f --- /dev/null +++ b/Brizco.Api/Controllers/NotificationController.cs @@ -0,0 +1,21 @@ +namespace Brizco.Api.Controllers; + +public class NotificationController : ICarterModule +{ + public void AddRoutes(IEndpointRouteBuilder app) + { + var group = app.NewVersionedApi("Notifications") + .MapGroup("api/notification"); + + group.MapGet("", GetUserNotificationsAsync) + .WithDisplayName("Get User Notifications") + .WithDescription("Get user notifications , by JWT user id") + .RequireAuthorization(builder=>builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser()) + .HasApiVersion(1.0); + + } + + private async Task GetUserNotificationsAsync([FromQuery] int page, [FromQuery] int count, + [FromServices] IMediator mediator, CancellationToken cancellationToken) + => TypedResults.Ok(await mediator.Send(new GetNotificationsQuery(page, count), cancellationToken)); +} \ No newline at end of file diff --git a/Brizco.Api/Controllers/RecipeController.cs b/Brizco.Api/Controllers/RecipeController.cs index 35d451c..30b0b36 100644 --- a/Brizco.Api/Controllers/RecipeController.cs +++ b/Brizco.Api/Controllers/RecipeController.cs @@ -1,4 +1,5 @@ -using Marten; +using Brizco.Core.MartenServices.Abstracts; +using Marten; using System.Text.Json; namespace Brizco.Api.Controllers; diff --git a/Brizco.Common/Models/Entity/IMartenEntity.cs b/Brizco.Common/Models/Entity/IMartenEntity.cs index e390b8b..0bca9e9 100644 --- a/Brizco.Common/Models/Entity/IMartenEntity.cs +++ b/Brizco.Common/Models/Entity/IMartenEntity.cs @@ -2,8 +2,8 @@ public interface IMartenEntity { - string CreatedBy { get; } - string ModifiedBy { get; } - DateTime CreatedAt { get; } - DateTime ModifiedAt { get; } + //string CreatedBy { get; } + //string ModifiedBy { get; } + //DateTime CreatedAt { get; } + //DateTime ModifiedAt { get; } } \ No newline at end of file diff --git a/Brizco.Common/Models/Entity/MartenEntity.cs b/Brizco.Common/Models/Entity/MartenEntity.cs index 5adf940..42205a5 100644 --- a/Brizco.Common/Models/Entity/MartenEntity.cs +++ b/Brizco.Common/Models/Entity/MartenEntity.cs @@ -6,21 +6,22 @@ public class MartenEntity : IMartenEntity public Guid Id { get; set; } - [Display(Name = "تاریخ ساخت")] - public DateTime CreatedAt { get; set; } + //[Display(Name = "تاریخ ساخت")] + //public DateTime CreatedAt { get; set; } - [Display(Name = "ساخته شده توسط")] - public string CreatedBy { get; set; } = string.Empty; + //[Display(Name = "ساخته شده توسط")] + //public string CreatedBy { get; set; } = string.Empty; - [Display(Name = "اخرین تغییر در")] - public DateTime ModifiedAt { get; set; } + //[Display(Name = "اخرین تغییر در")] + //public DateTime ModifiedAt { get; set; } - [Display(Name = "اخرین تغییر توسط")] - public string ModifiedBy { get; set; } = string.Empty; + //[Display(Name = "اخرین تغییر توسط")] + //public string ModifiedBy { get; set; } = string.Empty; + public Guid ComplexId { get; set; } - public bool Equals(ApiEntity other) + public bool Equals(MartenEntity other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; @@ -32,7 +33,7 @@ public class MartenEntity : IMartenEntity if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; - return Equals((ApiEntity)obj); + return Equals((MartenEntity)obj); } public override int GetHashCode() diff --git a/Brizco.Core/Brizco.Core.csproj b/Brizco.Core/Brizco.Core.csproj index 28f13d4..f4f4b7e 100644 --- a/Brizco.Core/Brizco.Core.csproj +++ b/Brizco.Core/Brizco.Core.csproj @@ -34,6 +34,8 @@ + + @@ -46,6 +48,7 @@ + diff --git a/Brizco.Core/EntityServices/Abstracts/IBrewService.cs b/Brizco.Core/EntityServices/Abstracts/IBrewService.cs deleted file mode 100644 index 7c5cd11..0000000 --- a/Brizco.Core/EntityServices/Abstracts/IBrewService.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Brizco.Core.EntityServices.Abstracts; - -public interface IBrewService : IScopedDependency -{ - public Task GetLastBrewAsync(string recipeName, CancellationToken cancellationToken = default); - public Task GetBrewAsync(string recipeName , CancellationToken cancellationToken = default); - public Task AddBrewAsync(string recipeName,JsonDocument recipeObj, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/Brizco.Core/MartenServices/Abstracts/IBrewService.cs b/Brizco.Core/MartenServices/Abstracts/IBrewService.cs new file mode 100644 index 0000000..8cd177d --- /dev/null +++ b/Brizco.Core/MartenServices/Abstracts/IBrewService.cs @@ -0,0 +1,8 @@ +namespace Brizco.Core.MartenServices.Abstracts; + +public interface IBrewService : IScopedDependency +{ + public Task GetLastBrewAsync(string recipeName, CancellationToken cancellationToken = default); + public Task GetBrewAsync(string recipeName, CancellationToken cancellationToken = default); + public Task AddBrewAsync(string recipeName, JsonDocument recipeObj, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Brizco.Core/EntityServices/BrewService.cs b/Brizco.Core/MartenServices/BrewService.cs similarity index 93% rename from Brizco.Core/EntityServices/BrewService.cs rename to Brizco.Core/MartenServices/BrewService.cs index 865b373..0d486e5 100644 --- a/Brizco.Core/EntityServices/BrewService.cs +++ b/Brizco.Core/MartenServices/BrewService.cs @@ -1,15 +1,14 @@ using Newtonsoft.Json; using System.Reflection; -using Brizco.Domain; -namespace Brizco.Core.EntityServices; +namespace Brizco.Core.MartenServices; public class BrewService : IBrewService { private readonly IMartenRepositoryWrapper _martenRepositoryWrapper; private readonly ICurrentUserService _currentUserService; - public BrewService(IMartenRepositoryWrapper martenRepositoryWrapper,ICurrentUserService currentUserService) + public BrewService(IMartenRepositoryWrapper martenRepositoryWrapper, ICurrentUserService currentUserService) { _martenRepositoryWrapper = martenRepositoryWrapper; _currentUserService = currentUserService; @@ -20,10 +19,10 @@ public class BrewService : IBrewService if (type == null) throw new AppException("Recipe not found", ApiResultStatusCode.NotFound); if (_currentUserService.ComplexId == null) - throw new BaseApiException(ApiResultStatusCode.BadRequest,"Complex id is null"); + throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is null"); if (!Guid.TryParse(_currentUserService.ComplexId, out Guid complexId)) throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is wrong"); - + var baseRecipe = await _martenRepositoryWrapper.SetRepository() .GetEntityAsync(s => s.ComplexId == complexId && s.Name == recipeName, cancellationToken); object? recipe; @@ -49,7 +48,7 @@ public class BrewService : IBrewService throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is wrong"); var baseRecipe = await _martenRepositoryWrapper.SetRepository() - .GetEntityAsync(s =>s.ComplexId==complexId && s.Name == recipeName, cancellationToken); + .GetEntityAsync(s => s.ComplexId == complexId && s.Name == recipeName, cancellationToken); object? recipe; if (baseRecipe == null) { @@ -92,7 +91,7 @@ public class BrewService : IBrewService throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is wrong"); var baseRecipe = await _martenRepositoryWrapper.SetRepository() - .GetEntityAsync(s =>s.ComplexId == complexId && s.Name == recipeName, cancellationToken); + .GetEntityAsync(s => s.ComplexId == complexId && s.Name == recipeName, cancellationToken); if (baseRecipe == null) { diff --git a/Brizco.Domain/CommandQueries/Commands/NotificationCommands.cs b/Brizco.Domain/CommandQueries/Commands/NotificationCommands.cs new file mode 100644 index 0000000..9c9a3bb --- /dev/null +++ b/Brizco.Domain/CommandQueries/Commands/NotificationCommands.cs @@ -0,0 +1,4 @@ +namespace Brizco.Domain.CommandQueries.Commands; + +public record CreateNotificationCommand(string Message,Guid UserId) : IRequest; +public record ReadNotificationCommand(Guid Id) : IRequest; \ No newline at end of file diff --git a/Brizco.Domain/CommandQueries/Queries/NotificationQueries.cs b/Brizco.Domain/CommandQueries/Queries/NotificationQueries.cs new file mode 100644 index 0000000..46d14dc --- /dev/null +++ b/Brizco.Domain/CommandQueries/Queries/NotificationQueries.cs @@ -0,0 +1,5 @@ +using Brizco.Domain.MartenEntities.Notifications; + +namespace Brizco.Domain.CommandQueries.Queries; + +public record GetNotificationsQuery(int Page,int Count, Guid? UserId=null) : IRequest>; \ No newline at end of file diff --git a/Brizco.Domain/MartenEntities/Brews/BaseBrew.cs b/Brizco.Domain/MartenEntities/Brews/BaseBrew.cs index d30f3c7..22ddc47 100644 --- a/Brizco.Domain/MartenEntities/Brews/BaseBrew.cs +++ b/Brizco.Domain/MartenEntities/Brews/BaseBrew.cs @@ -6,5 +6,4 @@ public class BaseBrew : MartenEntity public string CurrentBrewJson { get; set; } = string.Empty; public string DotnetType { get; set; } = string.Empty; public List PastBrewsJson { get; set; } = new(); - public Guid ComplexId { get; set; } } \ No newline at end of file diff --git a/Brizco.Domain/MartenEntities/Notifications/Notification.cs b/Brizco.Domain/MartenEntities/Notifications/Notification.cs new file mode 100644 index 0000000..881a74d --- /dev/null +++ b/Brizco.Domain/MartenEntities/Notifications/Notification.cs @@ -0,0 +1,9 @@ +namespace Brizco.Domain.MartenEntities.Notifications; + +public class Notification : MartenEntity +{ + public string Message { get; set; } = string.Empty; + public bool IsRead { get; set; } + public Guid UserId { get; set; } + public DateTime CreatedAt { get; set; } +} \ No newline at end of file diff --git a/Brizco.Infrastructure/Marten/MartenRepository.cs b/Brizco.Infrastructure/Marten/MartenRepository.cs index 7d0a1c7..a8376f7 100644 --- a/Brizco.Infrastructure/Marten/MartenRepository.cs +++ b/Brizco.Infrastructure/Marten/MartenRepository.cs @@ -24,6 +24,12 @@ public class MartenRepository : IMartenRepository var entities = await session.Query().Where(expression).ToListAsync(cancellation); return entities.ToList(); } + public async Task> GetQueryAsync(Expression> expression) + { + await using var session = _documentStore.QuerySession(); + var entities = session.Query().Where(expression); + return entities; + } public async Task GetEntityAsync(Guid id, CancellationToken cancellation) { diff --git a/Brizco.Repository/Brizco.Repository.csproj b/Brizco.Repository/Brizco.Repository.csproj index e0ad4d9..939a616 100644 --- a/Brizco.Repository/Brizco.Repository.csproj +++ b/Brizco.Repository/Brizco.Repository.csproj @@ -27,6 +27,7 @@ + @@ -47,6 +48,7 @@ + diff --git a/Brizco.Repository/MartenHandlers/Notifications/CreateNotificationHandlerCommand.cs b/Brizco.Repository/MartenHandlers/Notifications/CreateNotificationHandlerCommand.cs new file mode 100644 index 0000000..de0b94e --- /dev/null +++ b/Brizco.Repository/MartenHandlers/Notifications/CreateNotificationHandlerCommand.cs @@ -0,0 +1,33 @@ +using Brizco.Domain.MartenEntities.Notifications; + +namespace Brizco.Repository.MartenHandlers.Notifications; + +public class CreateNotificationHandlerCommand : IRequestHandler +{ + private readonly IMartenRepositoryWrapper _martenRepositoryWrapper; + private readonly ICurrentUserService _currentUserService; + + public CreateNotificationHandlerCommand(IMartenRepositoryWrapper martenRepositoryWrapper, ICurrentUserService currentUserService) + { + _martenRepositoryWrapper = martenRepositoryWrapper; + _currentUserService = currentUserService; + } + public async Task Handle(CreateNotificationCommand request, CancellationToken cancellationToken) + { + if (_currentUserService.ComplexId == null) + throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is null"); + if (!Guid.TryParse(_currentUserService.ComplexId, out Guid complexId)) + throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is wrong"); + var notification = new Notification + { + Message = request.Message, + UserId = request.UserId, + ComplexId = complexId, + CreatedAt = DateTime.Now + }; + + await _martenRepositoryWrapper.SetRepository() + .AddOrUpdateEntityAsync(notification, cancellationToken); + return notification.Id; + } +} \ No newline at end of file diff --git a/Brizco.Repository/MartenHandlers/Notifications/GetNotificationsQueryHandler.cs b/Brizco.Repository/MartenHandlers/Notifications/GetNotificationsQueryHandler.cs new file mode 100644 index 0000000..023ddd9 --- /dev/null +++ b/Brizco.Repository/MartenHandlers/Notifications/GetNotificationsQueryHandler.cs @@ -0,0 +1,44 @@ +using Brizco.Domain.MartenEntities.Notifications; + +namespace Brizco.Repository.MartenHandlers.Notifications; + +public class GetNotificationsQueryHandler : IRequestHandler> +{ + private readonly IMartenRepositoryWrapper _martenRepositoryWrapper; + private readonly ICurrentUserService _currentUserService; + + public GetNotificationsQueryHandler(IMartenRepositoryWrapper martenRepositoryWrapper,ICurrentUserService currentUserService) + { + _martenRepositoryWrapper = martenRepositoryWrapper; + _currentUserService = currentUserService; + } + + public async Task> Handle(GetNotificationsQuery request, CancellationToken cancellationToken) + { + + if (_currentUserService.ComplexId == null) + throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is null"); + if (!Guid.TryParse(_currentUserService.ComplexId, out Guid complexId)) + throw new BaseApiException(ApiResultStatusCode.BadRequest, "Complex id is wrong"); + + if (request.UserId == default) + throw new BaseApiException(ApiResultStatusCode.BadRequest, "User id is null"); + Guid userId; + if (request.UserId == null) + { + if (_currentUserService.UserId == null) + throw new BaseApiException(ApiResultStatusCode.BadRequest, "User id is null"); + if (!Guid.TryParse(_currentUserService.UserId, out userId)) + throw new BaseApiException(ApiResultStatusCode.BadRequest, "User id is wrong"); + } + else + userId = request.UserId.Value; + + var notifications = await _martenRepositoryWrapper.SetRepository() + .GetQueryAsync(n => n.ComplexId == complexId && n.UserId == userId); + var response = await notifications.Take(new Range(request.Page * request.Count, (request.Page * request.Count) + request.Count)) + .ToListAsync(cancellationToken); + + return response; + } +} \ No newline at end of file diff --git a/Brizco.Repository/MartenHandlers/Notifications/ReadNotificationCommandHandler.cs b/Brizco.Repository/MartenHandlers/Notifications/ReadNotificationCommandHandler.cs new file mode 100644 index 0000000..447e528 --- /dev/null +++ b/Brizco.Repository/MartenHandlers/Notifications/ReadNotificationCommandHandler.cs @@ -0,0 +1,28 @@ +using Brizco.Domain.MartenEntities.Notifications; + +namespace Brizco.Repository.MartenHandlers.Notifications; + +public class ReadNotificationCommandHandler : IRequestHandler +{ + private readonly IMartenRepositoryWrapper _martenRepositoryWrapper; + private readonly ICurrentUserService _currentUserService; + + public ReadNotificationCommandHandler(IMartenRepositoryWrapper martenRepositoryWrapper, ICurrentUserService currentUserService) + { + _martenRepositoryWrapper = martenRepositoryWrapper; + _currentUserService = currentUserService; + } + public async Task Handle(ReadNotificationCommand request, CancellationToken cancellationToken) + { + var notification = await _martenRepositoryWrapper.SetRepository() + .GetEntityAsync(request.Id, cancellationToken); + if (notification == null) + throw new BaseApiException(ApiResultStatusCode.NotFound,"Notification not found"); + + notification.IsRead = true; + + await _martenRepositoryWrapper.SetRepository() + .AddOrUpdateEntityAsync(notification, cancellationToken); + return true; + } +} \ No newline at end of file diff --git a/Brizco.Repository/Repositories/Marten/IMartenRepository.cs b/Brizco.Repository/Repositories/Marten/IMartenRepository.cs index 6f57bac..9820652 100644 --- a/Brizco.Repository/Repositories/Marten/IMartenRepository.cs +++ b/Brizco.Repository/Repositories/Marten/IMartenRepository.cs @@ -6,6 +6,7 @@ public interface IMartenRepository : IScopedDependency where TMar { Task> GetEntitiesAsync(CancellationToken cancellation = default); Task> GetEntitiesAsync(Expression> expression, CancellationToken cancellation = default); + Task> GetQueryAsync(Expression> expression); Task GetEntityAsync(Guid id, CancellationToken cancellation = default); Task GetEntityAsync(Expression> expression, CancellationToken cancellation = default);