From a03d2269a63de6559fb3e999b0217dfea4bcc0ff Mon Sep 17 00:00:00 2001 From: Anatolii Grynchuk Date: Sat, 2 May 2026 01:07:13 +0300 Subject: [PATCH] feat: add MediatR handlers for template and channel CRUD - ServiceResult, ServiceError, ServiceErrorCode, Unit, ServiceResultHelper in Services/Core - RequestHandler base class (MediatR-adapted, DoOnHandle pattern) - EmailTemplate handlers: Create, Update, Delete, Get, GetByService - EmailChannel handlers: Create, Update, Delete, Get, GetByService Ref: IT-628 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Core/RequestHandler.cs | 24 ++++++++++ .../Core/ServiceError.cs | 13 +++++ .../Core/ServiceErrorCode.cs | 8 ++++ .../Core/ServiceResult.cs | 17 +++++++ .../Core/ServiceResultHelper.cs | 9 ++++ .../Core/Unit.cs | 6 +++ .../Create/CreateEmailChannelCommand.cs | 16 +++++++ .../Create/CreateEmailChannelHandler.cs | 43 +++++++++++++++++ .../Delete/DeleteEmailChannelCommand.cs | 8 ++++ .../Delete/DeleteEmailChannelHandler.cs | 35 ++++++++++++++ .../Get/GetEmailChannelHandler.cs | 33 +++++++++++++ .../EmailChannels/Get/GetEmailChannelQuery.cs | 8 ++++ .../GetByService/GetEmailChannelsHandler.cs | 30 ++++++++++++ .../GetByService/GetEmailChannelsQuery.cs | 8 ++++ .../Update/UpdateEmailChannelCommand.cs | 16 +++++++ .../Update/UpdateEmailChannelHandler.cs | 43 +++++++++++++++++ .../Create/CreateEmailTemplateCommand.cs | 15 ++++++ .../Create/CreateEmailTemplateHandler.cs | 48 +++++++++++++++++++ .../Delete/DeleteEmailTemplateCommand.cs | 11 +++++ .../Delete/DeleteEmailTemplateHandler.cs | 36 ++++++++++++++ .../Get/GetEmailTemplateHandler.cs | 34 +++++++++++++ .../Get/GetEmailTemplateQuery.cs | 8 ++++ .../GetByService/GetEmailTemplatesHandler.cs | 30 ++++++++++++ .../GetByService/GetEmailTemplatesQuery.cs | 8 ++++ .../Update/UpdateEmailTemplateCommand.cs | 16 +++++++ .../Update/UpdateEmailTemplateHandler.cs | 42 ++++++++++++++++ 26 files changed, 565 insertions(+) create mode 100644 HrynCo.NotificationService.Services/Core/RequestHandler.cs create mode 100644 HrynCo.NotificationService.Services/Core/ServiceError.cs create mode 100644 HrynCo.NotificationService.Services/Core/ServiceErrorCode.cs create mode 100644 HrynCo.NotificationService.Services/Core/ServiceResult.cs create mode 100644 HrynCo.NotificationService.Services/Core/ServiceResultHelper.cs create mode 100644 HrynCo.NotificationService.Services/Core/Unit.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelCommand.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelCommand.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelQuery.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsQuery.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelCommand.cs create mode 100644 HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateCommand.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateCommand.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateQuery.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesHandler.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesQuery.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateCommand.cs create mode 100644 HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateHandler.cs diff --git a/HrynCo.NotificationService.Services/Core/RequestHandler.cs b/HrynCo.NotificationService.Services/Core/RequestHandler.cs new file mode 100644 index 0000000..d48a483 --- /dev/null +++ b/HrynCo.NotificationService.Services/Core/RequestHandler.cs @@ -0,0 +1,24 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.Services.Logging; +using MediatR; +using Serilog; + +namespace HrynCo.NotificationService.Services.Core; + +public abstract class RequestHandler : IRequestHandler + where TRequest : IRequest +{ + protected RequestHandler(IContextualSerilogLogger logger, IUnitOfWork unitOfWork) + { + Logger = logger.Logger; + UnitOfWork = unitOfWork; + } + + protected ILogger Logger { get; } + protected IUnitOfWork UnitOfWork { get; } + + public Task Handle(TRequest request, CancellationToken cancellationToken) => + DoOnHandle(request, cancellationToken); + + protected abstract Task DoOnHandle(TRequest request, CancellationToken cancellationToken); +} diff --git a/HrynCo.NotificationService.Services/Core/ServiceError.cs b/HrynCo.NotificationService.Services/Core/ServiceError.cs new file mode 100644 index 0000000..0d084f6 --- /dev/null +++ b/HrynCo.NotificationService.Services/Core/ServiceError.cs @@ -0,0 +1,13 @@ +namespace HrynCo.NotificationService.Services.Core; + +public sealed record ServiceError +{ + public ServiceError(string message, ServiceErrorCode? code = null) + { + Message = message; + Code = code; + } + + public ServiceErrorCode? Code { get; set; } + public string Message { get; set; } +} diff --git a/HrynCo.NotificationService.Services/Core/ServiceErrorCode.cs b/HrynCo.NotificationService.Services/Core/ServiceErrorCode.cs new file mode 100644 index 0000000..1eca23f --- /dev/null +++ b/HrynCo.NotificationService.Services/Core/ServiceErrorCode.cs @@ -0,0 +1,8 @@ +namespace HrynCo.NotificationService.Services.Core; + +public enum ServiceErrorCode +{ + NotFound = 1, + Conflict = 2, + InvalidRequest = 3, +} diff --git a/HrynCo.NotificationService.Services/Core/ServiceResult.cs b/HrynCo.NotificationService.Services/Core/ServiceResult.cs new file mode 100644 index 0000000..28f1292 --- /dev/null +++ b/HrynCo.NotificationService.Services/Core/ServiceResult.cs @@ -0,0 +1,17 @@ +namespace HrynCo.NotificationService.Services.Core; + +public record ServiceResult +{ + public ServiceError? Error { get; private set; } + public bool IsSuccess { get; private set; } + public TResult? Result { get; private set; } + + public static ServiceResult Success(TResult result) => + new() { IsSuccess = true, Result = result }; + + public static ServiceResult Failure(ServiceError error) => + new() { IsSuccess = false, Error = error }; + + public static ServiceResult Failure(string message, ServiceErrorCode? code = null) => + Failure(new ServiceError(message, code)); +} diff --git a/HrynCo.NotificationService.Services/Core/ServiceResultHelper.cs b/HrynCo.NotificationService.Services/Core/ServiceResultHelper.cs new file mode 100644 index 0000000..b98490e --- /dev/null +++ b/HrynCo.NotificationService.Services/Core/ServiceResultHelper.cs @@ -0,0 +1,9 @@ +namespace HrynCo.NotificationService.Services.Core; + +public static class ServiceResultHelper +{ + public static ServiceResult Success(T result) => ServiceResult.Success(result); + + public static ServiceResult Failure(string errorMessage, ServiceErrorCode? errorCode = null) => + ServiceResult.Failure(errorMessage, errorCode); +} diff --git a/HrynCo.NotificationService.Services/Core/Unit.cs b/HrynCo.NotificationService.Services/Core/Unit.cs new file mode 100644 index 0000000..7b36323 --- /dev/null +++ b/HrynCo.NotificationService.Services/Core/Unit.cs @@ -0,0 +1,6 @@ +namespace HrynCo.NotificationService.Services.Core; + +public readonly struct Unit +{ + public static readonly Unit Value = new(); +} diff --git a/HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelCommand.cs b/HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelCommand.cs new file mode 100644 index 0000000..1045c73 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelCommand.cs @@ -0,0 +1,16 @@ +using HrynCo.NotificationService.DAL.Abstract.Providers; +using MediatR; +using HrynCo.NotificationService.Services.Core; + +namespace HrynCo.NotificationService.Services.EmailChannels.Create; + +public sealed record CreateEmailChannelCommand( + string ServiceName, + int Priority, + EmailChannelType ChannelType, + EmailChannelSettings Settings, + int? DailyLimit, + int? MonthlyLimit, + int WarnThresholdPercent, + bool IsActive +) : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelHandler.cs b/HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelHandler.cs new file mode 100644 index 0000000..7538abe --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Create/CreateEmailChannelHandler.cs @@ -0,0 +1,43 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Providers; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailChannels.Create; + +internal sealed class CreateEmailChannelHandler + : RequestHandler> +{ + private readonly IEmailChannelRepository _channels; + + public CreateEmailChannelHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailChannelRepository channels) + : base(logger, unitOfWork) + { + _channels = channels; + } + + protected override async Task> DoOnHandle( + CreateEmailChannelCommand request, CancellationToken cancellationToken) + { + var channel = new EmailChannel + { + ServiceName = request.ServiceName, + Priority = request.Priority, + EmailChannelType = request.ChannelType, + Settings = request.Settings, + DailyLimit = request.DailyLimit, + MonthlyLimit = request.MonthlyLimit, + WarnThresholdPercent = request.WarnThresholdPercent, + IsActive = request.IsActive + }; + + await _channels.AddAsync(channel, cancellationToken); + + return Success(channel.Id); + } +} diff --git a/HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelCommand.cs b/HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelCommand.cs new file mode 100644 index 0000000..f5dfb40 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelCommand.cs @@ -0,0 +1,8 @@ +using MediatR; +using HrynCo.NotificationService.Services.Core; +using Unit = HrynCo.NotificationService.Services.Core.Unit; + +namespace HrynCo.NotificationService.Services.EmailChannels.Delete; + +public sealed record DeleteEmailChannelCommand(Guid Id) + : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelHandler.cs b/HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelHandler.cs new file mode 100644 index 0000000..9d126cd --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Delete/DeleteEmailChannelHandler.cs @@ -0,0 +1,35 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailChannels.Delete; + +internal sealed class DeleteEmailChannelHandler + : RequestHandler> +{ + private readonly IEmailChannelRepository _channels; + + public DeleteEmailChannelHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailChannelRepository channels) + : base(logger, unitOfWork) + { + _channels = channels; + } + + protected override async Task> DoOnHandle( + DeleteEmailChannelCommand request, CancellationToken cancellationToken) + { + var channel = await _channels.GetByIdAsync(request.Id, cancellationToken); + + if (channel is null) + return Failure("Email channel not found.", ServiceErrorCode.NotFound); + + await _channels.DeleteAsync(channel, cancellationToken); + + return Success(Unit.Value); + } +} diff --git a/HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelHandler.cs b/HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelHandler.cs new file mode 100644 index 0000000..a85d45d --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelHandler.cs @@ -0,0 +1,33 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Providers; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailChannels.Get; + +internal sealed class GetEmailChannelHandler + : RequestHandler> +{ + private readonly IEmailChannelRepository _channels; + + public GetEmailChannelHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailChannelRepository channels) + : base(logger, unitOfWork) + { + _channels = channels; + } + + protected override async Task> DoOnHandle( + GetEmailChannelQuery request, CancellationToken cancellationToken) + { + var channel = await _channels.GetByIdAsync(request.Id, cancellationToken); + + return channel is null + ? Failure("Email channel not found.", ServiceErrorCode.NotFound) + : Success(channel); + } +} diff --git a/HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelQuery.cs b/HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelQuery.cs new file mode 100644 index 0000000..d105cee --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Get/GetEmailChannelQuery.cs @@ -0,0 +1,8 @@ +using HrynCo.NotificationService.DAL.Abstract.Providers; +using MediatR; +using HrynCo.NotificationService.Services.Core; + +namespace HrynCo.NotificationService.Services.EmailChannels.Get; + +public sealed record GetEmailChannelQuery(Guid Id) + : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsHandler.cs b/HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsHandler.cs new file mode 100644 index 0000000..b8af894 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsHandler.cs @@ -0,0 +1,30 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Providers; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailChannels.GetByService; + +internal sealed class GetEmailChannelsHandler + : RequestHandler>> +{ + private readonly IEmailChannelRepository _channels; + + public GetEmailChannelsHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailChannelRepository channels) + : base(logger, unitOfWork) + { + _channels = channels; + } + + protected override async Task>> DoOnHandle( + GetEmailChannelsQuery request, CancellationToken cancellationToken) + { + var channels = await _channels.GetByServiceAsync(request.ServiceName, cancellationToken); + return Success(channels); + } +} diff --git a/HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsQuery.cs b/HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsQuery.cs new file mode 100644 index 0000000..ddf7864 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/GetByService/GetEmailChannelsQuery.cs @@ -0,0 +1,8 @@ +using HrynCo.NotificationService.DAL.Abstract.Providers; +using MediatR; +using HrynCo.NotificationService.Services.Core; + +namespace HrynCo.NotificationService.Services.EmailChannels.GetByService; + +public sealed record GetEmailChannelsQuery(string ServiceName) + : IRequest>>; diff --git a/HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelCommand.cs b/HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelCommand.cs new file mode 100644 index 0000000..7f81669 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelCommand.cs @@ -0,0 +1,16 @@ +using HrynCo.NotificationService.DAL.Abstract.Providers; +using MediatR; +using HrynCo.NotificationService.Services.Core; +using Unit = HrynCo.NotificationService.Services.Core.Unit; + +namespace HrynCo.NotificationService.Services.EmailChannels.Update; + +public sealed record UpdateEmailChannelCommand( + Guid Id, + int Priority, + EmailChannelSettings Settings, + int? DailyLimit, + int? MonthlyLimit, + int WarnThresholdPercent, + bool IsActive +) : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelHandler.cs b/HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelHandler.cs new file mode 100644 index 0000000..5c8f1ed --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailChannels/Update/UpdateEmailChannelHandler.cs @@ -0,0 +1,43 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailChannels.Update; + +internal sealed class UpdateEmailChannelHandler + : RequestHandler> +{ + private readonly IEmailChannelRepository _channels; + + public UpdateEmailChannelHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailChannelRepository channels) + : base(logger, unitOfWork) + { + _channels = channels; + } + + protected override async Task> DoOnHandle( + UpdateEmailChannelCommand request, CancellationToken cancellationToken) + { + var channel = await _channels.GetByIdAsync(request.Id, cancellationToken); + + if (channel is null) + return Failure("Email channel not found.", ServiceErrorCode.NotFound); + + channel.Priority = request.Priority; + channel.Settings = request.Settings; + channel.DailyLimit = request.DailyLimit; + channel.MonthlyLimit = request.MonthlyLimit; + channel.WarnThresholdPercent = request.WarnThresholdPercent; + channel.IsActive = request.IsActive; + channel.Updated = DateTimeOffset.UtcNow; + + await _channels.UpdateAsync(channel, cancellationToken); + + return Success(Unit.Value); + } +} diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateCommand.cs b/HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateCommand.cs new file mode 100644 index 0000000..6792aae --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateCommand.cs @@ -0,0 +1,15 @@ +using HrynCo.NotificationService.DAL.Abstract.Templates; +using MediatR; +using HrynCo.NotificationService.Services.Core; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Create; + +public sealed record CreateEmailTemplateCommand( + string ServiceName, + string Key, + string LanguageCode, + string Subject, + string HtmlBody, + string TextBody, + IReadOnlyList Variables +) : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateHandler.cs b/HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateHandler.cs new file mode 100644 index 0000000..33c44e0 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Create/CreateEmailTemplateHandler.cs @@ -0,0 +1,48 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.DAL.Abstract.Templates; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Create; + +internal sealed class CreateEmailTemplateHandler + : RequestHandler> +{ + private readonly IEmailTemplateRepository _templates; + + public CreateEmailTemplateHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailTemplateRepository templates) + : base(logger, unitOfWork) + { + _templates = templates; + } + + protected override async Task> DoOnHandle( + CreateEmailTemplateCommand request, CancellationToken cancellationToken) + { + var existing = await _templates.GetAsync( + request.ServiceName, request.Key, request.LanguageCode, cancellationToken); + + if (existing is not null) + return Failure("Template already exists.", ServiceErrorCode.Conflict); + + var template = new EmailTemplate + { + ServiceName = request.ServiceName, + Key = request.Key, + LanguageCode = request.LanguageCode, + Subject = request.Subject, + HtmlBody = request.HtmlBody, + TextBody = request.TextBody, + Variables = request.Variables + }; + + await _templates.AddAsync(template, cancellationToken); + + return Success(template.Id); + } +} diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateCommand.cs b/HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateCommand.cs new file mode 100644 index 0000000..e52dc73 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateCommand.cs @@ -0,0 +1,11 @@ +using MediatR; +using HrynCo.NotificationService.Services.Core; +using Unit = HrynCo.NotificationService.Services.Core.Unit; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Delete; + +public sealed record DeleteEmailTemplateCommand( + string ServiceName, + string Key, + string LanguageCode +) : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateHandler.cs b/HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateHandler.cs new file mode 100644 index 0000000..4e924c0 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Delete/DeleteEmailTemplateHandler.cs @@ -0,0 +1,36 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Delete; + +internal sealed class DeleteEmailTemplateHandler + : RequestHandler> +{ + private readonly IEmailTemplateRepository _templates; + + public DeleteEmailTemplateHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailTemplateRepository templates) + : base(logger, unitOfWork) + { + _templates = templates; + } + + protected override async Task> DoOnHandle( + DeleteEmailTemplateCommand request, CancellationToken cancellationToken) + { + var template = await _templates.GetAsync( + request.ServiceName, request.Key, request.LanguageCode, cancellationToken); + + if (template is null) + return Failure("Template not found.", ServiceErrorCode.NotFound); + + await _templates.DeleteAsync(template, cancellationToken); + + return Success(Unit.Value); + } +} diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateHandler.cs b/HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateHandler.cs new file mode 100644 index 0000000..24b1f75 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateHandler.cs @@ -0,0 +1,34 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.DAL.Abstract.Templates; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Get; + +internal sealed class GetEmailTemplateHandler + : RequestHandler> +{ + private readonly IEmailTemplateRepository _templates; + + public GetEmailTemplateHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailTemplateRepository templates) + : base(logger, unitOfWork) + { + _templates = templates; + } + + protected override async Task> DoOnHandle( + GetEmailTemplateQuery request, CancellationToken cancellationToken) + { + var template = await _templates.GetAsync( + request.ServiceName, request.Key, request.LanguageCode, cancellationToken); + + return template is null + ? Failure("Template not found.", ServiceErrorCode.NotFound) + : Success(template); + } +} diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateQuery.cs b/HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateQuery.cs new file mode 100644 index 0000000..f87ab38 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Get/GetEmailTemplateQuery.cs @@ -0,0 +1,8 @@ +using HrynCo.NotificationService.DAL.Abstract.Templates; +using MediatR; +using HrynCo.NotificationService.Services.Core; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Get; + +public sealed record GetEmailTemplateQuery(string ServiceName, string Key, string LanguageCode) + : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesHandler.cs b/HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesHandler.cs new file mode 100644 index 0000000..7fb5fc0 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesHandler.cs @@ -0,0 +1,30 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.DAL.Abstract.Templates; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailTemplates.GetByService; + +internal sealed class GetEmailTemplatesHandler + : RequestHandler>> +{ + private readonly IEmailTemplateRepository _templates; + + public GetEmailTemplatesHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailTemplateRepository templates) + : base(logger, unitOfWork) + { + _templates = templates; + } + + protected override async Task>> DoOnHandle( + GetEmailTemplatesQuery request, CancellationToken cancellationToken) + { + var templates = await _templates.GetByServiceAsync(request.ServiceName, cancellationToken); + return Success(templates); + } +} diff --git a/HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesQuery.cs b/HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesQuery.cs new file mode 100644 index 0000000..70dbb2d --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/GetByService/GetEmailTemplatesQuery.cs @@ -0,0 +1,8 @@ +using HrynCo.NotificationService.DAL.Abstract.Templates; +using MediatR; +using HrynCo.NotificationService.Services.Core; + +namespace HrynCo.NotificationService.Services.EmailTemplates.GetByService; + +public sealed record GetEmailTemplatesQuery(string ServiceName) + : IRequest>>; diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateCommand.cs b/HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateCommand.cs new file mode 100644 index 0000000..fca25be --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateCommand.cs @@ -0,0 +1,16 @@ +using HrynCo.NotificationService.DAL.Abstract.Templates; +using MediatR; +using HrynCo.NotificationService.Services.Core; +using Unit = HrynCo.NotificationService.Services.Core.Unit; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Update; + +public sealed record UpdateEmailTemplateCommand( + string ServiceName, + string Key, + string LanguageCode, + string Subject, + string HtmlBody, + string TextBody, + IReadOnlyList Variables +) : IRequest>; diff --git a/HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateHandler.cs b/HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateHandler.cs new file mode 100644 index 0000000..597e200 --- /dev/null +++ b/HrynCo.NotificationService.Services/EmailTemplates/Update/UpdateEmailTemplateHandler.cs @@ -0,0 +1,42 @@ +using HrynCo.NotificationService.DAL.Abstract; +using HrynCo.NotificationService.DAL.Abstract.Repositories; +using HrynCo.NotificationService.Services.Core; +using HrynCo.NotificationService.Services.Logging; +using static HrynCo.NotificationService.Services.Core.ServiceResultHelper; + +namespace HrynCo.NotificationService.Services.EmailTemplates.Update; + +internal sealed class UpdateEmailTemplateHandler + : RequestHandler> +{ + private readonly IEmailTemplateRepository _templates; + + public UpdateEmailTemplateHandler( + IContextualSerilogLogger logger, + IUnitOfWork unitOfWork, + IEmailTemplateRepository templates) + : base(logger, unitOfWork) + { + _templates = templates; + } + + protected override async Task> DoOnHandle( + UpdateEmailTemplateCommand request, CancellationToken cancellationToken) + { + var template = await _templates.GetAsync( + request.ServiceName, request.Key, request.LanguageCode, cancellationToken); + + if (template is null) + return Failure("Template not found.", ServiceErrorCode.NotFound); + + template.Subject = request.Subject; + template.HtmlBody = request.HtmlBody; + template.TextBody = request.TextBody; + template.Variables = request.Variables; + template.Updated = DateTimeOffset.UtcNow; + + await _templates.UpdateAsync(template, cancellationToken); + + return Success(Unit.Value); + } +}