From a8759eb8e3ce5892a11fae2a42dbd0ec770bd693 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Fri, 7 Jul 2023 15:28:49 +0200 Subject: [PATCH 01/21] Upload Clan architecure Template Modify Modules to fit Clean Architecture --- .../ApplicationTypes/API/DevonfwApi.cs | 10 + .../Exception/ExceptionHandlingMiddleware.cs | 96 ++--- .../Middleware/Logs/LoggerMiddleware.cs | 96 +++++ .../Middleware/MiddlewareConfiguration.cs | 4 + .../Devon4Net.Infrastructure.Common.csproj | 1 + .../Helpers/AutoRegisterHelpers.cs | 42 +-- .../Interfaces/IRecyclableMemoryHelper.cs | 7 + .../Helpers/JsonHelper.cs | 9 +- .../Helpers/RecyclableMemoryHelper.cs | 43 +++ .../LogConfiguration.cs | 104 ++---- .../Behaviors/ValidationBehavior.cs | 45 +++ .../Command/CommandBase.cs | 4 +- .../Common/ActionBase.cs | 4 +- .../Devon4Net.Infrastructure.MediatR.csproj | 6 + .../MediatRConfiguration.cs | 15 +- .../Query/QueryBase.cs | 2 +- .../Samples/Query/GetUserQuery.cs | 5 +- .../Common/CosmosConfigurationParams.cs | 13 +- .../Common/DatabaseConfiguration.cs | 229 ++++++------ .../Common/PredicateBuilder.cs | 149 ++++---- ...Devon4Net.Infrastructure.UnitOfWork.csproj | 83 ++--- .../Enums/DatabaseType.cs | 29 +- .../Exceptions/ContextNullException.cs | 35 +- .../Exceptions/RepositoryNotFoundException.cs | 35 +- .../Exceptions/TransactionNullException.cs | 35 +- .../Pagination/PaginationBase.cs | 27 +- .../Pagination/PaginationResult.cs | 17 +- .../Projector/IProjector.cs | 18 + .../Projector/Projector.cs | 39 ++ .../RawSqlRepository/IRawSqlRepository.cs | 43 +++ .../RawSqlRepository/RawSqlRepository.cs | 66 ++++ .../Repository/IRepository.cs | 56 ++- .../Repository/Repository.cs | 337 ++++++++++-------- .../Service/IService.cs | 6 - .../Service/Service.cs | 15 - .../UnitOfWork/IUnitOfWork.cs | 29 +- .../UnitOfWork/UnitOfWork.cs | 164 +++------ .../UnitOfWorkConfiguration.cs | 33 +- .../AssemblyReference.cs | 3 + .../Converters/EmployeeConverter.cs | 30 ++ .../Devon4Net - Backup.Application.csproj | 28 ++ .../Devon4Net.Application.csproj | 28 ++ .../Devon4Net.Application/Dtos/EmployeeDto.cs | 32 ++ .../Exceptions/EmployeeNotFoundException.cs | 57 +++ .../CreateEmployee/CreateEmployeeCommand.cs | 7 + .../CreateEmployeeCommandValidation.cs | 14 + .../CreateEmployee/CreateEmployeeHandler.cs | 46 +++ .../GetAllEmployees/GetAllEmployeesHandler.cs | 24 ++ .../GetAllEmployees/GetAllEmployeesQuery.cs | 6 + .../GetEmployeeById/GetEmployeeByIdHandler.cs | 22 ++ .../GetEmployeeById/GetEmployeeByIdQuery.cs | 6 + .../Ports/IEmployeeUoW.cs | 8 + .../Ports/Projectors/IEmployeeProjector.cs | 8 + .../Ports/Repositories/IEmployeeRepository.cs | 41 +++ .../Devon4Net.Domain/Devon4Net.Domain.csproj | 12 + .../Devon4Net.Domain/Entities/Employee.cs | 27 ++ .../Adapters/Projectors/EmployeeProjector.cs | 30 ++ .../Repositories/EmployeeRepository.cs | 72 ++++ .../AssemblyReference.cs | 3 + .../Devon4Net.Infrastructure.csproj | 13 + .../Persistence/EmployeeContext.cs | 44 +++ .../Devon4Net.IntegrationTests.csproj | 24 ++ .../Devon4Net.IntegrationTests/UnitTest1.cs | 15 + .../Devon4Net.Presentation/Configuration.cs | 59 +++ .../Controllers/EmployeeController.cs | 41 +++ .../Devon4Net.Presentation.csproj | 23 ++ .../Devon4Net.Presentation/Program.cs | 42 +++ .../Properties/launchSettings.json | 51 +++ .../appsettings.Development.json | 138 +++++++ .../Devon4Net.Presentation/appsettings.json | 3 + .../Devon4Net.Presentation/libman.json | 5 + .../Devon4Net.UnitTests.csproj | 23 ++ .../FileManagement/Services/FileService.cs | 13 +- .../Configuration/DIConfiguration.cs | 2 +- .../Data/Repositories/FileRepository.cs | 2 +- .../RepositoryInterfaces/IFileRepository.cs | 2 +- .../Program.cs | 2 +- .../Service/EmployeeService.cs | 11 +- .../Commands/CreateTodoCommand.cs | 2 +- .../MediatRManagement/Queries/GetTodoQuery.cs | 2 +- .../TodoManagement/Service/TodoService.cs | 11 +- .../Configuration/DevonConfiguration.cs | 39 +- .../Data/Repositories/EmployeeRepository.cs | 2 +- .../Data/Repositories/TodoRepository.cs | 2 +- .../IEmployeeRepository.cs | 2 +- .../RepositoryInterfaces/ITodoRepository.cs | 2 +- .../Devon4Net.Application.WebAPI/Program.cs | 2 +- source/devon4net.sln | 76 +++- 88 files changed, 2227 insertions(+), 881 deletions(-) create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Helpers/Interfaces/IRecyclableMemoryHelper.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Helpers/RecyclableMemoryHelper.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.MediatR/Behaviors/ValidationBehavior.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/IRawSqlRepository.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/RawSqlRepository.cs delete mode 100644 source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/IService.cs delete mode 100644 source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/Service.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/AssemblyReference.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net - Backup.Application.csproj create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net.Application.csproj create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeHandler.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesQuery.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Ports/IEmployeeUoW.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Domain/Devon4Net.Domain.csproj create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Infrastructure/AssemblyReference.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Devon4Net.Infrastructure.csproj create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj create mode 100644 source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/UnitTest1.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/Devon4Net.Presentation.csproj create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/Program.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/Properties/launchSettings.json create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.Development.json create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.json create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Presentation/libman.json create mode 100644 source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs index 12a88574..384ec9a5 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs @@ -11,6 +11,8 @@ using Devon4Net.Infrastructure.Common.Application.ApplicationTypes.API.Servers; using Devon4Net.Infrastructure.Common.Application.ApplicationTypes.API.Configuration; using Devon4Net.Infrastructure.Common.Application.Attributes; +using Devon4Net.Infrastructure.Common.Helpers; +using Devon4Net.Infrastructure.Common.Helpers.Interfaces; namespace Devon4Net.Infrastructure.Common.Application.ApplicationTypes.API { @@ -39,6 +41,8 @@ public static void InitializeDevonfwApi(this IWebHostBuilder builder, IHostBuild public static DevonfwOptions SetupDevonfw(this IServiceCollection services, IConfiguration configuration) { + services?.SetupDevonfwServices(); + DevonfwOptions = services?.GetTypedOptions(configuration, OptionsDefinition.DefaultSettingsNodeName); if (DevonfwOptions == null) @@ -59,5 +63,11 @@ public static DevonfwOptions SetupDevonfw(this IServiceCollection services, ICon return DevonfwOptions; } + + private static void SetupDevonfwServices(this IServiceCollection services) + { + services.AddSingleton(); + services.AddTransient(typeof(IJsonHelper), typeof(JsonHelper)); + } } } diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs index 46d78d89..d4f9b53a 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs @@ -1,64 +1,68 @@ using System.Text.Json; using Devon4Net.Infrastructure.Common.Exceptions; -using Devon4Net.Infrastructure.Common; using Microsoft.AspNetCore.Http; -namespace Devon4Net.Infrastructure.Common.Application.Middleware.Exception +namespace Devon4Net.Infrastructure.Common.Application.Middleware.Exception; + +public class ExceptionHandlingMiddleware { - public class ExceptionHandlingMiddleware + private readonly RequestDelegate _next; + + public ExceptionHandlingMiddleware(RequestDelegate next) { - private readonly RequestDelegate _next; + _next = next; + } - public ExceptionHandlingMiddleware(RequestDelegate next) + public async Task Invoke(HttpContext context) + { + try { - _next = next; + await _next(context).ConfigureAwait(false); } - - public async Task Invoke(HttpContext context) + catch (System.Exception ex) { - try - { - await _next(context).ConfigureAwait(false); - } - catch (System.Exception ex) - { - await HandleException(ref context, ref ex).ConfigureAwait(false); - } + await HandleException(ref context, ref ex).ConfigureAwait(false); } + } - private static Task HandleException(ref HttpContext context, ref System.Exception exception) - { - Devon4NetLogger.Error(exception); - - var exceptionTypeValue = exception.GetType(); - var exceptionInterfaces = exceptionTypeValue.GetInterfaces().Select(i => i.Name).ToList(); - exceptionInterfaces.Add(exceptionTypeValue.Name); + private static Task HandleException(ref HttpContext context, ref System.Exception exception) + { + Devon4NetLogger.Error(exception.Message); + if(exception.InnerException != null) + Devon4NetLogger.Debug(exception.InnerException?.ToString()); - return exceptionInterfaces switch - { - { } exceptionType when exceptionType.Contains("InvalidDataException") => HandleContext(ref context, - StatusCodes.Status422UnprocessableEntity), - { } exceptionType when exceptionType.Contains("ArgumentException") || - exceptionType.Contains("ArgumentNullException") || - exceptionType.Contains("NotFoundException") || - exceptionType.Contains("FileNotFoundException") => HandleContext(ref context, - StatusCodes.Status400BadRequest), - { } exceptionType when exceptionType.Contains("IWebApiException") => HandleContext(ref context, - ((IWebApiException) exception).StatusCode, exception.Message, - ((IWebApiException) exception).ShowMessage), - _ => HandleContext(ref context, StatusCodes.Status500InternalServerError, exception.Message) - }; - } + var exceptionTypeValue = exception.GetType(); + var exceptionInterfaces = exceptionTypeValue.GetInterfaces().Select(i => i.Name).ToList(); + exceptionInterfaces.Add(exceptionTypeValue.Name); - private static Task HandleContext(ref HttpContext context, int? statusCode = null, string errorMessage = null, bool showMessage = false) + return exceptionInterfaces switch { - context.Response.Headers.Clear(); - context.Response.StatusCode = statusCode ?? StatusCodes.Status500InternalServerError; + { } when exceptionInterfaces.Contains("InvalidDataException") => HandleContext(ref context, + StatusCodes.Status422UnprocessableEntity), + { } when exceptionInterfaces.Contains("ArgumentException") || + exceptionInterfaces.Contains("ArgumentNullException") || + exceptionInterfaces.Contains("NotFoundException") || + exceptionInterfaces.Contains("FileNotFoundException") => HandleContext(ref context, + StatusCodes.Status400BadRequest), + { } when exceptionInterfaces.Contains("ValidationException") => HandleContext(ref context, + StatusCodes.Status400BadRequest, exception.Message, true), + { } when exceptionInterfaces.Contains("IWebApiException") => HandleContext(ref context, + ((IWebApiException)exception).StatusCode, exception.Message, + ((IWebApiException)exception).ShowMessage), + _ => HandleContext(ref context, StatusCodes.Status500InternalServerError, exception.Message) + }; + } - if (!showMessage || statusCode == StatusCodes.Status204NoContent || string.IsNullOrEmpty(errorMessage) ) return Task.CompletedTask; + private static Task HandleContext(ref HttpContext context, int? statusCode = null, string errorMessage = null, + bool showMessage = false) + { + context.Response.Headers.Clear(); + context.Response.StatusCode = statusCode ?? StatusCodes.Status500InternalServerError; - context.Response.ContentType = "application/json"; - return context.Response.WriteAsync(JsonSerializer.Serialize(new { error = errorMessage })); - } + if (!showMessage || statusCode == StatusCodes.Status204NoContent || string.IsNullOrEmpty(errorMessage)) + return Task.CompletedTask; + + context.Response.ContentType = "application/json"; + return context.Response.WriteAsync(JsonSerializer.Serialize(new { error = errorMessage })); } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs new file mode 100644 index 00000000..3f723391 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs @@ -0,0 +1,96 @@ +using System.Text; +using Devon4Net.Infrastructure.Common.Helpers.Interfaces; +using Microsoft.AspNetCore.Http; +using Serilog; +using Serilog.Events; + +namespace Devon4Net.Infrastructure.Common.Application.Middleware.Logs; + +public class LoggerMiddleware +{ + private readonly RequestDelegate _next; + private const string ResponseBodyEntity = "Response body"; + private const string RequestBodyEntity = "Request body"; + private const int MaxBodyResponse = 150; + + public LoggerMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context, IRecyclableMemoryHelper recyclableMemoryHelper) + { + try + { + using var bodyReader = recyclableMemoryHelper.GetMemoryStream(); + context.Request.EnableBuffering(); + + var originalResponseBody = context.Response.Body; + context.Response.Body = bodyReader; + + Devon4NetLogger.Information($"REQUEST STARTED {context.TraceIdentifier} | HttpMethod: {context.Request.Method} | Path: {context.Request.Path} "); + + await _next(context).ConfigureAwait(false); + bodyReader.Seek(0, SeekOrigin.Begin); + await bodyReader.CopyToAsync(originalResponseBody).ConfigureAwait(false); + + + if (Log.IsEnabled(LogEventLevel.Information)) + { + if (context.Response.StatusCode is < StatusCodes.Status200OK or > StatusCodes.Status300MultipleChoices) Devon4NetLogger.Information(await HandleResponseStream(context.TraceIdentifier, context?.Request?.Body, RequestBodyEntity).ConfigureAwait(false)); + Devon4NetLogger.Information(await HandleResponseStream(context.TraceIdentifier, context.Response.Body, ResponseBodyEntity, true).ConfigureAwait(false)); + Devon4NetLogger.Information($"REQUEST FINISHED {context.TraceIdentifier} | Status Code: {context.Response.StatusCode}"); + } + if (Log.IsEnabled(LogEventLevel.Warning)) + { + if (context.Response.StatusCode is >= StatusCodes.Status200OK and < StatusCodes.Status300MultipleChoices) return; + Devon4NetLogger.Warning(await HandleResponseStream(context.TraceIdentifier, context.Response.Body, ResponseBodyEntity, true).ConfigureAwait(false)); + Devon4NetLogger.Warning($"REQUEST FINISHED {context.TraceIdentifier} Status Code: {context.Response.StatusCode}"); + } + else + { + Devon4NetLogger.Debug(await HandleResponseStream(context.TraceIdentifier, context?.Request?.Body, RequestBodyEntity).ConfigureAwait(false)); + Devon4NetLogger.Debug(await HandleResponseStream(context.TraceIdentifier, context.Response.Body, ResponseBodyEntity, true).ConfigureAwait(false)); + Devon4NetLogger.Debug($"REQUEST FINISHED {context.TraceIdentifier} | Status Code: {context.Response.StatusCode}"); + } + } + catch (System.Exception ex) + { + Devon4NetLogger.Error($"REQUEST FINISHED {context.TraceIdentifier} | Status Code: {context.Response.StatusCode} | Exception: {ex.Message}"); + throw; + } + } + + private static async Task HandleResponseStream(string identifier, Stream bodyStream, string entityDisclaimer, bool trimBody = false) + { + var stringBuilder = new StringBuilder(); + + stringBuilder.Append($"REQUEST {identifier} {entityDisclaimer}: "); + + if (bodyStream?.CanRead == false) + { + stringBuilder.Append("No data found."); + return stringBuilder.ToString(); + } + + using var bodyReader = new StreamReader(bodyStream); + bodyStream.Seek(0, SeekOrigin.Begin); + + if (trimBody) + { + var buffer = new char[MaxBodyResponse]; + var count = await bodyReader.ReadBlockAsync(buffer, 0, MaxBodyResponse); + stringBuilder.Append(new string(buffer, 0, count)); + } + else + { + var text = await bodyReader.ReadToEndAsync().ConfigureAwait(false); + if(string.IsNullOrWhiteSpace(text)) return default; + stringBuilder.Append(text.Replace("\n", "")); + } + + bodyStream.Seek(0, SeekOrigin.Begin); + + return stringBuilder.ToString(); + } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs index 00dae883..64e09d3b 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs @@ -8,6 +8,7 @@ using Devon4Net.Infrastructure.Common.Application.Middleware.Exception; using Devon4Net.Infrastructure.Common.Application.Middleware.Headers; using Devon4Net.Infrastructure.Common.Application.Middleware.Certificates; +using Devon4Net.Infrastructure.Common.Application.Middleware.Logs; namespace Devon4Net.Infrastructure.Common.Application.Middleware { @@ -16,6 +17,9 @@ public static class MiddlewareConfiguration public static void SetupMiddleware(this IApplicationBuilder builder, IServiceCollection services) { using var serviceProvider = services.BuildServiceProvider(); + + builder.UseMiddleware(); + var killSwitch = serviceProvider.GetService>()?.Value; var certificates = serviceProvider.GetService>()?.Value; diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj b/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj index ab61806c..e12007bf 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj +++ b/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj @@ -36,6 +36,7 @@ + diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Helpers/AutoRegisterHelpers.cs b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/AutoRegisterHelpers.cs index c1ec1ec6..ff81c739 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Helpers/AutoRegisterHelpers.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/AutoRegisterHelpers.cs @@ -22,7 +22,7 @@ public static class AutoRegisterHelpers public static AutoRegisterData RegisterAssemblyPublicNonGenericClasses(this IServiceCollection services, params Assembly[] assemblies) { if (assemblies.Length == 0) - assemblies = new[] {Assembly.GetCallingAssembly()}; + assemblies = new[] { Assembly.GetCallingAssembly() }; var allPublicTypes = assemblies.SelectMany(x => x.GetExportedTypes() .Where(y => y.IsClass && !y.IsAbstract && !y.IsGenericType && !y.IsNested)); @@ -50,12 +50,12 @@ public static AutoRegisterData Where(this AutoRegisterData autoRegData, FuncAutoRegister data produced by method /// Allows you to define the lifetime of the service - defaults to ServiceLifetime.Transient /// - public static IServiceCollection AsPublicImplementedInterfaces(this AutoRegisterData autoRegData, + public static IServiceCollection AsPublicImplementedInterfaces(this AutoRegisterData autoRegData, ServiceLifetime lifetime = ServiceLifetime.Transient) { if (autoRegData == null) throw new ArgumentNullException(nameof(autoRegData)); - foreach (var classType in (autoRegData.TypeFilter == null - ? autoRegData.TypesToConsider + foreach (var classType in (autoRegData.TypeFilter == null + ? autoRegData.TypesToConsider : autoRegData.TypesToConsider.Where(autoRegData.TypeFilter))) { var interfaces = classType.GetTypeInfo().ImplementedInterfaces; @@ -86,37 +86,37 @@ public static IServiceCollection AsSingletonPublicImplementedClasses(this AutoRe return autoRegData.Services; } - public static void AutoRegisterClasses(this IServiceCollection services, List assemblyContainerToScan, string sufixName = "Service") + public static void AutoRegisterClasses(this IServiceCollection services, List assembliesNamespaceToScan, + List suffixNames, ServiceLifetime serviceLifetime) { - if (assemblyContainerToScan == null || assemblyContainerToScan.Count == 0|| string.IsNullOrEmpty(sufixName)) return; + if (assembliesNamespaceToScan == null || assembliesNamespaceToScan.Count == 0 || suffixNames == null || + suffixNames.Count == 0) return; - foreach (var assembly in assemblyContainerToScan) + foreach (var assembly in assembliesNamespaceToScan) { - var assemblyToScan = Assembly.GetAssembly(assembly); - if (assemblyToScan == null) continue; - - services.RegisterAssemblyPublicNonGenericClasses(assemblyToScan) - .Where(x => x.Name.EndsWith(sufixName)) - .AsPublicImplementedInterfaces(); + foreach (var suffixName in suffixNames) + services.RegisterAssemblyPublicNonGenericClasses(assembly) + .Where(x => x.Name.EndsWith(suffixName)) + .AsPublicImplementedInterfaces(serviceLifetime); } } - public static void AutoRegisterClasses(this IServiceCollection services, List assemblyContainerToScan, List sufixNames) + public static void AutoRegisterClasses(this IServiceCollection services, List assemblyContainerToScan, + List suffixNames, ServiceLifetime serviceLifetime) { - if (assemblyContainerToScan == null || assemblyContainerToScan.Count == 0 || sufixNames == null || sufixNames.Count == 0) return; + if (assemblyContainerToScan == null || assemblyContainerToScan.Count == 0 || suffixNames == null || + suffixNames.Count == 0) return; foreach (var assembly in assemblyContainerToScan) { var assemblyToScan = Assembly.GetAssembly(assembly); if (assemblyToScan == null) continue; - foreach (var sufixName in sufixNames) - { + foreach (var suffixName in suffixNames) services.RegisterAssemblyPublicNonGenericClasses(assemblyToScan) - .Where(x => x.Name.EndsWith(sufixName)) - .AsPublicImplementedInterfaces(); - } + .Where(x => x.Name.EndsWith(suffixName)) + .AsPublicImplementedInterfaces(serviceLifetime); } } } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Helpers/Interfaces/IRecyclableMemoryHelper.cs b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/Interfaces/IRecyclableMemoryHelper.cs new file mode 100644 index 00000000..4b0111da --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/Interfaces/IRecyclableMemoryHelper.cs @@ -0,0 +1,7 @@ +namespace Devon4Net.Infrastructure.Common.Helpers.Interfaces; + +public interface IRecyclableMemoryHelper +{ + MemoryStream GetMemoryStream(); + MemoryStream GetMemoryStream(byte[] byteArray); +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Helpers/JsonHelper.cs b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/JsonHelper.cs index acb28826..0836adc1 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Helpers/JsonHelper.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/JsonHelper.cs @@ -7,7 +7,7 @@ namespace Devon4Net.Infrastructure.Common.Helpers public class JsonHelper : IJsonHelper { private const string BuiltInTypes = "String, DateTime, DateTimeKind, DateTimeOffset, AsyncCallback, AttributeTargets, AttributeUsageAttribute, Boolean, Byte, Char, CharEnumerator, Base64FormattingOptions, DayOfWeek, DBNull, Decimal, Double, EnvironmentVariableTarget, EventHandler, GCCollectionMode, Guid, Int16, Int32, Int64, IntPtr, SByte, Single, TimeSpan, TimeZoneInfo, TypeCode, UInt16, UInt32, UInt64, UIntPtr"; - private readonly JsonSerializerOptions CamelJsonSerializerOptions = new () { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }; + private readonly JsonSerializerOptions CamelJsonSerializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }; private JsonSerializerOptions JsonSerializerOptions { get; } @@ -21,7 +21,7 @@ public JsonHelper(JsonSerializerOptions jsonSerializerOptions) JsonSerializerOptions = jsonSerializerOptions; } - public T Deserialize(string input, bool useCamelCase= false) + public T Deserialize(string input, bool useCamelCase = false) { if (string.IsNullOrEmpty(input)) { @@ -40,7 +40,7 @@ public List Deserialize(List input, bool useCamelCase = false) { if (input == null || input.Count == 0) { - return default; + return new List(); } var result = new List(); @@ -62,6 +62,7 @@ public async Task Serialize(T input) using var reader = new StreamReader(stream); return await reader.ReadToEndAsync().ConfigureAwait(false); } + public string Serialize(object toPrint, bool useCamelCase = false) { return JsonSerializer.Serialize(toPrint, useCamelCase ? CamelJsonSerializerOptions : null); @@ -76,4 +77,4 @@ public async Task SerializeAsync(T input) return await reader.ReadToEndAsync().ConfigureAwait(false); } } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Helpers/RecyclableMemoryHelper.cs b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/RecyclableMemoryHelper.cs new file mode 100644 index 00000000..33cacea2 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/RecyclableMemoryHelper.cs @@ -0,0 +1,43 @@ +using Devon4Net.Infrastructure.Common.Helpers.Interfaces; +using Microsoft.IO; + +namespace Devon4Net.Infrastructure.Common.Helpers; + +public class RecyclableMemoryHelper : IRecyclableMemoryHelper +{ + private int BlockSize { get; set; } + private int LargeBufferMultiple { get; set; } + private int MaxBufferSize { get; set; } + private int MaximumFreeSmallPoolBytes { get; set; } + private RecyclableMemoryStreamManager RecyclableMemoryStreamManager { get; } + + public RecyclableMemoryHelper() + { + BlockSize = 1024; + LargeBufferMultiple = 80 * 1024; + MaxBufferSize = 16 * LargeBufferMultiple; + MaximumFreeSmallPoolBytes = 250 * BlockSize; + RecyclableMemoryStreamManager = new RecyclableMemoryStreamManager(BlockSize, LargeBufferMultiple, MaxBufferSize) + { + AggressiveBufferReturn = true, + GenerateCallStacks = false, + MaximumFreeLargePoolBytes = MaximumFreeSmallPoolBytes, + MaximumFreeSmallPoolBytes = MaximumFreeSmallPoolBytes + }; + } + + public RecyclableMemoryHelper(RecyclableMemoryStreamManager recyclableMemoryStreamManager) + { + RecyclableMemoryStreamManager = recyclableMemoryStreamManager; + } + + public MemoryStream GetMemoryStream() + { + return RecyclableMemoryStreamManager.GetStream(); + } + + public MemoryStream GetMemoryStream(byte[] byteArray) + { + return RecyclableMemoryStreamManager.GetStream(byteArray); + } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Logger/LogConfiguration.cs b/source/Modules/Devon4Net.Infrastructure.Logger/LogConfiguration.cs index 4291f742..6d5af294 100644 --- a/source/Modules/Devon4Net.Infrastructure.Logger/LogConfiguration.cs +++ b/source/Modules/Devon4Net.Infrastructure.Logger/LogConfiguration.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using Serilog; using Serilog.Sinks.Graylog; +using Serilog.Sinks.Graylog.Core.Transport; namespace Devon4Net.Infrastructure.Logger; @@ -23,32 +24,28 @@ public static void SetupLog(this IServiceCollection services, IConfiguration con if (logOptions == null) return; - LoggerConfiguration = CreateLoggerConfiguration(); - + LoggerConfiguration = CreateLoggerConfiguration(configuration); + services.AddSingleton(ConfigureLog(logOptions)); services.AddSingleton(LoggerConfiguration); - - if (logOptions.UseAopTrace) - { - SetupLogAop(ref services, logOptions); - } + + if (logOptions.UseAopTrace) SetupLogAop(ref services, logOptions); } private static ILoggerFactory ConfigureLog(LogOptions logOptions) { - SetLogLevel(logOptions.LogLevel?.Default!); ConfigureLogFile(logOptions); ConfigureLogSeq(logOptions); ConfigureLogSqLiteDb(logOptions); SetupGraylog(logOptions); - + return CreateLoggerFactory(); } private static void ConfigureLogFile(LogOptions logOptions) { if (!logOptions.UseLogFile) return; - + var logFile = logOptions.LogFile != null ? string.Format(logOptions.LogFile, DateTime.Today.ToShortDateString().Replace("/", string.Empty)) : DefaultLogFile; @@ -58,28 +55,28 @@ private static void ConfigureLogFile(LogOptions logOptions) private static void ConfigureLogSeq(LogOptions logOptions) { if (!string.IsNullOrEmpty(logOptions.SeqLogServerHost)) - { LoggerConfiguration = LoggerConfiguration.WriteTo.Seq(logOptions.SeqLogServerHost); - } } - + private static void ConfigureLogSqLiteDb(LogOptions logOptions) { if (logOptions.UseSqLiteDb && !string.IsNullOrEmpty(logOptions.SqliteDatabase)) - { - LoggerConfiguration = LoggerConfiguration.WriteTo.SQLite(GetValidPath(logOptions.SqliteDatabase, DefaultSqliteFile)); - } + LoggerConfiguration = + LoggerConfiguration.WriteTo.SQLite(GetValidPath(logOptions.SqliteDatabase, DefaultSqliteFile)); } - - private static LoggerConfiguration CreateLoggerConfiguration() + + private static LoggerConfiguration CreateLoggerConfiguration(IConfiguration configuration) { - return new LoggerConfiguration().Enrich.FromLogContext().WriteTo.Console(); //NOSONAR false positive + return new LoggerConfiguration() //NOSONAR false positive + .ReadFrom.Configuration(configuration) + .Enrich.FromLogContext().WriteTo.Console(); } - + private static ILoggerFactory CreateLoggerFactory() { Log.Logger = LoggerConfiguration.CreateLogger(); - return LoggerFactory.Create(logging => { logging.AddSerilog(Log.Logger); }).AddSerilog(); + + return LoggerFactory.Create(logging => logging.AddSerilog(Log.Logger)); } private static void SetupLogAop(ref IServiceCollection services, LogOptions logOptions) @@ -89,69 +86,40 @@ private static void SetupLogAop(ref IServiceCollection services, LogOptions logO services.AddMvc(options => options.Filters.Add(new AopControllerAttribute(logOptions.UseAopTrace))); services.AddMvc(options => options.Filters.Add(new AopExceptionFilterAttribute())); } - + private static void SetupGraylog(LogOptions logOptions) { - if (logOptions?.UseGraylog == true && logOptions.GrayLog != null) + if (logOptions?.UseGraylog != true || logOptions.GrayLog == null) return; + var graylogOptions = logOptions.GrayLog; + var graylogConfig = new GraylogSinkOptions { - var graylogOptions = logOptions?.GrayLog; - var graylogConfig = new GraylogSinkOptions - { - HostnameOrAddress = graylogOptions.GrayLogHost, - TransportType = GetGraylogTransportTypeFromString(graylogOptions.GrayLogProtocol), - Host = graylogOptions.GrayLogHost, - Port = graylogOptions.GrayLogPort, - MaxMessageSizeInUdp = graylogOptions.MaxUdpMessageSize - }; - - LoggerConfiguration = LoggerConfiguration.WriteTo.Graylog(graylogConfig); - } + HostnameOrAddress = graylogOptions.GrayLogHost, + TransportType = GetGraylogTransportTypeFromString(graylogOptions.GrayLogProtocol), + Host = graylogOptions.GrayLogHost, + Port = graylogOptions.GrayLogPort, + MaxMessageSizeInUdp = graylogOptions.MaxUdpMessageSize + }; + + LoggerConfiguration = LoggerConfiguration.WriteTo.Graylog(graylogConfig); } - private static Serilog.Sinks.Graylog.Core.Transport.TransportType GetGraylogTransportTypeFromString(string transportType) + private static TransportType GetGraylogTransportTypeFromString(string transportType) { - if (transportType == null) return Serilog.Sinks.Graylog.Core.Transport.TransportType.Udp; + if (transportType == null) return TransportType.Udp; return transportType.ToLower() switch { - "tcp" => Serilog.Sinks.Graylog.Core.Transport.TransportType.Tcp, - "udp" => Serilog.Sinks.Graylog.Core.Transport.TransportType.Udp, - "http" => Serilog.Sinks.Graylog.Core.Transport.TransportType.Http, - _ => Serilog.Sinks.Graylog.Core.Transport.TransportType.Udp + "tcp" => TransportType.Tcp, + "udp" => TransportType.Udp, + "http" => TransportType.Http, + _ => TransportType.Udp }; } private static string GetValidPath(string inputPath, string optionalFileName) { if (string.IsNullOrEmpty(inputPath) || string.IsNullOrEmpty(Path.GetFileName(inputPath))) - { return Path.Combine(FileOperations.GetApplicationPath() ?? string.Empty, optionalFileName); - } return Path.GetFullPath(inputPath); } - - private static void SetLogLevel(string logEventLevel = "verbose") - { - switch (logEventLevel.ToLower()) - { - case "warning": - LoggerConfiguration.MinimumLevel.Warning(); - return; - case "verbose": - LoggerConfiguration.MinimumLevel.Verbose(); - return; - case "fatal": - LoggerConfiguration.MinimumLevel.Fatal(); - return; - case "error": - LoggerConfiguration.MinimumLevel.Error(); - return; - case "information": - LoggerConfiguration.MinimumLevel.Information(); - return; - default: - LoggerConfiguration.MinimumLevel.Debug(); - return; - } - } } \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Behaviors/ValidationBehavior.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Behaviors/ValidationBehavior.cs new file mode 100644 index 00000000..354c8b94 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Behaviors/ValidationBehavior.cs @@ -0,0 +1,45 @@ +using FluentValidation; +using MediatR; + +namespace Devon4Net.Infrastructure.MediatR.Behaviors; + +/// +/// Represents the validation behavior middleware. +/// +/// The request type. +/// The response type. +public sealed class ValidationBehavior : IPipelineBehavior + where TRequest : class, IRequest + where TResponse : class +{ + private readonly IEnumerable> _validators; + + /// + /// Initializes a new instance of the class. + /// + /// The validator for the current request type. + public ValidationBehavior(IEnumerable> validators) => _validators = validators; + + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken) + { + if (!_validators.Any()) + { + return await next(); + } + + var context = new ValidationContext(request); + + var failures = _validators + .Select(v => v.Validate(context)) + .SelectMany(result => result.Errors) + .Where(f => f != null) + .ToList(); + + if (failures.Count != 0) + { + throw new ValidationException(failures); + } + + return await next(); + } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs index cb858b73..1d1da3f9 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs @@ -2,7 +2,7 @@ namespace Devon4Net.Infrastructure.MediatR.Command { - public class CommandBase : ActionBase where T : class + public record CommandBase : ActionBase where T : class { } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Common/ActionBase.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Common/ActionBase.cs index a1537375..ab5896fa 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Common/ActionBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Common/ActionBase.cs @@ -2,7 +2,7 @@ namespace Devon4Net.Infrastructure.MediatR.Common { - public class ActionBase : IRequest where T : class + public record ActionBase : IRequest where T : class { public DateTime Timestamp { get; } public string MessageType { get; } @@ -15,4 +15,4 @@ protected ActionBase() MessageType = GetType().Name; } } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Devon4Net.Infrastructure.MediatR.csproj b/source/Modules/Devon4Net.Infrastructure.MediatR/Devon4Net.Infrastructure.MediatR.csproj index ba9f9a63..6c6e0126 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Devon4Net.Infrastructure.MediatR.csproj +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Devon4Net.Infrastructure.MediatR.csproj @@ -22,6 +22,12 @@ True + + + + + + diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs index 9fc25326..e987f491 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs @@ -25,21 +25,16 @@ public static void SetupMediatR(this IServiceCollection services, IConfiguration var mediatROptions = services.GetTypedOptions(configuration, OptionsDefinition.MediatR); if (mediatROptions?.EnableMediatR != true) return; - ConfigureMediatRGenericDependencyInjection(ref services); + services.AddTransient(typeof(IMediatRHandler), typeof(MediatRHandler)); + if(mediatROptions?.Backup.UseLocalBackup != true) return; SetupMediatRBackupLocalDatabase(ref services, ref mediatROptions); } - private static void ConfigureMediatRGenericDependencyInjection(ref IServiceCollection services) + private static void SetupMediatRBackupLocalDatabase(ref IServiceCollection services, ref MediatROptions mediatROptions) { - services.AddTransient(typeof(IJsonHelper), typeof(JsonHelper)); services.AddTransient(typeof(ILiteDbRepository), typeof(LiteDbRepository)); services.AddTransient(typeof(IMediatRBackupService), typeof(MediatRBackupService)); - services.AddTransient(typeof(IMediatRHandler), typeof(MediatRHandler)); - services.AddMediatR(Assembly.GetExecutingAssembly()); - } - - private static void SetupMediatRBackupLocalDatabase(ref IServiceCollection services, ref MediatROptions mediatROptions) - { + Devon4NetLogger.Information("Please setup your database in order to have the RabbitMq messaging backup feature"); if (mediatROptions.Backup?.UseLocalBackup != true) return; Devon4NetLogger.Information("RabbitMq messaging backup feature is going to be used via LiteDb"); @@ -48,4 +43,4 @@ private static void SetupMediatRBackupLocalDatabase(ref IServiceCollection servi services.AddTransient(typeof(IMediatRBackupLiteDbService), typeof(MediatRBackupLiteDbService)); } } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs index 37789aad..4199141b 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs @@ -2,7 +2,7 @@ namespace Devon4Net.Infrastructure.MediatR.Query { - public abstract class QueryBase : ActionBase where T : class + public record QueryBase : ActionBase where T : class { } } \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Samples/Query/GetUserQuery.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Samples/Query/GetUserQuery.cs index f16a887c..d123930a 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Samples/Query/GetUserQuery.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Samples/Query/GetUserQuery.cs @@ -3,7 +3,7 @@ namespace Devon4Net.Infrastructure.MediatR.Samples.Query { - public class GetUserQuery : QueryBase + public record GetUserQuery : QueryBase { public Guid UserId { get; set; } @@ -11,6 +11,5 @@ public GetUserQuery(Guid userId) { UserId = userId; } - } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/CosmosConfigurationParams.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/CosmosConfigurationParams.cs index ae45c04e..37c8bda0 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/CosmosConfigurationParams.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/CosmosConfigurationParams.cs @@ -1,9 +1,8 @@ -namespace Devon4Net.Domain.UnitOfWork.Common +namespace Devon4Net.Domain.UnitOfWork.Common; + +public class CosmosConfigurationParams { - public class CosmosConfigurationParams - { - public string Endpoint { get; set; } - public string Key { get; set; } - public string DatabaseName { get; set; } - } + public string Endpoint { get; set; } + public string Key { get; set; } + public string DatabaseName { get; set; } } \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/DatabaseConfiguration.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/DatabaseConfiguration.cs index 5d382b6d..86316a3d 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/DatabaseConfiguration.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/DatabaseConfiguration.cs @@ -1,133 +1,148 @@ -using Devon4Net.Domain.UnitOfWork.Enums; +using Devon4Net.Domain.UnitOfWork.Common; +using Devon4Net.Domain.UnitOfWork.Enums; using Devon4Net.Infrastructure.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -namespace Devon4Net.Domain.UnitOfWork.Common +namespace Devon4Net.Infrastructure.UnitOfWork.Common; + +public static class SetupDatabaseConfiguration { - public static class SetupDatabaseConfiguration - { - private const int MaxRetryDelay = 30; - private const int MaxRetryCount = 10; - private static ServiceLifetime ServiceLifetime { get; set; } + private const int MaxRetryDelay = 30; + private const int MaxRetryCount = 10; + private static ServiceLifetime ServiceLifetime { get; set; } - public static void SetupDatabase(this IServiceCollection services, string connectionString, DatabaseType databaseType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient, CosmosConfigurationParams cosmosConfigurationParams = null) where T : DbContext - { - if (string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentException($"The provided connection string ({connectionString}) provided does not exists."); - ServiceLifetime = serviceLifetime; - SetDatabase(services, databaseType, cosmosConfigurationParams, connectionString); - } + public static void SetupDatabase(this IServiceCollection services, string connectionString, + DatabaseType databaseType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient, + CosmosConfigurationParams cosmosConfigurationParams = null) where T : DbContext + { + if (string.IsNullOrWhiteSpace(connectionString)) + throw new ArgumentException( + $"The provided connection string ({connectionString}) provided does not exists."); + ServiceLifetime = serviceLifetime; + SetDatabase(services, databaseType, cosmosConfigurationParams, connectionString); + } - public static async Task SetupDatabase(this IServiceCollection services, IConfiguration configuration, string connectionStringName, DatabaseType databaseType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient, bool migrate = false, CosmosConfigurationParams cosmosConfigurationParams = null) where T : DbContext - { - IConfigurationSection connectionString = GetConnectionString(configuration, connectionStringName); + public static async Task SetupDatabase(this IServiceCollection services, IConfiguration configuration, + string connectionStringName, DatabaseType databaseType, + ServiceLifetime serviceLifetime = ServiceLifetime.Transient, bool migrate = false, + CosmosConfigurationParams cosmosConfigurationParams = null) where T : DbContext + { + var connectionString = GetConnectionString(configuration, connectionStringName); - ServiceLifetime = serviceLifetime; + ServiceLifetime = serviceLifetime; - SetDatabase(services, databaseType, cosmosConfigurationParams, connectionString.Value); - if (migrate) await Migrate(services).ConfigureAwait(false); - } + SetDatabase(services, databaseType, cosmosConfigurationParams, connectionString.Value); + if (migrate) await Migrate(services).ConfigureAwait(false); + } - private static IConfigurationSection GetConnectionString(IConfiguration configuration, string connectionStringName) - { - var applicationConnectionStrings = configuration.GetSection("ConnectionStrings").GetChildren(); - if (applicationConnectionStrings == null) throw new ArgumentException("There are no connection strings provided."); - var connectionString = applicationConnectionStrings.FirstOrDefault(c => string.Equals(c.Key, connectionStringName, StringComparison.CurrentCultureIgnoreCase)); - if (connectionString == null || string.IsNullOrEmpty(connectionString.Value)) throw new ArgumentException($"The provided connection string ({connectionStringName}) provided does not exists."); - return connectionString; - } + private static IConfigurationSection GetConnectionString(IConfiguration configuration, string connectionStringName) + { + var applicationConnectionStrings = configuration.GetSection("ConnectionStrings").GetChildren(); + if (applicationConnectionStrings == null) + throw new ArgumentException("There are no connection strings provided."); + var connectionString = applicationConnectionStrings.FirstOrDefault(c => + string.Equals(c.Key, connectionStringName, StringComparison.CurrentCultureIgnoreCase)); + if (connectionString == null || string.IsNullOrEmpty(connectionString.Value)) + throw new ArgumentException( + $"The provided connection string ({connectionStringName}) provided does not exists."); + return connectionString; + } - private static async Task Migrate(IServiceCollection services) where T : DbContext + private static async Task Migrate(IServiceCollection services) where T : DbContext + { + try { - try + using var sp = services.BuildServiceProvider(); + if (sp == null) { - using var sp = services.BuildServiceProvider(); - if (sp == null) - { - Devon4NetLogger.Error($"Unable to build the service provider, the migration {typeof(T).FullName} will not be launched"); - } - else - { - var context = sp.GetService(typeof(T)); - if (context == null) - { - Devon4NetLogger.Error($"Unable to resolve {typeof(T).FullName} and the migration will not be launched"); - } - - ((T)context)?.Database.Migrate(); - await sp.DisposeAsync().ConfigureAwait(false); - } + Devon4NetLogger.Error( + $"Unable to build the service provider, the migration {typeof(T).FullName} will not be launched"); } - catch (Exception ex) + else { - Devon4NetLogger.Fatal(ex); + var context = sp.GetService(typeof(T)); + if (context == null) + Devon4NetLogger.Error( + $"Unable to resolve {typeof(T).FullName} and the migration will not be launched"); + + ((T)context)?.Database.Migrate(); + await sp.DisposeAsync().ConfigureAwait(false); } } + catch (Exception ex) + { + Devon4NetLogger.Fatal(ex); + } + } - private static void SetDatabase(IServiceCollection services, DatabaseType databaseType, - CosmosConfigurationParams cosmosConfigurationParams, string connectionString) where T : DbContext + private static void SetDatabase(IServiceCollection services, DatabaseType databaseType, + CosmosConfigurationParams cosmosConfigurationParams, string connectionString) where T : DbContext + { + switch (databaseType) { - switch (databaseType) - { - case DatabaseType.SqlServer: - services.AddDbContext(options => options.UseSqlServer(connectionString, - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.EnableRetryOnFailure( - maxRetryCount: MaxRetryCount, - maxRetryDelay: TimeSpan.FromSeconds(MaxRetryDelay), - errorNumbersToAdd: null); - }),ServiceLifetime); - break; - case DatabaseType.InMemory: - services.AddDbContext(options => options.UseInMemoryDatabase(connectionString), ServiceLifetime); - break; - case DatabaseType.MySql: - services.AddDbContext(options => options.UseMySql(ServerVersion.AutoDetect(connectionString), sqlOptions => - { - sqlOptions.EnableRetryOnFailure( - maxRetryCount: MaxRetryCount, - maxRetryDelay: TimeSpan.FromSeconds(MaxRetryDelay), - errorNumbersToAdd: new List()); - }),ServiceLifetime); - break; - case DatabaseType.MariaDb: - services.AddDbContext(options => options.UseMySql(ServerVersion.AutoDetect(connectionString), sqlOptions => + case DatabaseType.SqlServer: + services.AddDbContext(options => options.UseSqlServer(connectionString), ServiceLifetime); + break; + + case DatabaseType.InMemory: + services.AddDbContext(options => options.UseInMemoryDatabase(connectionString), ServiceLifetime); + break; + + case DatabaseType.MySql: + services.AddDbContext(options => options.UseMySql(ServerVersion.AutoDetect(connectionString), + sqlOptions => { sqlOptions.EnableRetryOnFailure( - maxRetryCount: MaxRetryCount, - maxRetryDelay: TimeSpan.FromSeconds(MaxRetryDelay), - errorNumbersToAdd: new List()); - }),ServiceLifetime); - break; - case DatabaseType.Sqlite: - services.AddDbContext(options => options.UseSqlite(connectionString), ServiceLifetime); - break; - case DatabaseType.Cosmos: - if (cosmosConfigurationParams == null) - throw new ArgumentException("The Cosmos configuration can not be null."); - services.AddDbContext(options => options.UseCosmos(cosmosConfigurationParams.Endpoint, - cosmosConfigurationParams.Key, cosmosConfigurationParams.DatabaseName), ServiceLifetime); - break; - case DatabaseType.PostgreSQL: - services.AddDbContext(options => options.UseNpgsql(connectionString, sqlOptions => + MaxRetryCount, + TimeSpan.FromSeconds(MaxRetryDelay), + new List()); + }), ServiceLifetime); + break; + + case DatabaseType.MariaDb: + services.AddDbContext(options => options.UseMySql(ServerVersion.AutoDetect(connectionString), + sqlOptions => { sqlOptions.EnableRetryOnFailure( - maxRetryCount: MaxRetryCount, - maxRetryDelay: TimeSpan.FromSeconds(MaxRetryDelay), - errorCodesToAdd: new List()); - }),ServiceLifetime); - break; - case DatabaseType.FireBird: - services.AddDbContext(options => options.UseFirebird(connectionString), ServiceLifetime); - break; - case DatabaseType.Oracle: - services.AddDbContext(options => options.UseOracle(connectionString), ServiceLifetime); - break; - default: - throw new ArgumentException("Not provided a database driver"); - } + MaxRetryCount, + TimeSpan.FromSeconds(MaxRetryDelay), + new List()); + }), ServiceLifetime); + break; + + case DatabaseType.Sqlite: + services.AddDbContext(options => options.UseSqlite(connectionString), ServiceLifetime); + break; + + case DatabaseType.Cosmos: + if (cosmosConfigurationParams == null) + throw new ArgumentException("The Cosmos configuration can not be null."); + services.AddDbContext(options => options.UseCosmos(cosmosConfigurationParams.Endpoint, + cosmosConfigurationParams.Key, cosmosConfigurationParams.DatabaseName), ServiceLifetime); + break; + + case DatabaseType.PostgreSQL: + services.AddDbContext(options => options.UseNpgsql(connectionString, sqlOptions => + { + sqlOptions.EnableRetryOnFailure( + MaxRetryCount, + TimeSpan.FromSeconds(MaxRetryDelay), + new List()); + }), ServiceLifetime); + break; + + case DatabaseType.FireBird: + services.AddDbContext(options => options.UseFirebird(connectionString), ServiceLifetime); + break; + + case DatabaseType.Oracle: + services.AddDbContext(options => options.UseOracle(connectionString), ServiceLifetime); + break; + + default: + throw new ArgumentException("Not provided a database driver"); } } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/PredicateBuilder.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/PredicateBuilder.cs index 7c5913eb..3763bd95 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/PredicateBuilder.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Common/PredicateBuilder.cs @@ -1,89 +1,98 @@ using System.Linq.Expressions; -namespace Devon4Net.Domain.UnitOfWork.Common +namespace Devon4Net.Infrastructure.UnitOfWork.Common; + +public static class PredicateBuilder { - public static class PredicateBuilder + /// + /// Creates a predicate that evaluates to true. + /// + public static Expression> True() { - /// - /// Creates a predicate that evaluates to true. - /// - public static Expression> True() { return _ => true; } + return _ => true; + } - /// - /// Creates a predicate that evaluates to false. - /// - public static Expression> False() { return _ => false; } + /// + /// Creates a predicate that evaluates to false. + /// + public static Expression> False() + { + return _ => false; + } - /// - /// Creates a predicate expression from the specified lambda expression. - /// - public static Expression> Create(Expression> predicate) { return predicate; } + /// + /// Creates a predicate expression from the specified lambda expression. + /// + public static Expression> Create(Expression> predicate) + { + return predicate; + } - /// - /// Combines the first predicate with the second using the logical "and". - /// - public static Expression> And(this Expression> first, Expression> second) - { - return first.Compose(second, Expression.AndAlso); - } + /// + /// Combines the first predicate with the second using the logical "and". + /// + public static Expression> And(this Expression> first, + Expression> second) + { + return first.Compose(second, Expression.AndAlso); + } - /// - /// Combines the first predicate with the second using the logical "or". - /// - public static Expression> Or(this Expression> first, Expression> second) - { - return first.Compose(second, Expression.OrElse); - } + /// + /// Combines the first predicate with the second using the logical "or". + /// + public static Expression> Or(this Expression> first, + Expression> second) + { + return first.Compose(second, Expression.OrElse); + } - /// - /// Negates the predicate. - /// - public static Expression> Not(this Expression> expression) - { - var negated = Expression.Not(expression.Body); - return Expression.Lambda>(negated, expression.Parameters); - } + /// + /// Negates the predicate. + /// + public static Expression> Not(this Expression> expression) + { + var negated = Expression.Not(expression.Body); + return Expression.Lambda>(negated, expression.Parameters); + } - /// - /// Combines the first expression with the second using the specified merge function. - /// - static Expression Compose(this Expression first, Expression second, Func merge) - { - // zip parameters (map from parameters of second to parameters of first) - var map = first.Parameters - .Select((f, i) => new { f, s = second.Parameters[i] }) - .ToDictionary(p => p.s, p => p.f); + /// + /// Combines the first expression with the second using the specified merge function. + /// + private static Expression Compose(this Expression first, Expression second, + Func merge) + { + // zip parameters (map from parameters of second to parameters of first) + var map = first.Parameters + .Select((f, i) => new { f, s = second.Parameters[i] }) + .ToDictionary(p => p.s, p => p.f); - // replace parameters in the second lambda expression with the parameters in the first - var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); + // replace parameters in the second lambda expression with the parameters in the first + var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); - // create a merged lambda expression with parameters from the first expression - return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); - } + // create a merged lambda expression with parameters from the first expression + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } - class ParameterRebinder : ExpressionVisitor - { - readonly Dictionary _map; + private sealed class ParameterRebinder : ExpressionVisitor + { + private readonly Dictionary _map; - private ParameterRebinder(Dictionary map) - { - this._map = map ?? new Dictionary(); - } + private ParameterRebinder(Dictionary map) + { + _map = map ?? new Dictionary(); + } - public static Expression ReplaceParameters(Dictionary map, Expression exp) - { - return new ParameterRebinder(map).Visit(exp); - } + public static Expression ReplaceParameters(Dictionary map, + Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } - protected override Expression VisitParameter(ParameterExpression node) - { - if (_map.TryGetValue(node, out ParameterExpression replacement)) - { - node = replacement; - } + protected override Expression VisitParameter(ParameterExpression node) + { + if (_map.TryGetValue(node, out var replacement)) node = replacement; - return base.VisitParameter(node); - } + return base.VisitParameter(node); } } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Devon4Net.Infrastructure.UnitOfWork.csproj b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Devon4Net.Infrastructure.UnitOfWork.csproj index 70642011..e6b0c445 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Devon4Net.Infrastructure.UnitOfWork.csproj +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Devon4Net.Infrastructure.UnitOfWork.csproj @@ -1,73 +1,42 @@  - - EF Unit of work library to work with devon4net (devonfw) - net6.0 - enable - True - Capgemini, ADCenter Valencia, Traiectum Team - Capgemini S.A. - Entity framework unit of work implementation - 6.0.11 - Copyright © Capgemini - https://github.com/devonfw/devon4net - https://github.com/devonfw/devon4net - https://github.com/devonfw/ide/blob/master/LICENSE/ - git - devonfw;devon4net;Capgemini;UOW;unit of work;EF; entity framework - NET 6.0+ compatibility version - devonfw.png - README.md - LICENSE - True - + + UoW devon4net + net6.0 + enable + - - 1701;1702;8034;1705;NU1605;NU1608;NU1701;CS1705 - 0 + + 1701;1702;8034;1705;NU1605;NU1608;NU1701;CS1705 + 0 - - 1701;1702;8034;1705;NU1605;NU1608;NU1701;CS1705 - 0 + + 1701;1702;8034;1705;NU1605;NU1608;NU1701;CS1705 + 0 - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - - - True - \ - - - True - \ - - - True - \ - - - diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Enums/DatabaseType.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Enums/DatabaseType.cs index 5e2471b3..95d08f8e 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Enums/DatabaseType.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Enums/DatabaseType.cs @@ -1,16 +1,15 @@ -namespace Devon4Net.Domain.UnitOfWork.Enums +namespace Devon4Net.Domain.UnitOfWork.Enums; + +public enum DatabaseType { - public enum DatabaseType - { - SqlServer = 1, - Sqlite = 2, - InMemory = 3, - Cosmos = 4, - PostgreSQL = 5, - MySql = 6, - MariaDb = 7, - FireBird = 8, - Oracle = 9, - MSAccess = 10 - } -} + SqlServer = 1, + Sqlite = 2, + InMemory = 3, + Cosmos = 4, + PostgreSQL = 5, + MySql = 6, + MariaDb = 7, + FireBird = 8, + Oracle = 9, + MSAccess = 10 +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/ContextNullException.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/ContextNullException.cs index 3381e189..7cf77137 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/ContextNullException.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/ContextNullException.cs @@ -1,23 +1,24 @@ -namespace Devon4Net.Domain.UnitOfWork.Exceptions +using System.Runtime.Serialization; + +namespace Devon4Net.Domain.UnitOfWork.Exceptions; + +[Serializable] +public class ContextNullException : Exception { - [Serializable] - public class ContextNullException : Exception + public ContextNullException() { - public ContextNullException() : base() - { - } + } - public ContextNullException(string message) : base(message) - { - } + public ContextNullException(string message) : base(message) + { + } - public ContextNullException(string message, Exception innerException) : base(message, innerException) - { - } + public ContextNullException(string message, Exception innerException) : base(message, innerException) + { + } - protected ContextNullException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) - : base(serializationInfo, streamingContext) - { - } + protected ContextNullException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/RepositoryNotFoundException.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/RepositoryNotFoundException.cs index 7037a097..76ad43d9 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/RepositoryNotFoundException.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/RepositoryNotFoundException.cs @@ -1,23 +1,24 @@ -namespace Devon4Net.Domain.UnitOfWork.Exceptions +using System.Runtime.Serialization; + +namespace Devon4Net.Domain.UnitOfWork.Exceptions; + +[Serializable] +public class RepositoryNotFoundException : Exception { - [Serializable] - public class RepositoryNotFoundException : Exception + public RepositoryNotFoundException() { - public RepositoryNotFoundException() : base() - { - } + } - public RepositoryNotFoundException(string message) : base(message) - { - } + public RepositoryNotFoundException(string message) : base(message) + { + } - public RepositoryNotFoundException(string message, Exception innerException) : base(message, innerException) - { - } + public RepositoryNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } - protected RepositoryNotFoundException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) - : base(serializationInfo, streamingContext) - { - } + protected RepositoryNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/TransactionNullException.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/TransactionNullException.cs index c5fbc9ee..1c7f709b 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/TransactionNullException.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Exceptions/TransactionNullException.cs @@ -1,23 +1,24 @@ -namespace Devon4Net.Domain.UnitOfWork.Exceptions +using System.Runtime.Serialization; + +namespace Devon4Net.Domain.UnitOfWork.Exceptions; + +[Serializable] +public class TransactionNullException : Exception { - [Serializable] - public class TransactionNullException : Exception + public TransactionNullException() { - public TransactionNullException() : base() - { - } + } - public TransactionNullException(string message) : base(message) - { - } + public TransactionNullException(string message) : base(message) + { + } - public TransactionNullException(string message, Exception innerException) : base(message, innerException) - { - } + public TransactionNullException(string message, Exception innerException) : base(message, innerException) + { + } - protected TransactionNullException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) - : base(serializationInfo, streamingContext) - { - } + protected TransactionNullException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationBase.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationBase.cs index bb013159..360930f6 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationBase.cs @@ -1,20 +1,13 @@ -namespace Devon4Net.Domain.UnitOfWork.Pagination +namespace Devon4Net.Domain.UnitOfWork.Pagination; + +public abstract class PaginationBase { - public abstract class PaginationBase - { - public int CurrentPage { get; set; } - public int PageCount { get; set; } - public int PageSize { get; set; } - public int RowCount { get; set; } + public int CurrentPage { get; set; } + public int PageCount { get; set; } + public int PageSize { get; set; } + public int RowCount { get; set; } - public int FirstRowOnPage - { - get { return ((CurrentPage - 1) * PageSize) + 1; } - } + public int FirstRowOnPage => (CurrentPage - 1) * PageSize + 1; - public int LastRowOnPage - { - get { return Math.Min(CurrentPage * PageSize, RowCount); } - } - } -} + public int LastRowOnPage => Math.Min(CurrentPage * PageSize, RowCount); +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationResult.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationResult.cs index 4e77f0bd..35ab8075 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationResult.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Pagination/PaginationResult.cs @@ -1,12 +1,11 @@ -namespace Devon4Net.Domain.UnitOfWork.Pagination +namespace Devon4Net.Domain.UnitOfWork.Pagination; + +public class PaginationResult : PaginationBase where T : class { - public class PaginationResult : PaginationBase where T : class + public PaginationResult() { - public IList Results { get; set; } - - public PaginationResult() - { - Results = new List(); - } + Results = new List(); } -} + + public IList Results { get; set; } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs new file mode 100644 index 00000000..51ba7c25 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs @@ -0,0 +1,18 @@ +using System.Linq.Expressions; + +namespace Devon4Net.Infrastructure.UnitOfWork.Projector; + +public interface IProjector +{ + /// + /// Get Projection from entity + /// + /// + /// + /// + /// + /// + Task> GetProjection( + Func, IQueryable> projection, + Expression> filter = null) where TEntity : class; +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs new file mode 100644 index 00000000..8474cabe --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs @@ -0,0 +1,39 @@ +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; + +namespace Devon4Net.Infrastructure.UnitOfWork.Projector; + +public class Projector : IProjector +{ + private readonly DbContext _context; + + protected Projector(DbContext context) + { + _context = context; + } + + /// + /// Get Projection from entity + /// + /// + /// + /// + /// + /// + public async Task> GetProjection( + Func, IQueryable> projection, + Expression> filter = null) where TEntity : class + { + var dbSet = _context.Set(); + + var query = dbSet.AsQueryable(); + if (filter != null) + { + query = query.Where(filter); + } + + var projectionQuery = projection(query); + + return await projectionQuery.ToListAsync(); + } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/IRawSqlRepository.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/IRawSqlRepository.cs new file mode 100644 index 00000000..2ff84fb5 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/IRawSqlRepository.cs @@ -0,0 +1,43 @@ +using System.Data; + +namespace Devon4Net.Infrastructure.UnitOfWork.RawSqlRepository +{ + public interface IRawSqlRepository + { + /// + /// This method executes a SQL query against the database using the Dapper library. + /// + /// + /// + /// + /// + /// + /// It returns an IEnumerable of the provided object containing the results of the query. + /// + Task> QueryAsync(string sql, object parameters = null, IDbTransaction transaction = null); + + /// + /// This method executes a SQL query against the database using the Dapper library. + /// + /// + /// + /// + /// + /// + /// Returns the first result, or null if the query returns no results. + /// + Task QueryFirstOrDefaultAsync(string sql, object parameters = null, IDbTransaction transaction = null); + + /// + /// This method executes a SQL query against the database using the Dapper library. + /// + /// + /// + /// + /// + /// + /// It returns the number of affected rows. + /// + Task ExecuteAsync(string sql, object parameters = null, IDbTransaction transaction = null); + } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/RawSqlRepository.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/RawSqlRepository.cs new file mode 100644 index 00000000..b52430e8 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/RawSqlRepository/RawSqlRepository.cs @@ -0,0 +1,66 @@ +using System.Data; +using Dapper; +using Microsoft.EntityFrameworkCore; + +namespace Devon4Net.Infrastructure.UnitOfWork.RawSqlRepository +{ + public class RawSqlRepository : IRawSqlRepository + { + /// + /// Dapper Base Repository + /// + /// The data base context to work with + /// + protected RawSqlRepository(DbContext context) + { + DbContext = context; + } + + private DbContext DbContext { get; set; } + + /// + /// This method executes a SQL query against the database using the Dapper library. + /// + /// + /// + /// + /// + /// + /// It returns an IEnumerable of the provided object containing the results of the query. + /// + public async Task> QueryAsync(string sql, object parameters = null, IDbTransaction transaction = null) + { + return await DbContext.Database.GetDbConnection().QueryAsync(sql, parameters, transaction); + } + + /// + /// This method executes a SQL query against the database using the Dapper library. + /// + /// + /// + /// + /// + /// + /// Returns the first result, or null if the query returns no results. + /// + public async Task QueryFirstOrDefaultAsync(string sql, object parameters = null, IDbTransaction transaction = null) + { + return await DbContext.Database.GetDbConnection().QueryFirstOrDefaultAsync(sql, parameters, transaction); + } + + /// + /// This method executes a SQL query against the database using the Dapper library. + /// + /// + /// + /// + /// + /// + /// It returns the number of affected rows. + /// + public async Task ExecuteAsync(string sql, object parameters = null, IDbTransaction transaction = null) + { + return await DbContext.Database.GetDbConnection().ExecuteAsync(sql, parameters, transaction); + } + } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/IRepository.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/IRepository.cs index 20a16ece..97f18a68 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/IRepository.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/IRepository.cs @@ -1,23 +1,43 @@ using System.ComponentModel; using System.Linq.Expressions; using Devon4Net.Domain.UnitOfWork.Pagination; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; -namespace Devon4Net.Domain.UnitOfWork.Repository +namespace Devon4Net.Infrastructure.UnitOfWork.Repository; + +public interface IRepository where T : class { - public interface IRepository where T : class - { - Task Create(T entity, bool autoSaveChanges = true, bool detach = true); - Task Update(T entity, bool autoSaveChanges = true, bool detach = true); - Task Delete(T entity, bool autoSaveChanges = true, bool detach = true); - Task Delete(Expression> predicate = null, bool autoSaveChanges = true); - Task GetFirstOrDefault(Expression> predicate = null); - Task GetLastOrDefault(Expression> predicate = null); - Task> Get(Expression> predicate = null); - Task> Get(IList include, Expression> predicate = null); - Task> Get(int currentPage, int pageSize, IList includedNestedFiels, Expression> predicate = null); - Task> Get(int currentPage, int pageSize, Expression> predicate = null); - Task> Get(Expression> predicate, Expression> keySelector, ListSortDirection sortDirection); - Task> Get(int currentPage, int pageSize, Expression> predicate, Expression> keySelector, ListSortDirection sortDirection); - Task Count(Expression> predicate = null); - } -} + Task Create(T entity, bool autoSaveChanges = true, bool detach = true); + + Task Update(T entity, bool autoSaveChanges = true, bool detach = true); + + Task Delete(T entity, bool autoSaveChanges = true, bool detach = true); + + Task Delete(Expression> predicate = null, bool autoSaveChanges = true); + + Task GetFirstOrDefault(Expression> predicate = null); + + Task GetLastOrDefault(Expression> predicate = null); + + Task> Get(Expression> predicate = null); + + Task> Get(IList include, Expression> predicate = null); + + Task> Get(int currentPage, int pageSize, IList includedNestedFiels, + Expression> predicate = null); + + Task> Get(int currentPage, int pageSize, Expression> predicate = null); + + Task> Get(Expression> predicate, Expression> keySelector, + ListSortDirection sortDirection); + + Task> Get(int currentPage, int pageSize, Expression> predicate, + Expression> keySelector, ListSortDirection sortDirection); + + Task Count(Expression> predicate = null); + + Task> ExecuteSpGetList(string spName, params SqlParameter[] parameters); + + Task ExecuteSp(string spName, params SqlParameter[] parameters); +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/Repository.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/Repository.cs index 79d1a18a..d66c30e4 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/Repository.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Repository/Repository.cs @@ -1,198 +1,243 @@ using System.ComponentModel; using System.Linq.Expressions; +using System.Runtime.CompilerServices; using Devon4Net.Domain.UnitOfWork.Exceptions; using Devon4Net.Domain.UnitOfWork.Pagination; using Devon4Net.Infrastructure.Common; +using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; -namespace Devon4Net.Domain.UnitOfWork.Repository +namespace Devon4Net.Infrastructure.UnitOfWork.Repository; + +public class Repository : IRepository where T : class { - public class Repository : IRepository where T : class + /// + /// Initialization class + /// + /// The data base context to work with + /// + /// Sets the AutoDetectChangesEnabled, LazyLoadingEnabled and QueryTrackingBehavior flag + /// to true or false + /// + protected Repository(DbContext context, bool dbContextBehaviour = false) { - private DbContext DbContext { get; set; } - private bool DbContextBehaviour { get; } + DbContext = context; + DbContextBehaviour = dbContextBehaviour; + } - private IQueryable Queryable => SetQuery(); + private DbContext DbContext { get; set; } + private bool DbContextBehaviour { get; } - /// - /// Initialization class - /// - /// The data base context to work with - /// Sets the AutoDetectChangesEnabled, LazyLoadingEnabled and QueryTrackingBehavior flag to true or false - public Repository(DbContext context, bool dbContextBehaviour = false) - { - DbContext = context; - DbContextBehaviour = dbContextBehaviour; - } + private IQueryable Queryable => SetQuery(); - public async Task Create(T entity, bool autoSaveChanges= true, bool detach = true) + public async Task Create(T entity, bool autoSaveChanges = true, bool detach = true) + { + try { - try - { - var result = await DbContext.Set().AddAsync(entity).ConfigureAwait(false); - result.State = EntityState.Added; - if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); - if (detach) result.State = EntityState.Detached; - return result.Entity; - } - catch (Exception ex) - { - Devon4NetLogger.Error(ex); - throw; - } + var result = await DbContext.Set().AddAsync(entity).ConfigureAwait(false); + result.State = EntityState.Added; + if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); + if (detach) result.State = EntityState.Detached; + return result.Entity; } - - public async Task Update(T entity, bool autoSaveChanges = true, bool detach = true) + catch (Exception ex) { - try - { - var result = DbContext.Set().Update(entity); - result.State = EntityState.Modified; - if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); - if (detach) result.State = EntityState.Detached; - return result.Entity; - } - catch (Exception ex) - { - Devon4NetLogger.Error(ex); - throw; - } + Devon4NetLogger.Error(ex); + throw; } + } - public async Task Delete(T entity, bool autoSaveChanges = true, bool detach = true) + public async Task Update(T entity, bool autoSaveChanges = true, bool detach = true) + { + try { - try - { - var result = DbContext.Set().Remove(entity); - result.State = EntityState.Deleted; - if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); - if (detach) result.State = EntityState.Detached; - return result.Entity; - } - catch (Exception ex) - { - Devon4NetLogger.Error(ex); - throw; - } + var result = DbContext.Set().Update(entity); + result.State = EntityState.Modified; + if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); + if (detach) result.State = EntityState.Detached; + return result.Entity; } - - public async Task Delete(Expression> predicate = null, bool autoSaveChanges = true) + catch (Exception ex) { - try - { - var entities = await Get(predicate).ConfigureAwait(false); - DbContext.Set().RemoveRange(entities); - if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); - return true; - } - catch (Exception ex) - { - Devon4NetLogger.Error(ex); - throw; - } + Devon4NetLogger.Error(ex); + throw; } + } - public Task GetFirstOrDefault(Expression> predicate = null) + public async Task Delete(T entity, bool autoSaveChanges = true, bool detach = true) + { + try { - return GetQueryFromPredicate(predicate).FirstOrDefaultAsync(); + var result = DbContext.Set().Remove(entity); + result.State = EntityState.Deleted; + if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); + if (detach) result.State = EntityState.Detached; + return result.Entity; } - - public Task GetLastOrDefault(Expression> predicate = null) + catch (Exception ex) { - return GetQueryFromPredicate(predicate).LastOrDefaultAsync(); + Devon4NetLogger.Error(ex); + throw; } + } - public async Task> Get(Expression> predicate = null) + public async Task Delete(Expression> predicate = null, bool autoSaveChanges = true) + { + try { - return await GetQueryFromPredicate(predicate).ToListAsync().ConfigureAwait(false); + var entities = await Get(predicate).ConfigureAwait(false); + DbContext.Set().RemoveRange(entities); + if (autoSaveChanges) await DbContext.SaveChangesAsync().ConfigureAwait(false); + return true; } - - public async Task> Get(Expression> predicate, Expression> keySelector, ListSortDirection sortDirection) + catch (Exception ex) { - return await GetSortedQueryFromPredicate(predicate, keySelector, sortDirection).ToListAsync().ConfigureAwait(false); + Devon4NetLogger.Error(ex); + throw; } + } - public async Task> Get(IList include, Expression> predicate = null) - { - return await GetResultSetWithNestedProperties(include, predicate).ToListAsync().ConfigureAwait(false); - } + public Task GetFirstOrDefault(Expression> predicate = null) + { + return GetQueryFromPredicate(predicate).FirstOrDefaultAsync(); + } - public Task> Get(int currentPage, int pageSize, IList includedNestedFiels, Expression> predicate = null) - { - return GetResultSetWithNestedPropertiesPaged(currentPage, pageSize, includedNestedFiels, predicate); - } + public Task GetLastOrDefault(Expression> predicate = null) + { + return GetQueryFromPredicate(predicate).LastOrDefaultAsync(); + } - public Task> Get(int currentPage, int pageSize, Expression> predicate = null) - { - return GetPagedResult(currentPage, pageSize, GetQueryFromPredicate(predicate)); - } + public async Task> Get(Expression> predicate = null) + { + return await GetQueryFromPredicate(predicate).ToListAsync().ConfigureAwait(false); + } - public Task> Get(int currentPage, int pageSize, Expression> predicate , Expression> keySelector, ListSortDirection sortDirection) - { - return GetPagedResult(currentPage, pageSize, GetSortedQueryFromPredicate(predicate, keySelector, sortDirection)); - } + public async Task> Get(Expression> predicate, Expression> keySelector, + ListSortDirection sortDirection) + { + return await GetSortedQueryFromPredicate(predicate, keySelector, sortDirection).ToListAsync() + .ConfigureAwait(false); + } - public Task Count(Expression> predicate = null) - { - return predicate == null ? Queryable.LongCountAsync() : Queryable.LongCountAsync(predicate); - } + public async Task> Get(IList include, Expression> predicate = null) + { + return await GetResultSetWithNestedProperties(include, predicate).ToListAsync().ConfigureAwait(false); + } - private IQueryable GetQueryFromPredicate(Expression> predicate) - { - return predicate != null ? Queryable.Where(predicate): Queryable; - } + public Task> Get(int currentPage, int pageSize, IList includedNestedFiels, + Expression> predicate = null) + { + return GetResultSetWithNestedPropertiesPaged(currentPage, pageSize, includedNestedFiels, predicate); + } - private IQueryable GetSortedQueryFromPredicate(Expression> predicate, Expression> keySelector, ListSortDirection sortDirection) - { - if (sortDirection == ListSortDirection.Ascending) - { - return predicate != null ? Queryable.Where(predicate).OrderBy(keySelector) : Queryable.OrderBy(keySelector); - } + public Task> Get(int currentPage, int pageSize, Expression> predicate = null) + { + return GetPagedResult(currentPage, pageSize, GetQueryFromPredicate(predicate)); + } - return predicate != null ? Queryable.Where(predicate).OrderByDescending(keySelector) : Queryable.OrderByDescending(keySelector); - } + public Task> Get(int currentPage, int pageSize, Expression> predicate, + Expression> keySelector, ListSortDirection sortDirection) + { + return GetPagedResult(currentPage, pageSize, + GetSortedQueryFromPredicate(predicate, keySelector, sortDirection)); + } - private IQueryable GetResultSetWithNestedProperties(IList includedNestedFiels, Expression> predicate = null) - { - return includedNestedFiels.Aggregate(GetQueryFromPredicate(predicate), (current, property) => current.Include(property)); - } + public Task Count(Expression> predicate = null) + { + return predicate == null ? Queryable.LongCountAsync() : Queryable.LongCountAsync(predicate); + } - private Task> GetResultSetWithNestedPropertiesPaged(int currentPage, int pageSize, IList includedNestedFiels, Expression> predicate = null) - { - return GetPagedResult(currentPage, pageSize, GetResultSetWithNestedProperties(includedNestedFiels, predicate)); - } + private IQueryable GetQueryFromPredicate(Expression> predicate) + { + return predicate != null ? Queryable.Where(predicate) : Queryable; + } - private static async Task> GetPagedResult(int currentPage, int pageSize, IQueryable resultList) - { - var pagedResult = new PaginationResult() { CurrentPage = currentPage, PageSize = pageSize, RowCount = await resultList.CountAsync().ConfigureAwait(false) }; + public Task> ExecuteSpGetList(string spName, params SqlParameter[] parameters) + { + GenerateSpQuery(spName, parameters, out var parameterValues, out var query); - var pageCount = (double)pagedResult.RowCount / pageSize; - pagedResult.PageCount = (int)Math.Ceiling(pageCount); + return DbContext.Set().FromSqlInterpolated(FormattableStringFactory.Create(query, parameterValues)) + .ToListAsync(); + } - var skip = (currentPage - 1) * pageSize; - pagedResult.Results = await resultList.AsNoTracking().Skip(skip).Take(pageSize).ToListAsync().ConfigureAwait(false); + public Task ExecuteSp(string spName, params SqlParameter[] parameters) + { + GenerateSpQuery(spName, parameters, out var parameterValues, out var query); - return pagedResult; - } + return DbContext.Database.ExecuteSqlInterpolatedAsync(FormattableStringFactory.Create(query, parameterValues)); + } - private IQueryable SetQuery() where S : class - { - SetContextBehaviour(DbContextBehaviour); - return DbContext.Set().AsNoTracking(); - } + private static void GenerateSpQuery(string spName, SqlParameter[] parameters, out string parameterValues, out string query) + { + var parameterList = new List(parameters); + var parameterPlaceholders = string.Join(", ", parameterList.Select((p, i) => $"@p{i}")); + parameterValues = string.Join(", ", parameterList.Select((p, i) => $"@p{i} = {p.Value}")); - private void SetContextBehaviour( bool enabled) - { - DbContext.ChangeTracker.AutoDetectChangesEnabled = enabled; + query = $"EXEC {spName} {parameterPlaceholders}"; + } - DbContext.ChangeTracker.LazyLoadingEnabled = enabled; + private IQueryable GetSortedQueryFromPredicate(Expression> predicate, + Expression> keySelector, ListSortDirection sortDirection) + { + if (sortDirection == ListSortDirection.Ascending) + return predicate != null ? Queryable.Where(predicate).OrderBy(keySelector) : Queryable.OrderBy(keySelector); - DbContext.ChangeTracker.QueryTrackingBehavior = enabled ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking; - } + return predicate != null + ? Queryable.Where(predicate).OrderByDescending(keySelector) + : Queryable.OrderByDescending(keySelector); + } - internal void SetContext(DbContext context) + private IQueryable GetResultSetWithNestedProperties(IList includedNestedFiels, + Expression> predicate = null) + { + return includedNestedFiels.Aggregate(GetQueryFromPredicate(predicate), + (current, property) => current.Include(property)); + } + + private Task> GetResultSetWithNestedPropertiesPaged(int currentPage, int pageSize, + IList includedNestedFiels, Expression> predicate = null) + { + return GetPagedResult(currentPage, pageSize, GetResultSetWithNestedProperties(includedNestedFiels, predicate)); + } + + private static async Task> GetPagedResult(int currentPage, int pageSize, + IQueryable resultList) + { + var pagedResult = new PaginationResult { - DbContext = context ?? throw new ContextNullException(nameof(context)); - } + CurrentPage = currentPage, + PageSize = pageSize, + RowCount = await resultList.CountAsync().ConfigureAwait(false) + }; + + var pageCount = (double)pagedResult.RowCount / pageSize; + pagedResult.PageCount = (int)Math.Ceiling(pageCount); + + var skip = (currentPage - 1) * pageSize; + pagedResult.Results = + await resultList.AsNoTracking().Skip(skip).Take(pageSize).ToListAsync().ConfigureAwait(false); + + return pagedResult; + } + + private IQueryable SetQuery() where S : class + { + SetContextBehaviour(DbContextBehaviour); + return DbContext.Set().AsNoTracking(); + } + + private void SetContextBehaviour(bool enabled) + { + DbContext.ChangeTracker.AutoDetectChangesEnabled = enabled; + + DbContext.ChangeTracker.LazyLoadingEnabled = enabled; + + DbContext.ChangeTracker.QueryTrackingBehavior = + enabled ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking; + } + + internal void SetContext(DbContext context) + { + DbContext = context ?? throw new ContextNullException(nameof(context)); } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/IService.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/IService.cs deleted file mode 100644 index ec313c02..00000000 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/IService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Devon4Net.Domain.UnitOfWork.Service -{ - public interface IService - { - } -} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/Service.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/Service.cs deleted file mode 100644 index 65188e7d..00000000 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Service/Service.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Devon4Net.Domain.UnitOfWork.UnitOfWork; -using Microsoft.EntityFrameworkCore; - -namespace Devon4Net.Domain.UnitOfWork.Service -{ - public class Service : IService where TContext : DbContext - { - public IUnitOfWork UoW { get; } - - public Service(IUnitOfWork uoW) - { - UoW = uoW; - } - } -} diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/IUnitOfWork.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/IUnitOfWork.cs index 738107db..b2256f7a 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/IUnitOfWork.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/IUnitOfWork.cs @@ -1,14 +1,21 @@ -using Microsoft.EntityFrameworkCore; +using System.Data; using Microsoft.EntityFrameworkCore.Storage; -namespace Devon4Net.Domain.UnitOfWork.UnitOfWork +namespace Devon4Net.Infrastructure.UnitOfWork.UnitOfWork; + +public interface IUnitOfWork { - public interface IUnitOfWork where TContext : DbContext - { - Task GetTransaction(); - Task Commit(IDbContextTransaction transaction); - T Repository() where T : class; - T Repository() where T : class where TS : class; - IExecutionStrategy CreateExecutionStrategy(); - } -} + /// + /// Begins the transaction + /// + Task GetDbTransaction(int secondsTimeout = 30); + + IExecutionStrategy CreateExecutionStrategy(); + + Task SaveChanges(); + + /// + /// Commit the transaction if is correct, else rollback. Both cases dispose. + /// + Task CommitTransaction(IDbTransaction transaction); +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/UnitOfWork.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/UnitOfWork.cs index 207488ee..54ba6d94 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/UnitOfWork.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWork/UnitOfWork.cs @@ -1,146 +1,62 @@ -using Devon4Net.Domain.UnitOfWork.Exceptions; -using Devon4Net.Domain.UnitOfWork.Repository; +using System.Data; +using Devon4Net.Domain.UnitOfWork.Exceptions; using Devon4Net.Infrastructure.Common; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; -using System.Globalization; -namespace Devon4Net.Domain.UnitOfWork.UnitOfWork +namespace Devon4Net.Infrastructure.UnitOfWork.UnitOfWork; + +public class UnitOfWork : IUnitOfWork where TContext : DbContext { - public class UnitOfWork : IUnitOfWork where TContext : DbContext, IDisposable + private const int DefaultSecondsTimeout = 30; + + protected UnitOfWork(TContext context) { - private TContext Context { get; } - private IServiceProvider ServiceProvider { get; } + Context = context ?? throw new ContextNullException(nameof(context)); + } - public UnitOfWork(TContext context, IServiceProvider serviceProvider) - { - Context = context ?? throw new ContextNullException(nameof(context)); - ServiceProvider = serviceProvider; - } + private TContext Context { get; } - public Task GetTransaction() - { - return Context.Database.BeginTransactionAsync(); - } + public async Task GetDbTransaction(int secondsTimeout = DefaultSecondsTimeout) + { + Context.Database.SetCommandTimeout(secondsTimeout); - public IExecutionStrategy CreateExecutionStrategy() - { - return Context.Database.CreateExecutionStrategy(); - } + var dbContextTransaction = await Context.Database.BeginTransactionAsync(); + return dbContextTransaction.GetDbTransaction(); + } - public Task Commit(IDbContextTransaction transaction) - { - if (transaction == null) - { - throw new TransactionNullException("Transaction cannot be null to perform transaction operations."); - } + public Task SaveChanges() + { + //Auditable entities + return Context.SaveChangesAsync(); + } - var transactionSavePointName = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff", CultureInfo.InvariantCulture); + public IExecutionStrategy CreateExecutionStrategy() + { + return Context.Database.CreateExecutionStrategy(); + } - return Task.Run(async () => - { - try - { - transaction.CreateSavepoint(transactionSavePointName); - _ = await SaveChanges().ConfigureAwait(false); - await transaction.CommitAsync().ConfigureAwait(false); - } - catch (DbUpdateConcurrencyException ex) - { - Devon4NetLogger.Error(ex); - RollbackTransaction(transaction, transactionSavePointName); - throw; - } - catch (Exception ex) - { - Devon4NetLogger.Error(ex); - RollbackTransaction(transaction, transactionSavePointName); - throw; - } - }); - } + public async Task CommitTransaction(IDbTransaction transaction) + { + if (transaction == null) + throw new TransactionNullException("Transaction cannot be null to perform transaction operations."); - private async Task SaveChanges() + try { - try - { - await Context.SaveChangesAsync().ConfigureAwait(false); - return true; - } - catch (OperationCanceledException ex) - { - Devon4NetLogger.Error(ex); - throw; - } - catch (DbUpdateConcurrencyException ex) - { - foreach (var entry in ex.Entries) - { - var databaseValues = entry.GetDatabaseValues(); - - // Refresh original values to bypass next concurrency check - entry.OriginalValues.SetValues(databaseValues); - } - - Devon4NetLogger.Error(ex); - throw; - } - catch (DbUpdateException ex) - { - Devon4NetLogger.Error(ex); - throw; - } - } + await SaveChanges(); - /// - /// ets the typed repository class - /// - /// The inherited repository class - /// The instantiated repository class - public T Repository() where T : class - { - return GetRepository(); + transaction.Commit(); } - - /// - /// Gets the typed repository class and sets the database context - /// - /// The inherited repository class - /// The entity class - /// The instantiated repository class - public T Repository() where T : class where TS : class + catch (Exception ex) { - var repository = GetRepository(); - - (repository as Repository)?.SetContext(Context); - - return repository; + Devon4NetLogger.Error(ex); + transaction.Rollback(); + throw; } - - private static void RollbackTransaction(IDbContextTransaction transaction, string savePoint) + finally { - if (transaction == null) - { - const string message = "Transaction cannot be null to perform transaction operations"; - Devon4NetLogger.Error(message); - throw new TransactionNullException(message); - } - - transaction.RollbackToSavepoint(savePoint); + Context.Database.SetCommandTimeout(DefaultSecondsTimeout); transaction.Dispose(); } - - private T GetRepository() where T : class - { - var repositoryType = typeof(T); - var repository = ServiceProvider.GetService(repositoryType); - - if (repository == null) - { - throw new RepositoryNotFoundException($"The repository {repositoryType.Name} was not found in the IOC container. Plase register the repository during startup."); - } - - return repository as T; - } } -} +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWorkConfiguration.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWorkConfiguration.cs index f8da4547..7b292686 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWorkConfiguration.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/UnitOfWorkConfiguration.cs @@ -1,31 +1,12 @@ -using Devon4Net.Domain.UnitOfWork.Repository; -using Devon4Net.Domain.UnitOfWork.UnitOfWork; -using Devon4Net.Infrastructure.Common.Helpers; +using Devon4Net.Infrastructure.UnitOfWork.Repository; using Microsoft.Extensions.DependencyInjection; -namespace Devon4Net.Infrastructure.UnitOfWork +namespace Devon4Net.Infrastructure.UnitOfWork; + +public static class UnitOfWorkConfiguration { - public static class UnitOfWorkConfiguration + public static void SetupUnitOfWork(this IServiceCollection services) { - public static void SetupUnitOfWork(this IServiceCollection services) - { - services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); - services.AddTransient(typeof(IUnitOfWork<>), typeof(UnitOfWork<>)); - } - - /// - /// Auto registers the Service and Repository classes from the assembly that contains the assemblyContainerClass class - /// - /// dotner service collection - /// Type of the class that is located in the assembly to scan - /// Sufix namme of Service class - /// Sufix namme of Repository class - public static void SetupUnitOfWork(this IServiceCollection services, Type assemblyContainerClass, string serviceSufix = "Service", string repositorySufix = "Repository") - { - services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); - services.AddTransient(typeof(IUnitOfWork<>), typeof(UnitOfWork<>)); - - services.AutoRegisterClasses(new List { assemblyContainerClass }, new List { serviceSufix, repositorySufix }); - } + services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); } -} +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/AssemblyReference.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/AssemblyReference.cs new file mode 100644 index 00000000..c685a74f --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/AssemblyReference.cs @@ -0,0 +1,3 @@ +namespace Devon4Net.Application; + +public sealed record AssemblyReference; \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs new file mode 100644 index 00000000..a4766aa8 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs @@ -0,0 +1,30 @@ +using Devon4Net.Application.Dtos; +using Devon4Net.Domain.Entities; + +namespace Devon4Net.Application.Converters +{ + /// + /// EmployeeConverter + /// + public static class EmployeeConverter + { + /// + /// ModelToDto transformation + /// + /// + /// + public static EmployeeDto ModelToDto(Employee? item) + { + if (item == null) return new EmployeeDto(); + + return new EmployeeDto + { + Id = item.Id, + Name = item.Name, + Surname = item.Surname, + Mail = item.Mail + }; + } + + } +} diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net - Backup.Application.csproj b/source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net - Backup.Application.csproj new file mode 100644 index 00000000..caa546ce --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net - Backup.Application.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net.Application.csproj b/source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net.Application.csproj new file mode 100644 index 00000000..f951b8a0 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Devon4Net.Application.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs new file mode 100644 index 00000000..b78ef2c8 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; + +namespace Devon4Net.Application.Dtos; + +/// +/// Employee definition +/// +public class EmployeeDto +{ + /// + /// the Id + /// + public long Id { get; set; } + + /// + /// the Name + /// + [Required] + public string? Name { get; set; } + + /// + /// the Surname + /// + [Required] + public string? Surname { get; set; } + + /// + /// the Mail + /// + [Required] + public string? Mail { get; set; } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs new file mode 100644 index 00000000..2bebddb5 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs @@ -0,0 +1,57 @@ +using Devon4Net.Infrastructure.Common.Exceptions; +using Microsoft.AspNetCore.Http; + +namespace Devon4Net.Application.Exceptions +{ + /// + /// Custom exception EmployeeNotFoundException + /// + [Serializable] + public class EmployeeNotFoundException : Exception, IWebApiException + { + /// + /// The forced http status code to be fired on the exception manager + /// + public int StatusCode => StatusCodes.Status404NotFound; + + /// + /// Show the message on the response? + /// + public bool ShowMessage => true; + + /// + /// Initializes a new instance of the class. + /// + public EmployeeNotFoundException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + public EmployeeNotFoundException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public EmployeeNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + protected EmployeeNotFoundException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } + } +} diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs new file mode 100644 index 00000000..e89782b5 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs @@ -0,0 +1,7 @@ +using Devon4Net.Application.Dtos; +using Devon4Net.Infrastructure.MediatR.Command; + +namespace Devon4Net.Application.Features.Command.CreateEmployee +{ + public record CreateEmployeeCommand(string? Name, string? Surname, string? Mail) : CommandBase; +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs new file mode 100644 index 00000000..86b79658 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace Devon4Net.Application.Features.Command.CreateEmployee +{ + public class InsertAuthorCommandValidation : AbstractValidator + { + public InsertAuthorCommandValidation() + { + RuleFor(x => x.Name).NotEmpty().WithMessage("FistName must be not empty"); + RuleFor(x => x.Surname).NotEmpty(); + RuleFor(x => x.Mail).NotEmpty().EmailAddress(); + } + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeHandler.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeHandler.cs new file mode 100644 index 00000000..473ad191 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeHandler.cs @@ -0,0 +1,46 @@ +using Devon4Net.Application.Converters; +using Devon4Net.Application.Dtos; +using Devon4Net.Application.Ports.Repositories; +using Devon4Net.Infrastructure.Common; +using MediatR; + +namespace Devon4Net.Application.Features.Command.CreateEmployee; + +public class CreateEmployeeHandler : IRequestHandler +{ + private readonly IEmployeeRepository _employeeRepository; + + public CreateEmployeeHandler(IEmployeeRepository employeeRepository) + { + _employeeRepository = employeeRepository; + } + + public async Task Handle(CreateEmployeeCommand request, CancellationToken cancellationToken) + { + Devon4NetLogger.Debug("Started CreateEmployeeHandler"); + + CheckRequestParams(request); + + var employee = await _employeeRepository.Create(request.Name!, request.Surname!, request.Mail!); + + return EmployeeConverter.ModelToDto(employee); + } + + private static void CheckRequestParams(CreateEmployeeCommand request) + { + if (string.IsNullOrEmpty(request.Name) || string.IsNullOrWhiteSpace(request.Name)) + { + throw new ArgumentException("The 'name' field can not be null."); + } + + if (string.IsNullOrEmpty(request.Surname) || string.IsNullOrWhiteSpace(request.Surname)) + { + throw new ArgumentException("The 'surName' field can not be null."); + } + + if (string.IsNullOrEmpty(request.Mail) || string.IsNullOrWhiteSpace(request.Mail)) + { + throw new ArgumentException("The 'mail' field can not be null."); + } + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs new file mode 100644 index 00000000..8cc4d8ce --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs @@ -0,0 +1,24 @@ +using Devon4Net.Application.Converters; +using Devon4Net.Application.Dtos; +using Devon4Net.Application.Ports.Repositories; +using Devon4Net.Infrastructure.Common; +using MediatR; + +namespace Devon4Net.Application.Features.Queries.GetAllEmployees; + +public class GetAllEmployeesHandler : IRequestHandler> +{ + private readonly IEmployeeRepository _employeeRepository; + + public GetAllEmployeesHandler(IEmployeeRepository employeeRepository) + { + _employeeRepository = employeeRepository; + } + + public async Task> Handle(GetAllEmployeesQuery request, CancellationToken cancellationToken) + { + Devon4NetLogger.Debug("Started GetAllEmployeesHandler"); + var result = await _employeeRepository.GetEmployee().ConfigureAwait(false); + return result.Select(EmployeeConverter.ModelToDto); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesQuery.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesQuery.cs new file mode 100644 index 00000000..2d29ecb8 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesQuery.cs @@ -0,0 +1,6 @@ +using Devon4Net.Application.Dtos; +using Devon4Net.Infrastructure.MediatR.Query; + +namespace Devon4Net.Application.Features.Queries.GetAllEmployees; + +public record GetAllEmployeesQuery : QueryBase>; \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs new file mode 100644 index 00000000..1bef01c2 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs @@ -0,0 +1,22 @@ +using Devon4Net.Application.Dtos; +using Devon4Net.Application.Ports.Projectors; +using Devon4Net.Infrastructure.Common; +using MediatR; + +namespace Devon4Net.Application.Features.Queries.GetEmployeeById; + +public class GetEmployeeByIdHandler : IRequestHandler +{ + private readonly IEmployeeProjector _employeeProjector; + + public GetEmployeeByIdHandler(IEmployeeProjector employeeProjector) + { + _employeeProjector = employeeProjector; + } + + public async Task Handle(GetEmployeeByIdQuery request, CancellationToken cancellationToken) + { + Devon4NetLogger.Debug("Started GetAllEmployeesHandler"); + return await _employeeProjector.GetEmployeeById(request.Id); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs new file mode 100644 index 00000000..2cda535a --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs @@ -0,0 +1,6 @@ +using Devon4Net.Application.Dtos; +using Devon4Net.Infrastructure.MediatR.Query; + +namespace Devon4Net.Application.Features.Queries.GetEmployeeById; + +public record GetEmployeeByIdQuery(long Id) : QueryBase; \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/IEmployeeUoW.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/IEmployeeUoW.cs new file mode 100644 index 00000000..855b4732 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/IEmployeeUoW.cs @@ -0,0 +1,8 @@ +using Devon4Net.Infrastructure.UnitOfWork.UnitOfWork; + +namespace Devon4Net.Application.Ports; + +public interface IEmployeeUoW: IUnitOfWork +{ + +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs new file mode 100644 index 00000000..8d4afe3e --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs @@ -0,0 +1,8 @@ +using Devon4Net.Application.Dtos; + +namespace Devon4Net.Application.Ports.Projectors; + +public interface IEmployeeProjector +{ + public Task GetEmployeeById(long id); +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs new file mode 100644 index 00000000..78d01296 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs @@ -0,0 +1,41 @@ +using System.Linq.Expressions; +using Devon4Net.Domain.Entities; +using Devon4Net.Infrastructure.UnitOfWork.Repository; + +namespace Devon4Net.Application.Ports.Repositories; + +/// +/// EmployeeRepository interface +/// +public interface IEmployeeRepository : IRepository +{ + /// + /// GetEmployee + /// + /// + /// + Task> GetEmployee(Expression>? predicate = null); + + /// + /// GetEmployeeById + /// + /// + /// + Task GetEmployeeById(long id); + + /// + /// Create + /// + /// + /// + /// + /// + Task Create(string name, string surName, string mail); + + /// + /// DeleteEmployeeById + /// + /// + /// + Task DeleteEmployeeById(long id); +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Domain/Devon4Net.Domain.csproj b/source/Templates/CleanArchitecture/Devon4Net.Domain/Devon4Net.Domain.csproj new file mode 100644 index 00000000..c76aed46 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Domain/Devon4Net.Domain.csproj @@ -0,0 +1,12 @@ + + + + net6.0 + enable + + + + $(SuppressWarnings);CA2211;CA1822;CA1303 + + + diff --git a/source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs b/source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs new file mode 100644 index 00000000..a3857377 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs @@ -0,0 +1,27 @@ +namespace Devon4Net.Domain.Entities; + +/// +/// Entity class for Employee +/// +public class Employee +{ + /// + /// Id + /// + public long Id { get; set; } + + /// + /// Name + /// + public string Name { get; set; } + + /// + /// Surname + /// + public string Surname { get; set; } + + /// + /// mail + /// + public string Mail { get; set; } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs new file mode 100644 index 00000000..9e7ba5a8 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs @@ -0,0 +1,30 @@ +using Devon4Net.Application.Dtos; +using Devon4Net.Application.Exceptions; +using Devon4Net.Application.Ports.Projectors; +using Devon4Net.Domain.Entities; +using Devon4Net.Infrastructure.Persistence; +using Devon4Net.Infrastructure.UnitOfWork.Projector; + +namespace Devon4Net.Infrastructure.Adapters.Projectors; + +public class EmployeeProjector : Projector, IEmployeeProjector +{ + public EmployeeProjector(EmployeeContext context) : base(context) + { + } + + public async Task GetEmployeeById(long id) + { + var employeeDtos = await GetProjection((IQueryable employee) => employee + .Select(responseDto => new EmployeeDto + { + Name = responseDto.Name, + Surname = responseDto.Surname, + Mail = responseDto.Mail + }) + .Where(x => x.Id == id) + ); + + return employeeDtos.FirstOrDefault() ?? throw new EmployeeNotFoundException(); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs new file mode 100644 index 00000000..c7439249 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs @@ -0,0 +1,72 @@ +using System.Linq.Expressions; +using Devon4Net.Application.Ports.Repositories; +using Devon4Net.Domain.Entities; +using Devon4Net.Infrastructure.Common; +using Devon4Net.Infrastructure.Persistence; +using Devon4Net.Infrastructure.UnitOfWork.Repository; + +namespace Devon4Net.Infrastructure.Adapters.Repositories; + +public class EmployeeRepository : Repository, IEmployeeRepository +{ + /// + /// Constructor + /// + /// + public EmployeeRepository(EmployeeContext context) : base(context) + { + } + + /// + /// Get Employee method + /// + /// + /// + public Task> GetEmployee(Expression> predicate = null) + { + Devon4NetLogger.Debug("GetEmployee method from EmployeeRepository EmployeeService"); + return Get(predicate); + } + + /// + /// Gets the Employee by id + /// + /// + /// + public Task GetEmployeeById(long id) + { + Devon4NetLogger.Debug($"GetEmployeeById method from repository EmployeeService with value : {id}"); + return GetFirstOrDefault(t => t.Id == id); + } + + /// + /// Creates the Employee + /// + /// + /// + /// + /// + public Task Create(string name, string surName, string mail) + { + Devon4NetLogger.Debug($"SetEmployee method from repository EmployeeService with value : {name}"); + return Create(new Employee { Name = name, Surname = surName, Mail = mail }); + } + + /// + /// Deletes the Employee by id + /// + /// + /// + public async Task DeleteEmployeeById(long id) + { + Devon4NetLogger.Debug($"DeleteEmployeeById method from repository EmployeeService with value : {id}"); + var deleted = await Delete(t => t.Id == id).ConfigureAwait(false); + + if (deleted) + { + return id; + } + + throw new ArgumentException($"The Employee entity {id} has not been deleted."); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/AssemblyReference.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/AssemblyReference.cs new file mode 100644 index 00000000..6b89acf9 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/AssemblyReference.cs @@ -0,0 +1,3 @@ +namespace Devon4Net.Infrastructure; + +public sealed record AssemblyReference; \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Devon4Net.Infrastructure.csproj b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Devon4Net.Infrastructure.csproj new file mode 100644 index 00000000..d4e44e6c --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Devon4Net.Infrastructure.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + + + + + + + + diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs new file mode 100644 index 00000000..64aa2898 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs @@ -0,0 +1,44 @@ +using Devon4Net.Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Devon4Net.Infrastructure.Persistence; + +/// +/// Employee database context definition +/// +public class EmployeeContext : DbContext +{ + /// + /// Employee context definition + /// + /// + public EmployeeContext(DbContextOptions options) + : base(options) + { + } + + /// + /// Dbset + /// + public virtual DbSet Employee { get; set; } + + /// + /// Model rules definition + /// + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.Property(e => e.Name) + .IsRequired() + .HasMaxLength(255); + entity.Property(e => e.Surname) + .IsRequired() + .HasMaxLength(255); + entity.Property(e => e.Mail) + .IsRequired() + .HasMaxLength(255); + }); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj b/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj new file mode 100644 index 00000000..bdd1ae24 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/UnitTest1.cs b/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/UnitTest1.cs new file mode 100644 index 00000000..08ce9d82 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/UnitTest1.cs @@ -0,0 +1,15 @@ +using Xunit; + +namespace Devon4Net.IntegrationTests; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + //Arrange + //Act + //Assert + Assert.True(true); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs new file mode 100644 index 00000000..8827e3ec --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs @@ -0,0 +1,59 @@ +using Devon4Net.Domain.UnitOfWork.Enums; +using Devon4Net.Infrastructure.Common.Helpers; +using Devon4Net.Infrastructure.MediatR.Behaviors; +using FluentValidation; +using MediatR; +using System.Reflection; +using Devon4Net.Infrastructure.Persistence; +using Devon4Net.Infrastructure.UnitOfWork.Common; + +namespace Devon4Net.Presentation; + +public static class Configuration +{ + public static void SetupCustomDependencyInjection(this IServiceCollection services, IConfiguration configuration) + { + SetupDatabase(services, configuration); + SetupMediatRHandlers(services); + SetUpAutoRegisterClasses(services); + } + + private static void SetUpAutoRegisterClasses(IServiceCollection services) + { + List assemblyNamespaceToScan = new() + { + typeof(Application.AssemblyReference).Assembly, + typeof(Infrastructure.AssemblyReference).Assembly, + }; + + var suffixNamesToRegister = new List + { + "Projector", + "Repository", + "Service", + "QueryBuilder" + }; + + services.AutoRegisterClasses(assemblyNamespaceToScan, suffixNamesToRegister, ServiceLifetime.Transient); + } + + private static void SetupMediatRHandlers(IServiceCollection services) + { + var assembly = typeof(Application.AssemblyReference).Assembly; + + services.AddMediatR(assembly); + services.AddValidatorsFromAssembly(assembly); + + services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); + } + + /// + /// Setup here your database connections. + /// + /// + /// + private static void SetupDatabase(IServiceCollection services, IConfiguration configuration) + { + services.SetupDatabase(configuration, "Employee", DatabaseType.InMemory).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs new file mode 100644 index 00000000..7c6e1f21 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs @@ -0,0 +1,41 @@ +using Devon4Net.Application.Dtos; +using Devon4Net.Application.Features.Command.CreateEmployee; +using Devon4Net.Application.Features.Queries.GetAllEmployees; +using Devon4Net.Application.Features.Queries.GetEmployeeById; +using Devon4Net.Infrastructure.MediatR.Handler; +using Microsoft.AspNetCore.Mvc; + +namespace Devon4Net.Presentation.Controllers; + +[ApiController] +[Route("[controller]")] +public class EmployeeController : ControllerBase +{ + private readonly IMediatRHandler _mediator; + + public EmployeeController(IMediatRHandler mediator) + { + _mediator = mediator; + } + + [HttpPost("CreateEmployee")] + public async Task CreateEmployee([FromBody] EmployeeDto employeeDto) + { + var command = new CreateEmployeeCommand(employeeDto.Name, employeeDto.Surname, employeeDto.Mail); + return await _mediator.CommandAsync(command); + } + + [HttpGet("GetEmployees")] + public async Task> GetAllEmployees() + { + var query = new GetAllEmployeesQuery(); + return await _mediator.QueryAsync(query); + } + + [HttpGet("GetEmployeeById")] + public async Task GetAllEmployees([FromQuery] long id) + { + var query = new GetEmployeeByIdQuery(id); + return await _mediator.QueryAsync(query); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Devon4Net.Presentation.csproj b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Devon4Net.Presentation.csproj new file mode 100644 index 00000000..976b4b70 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Devon4Net.Presentation.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + + diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Program.cs b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Program.cs new file mode 100644 index 00000000..f02d00ac --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Program.cs @@ -0,0 +1,42 @@ +using Devon4Net.Infrastructure.Common.Application.ApplicationTypes.API; +using Devon4Net.Infrastructure.Common.Application.Middleware; +using Devon4Net.Infrastructure.Cors; +using Devon4Net.Infrastructure.Logger; +using Devon4Net.Infrastructure.MediatR; +using Devon4Net.Infrastructure.UnitOfWork; +using Devon4Net.Presentation; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container +builder.WebHost.InitializeDevonfwApi(builder.Host); +builder.Services.AddControllers(); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.SetupDevonfw(builder.Configuration); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Services.SetupLog(builder.Configuration); +builder.Services.SetupMiddleware(builder.Configuration); +builder.Services.SetupUnitOfWork(); +builder.Services.SetupMediatR(builder.Configuration); +builder.Services.SetupCustomDependencyInjection(builder.Configuration); +builder.Services.SetupCors(builder.Configuration); + +var app = builder.Build(); +app.SetupCors(); +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.SetupMiddleware(builder.Services); + +app.UseHttpsRedirection(); +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Properties/launchSettings.json b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Properties/launchSettings.json new file mode 100644 index 00000000..d1c57178 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Properties/launchSettings.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:39688", + "sslPort": 44302 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:8085" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:8085" + }, + "App1.Presentation": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7235;http://localhost:5263", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.Development.json b/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.Development.json new file mode 100644 index 00000000..1684214b --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.Development.json @@ -0,0 +1,138 @@ +{ + "devonfw": { + "UseDetailedErrorsKey": true, + "UseIIS": false, + "UseSwagger": true, + "UseXsrf": true, + "UseModelStateValidation": true, + "Environment": "Development", + "ForceUseHttpsRedirection": false, + "Kestrel": { + "UseHttps": false, + "HttpProtocol": "Http1", //Http1, Http2, Http1AndHttp2, none + "ApplicationPort": 8085, + "SslProtocol": "none", //Tls12, Tls13, none. For Https2 Tls12 is needed + "ExtraSettings": { + "KeepAliveTimeout": 120, //in seconds + "MaxConcurrentConnections": 100, + "MaxConcurrentUpgradedConnections": 100, + "MaxRequestBodySize": 28.6, //In MB. The default maximum request body size is 30,000,000 bytes, which is approximately 28.6 MB + "Http2MaxStreamsPerConnection": 100, + "Http2InitialConnectionWindowSize": 131072, // From 65,535 and less than 2^31 (2,147,483,648) + "Http2InitialStreamWindowSize": 98304, // From 65,535 and less than 2^31 (2,147,483,648) + "AllowSynchronousIO": true + } + } + }, + + "Certificates": { + "ServerCertificate": { + "Certificate": "", + "CertificatePassword": "" + }, + "ClientCertificate": { + "RequireClientCertificate": false, + "CheckCertificateRevocation": false, + "ClientCertificates": { + "Whitelist": [] + } + } + }, + + "ConnectionStrings": { + "Employee": "Employee" + }, + + "JWT": { + "Audience": "devon4Net", + "Issuer": "devon4Net", + "TokenExpirationTime": 60, + "ValidateIssuer": true, + "ValidateIssuerSigningKey": true, + "ValidateLifetime": true, + "RequireSignedTokens": true, + "RequireExpirationTime": true, + "RequireAudience": true, + "ClockSkew": 5, + "Security": { + "SecretKeyEncryptionAlgorithm": "", + //HmacSha256, HmacSha384, HmacSha512, HmacSha256Signature, HmacSha384Signature, HmacSha512Signature + "SecretKey": "", + "Certificate": "", + "CertificatePassword": "", + "CertificateEncryptionAlgorithm": "", + "RefreshTokenEncryptionAlgorithm": "" + //HmacSha256, HmacSha384, HmacSha512, HmacSha256Signature, HmacSha384Signature, HmacSha512Signature + } + }, + + "KillSwitch": { + "UseKillSwitch": false, + "EnableRequests": false, + "HttpStatusCode": 403 + }, + + "Serilog": { + "UseLogFile": true, + "UseSQLiteDb": true, + "UseGraylog": false, + "UseAOPTrace": false, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Error", + "Microsoft.EntityFrameworkCore": "Warning" + } + }, + "SqliteDatabase": "logs/log.db", + "LogFile": "logs/{0}_devonfw.log", + "SeqLogServerHost": "", + "GrayLog": { + "GrayLogHost": "127.0.0.1", + "GrayLogPort": "12201", + "GrayLogProtocol": "UDP", + "MaxUdpMessageSize": 8192 + } + }, + + "Cors": //[], //Empty array allows all origins with the policy "CorsPolicy" + [ //Comma separated Origins, Headers, ExposedHeaders and Methods + { + "CorsPolicy": "CorsPolicy", + "Origins": "http://localhost:4200,https://localhost:4200,http://localhost,https://localhost;http://localhost:8085,https://localhost:8085", + "Headers": "accept,content-type,origin,x-custom-header,authorization,cAppVersion,cStructure,cUser,cPostId", + "ExposedHeaders": "x-custom-header,custom-authorization", + "Methods": "GET,POST,HEAD,PUT,DELETE", + "AllowCredentials": true + } + ], + + "Swagger": { + "Version": "v1", + "Title": "devon4net API", + "Description": "devon4net API Contract", + "Terms": "https://www.devonfw.com/terms-of-use/", + "Contact": { + "Name": "devonfw", + "Email": "sample@mail.com", + "Url": "https://www.devonfw.com" + }, + "License": { + "Name": "devonfw - Terms of Use", + "Url": "https://www.devonfw.com/terms-of-use/" + }, + "Endpoint": { + "Name": "V1 Docs", + "Url": "/swagger/v1/swagger.json", + "UrlUi": "swagger", + "RouteTemplate": "swagger/v1/{documentName}/swagger.json" + } + }, + + "MediatR": { + "EnableMediatR": true, + "Backup": { + "UseLocalBackup": false, + } + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.json b/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.json new file mode 100644 index 00000000..84c0f344 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.json @@ -0,0 +1,3 @@ +{ + "Environment": "Development" +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/libman.json b/source/Templates/CleanArchitecture/Devon4Net.Presentation/libman.json new file mode 100644 index 00000000..ceee2710 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/libman.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "defaultProvider": "cdnjs", + "libraries": [] +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj b/source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj new file mode 100644 index 00000000..76ce160a --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Business/FileManagement/Services/FileService.cs b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Business/FileManagement/Services/FileService.cs index 352d3ff3..15fd9cd1 100644 --- a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Business/FileManagement/Services/FileService.cs +++ b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Business/FileManagement/Services/FileService.cs @@ -1,21 +1,16 @@ - -using Devon4Net.Domain.UnitOfWork.Service; -using Devon4Net.Domain.UnitOfWork.UnitOfWork; -using Devon4Net.Application.Kafka.Consumer.Domain.Database; -using Devon4Net.Application.Kafka.Consumer.Domain.RepositoryInterfaces; +using Devon4Net.Application.Kafka.Consumer.Domain.RepositoryInterfaces; using Devon4Net.Application.Kafka.Consumer.Business.FileManagement.Dto; using Devon4Net.Application.Kafka.Consumer.Business.FileManagement.Converters; namespace Devon4Net.Application.Kafka.Consumer.Business.FileManagement.Services { - - public class FileService: Service, IFileService + public class FileService: IFileService { private readonly IFileRepository _fileRepository; - public FileService(IUnitOfWork uoW) : base(uoW) + public FileService(IFileRepository fileRepository) { - _fileRepository = uoW.Repository(); + _fileRepository = fileRepository; } public DataPieceDto CreateFile(DataPieceDto dataPiece) diff --git a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Configuration/DIConfiguration.cs b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Configuration/DIConfiguration.cs index 4e142207..0e2fa125 100644 --- a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Configuration/DIConfiguration.cs +++ b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Configuration/DIConfiguration.cs @@ -1,6 +1,6 @@ using Devon4Net.Application.Kafka.Consumer.Domain.Database; -using Devon4Net.Domain.UnitOfWork.Common; using Devon4Net.Domain.UnitOfWork.Enums; +using Devon4Net.Infrastructure.UnitOfWork.Common; namespace Devon4Net.Application.Kafka.Consumer.Configuration { diff --git a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Data/Repositories/FileRepository.cs b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Data/Repositories/FileRepository.cs index bac7ae4e..afee1427 100644 --- a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Data/Repositories/FileRepository.cs +++ b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Data/Repositories/FileRepository.cs @@ -1,7 +1,7 @@ using Devon4Net.Application.Kafka.Consumer.Domain.Database; using Devon4Net.Application.Kafka.Consumer.Domain.Entities; using Devon4Net.Application.Kafka.Consumer.Domain.RepositoryInterfaces; -using Devon4Net.Domain.UnitOfWork.Repository; +using Devon4Net.Infrastructure.UnitOfWork.Repository; namespace Devon4Net.Application.Kafka.Consumer.Data.Repositories { diff --git a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Domain/RepositoryInterfaces/IFileRepository.cs b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Domain/RepositoryInterfaces/IFileRepository.cs index 8af60615..e6786ef3 100644 --- a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Domain/RepositoryInterfaces/IFileRepository.cs +++ b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Domain/RepositoryInterfaces/IFileRepository.cs @@ -1,5 +1,5 @@ using Devon4Net.Application.Kafka.Consumer.Domain.Entities; -using Devon4Net.Domain.UnitOfWork.Repository; +using Devon4Net.Infrastructure.UnitOfWork.Repository; namespace Devon4Net.Application.Kafka.Consumer.Domain.RepositoryInterfaces { diff --git a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Program.cs b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Program.cs index 058904f4..75f2fc0c 100644 --- a/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Program.cs +++ b/source/Templates/Kafka/Devon4Net.Application.Kafka.Consumer/Program.cs @@ -24,7 +24,7 @@ //UoW CONFIGURATION builder.Services.SetupDependencyInjection(builder.Configuration); -builder.Services.SetupUnitOfWork(typeof(DIConfiguration)); +builder.Services.SetupUnitOfWork(); //KAFKA CONFIGURATION builder.Services.SetupKafka(builder.Configuration); diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Service/EmployeeService.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Service/EmployeeService.cs index 038987ef..a867273e 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Service/EmployeeService.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Service/EmployeeService.cs @@ -1,11 +1,8 @@ using System.Linq.Expressions; -using Devon4Net.Domain.UnitOfWork.Service; -using Devon4Net.Domain.UnitOfWork.UnitOfWork; using Devon4Net.Infrastructure.Common; using Devon4Net.Application.WebAPI.Business.EmployeeManagement.Converters; using Devon4Net.Application.WebAPI.Business.EmployeeManagement.Dto; using Devon4Net.Application.WebAPI.Business.EmployeeManagement.Exceptions; -using Devon4Net.Application.WebAPI.Domain.Database; using Devon4Net.Application.WebAPI.Domain.Entities; using Devon4Net.Application.WebAPI.Domain.RepositoryInterfaces; @@ -14,17 +11,17 @@ namespace Devon4Net.Application.WebAPI.Business.EmployeeManagement.Service /// /// Employee service implementation /// - public class EmployeeService: Service, IEmployeeService + public class EmployeeService: IEmployeeService { private readonly IEmployeeRepository _employeeRepository; /// /// Constructor /// - /// - public EmployeeService(IUnitOfWork uoW) : base(uoW) + /// + public EmployeeService(IEmployeeRepository employeeRepository) { - _employeeRepository = uoW.Repository(); + _employeeRepository = employeeRepository; } /// diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Commands/CreateTodoCommand.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Commands/CreateTodoCommand.cs index ba601294..b2cb2f59 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Commands/CreateTodoCommand.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Commands/CreateTodoCommand.cs @@ -6,7 +6,7 @@ namespace Devon4Net.Application.WebAPI.Business.MediatRManagement.Commands /// /// THe command to create a TO-DO /// - public class CreateTodoCommand : CommandBase + public record CreateTodoCommand : CommandBase { /// /// Description diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Queries/GetTodoQuery.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Queries/GetTodoQuery.cs index a2b30ca8..55c8b1d5 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Queries/GetTodoQuery.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Queries/GetTodoQuery.cs @@ -5,7 +5,7 @@ namespace Devon4Net.Application.WebAPI.Business.MediatRManagement.Queries /// /// /// - public class GetTodoQuery : QueryBase + public record GetTodoQuery : QueryBase { diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/TodoManagement/Service/TodoService.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/TodoManagement/Service/TodoService.cs index 94b36d46..7aa14e94 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/TodoManagement/Service/TodoService.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/TodoManagement/Service/TodoService.cs @@ -1,10 +1,7 @@ using System.Linq.Expressions; -using Devon4Net.Domain.UnitOfWork.Service; -using Devon4Net.Domain.UnitOfWork.UnitOfWork; using Devon4Net.Infrastructure.Common; using Devon4Net.Application.WebAPI.Business.TodoManagement.Converters; using Devon4Net.Application.WebAPI.Business.TodoManagement.Dto; -using Devon4Net.Application.WebAPI.Domain.Database; using Devon4Net.Application.WebAPI.Domain.Entities; using Devon4Net.Application.WebAPI.Domain.RepositoryInterfaces; @@ -13,17 +10,17 @@ namespace Devon4Net.Application.WebAPI.Business.TodoManagement.Service /// /// Service implementation /// - public class TodoService: Service, ITodoService + public class TodoService: ITodoService { private readonly ITodoRepository _todoRepository; /// /// Constructor /// - /// - public TodoService(IUnitOfWork uoW) : base(uoW) + /// + public TodoService(ITodoRepository todoRepository) { - _todoRepository = uoW.Repository(); + _todoRepository = todoRepository; } /// diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Configuration/DevonConfiguration.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Configuration/DevonConfiguration.cs index 47e6615b..956969d2 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Configuration/DevonConfiguration.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Configuration/DevonConfiguration.cs @@ -1,4 +1,4 @@ -using Devon4Net.Application.WebAPI.Configuration; +using System.Reflection; using Devon4Net.Application.WebAPI.Business.EmployeeManagement.Dto; using Devon4Net.Application.WebAPI.Business.EmployeeManagement.Validators; using Devon4Net.Application.WebAPI.Business.MediatRManagement.Commands; @@ -15,19 +15,16 @@ using Devon4Net.Infrastructure.FluentValidation; using Devon4Net.Infrastructure.JWT.Common; using Devon4Net.Infrastructure.MediatR.Options; -using Devon4Net.Infrastructure.MediatR.Samples.Handler; -using Devon4Net.Infrastructure.MediatR.Samples.Model; -using Devon4Net.Infrastructure.MediatR.Samples.Query; using Devon4Net.Infrastructure.RabbitMQ.Options; using Devon4Net.Infrastructure.RabbitMQ.Samples.Handllers; using FluentValidation; -using FluentValidation.AspNetCore; using MediatR; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System.Security.Claims; +using Devon4Net.Infrastructure.Common.Helpers; +using Devon4Net.Infrastructure.MediatR.Behaviors; using Devon4Net.Infrastructure.RabbitMQ; +using Devon4Net.Infrastructure.UnitOfWork.Common; namespace Devon4Net.Application.WebAPI.Configuration { @@ -48,6 +45,7 @@ public static class DevonConfiguration public static void SetupCustomDependencyInjection(this IServiceCollection services, IConfiguration configuration) { SetupDatabase(services, configuration); + SetUpAutoRegisterClasses(services); SetupJwtPolicies(services); SetupFluentValidators(services); @@ -66,6 +64,24 @@ public static void SetupCustomDependencyInjection(this IServiceCollection servic SetupMediatRHandlers(services); } } + + private static void SetUpAutoRegisterClasses(IServiceCollection services) + { + List assemblyNamespaceToScan = new() + { + Assembly.GetExecutingAssembly(), + }; + + var suffixNamesToRegister = new List + { + "Projector", + "Repository", + "Service", + "QueryBuilder" + }; + + services.AutoRegisterClasses(assemblyNamespaceToScan, suffixNamesToRegister, ServiceLifetime.Transient); + } private static void SetupRabbitHandlers(IServiceCollection services) { @@ -75,9 +91,12 @@ private static void SetupRabbitHandlers(IServiceCollection services) private static void SetupMediatRHandlers(IServiceCollection services) { - services.AddTransient(typeof(IRequestHandler), typeof(GetUserhandler)); - services.AddTransient(typeof(IRequestHandler), typeof(GetTodoHandler)); - services.AddTransient(typeof(IRequestHandler), typeof(CreateTodoHandler)); + var assembly = Assembly.GetExecutingAssembly(); + + services.AddMediatR(assembly); + services.AddValidatorsFromAssembly(assembly); + + services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); } private static void SetupFluentValidators(IServiceCollection services) diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/EmployeeRepository.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/EmployeeRepository.cs index 662cd7f8..07c6d370 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/EmployeeRepository.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/EmployeeRepository.cs @@ -1,9 +1,9 @@ using System.Linq.Expressions; -using Devon4Net.Domain.UnitOfWork.Repository; using Devon4Net.Infrastructure.Common; using Devon4Net.Application.WebAPI.Domain.Database; using Devon4Net.Application.WebAPI.Domain.Entities; using Devon4Net.Application.WebAPI.Domain.RepositoryInterfaces; +using Devon4Net.Infrastructure.UnitOfWork.Repository; namespace Devon4Net.Application.WebAPI.Data.Repositories { diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/TodoRepository.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/TodoRepository.cs index cf2db91d..615aa27a 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/TodoRepository.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Data/Repositories/TodoRepository.cs @@ -1,10 +1,10 @@ using System.Linq.Expressions; -using Devon4Net.Domain.UnitOfWork.Repository; using Devon4Net.Infrastructure.Common; using Devon4Net.Application.WebAPI.Business.TodoManagement.Validators; using Devon4Net.Application.WebAPI.Domain.Database; using Devon4Net.Application.WebAPI.Domain.Entities; using Devon4Net.Application.WebAPI.Domain.RepositoryInterfaces; +using Devon4Net.Infrastructure.UnitOfWork.Repository; namespace Devon4Net.Application.WebAPI.Data.Repositories { diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/IEmployeeRepository.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/IEmployeeRepository.cs index 51c42f02..e9a17f38 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/IEmployeeRepository.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/IEmployeeRepository.cs @@ -1,6 +1,6 @@ using System.Linq.Expressions; -using Devon4Net.Domain.UnitOfWork.Repository; using Devon4Net.Application.WebAPI.Domain.Entities; +using Devon4Net.Infrastructure.UnitOfWork.Repository; namespace Devon4Net.Application.WebAPI.Domain.RepositoryInterfaces { diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/ITodoRepository.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/ITodoRepository.cs index 5966defb..a2bab38a 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/ITodoRepository.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Domain/RepositoryInterfaces/ITodoRepository.cs @@ -1,6 +1,6 @@ using System.Linq.Expressions; -using Devon4Net.Domain.UnitOfWork.Repository; using Devon4Net.Application.WebAPI.Domain.Entities; +using Devon4Net.Infrastructure.UnitOfWork.Repository; namespace Devon4Net.Application.WebAPI.Domain.RepositoryInterfaces { diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Program.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Program.cs index 7703887f..af683584 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Program.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Program.cs @@ -27,7 +27,7 @@ builder.Services.SetupCircuitBreaker(builder.Configuration); builder.Services.SetupCors(builder.Configuration); builder.Services.SetupJwt(builder.Configuration); -builder.Services.SetupUnitOfWork(typeof(Program)); +builder.Services.SetupUnitOfWork(); builder.Services.SetupLiteDb(builder.Configuration); builder.Services.SetupRabbitMq(builder.Configuration); builder.Services.SetupMediatR(builder.Configuration); diff --git a/source/devon4net.sln b/source/devon4net.sln index d3604b47..2a5a8697 100644 --- a/source/devon4net.sln +++ b/source/devon4net.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33027.239 @@ -110,6 +109,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Infrastructure.Az EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Infrastructure.Azure.Configuration", "Modules\Devon4Net.Infrastructure.Azure.Configuration\Devon4Net.Infrastructure.Azure.Configuration.csproj", "{A82A163B-89B5-4989-B6D7-17D2BC9B14CC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CleanArchitecture", "CleanArchitecture", "{4EC816D6-3F55-41AD-B805-955F251C03EB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Application", "Templates\CleanArchitecture\Devon4Net.Application\Devon4Net.Application.csproj", "{16D8E11F-7734-4B69-92E8-F1CD960A2C38}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Domain", "Templates\CleanArchitecture\Devon4Net.Domain\Devon4Net.Domain.csproj", "{5C1E97EE-D116-4237-A86C-496E123F429D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Infrastructure", "Templates\CleanArchitecture\Devon4Net.Infrastructure\Devon4Net.Infrastructure.csproj", "{A65EBB59-D598-46A0-B80C-0840D97E3A69}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.IntegrationTests", "Templates\CleanArchitecture\Devon4Net.IntegrationTests\Devon4Net.IntegrationTests.csproj", "{727B7E51-F913-4196-99E3-B9D01CDBA2E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Presentation", "Templates\CleanArchitecture\Devon4Net.Presentation\Devon4Net.Presentation.csproj", "{93305326-BB9C-4F7D-B6FA-41B78100C4E4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.UnitTests", "Templates\CleanArchitecture\Devon4Net.UnitTests\Devon4Net.UnitTests.csproj", "{4C685698-3083-4BB0-BFF4-6DA152115D2C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B55F2379-2A06-4761-B1E7-DAD1D14A93BC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3A21B19A-CDDB-4849-87F2-EBC575EA889F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -422,6 +439,54 @@ Global {A82A163B-89B5-4989-B6D7-17D2BC9B14CC}.Release|Any CPU.Build.0 = Release|Any CPU {A82A163B-89B5-4989-B6D7-17D2BC9B14CC}.Release|x64.ActiveCfg = Release|Any CPU {A82A163B-89B5-4989-B6D7-17D2BC9B14CC}.Release|x64.Build.0 = Release|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Debug|x64.ActiveCfg = Debug|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Debug|x64.Build.0 = Debug|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Release|Any CPU.Build.0 = Release|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Release|x64.ActiveCfg = Release|Any CPU + {16D8E11F-7734-4B69-92E8-F1CD960A2C38}.Release|x64.Build.0 = Release|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Debug|x64.ActiveCfg = Debug|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Debug|x64.Build.0 = Debug|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Release|Any CPU.Build.0 = Release|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Release|x64.ActiveCfg = Release|Any CPU + {5C1E97EE-D116-4237-A86C-496E123F429D}.Release|x64.Build.0 = Release|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Debug|x64.ActiveCfg = Debug|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Debug|x64.Build.0 = Debug|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Release|Any CPU.Build.0 = Release|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Release|x64.ActiveCfg = Release|Any CPU + {A65EBB59-D598-46A0-B80C-0840D97E3A69}.Release|x64.Build.0 = Release|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Debug|x64.Build.0 = Debug|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Release|Any CPU.Build.0 = Release|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Release|x64.ActiveCfg = Release|Any CPU + {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Release|x64.Build.0 = Release|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|x64.ActiveCfg = Debug|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|x64.Build.0 = Debug|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|Any CPU.Build.0 = Release|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|x64.ActiveCfg = Release|Any CPU + {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|x64.Build.0 = Release|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Debug|x64.ActiveCfg = Debug|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Debug|x64.Build.0 = Debug|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Release|Any CPU.Build.0 = Release|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Release|x64.ActiveCfg = Release|Any CPU + {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -475,6 +540,15 @@ Global {2A6DA422-D18A-4D4F-BA31-31149F32DFC2} = {60557548-CF99-4CCD-A25D-27150705E43B} {7544381B-3306-43F8-AFE7-B93DDB4C3F7E} = {60557548-CF99-4CCD-A25D-27150705E43B} {A82A163B-89B5-4989-B6D7-17D2BC9B14CC} = {60557548-CF99-4CCD-A25D-27150705E43B} + {4EC816D6-3F55-41AD-B805-955F251C03EB} = {DB733C6F-054A-443A-B3E2-0EEC7D2BCB4F} + {16D8E11F-7734-4B69-92E8-F1CD960A2C38} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} + {5C1E97EE-D116-4237-A86C-496E123F429D} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} + {A65EBB59-D598-46A0-B80C-0840D97E3A69} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} + {727B7E51-F913-4196-99E3-B9D01CDBA2E2} = {3A21B19A-CDDB-4849-87F2-EBC575EA889F} + {93305326-BB9C-4F7D-B6FA-41B78100C4E4} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} + {4C685698-3083-4BB0-BFF4-6DA152115D2C} = {3A21B19A-CDDB-4849-87F2-EBC575EA889F} + {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} = {4EC816D6-3F55-41AD-B805-955F251C03EB} + {3A21B19A-CDDB-4849-87F2-EBC575EA889F} = {4EC816D6-3F55-41AD-B805-955F251C03EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E2CBC81F-F2EE-4BD5-B567-AAF8057444D1} From 33a5092b38d128a19028f68ba336ddc5ae496484 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Fri, 18 Aug 2023 09:56:15 +0200 Subject: [PATCH 02/21] [Add] Created Implementation for EmployeeUoW --- .../Devon4Net.Infrastructure/Adapters/EmployeeUoW.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/EmployeeUoW.cs diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/EmployeeUoW.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/EmployeeUoW.cs new file mode 100644 index 00000000..be84d2a4 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/EmployeeUoW.cs @@ -0,0 +1,12 @@ +using Devon4Net.Application.Ports; +using Devon4Net.Infrastructure.Persistence; +using Devon4Net.Infrastructure.UnitOfWork.UnitOfWork; + +namespace Devon4Net.Infrastructure.Adapters; + +public class EmployeeUoW : UnitOfWork, IEmployeeUoW +{ + public EmployeeUoW(EmployeeContext context) : base(context) + { + } +} \ No newline at end of file From a83436dee3959d83c3dff8027a3931b02b58e411 Mon Sep 17 00:00:00 2001 From: JosepFe Date: Fri, 18 Aug 2023 10:01:25 +0200 Subject: [PATCH 03/21] Create dotnet.yml action --- .github/workflows/dotnet.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/dotnet.yml diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 00000000..f405f824 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,28 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET + +on: + push: + branches: [ "develop", "clean_architecture_template" ] + pull_request: + branches: [ "develop" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal From 24d6499fb1ac5b803833b383ed3565f8af111d59 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Wed, 6 Sep 2023 16:21:16 +0200 Subject: [PATCH 04/21] Update projectos to allow pagination --- .../Projector/IProjector.cs | 8 +++-- .../Projector/Projector.cs | 11 ++++--- .../Properties/launchSettings.json | 2 +- .../GetAllEmployees/GetAllEmployeesHandler.cs | 16 +++++----- .../Ports/Projectors/IEmployeeProjector.cs | 2 ++ .../Adapters/Projectors/EmployeeProjector.cs | 31 +++++++++++++------ .../Controllers/EmployeeController.cs | 12 +++---- 7 files changed, 50 insertions(+), 32 deletions(-) diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs index 51ba7c25..2e4ab351 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/IProjector.cs @@ -7,12 +7,14 @@ public interface IProjector /// /// Get Projection from entity /// - /// - /// /// /// + /// + /// + /// /// Task> GetProjection( Func, IQueryable> projection, - Expression> filter = null) where TEntity : class; + int? page = null, + int? pageSize = null) where TEntity : class; } \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs index 8474cabe..972a1f2c 100644 --- a/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs +++ b/source/Modules/Devon4Net.Infrastructure.UnitOfWork/Projector/Projector.cs @@ -16,20 +16,23 @@ protected Projector(DbContext context) /// Get Projection from entity /// /// - /// + /// + /// /// /// /// public async Task> GetProjection( Func, IQueryable> projection, - Expression> filter = null) where TEntity : class + int? page = null, + int? pageSize = null) where TEntity : class { var dbSet = _context.Set(); var query = dbSet.AsQueryable(); - if (filter != null) + + if (page != null && pageSize != null) { - query = query.Where(filter); + query = query.Skip((int)(page - 1) * (int)pageSize).Take((int)pageSize); } var projectionQuery = projection(query); diff --git a/source/Templates/AWS/Devon4Net.Application.WebAPI.AwsServerless/Properties/launchSettings.json b/source/Templates/AWS/Devon4Net.Application.WebAPI.AwsServerless/Properties/launchSettings.json index 6ace25db..13c0272b 100644 --- a/source/Templates/AWS/Devon4Net.Application.WebAPI.AwsServerless/Properties/launchSettings.json +++ b/source/Templates/AWS/Devon4Net.Application.WebAPI.AwsServerless/Properties/launchSettings.json @@ -30,7 +30,7 @@ "Mock Lambda Test Tool": { "commandName": "Executable", "commandLineArgs": "--port 5050", - "workingDirectory": ".\\bin\\$(Configuration)\\net7.0", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" } } diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs index 8cc4d8ce..8e655590 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetAllEmployees/GetAllEmployeesHandler.cs @@ -1,6 +1,5 @@ -using Devon4Net.Application.Converters; -using Devon4Net.Application.Dtos; -using Devon4Net.Application.Ports.Repositories; +using Devon4Net.Application.Dtos; +using Devon4Net.Application.Ports.Projectors; using Devon4Net.Infrastructure.Common; using MediatR; @@ -8,17 +7,16 @@ namespace Devon4Net.Application.Features.Queries.GetAllEmployees; public class GetAllEmployeesHandler : IRequestHandler> { - private readonly IEmployeeRepository _employeeRepository; + private readonly IEmployeeProjector _employeeProjector; - public GetAllEmployeesHandler(IEmployeeRepository employeeRepository) + public GetAllEmployeesHandler(IEmployeeProjector employeeProjector) { - _employeeRepository = employeeRepository; + _employeeProjector = employeeProjector; } - public async Task> Handle(GetAllEmployeesQuery request, CancellationToken cancellationToken) + public Task> Handle(GetAllEmployeesQuery request, CancellationToken cancellationToken) { Devon4NetLogger.Debug("Started GetAllEmployeesHandler"); - var result = await _employeeRepository.GetEmployee().ConfigureAwait(false); - return result.Select(EmployeeConverter.ModelToDto); + return _employeeProjector.GetEmployees(); } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs index 8d4afe3e..a4f9738a 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs @@ -5,4 +5,6 @@ namespace Devon4Net.Application.Ports.Projectors; public interface IEmployeeProjector { public Task GetEmployeeById(long id); + + public Task> GetEmployees(); } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs index 9e7ba5a8..4b2264b9 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs @@ -15,16 +15,29 @@ public EmployeeProjector(EmployeeContext context) : base(context) public async Task GetEmployeeById(long id) { - var employeeDtos = await GetProjection((IQueryable employee) => employee - .Select(responseDto => new EmployeeDto - { - Name = responseDto.Name, - Surname = responseDto.Surname, - Mail = responseDto.Mail - }) - .Where(x => x.Id == id) - ); + var query = (IQueryable employeeQuery) => employeeQuery + .Select(employee => new EmployeeDto + { + Name = employee.Name, + Surname = employee.Surname, + Mail = employee.Mail + }) + .Where(employee => employee.Id == id); + + var employeeDtos = await GetProjection(query); return employeeDtos.FirstOrDefault() ?? throw new EmployeeNotFoundException(); } + + public Task> GetEmployees() + { + var query = (IQueryable employeeQuery) => employeeQuery.Select(employee => new EmployeeDto + { + Name = employee.Name, + Surname = employee.Surname, + Mail = employee.Mail + }); + + return GetProjection(query); + } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs index 7c6e1f21..f271654d 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Controllers/EmployeeController.cs @@ -19,23 +19,23 @@ public EmployeeController(IMediatRHandler mediator) } [HttpPost("CreateEmployee")] - public async Task CreateEmployee([FromBody] EmployeeDto employeeDto) + public Task CreateEmployee([FromBody] EmployeeDto employeeDto) { var command = new CreateEmployeeCommand(employeeDto.Name, employeeDto.Surname, employeeDto.Mail); - return await _mediator.CommandAsync(command); + return _mediator.CommandAsync(command); } [HttpGet("GetEmployees")] - public async Task> GetAllEmployees() + public Task> GetAllEmployees() { var query = new GetAllEmployeesQuery(); - return await _mediator.QueryAsync(query); + return _mediator.QueryAsync(query); } [HttpGet("GetEmployeeById")] - public async Task GetAllEmployees([FromQuery] long id) + public Task GetAllEmployees([FromQuery] long id) { var query = new GetEmployeeByIdQuery(id); - return await _mediator.QueryAsync(query); + return _mediator.QueryAsync(query); } } \ No newline at end of file From 940e4ad0705f37a129e96e3dc16cea071f673a1c Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Wed, 6 Sep 2023 16:23:54 +0200 Subject: [PATCH 05/21] Remove unused workflow --- .github/workflows/dotnet.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/dotnet.yml diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml deleted file mode 100644 index f405f824..00000000 --- a/.github/workflows/dotnet.yml +++ /dev/null @@ -1,28 +0,0 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net - -name: .NET - -on: - push: - branches: [ "develop", "clean_architecture_template" ] - pull_request: - branches: [ "develop" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 6.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal From 4743173f9566caddef6ca1a1ab5674548509ee47 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Thu, 7 Sep 2023 09:14:35 +0200 Subject: [PATCH 06/21] Remove redundant if condition --- .../MediatRConfiguration.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs index e987f491..805e704c 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/MediatRConfiguration.cs @@ -1,8 +1,5 @@ -using System.Reflection; -using Devon4Net.Infrastructure.Common.Constants; +using Devon4Net.Infrastructure.Common.Constants; using Devon4Net.Infrastructure.Common.Handlers; -using Devon4Net.Infrastructure.Common.Helpers; -using Devon4Net.Infrastructure.Common.Helpers.Interfaces; using Devon4Net.Infrastructure.LiteDb.LiteDb; using Devon4Net.Infrastructure.LiteDb.Repository; using Devon4Net.Infrastructure.Common; @@ -12,7 +9,6 @@ using Devon4Net.Infrastructure.MediatR.Domain.ServiceInterfaces; using Devon4Net.Infrastructure.MediatR.Handler; using Devon4Net.Infrastructure.MediatR.Options; -using MediatR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -26,17 +22,16 @@ public static void SetupMediatR(this IServiceCollection services, IConfiguration if (mediatROptions?.EnableMediatR != true) return; services.AddTransient(typeof(IMediatRHandler), typeof(MediatRHandler)); - if(mediatROptions?.Backup.UseLocalBackup != true) return; - SetupMediatRBackupLocalDatabase(ref services, ref mediatROptions); + if (mediatROptions.Backup?.UseLocalBackup != true) return; + SetupMediatRBackupLocalDatabase(ref services); } - private static void SetupMediatRBackupLocalDatabase(ref IServiceCollection services, ref MediatROptions mediatROptions) + private static void SetupMediatRBackupLocalDatabase(ref IServiceCollection services) { services.AddTransient(typeof(ILiteDbRepository), typeof(LiteDbRepository)); services.AddTransient(typeof(IMediatRBackupService), typeof(MediatRBackupService)); - + Devon4NetLogger.Information("Please setup your database in order to have the RabbitMq messaging backup feature"); - if (mediatROptions.Backup?.UseLocalBackup != true) return; Devon4NetLogger.Information("RabbitMq messaging backup feature is going to be used via LiteDb"); services.AddSingleton(); From 9c10d4cba53747769525f27281c94cf325f6de74 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Thu, 7 Sep 2023 09:53:27 +0200 Subject: [PATCH 07/21] Refactor code to improve sonar --- .../Middleware/Logs/LoggerMiddleware.cs | 17 +++++------ .../Command/CommandBase.cs | 2 +- .../Query/QueryBase.cs | 2 +- .../Exceptions/EmployeeNotFoundException.cs | 2 +- source/nuget.sh | 30 +++++++++++++++++++ 5 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 source/nuget.sh diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs index 3f723391..cd052e9c 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs @@ -27,17 +27,16 @@ public async Task Invoke(HttpContext context, IRecyclableMemoryHelper recyclable var originalResponseBody = context.Response.Body; context.Response.Body = bodyReader; - + Devon4NetLogger.Information($"REQUEST STARTED {context.TraceIdentifier} | HttpMethod: {context.Request.Method} | Path: {context.Request.Path} "); await _next(context).ConfigureAwait(false); bodyReader.Seek(0, SeekOrigin.Begin); await bodyReader.CopyToAsync(originalResponseBody).ConfigureAwait(false); - if (Log.IsEnabled(LogEventLevel.Information)) { - if (context.Response.StatusCode is < StatusCodes.Status200OK or > StatusCodes.Status300MultipleChoices) Devon4NetLogger.Information(await HandleResponseStream(context.TraceIdentifier, context?.Request?.Body, RequestBodyEntity).ConfigureAwait(false)); + if (context.Response.StatusCode is < StatusCodes.Status200OK or > StatusCodes.Status300MultipleChoices) Devon4NetLogger.Information(await HandleResponseStream(context.TraceIdentifier, context.Request?.Body, RequestBodyEntity).ConfigureAwait(false)); Devon4NetLogger.Information(await HandleResponseStream(context.TraceIdentifier, context.Response.Body, ResponseBodyEntity, true).ConfigureAwait(false)); Devon4NetLogger.Information($"REQUEST FINISHED {context.TraceIdentifier} | Status Code: {context.Response.StatusCode}"); } @@ -49,7 +48,7 @@ public async Task Invoke(HttpContext context, IRecyclableMemoryHelper recyclable } else { - Devon4NetLogger.Debug(await HandleResponseStream(context.TraceIdentifier, context?.Request?.Body, RequestBodyEntity).ConfigureAwait(false)); + Devon4NetLogger.Debug(await HandleResponseStream(context.TraceIdentifier, context.Request?.Body, RequestBodyEntity).ConfigureAwait(false)); Devon4NetLogger.Debug(await HandleResponseStream(context.TraceIdentifier, context.Response.Body, ResponseBodyEntity, true).ConfigureAwait(false)); Devon4NetLogger.Debug($"REQUEST FINISHED {context.TraceIdentifier} | Status Code: {context.Response.StatusCode}"); } @@ -64,10 +63,10 @@ public async Task Invoke(HttpContext context, IRecyclableMemoryHelper recyclable private static async Task HandleResponseStream(string identifier, Stream bodyStream, string entityDisclaimer, bool trimBody = false) { var stringBuilder = new StringBuilder(); - - stringBuilder.Append($"REQUEST {identifier} {entityDisclaimer}: "); - - if (bodyStream?.CanRead == false) + + stringBuilder.Append("REQUEST ").Append(identifier).Append(' ').Append(entityDisclaimer).Append(": "); + + if (bodyStream == null || !(bool)bodyStream?.CanRead) { stringBuilder.Append("No data found."); return stringBuilder.ToString(); @@ -85,7 +84,7 @@ private static async Task HandleResponseStream(string identifier, Stream else { var text = await bodyReader.ReadToEndAsync().ConfigureAwait(false); - if(string.IsNullOrWhiteSpace(text)) return default; + if (string.IsNullOrWhiteSpace(text)) return default; stringBuilder.Append(text.Replace("\n", "")); } diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs index 1d1da3f9..ab7f5c3d 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs @@ -2,7 +2,7 @@ namespace Devon4Net.Infrastructure.MediatR.Command { - public record CommandBase : ActionBase where T : class + public abstract record CommandBase : ActionBase where T : class { } } \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs index 4199141b..54c9146e 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs @@ -2,7 +2,7 @@ namespace Devon4Net.Infrastructure.MediatR.Query { - public record QueryBase : ActionBase where T : class + public abstract record QueryBase : ActionBase where T : class { } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs index 2bebddb5..74d5fc38 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs @@ -54,4 +54,4 @@ protected EmployeeNotFoundException(System.Runtime.Serialization.SerializationIn { } } -} +} \ No newline at end of file diff --git a/source/nuget.sh b/source/nuget.sh new file mode 100644 index 00000000..f036f56e --- /dev/null +++ b/source/nuget.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Directory where the NuGet packages will be stored +nugets_directory="$(pwd)/nugets" + +# Create the nugets directory if it doesn't exist +mkdir -p "$nugets_directory" + +# Ask the user for the version input +read -p "Enter the new version: " new_version + +# Find all csproj files and build/pack their projects +find . -type f -name "*.csproj" | while read -r csproj_file; do + echo "Processing project: $csproj_file" + + # Update the version in the csproj file + sed -i "s#.*#$new_version#g" "$csproj_file" + + # Get the directory of the csproj file + project_dir=$(dirname "$csproj_file") + + # Build the project + dotnet build --configuration Release "$project_dir" + + # Pack the project + dotnet pack --configuration Release "$project_dir" + + # Move the generated nupkg files to the nugets directory + mv "$project_dir"/bin/Release/*.nupkg "$nugets_directory" +done \ No newline at end of file From c478509122ad964be80dacd2a5a8acb2b585fb9c Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Thu, 7 Sep 2023 10:12:04 +0200 Subject: [PATCH 08/21] Sonar for EmployeeNotFoundException --- .../Middleware/Logs/LoggerMiddleware.cs | 2 +- .../Exceptions/EmployeeNotFoundException.cs | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs index cd052e9c..a8186257 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Logs/LoggerMiddleware.cs @@ -66,7 +66,7 @@ private static async Task HandleResponseStream(string identifier, Stream stringBuilder.Append("REQUEST ").Append(identifier).Append(' ').Append(entityDisclaimer).Append(": "); - if (bodyStream == null || !(bool)bodyStream?.CanRead) + if (bodyStream == null || !bodyStream.CanRead) { stringBuilder.Append("No data found."); return stringBuilder.ToString(); diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs index 74d5fc38..ece2bb84 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs @@ -1,5 +1,6 @@ using Devon4Net.Infrastructure.Common.Exceptions; using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Application.Exceptions { @@ -49,9 +50,28 @@ public EmployeeNotFoundException(string message, Exception innerException) : bas /// /// /// - protected EmployeeNotFoundException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) - : base(serializationInfo, streamingContext) + protected EmployeeNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base(serializationInfo.GetString("Message")) { } + + /// + /// Implements the ISerializable interface for custom serialization. + /// + /// The SerializationInfo that holds the serialized object data about the exception being thrown. + /// The StreamingContext that contains contextual information about the source or destination. + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info == null) + { + throw new ArgumentNullException(nameof(info)); + } + + // Serialize the message property + info.AddValue("Message", this.Message); + + // Call the base class implementation to save any other data that needs to be serialized. + base.GetObjectData(info, context); + } } } \ No newline at end of file From 9e12b34eacfdb4aa9e39da546ba09bca901ef62e Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Thu, 7 Sep 2023 10:41:21 +0200 Subject: [PATCH 09/21] Sonar improve for EmployeeNotFoundException --- .../Exceptions/EmployeeNotFoundException.cs | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs index ece2bb84..754ddd50 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs @@ -5,7 +5,7 @@ namespace Devon4Net.Application.Exceptions { /// - /// Custom exception EmployeeNotFoundException + /// Custom exception AdminNotFoundException /// [Serializable] public class EmployeeNotFoundException : Exception, IWebApiException @@ -21,57 +21,55 @@ public class EmployeeNotFoundException : Exception, IWebApiException public bool ShowMessage => true; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with a specified error message. /// - public EmployeeNotFoundException() + /// The message that describes the error. + public EmployeeNotFoundException(string message) + : base(message) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that caused this exception. /// /// The message that describes the error. - public EmployeeNotFoundException(string message) - : base(message) + /// The exception that caused this exception. + public EmployeeNotFoundException(string message, Exception innerException) + : base(message, innerException) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with serialized data. /// - /// - /// - public EmployeeNotFoundException(string message, Exception innerException) : base(message, innerException) + /// The that holds the serialized object data. + /// The that contains contextual information about the source or destination. + protected EmployeeNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) { } - /// - /// Initializes a new instance of the class. - /// - /// - /// - protected EmployeeNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) - : base(serializationInfo.GetString("Message")) + public EmployeeNotFoundException() : base() { } /// - /// Implements the ISerializable interface for custom serialization. + /// GetObjectData is called during serialization to save the exception object's data. /// - /// The SerializationInfo that holds the serialized object data about the exception being thrown. - /// The StreamingContext that contains contextual information about the source or destination. + /// The that holds the serialized object data. + /// The that contains contextual information about the source or destination. public override void GetObjectData(SerializationInfo info, StreamingContext context) { - if (info == null) + if (info != null) { - throw new ArgumentNullException(nameof(info)); + // Call the base class to save its data + base.GetObjectData(info, context); + } + else + { + throw new ArgumentNullException(nameof(info), "SerializationInfo must not be null."); } - - // Serialize the message property - info.AddValue("Message", this.Message); - - // Call the base class implementation to save any other data that needs to be serialized. - base.GetObjectData(info, context); } } } \ No newline at end of file From 01e3c79c55d15c52bb667fc2f7ce6443918752d9 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Thu, 7 Sep 2023 11:01:24 +0200 Subject: [PATCH 10/21] EmployeeNotFoundException sonar --- .../Exceptions/EmployeeNotFoundException.cs | 35 +++++-------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs index 754ddd50..dd7d12c6 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs @@ -5,7 +5,7 @@ namespace Devon4Net.Application.Exceptions { /// - /// Custom exception AdminNotFoundException + /// Custom exception EmployeeNotFoundException /// [Serializable] public class EmployeeNotFoundException : Exception, IWebApiException @@ -20,6 +20,10 @@ public class EmployeeNotFoundException : Exception, IWebApiException /// public bool ShowMessage => true; + public EmployeeNotFoundException() + { + } + /// /// Initializes a new instance of the class with a specified error message. /// @@ -43,33 +47,10 @@ public EmployeeNotFoundException(string message, Exception innerException) /// /// Initializes a new instance of the class with serialized data. /// - /// The that holds the serialized object data. - /// The that contains contextual information about the source or destination. - protected EmployeeNotFoundException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - - public EmployeeNotFoundException() : base() - { - } - - /// - /// GetObjectData is called during serialization to save the exception object's data. - /// - /// The that holds the serialized object data. - /// The that contains contextual information about the source or destination. - public override void GetObjectData(SerializationInfo info, StreamingContext context) + /// + /// + protected EmployeeNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { - if (info != null) - { - // Call the base class to save its data - base.GetObjectData(info, context); - } - else - { - throw new ArgumentNullException(nameof(info), "SerializationInfo must not be null."); - } } } } \ No newline at end of file From 23a758c1d1f79c485d22313eea66a9e25be80fe6 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Thu, 7 Sep 2023 11:11:43 +0200 Subject: [PATCH 11/21] Comand base removed body --- .../Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs | 4 +--- .../Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs index ab7f5c3d..dbb5c7fa 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Command/CommandBase.cs @@ -2,7 +2,5 @@ namespace Devon4Net.Infrastructure.MediatR.Command { - public abstract record CommandBase : ActionBase where T : class - { - } + public abstract record CommandBase : ActionBase where T : class; } \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs b/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs index 54c9146e..d749a862 100644 --- a/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs +++ b/source/Modules/Devon4Net.Infrastructure.MediatR/Query/QueryBase.cs @@ -2,7 +2,5 @@ namespace Devon4Net.Infrastructure.MediatR.Query { - public abstract record QueryBase : ActionBase where T : class - { - } + public abstract record QueryBase : ActionBase where T : class; } \ No newline at end of file From 3da11d2b86ff2ae47aa62f36391bad8b85cb738d Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Thu, 7 Sep 2023 11:31:34 +0200 Subject: [PATCH 12/21] Cleaning code --- .../Converters/EmployeeConverter.cs | 41 +++++---- .../Devon4Net.Application/Dtos/EmployeeDto.cs | 8 +- .../Exceptions/EmployeeNotFoundException.cs | 83 +++++++++---------- .../CreateEmployee/CreateEmployeeCommand.cs | 7 +- .../CreateEmployeeCommandValidation.cs | 15 ++-- .../Persistence/EmployeeContext.cs | 2 +- .../Devon4Net.Presentation/Configuration.cs | 2 +- 7 files changed, 77 insertions(+), 81 deletions(-) diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs index a4766aa8..3b3a756a 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Converters/EmployeeConverter.cs @@ -1,30 +1,29 @@ using Devon4Net.Application.Dtos; using Devon4Net.Domain.Entities; -namespace Devon4Net.Application.Converters +namespace Devon4Net.Application.Converters; + +/// +/// EmployeeConverter +/// +public static class EmployeeConverter { /// - /// EmployeeConverter + /// ModelToDto transformation /// - public static class EmployeeConverter + /// + /// + public static EmployeeDto ModelToDto(Employee? item) { - /// - /// ModelToDto transformation - /// - /// - /// - public static EmployeeDto ModelToDto(Employee? item) - { - if (item == null) return new EmployeeDto(); - - return new EmployeeDto - { - Id = item.Id, - Name = item.Name, - Surname = item.Surname, - Mail = item.Mail - }; - } + if (item == null) return new EmployeeDto(); + return new EmployeeDto + { + Id = item.Id, + Name = item.Name, + Surname = item.Surname, + Mail = item.Mail + }; } -} + +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs index b78ef2c8..5bd1c4e0 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs @@ -10,23 +10,23 @@ public class EmployeeDto /// /// the Id /// - public long Id { get; set; } + public long Id { get; init; } /// /// the Name /// [Required] - public string? Name { get; set; } + public string? Name { get; init; } /// /// the Surname /// [Required] - public string? Surname { get; set; } + public string? Surname { get; init; } /// /// the Mail /// [Required] - public string? Mail { get; set; } + public string? Mail { get; init; } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs index dd7d12c6..b3b7326e 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs @@ -2,55 +2,54 @@ using Microsoft.AspNetCore.Http; using System.Runtime.Serialization; -namespace Devon4Net.Application.Exceptions +namespace Devon4Net.Application.Exceptions; + +/// +/// Custom exception EmployeeNotFoundException +/// +[Serializable] +public class EmployeeNotFoundException : Exception, IWebApiException { /// - /// Custom exception EmployeeNotFoundException + /// The forced http status code to be fired on the exception manager /// - [Serializable] - public class EmployeeNotFoundException : Exception, IWebApiException - { - /// - /// The forced http status code to be fired on the exception manager - /// - public int StatusCode => StatusCodes.Status404NotFound; + public int StatusCode => StatusCodes.Status404NotFound; - /// - /// Show the message on the response? - /// - public bool ShowMessage => true; + /// + /// Show the message on the response? + /// + public bool ShowMessage => true; - public EmployeeNotFoundException() - { - } + public EmployeeNotFoundException() + { + } - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public EmployeeNotFoundException(string message) - : base(message) - { - } + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public EmployeeNotFoundException(string message) + : base(message) + { + } - /// - /// Initializes a new instance of the class with a specified error message - /// and a reference to the inner exception that caused this exception. - /// - /// The message that describes the error. - /// The exception that caused this exception. - public EmployeeNotFoundException(string message, Exception innerException) - : base(message, innerException) - { - } + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that caused this exception. + /// + /// The message that describes the error. + /// The exception that caused this exception. + public EmployeeNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + } - /// - /// Initializes a new instance of the class with serialized data. - /// - /// - /// - protected EmployeeNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Initializes a new instance of the class with serialized data. + /// + /// + /// + protected EmployeeNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs index e89782b5..b3d0b3be 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommand.cs @@ -1,7 +1,6 @@ using Devon4Net.Application.Dtos; using Devon4Net.Infrastructure.MediatR.Command; -namespace Devon4Net.Application.Features.Command.CreateEmployee -{ - public record CreateEmployeeCommand(string? Name, string? Surname, string? Mail) : CommandBase; -} \ No newline at end of file +namespace Devon4Net.Application.Features.Command.CreateEmployee; + +public record CreateEmployeeCommand(string? Name, string? Surname, string? Mail) : CommandBase; \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs index 86b79658..9558ca6b 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Command/CreateEmployee/CreateEmployeeCommandValidation.cs @@ -1,14 +1,13 @@ using FluentValidation; -namespace Devon4Net.Application.Features.Command.CreateEmployee +namespace Devon4Net.Application.Features.Command.CreateEmployee; + +public class InsertAuthorCommandValidation : AbstractValidator { - public class InsertAuthorCommandValidation : AbstractValidator + public InsertAuthorCommandValidation() { - public InsertAuthorCommandValidation() - { - RuleFor(x => x.Name).NotEmpty().WithMessage("FistName must be not empty"); - RuleFor(x => x.Surname).NotEmpty(); - RuleFor(x => x.Mail).NotEmpty().EmailAddress(); - } + RuleFor(x => x.Name).NotEmpty().WithMessage("FistName must be not empty"); + RuleFor(x => x.Surname).NotEmpty(); + RuleFor(x => x.Mail).NotEmpty().EmailAddress(); } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs index 64aa2898..5533dc06 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs @@ -18,7 +18,7 @@ public EmployeeContext(DbContextOptions options) } /// - /// Dbset + /// DbSet /// public virtual DbSet Employee { get; set; } diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs index 8827e3ec..611eaf70 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Presentation/Configuration.cs @@ -23,7 +23,7 @@ private static void SetUpAutoRegisterClasses(IServiceCollection services) List assemblyNamespaceToScan = new() { typeof(Application.AssemblyReference).Assembly, - typeof(Infrastructure.AssemblyReference).Assembly, + typeof(Infrastructure.AssemblyReference).Assembly }; var suffixNamesToRegister = new List From 9bc0aab8780feac88c342efeb37629576508ad3a Mon Sep 17 00:00:00 2001 From: JosepFe Date: Sun, 17 Sep 2023 13:03:10 +0200 Subject: [PATCH 13/21] remove nuget.sh --- source/nuget.sh | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 source/nuget.sh diff --git a/source/nuget.sh b/source/nuget.sh deleted file mode 100644 index f036f56e..00000000 --- a/source/nuget.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# Directory where the NuGet packages will be stored -nugets_directory="$(pwd)/nugets" - -# Create the nugets directory if it doesn't exist -mkdir -p "$nugets_directory" - -# Ask the user for the version input -read -p "Enter the new version: " new_version - -# Find all csproj files and build/pack their projects -find . -type f -name "*.csproj" | while read -r csproj_file; do - echo "Processing project: $csproj_file" - - # Update the version in the csproj file - sed -i "s#.*#$new_version#g" "$csproj_file" - - # Get the directory of the csproj file - project_dir=$(dirname "$csproj_file") - - # Build the project - dotnet build --configuration Release "$project_dir" - - # Pack the project - dotnet pack --configuration Release "$project_dir" - - # Move the generated nupkg files to the nugets directory - mv "$project_dir"/bin/Release/*.nupkg "$nugets_directory" -done \ No newline at end of file From fd676e99b951593767c2e1d0702c4323dfbf72f9 Mon Sep 17 00:00:00 2001 From: Josep Ferrandis Jorge Date: Mon, 18 Sep 2023 10:14:16 +0200 Subject: [PATCH 14/21] Removed unused project --- ...von4Net.Infrastructure.Azure.Common.csproj | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 source/Modules/Devon4Net.Infrastructure.Azure.Common/Devon4Net.Infrastructure.Azure.Common.csproj diff --git a/source/Modules/Devon4Net.Infrastructure.Azure.Common/Devon4Net.Infrastructure.Azure.Common.csproj b/source/Modules/Devon4Net.Infrastructure.Azure.Common/Devon4Net.Infrastructure.Azure.Common.csproj deleted file mode 100644 index 23abdbdd..00000000 --- a/source/Modules/Devon4Net.Infrastructure.Azure.Common/Devon4Net.Infrastructure.Azure.Common.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - net6.0 - enable - True - Capgemini, ADCenter Valencia, Traiectum Team - Capgemini S.A. - Component to work with Azure KeyVault - 6.0.6 - Copyright © Capgemini - https://github.com/devonfw/devon4net - https://github.com/devonfw/devon4net - git - devonfw;devon4net;Capgemini - NET 6.0+ compatibility version - - - - - - - - - - - - - - From cd925d8fec88b615b4a9d32c389eefc08bba7129 Mon Sep 17 00:00:00 2001 From: JosepFe Date: Wed, 14 Feb 2024 18:01:54 +0100 Subject: [PATCH 15/21] Rename Presentation project to WebAPI Modified WebApiException to abstract class --- .../ApplicationTypes/API/DevonfwApi.cs | 2 + .../ExceptionHandlingFilterAttribute.cs | 48 +++++++++++++ .../Exception/ExceptionHandlingMiddleware.cs | 68 ------------------ .../KillSwicth/KillSwicthMiddleware.cs | 60 +++------------- .../Middleware/MiddlewareConfiguration.cs | 2 - .../Exceptions/HttpCustomRequestException.cs | 22 ++++-- .../Exceptions/IWebApiException.cs | 16 ----- .../Exceptions/WebApiException.cs | 29 ++++++++ .../Exceptions/AdminNotFoundException.cs | 17 +++-- .../Exceptions/ConsumerException.cs | 17 +++-- .../Exceptions/ConsumerNotFoundException.cs | 17 +++-- .../Exceptions/DeliverMessageException.cs | 17 +++-- .../Exceptions/ProducerNotFoundException.cs | 17 +++-- .../Exceptions/EmployeeNotFoundException.cs | 18 ++--- .../Devon4Net.IntegrationTests.csproj | 2 +- .../Devon4Net.UnitTests.csproj | 2 +- .../Devon4Net.WebAPI/Certificates/RootCA.crt | 22 ++++++ .../Devon4Net.WebAPI/Certificates/RootCA.pem | 22 ++++++ .../Certificates/localhost.crt | 23 ++++++ .../Certificates/localhost.pfx | Bin 0 -> 2541 bytes .../Configuration.cs | 0 .../Controllers/EmployeeController.cs | 16 +++-- .../Devon4Net.WebAPI.csproj} | 6 +- .../Program.cs | 0 .../Properties/launchSettings.json | 0 .../appsettings.Development.json | 0 .../appsettings.json | 0 .../libman.json | 0 .../Exceptions/EmployeeNotFoundException.cs | 17 +++-- .../Exceptions/TodoNotFoundException.cs | 21 ++++-- source/devon4net.sln | 22 +++--- 31 files changed, 295 insertions(+), 208 deletions(-) create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Application/Attributes/ExceptionHandlingFilterAttribute.cs delete mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs delete mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Exceptions/IWebApiException.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Exceptions/WebApiException.cs create mode 100644 source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.crt create mode 100644 source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.pem create mode 100644 source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/localhost.crt create mode 100644 source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/localhost.pfx rename source/Templates/CleanArchitecture/{Devon4Net.Presentation => Devon4Net.WebAPI}/Configuration.cs (100%) rename source/Templates/CleanArchitecture/{Devon4Net.Presentation => Devon4Net.WebAPI}/Controllers/EmployeeController.cs (70%) rename source/Templates/CleanArchitecture/{Devon4Net.Presentation/Devon4Net.Presentation.csproj => Devon4Net.WebAPI/Devon4Net.WebAPI.csproj} (84%) rename source/Templates/CleanArchitecture/{Devon4Net.Presentation => Devon4Net.WebAPI}/Program.cs (100%) rename source/Templates/CleanArchitecture/{Devon4Net.Presentation => Devon4Net.WebAPI}/Properties/launchSettings.json (100%) rename source/Templates/CleanArchitecture/{Devon4Net.Presentation => Devon4Net.WebAPI}/appsettings.Development.json (100%) rename source/Templates/CleanArchitecture/{Devon4Net.Presentation => Devon4Net.WebAPI}/appsettings.json (100%) rename source/Templates/CleanArchitecture/{Devon4Net.Presentation => Devon4Net.WebAPI}/libman.json (100%) diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs index 384ec9a5..75afb636 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/ApplicationTypes/API/DevonfwApi.cs @@ -57,6 +57,8 @@ public static DevonfwOptions SetupDevonfw(this IServiceCollection services, ICon services?.AddMvc(options => options.Filters.Add(typeof(ModelStateCheckerAttribute))); } + services.AddScoped(); + if (DevonfwOptions.UseIIS) services?.ConfigureIIS(DevonfwOptions.IIS); if (DevonfwOptions.UseXsrf) services?.ConfigureXsrf(); diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Attributes/ExceptionHandlingFilterAttribute.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Attributes/ExceptionHandlingFilterAttribute.cs new file mode 100644 index 00000000..28be8c0d --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Attributes/ExceptionHandlingFilterAttribute.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc; +using Devon4Net.Infrastructure.Common.Exceptions; + +namespace Devon4Net.Infrastructure.Common.Application.Attributes +{ + public class ExceptionHandlingFilterAttribute : ExceptionFilterAttribute + { + public ExceptionHandlingFilterAttribute() { } + + public override Task OnExceptionAsync(ExceptionContext context) + { + if (context.Exception.InnerException != null) + Devon4NetLogger.Debug(context.Exception.InnerException?.ToString()!); + + context.Result = context.Exception switch + { + WebApiException webApiException => HandleContext(webApiException.StatusCode, webApiException.Message, webApiException.ShowMessage), + HttpRequestException httpRequestException => HandleContext((int?)httpRequestException.StatusCode, httpRequestException.Message), + _ => HandleContext(), + }; + + return Task.CompletedTask; + } + + private IActionResult HandleContext(int? statusCode = null, string? errorMessage = null, bool showMessage = false) + { + statusCode ??= StatusCodes.Status500InternalServerError; + + if (!showMessage || statusCode == StatusCodes.Status204NoContent || string.IsNullOrEmpty(errorMessage)) + { + return new ContentResult + { + StatusCode = (int)statusCode, + Content = string.Empty, + }; + } + + var response = new { error = errorMessage }; + + return new JsonResult(response) + { + StatusCode = (int)statusCode, + }; + } + } +} diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs deleted file mode 100644 index d4f9b53a..00000000 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/Exception/ExceptionHandlingMiddleware.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Text.Json; -using Devon4Net.Infrastructure.Common.Exceptions; -using Microsoft.AspNetCore.Http; - -namespace Devon4Net.Infrastructure.Common.Application.Middleware.Exception; - -public class ExceptionHandlingMiddleware -{ - private readonly RequestDelegate _next; - - public ExceptionHandlingMiddleware(RequestDelegate next) - { - _next = next; - } - - public async Task Invoke(HttpContext context) - { - try - { - await _next(context).ConfigureAwait(false); - } - catch (System.Exception ex) - { - await HandleException(ref context, ref ex).ConfigureAwait(false); - } - } - - private static Task HandleException(ref HttpContext context, ref System.Exception exception) - { - Devon4NetLogger.Error(exception.Message); - if(exception.InnerException != null) - Devon4NetLogger.Debug(exception.InnerException?.ToString()); - - var exceptionTypeValue = exception.GetType(); - var exceptionInterfaces = exceptionTypeValue.GetInterfaces().Select(i => i.Name).ToList(); - exceptionInterfaces.Add(exceptionTypeValue.Name); - - return exceptionInterfaces switch - { - { } when exceptionInterfaces.Contains("InvalidDataException") => HandleContext(ref context, - StatusCodes.Status422UnprocessableEntity), - { } when exceptionInterfaces.Contains("ArgumentException") || - exceptionInterfaces.Contains("ArgumentNullException") || - exceptionInterfaces.Contains("NotFoundException") || - exceptionInterfaces.Contains("FileNotFoundException") => HandleContext(ref context, - StatusCodes.Status400BadRequest), - { } when exceptionInterfaces.Contains("ValidationException") => HandleContext(ref context, - StatusCodes.Status400BadRequest, exception.Message, true), - { } when exceptionInterfaces.Contains("IWebApiException") => HandleContext(ref context, - ((IWebApiException)exception).StatusCode, exception.Message, - ((IWebApiException)exception).ShowMessage), - _ => HandleContext(ref context, StatusCodes.Status500InternalServerError, exception.Message) - }; - } - - private static Task HandleContext(ref HttpContext context, int? statusCode = null, string errorMessage = null, - bool showMessage = false) - { - context.Response.Headers.Clear(); - context.Response.StatusCode = statusCode ?? StatusCodes.Status500InternalServerError; - - if (!showMessage || statusCode == StatusCodes.Status204NoContent || string.IsNullOrEmpty(errorMessage)) - return Task.CompletedTask; - - context.Response.ContentType = "application/json"; - return context.Response.WriteAsync(JsonSerializer.Serialize(new { error = errorMessage })); - } -} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/KillSwicth/KillSwicthMiddleware.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/KillSwicth/KillSwicthMiddleware.cs index d58d6cee..8e7be34f 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/KillSwicth/KillSwicthMiddleware.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/KillSwicth/KillSwicthMiddleware.cs @@ -17,67 +17,23 @@ public KillSwicthMiddleware(RequestDelegate next) public async Task Invoke(HttpContext context, IOptionsMonitor killSwitch) { - try + if (killSwitch?.CurrentValue.UseKillSwitch == true) { - if (killSwitch?.CurrentValue.UseKillSwitch == true) + if (killSwitch.CurrentValue?.EnableRequests == true) { - if (killSwitch.CurrentValue?.EnableRequests == true) - { - await _next(context).ConfigureAwait(false); - } - else - { - context.Response.Headers.Clear(); - context.Response.StatusCode = killSwitch.CurrentValue?.HttpStatusCode > 0 ? killSwitch.CurrentValue.HttpStatusCode : 403; - await context.Response.WriteAsync(string.Empty).ConfigureAwait(false); - } + await _next(context).ConfigureAwait(false); } else { - await _next(context).ConfigureAwait(false); + context.Response.Headers.Clear(); + context.Response.StatusCode = killSwitch.CurrentValue?.HttpStatusCode > 0 ? killSwitch.CurrentValue.HttpStatusCode : 403; + await context.Response.WriteAsync(string.Empty).ConfigureAwait(false); } } -#pragma warning disable CA1031 // #warning directive - catch (System.Exception ex) -#pragma warning restore CA1031 // #warning directive + else { - await HandleException(ref context, ref ex).ConfigureAwait(false); + await _next(context).ConfigureAwait(false); } } - - private static Task HandleException(ref HttpContext context, ref System.Exception exception) - { - Devon4NetLogger.Error(exception); - - var exceptionTypeValue = exception.GetType(); - var exceptionInterfaces = exceptionTypeValue.GetInterfaces().Select(i => i.Name).ToList(); - exceptionInterfaces.Add(exceptionTypeValue.Name); - - return exceptionInterfaces switch - { - { } exceptionType when exceptionType.Contains("InvalidDataException") => HandleContext(ref context, - StatusCodes.Status422UnprocessableEntity), - { } exceptionType when exceptionType.Contains("ArgumentException") || - exceptionType.Contains("ArgumentNullException") || - exceptionType.Contains("NotFoundException") || - exceptionType.Contains("FileNotFoundException") => HandleContext(ref context, - StatusCodes.Status400BadRequest), - { } exceptionType when exceptionType.Contains("IWebApiException") => HandleContext(ref context, - ((IWebApiException) exception).StatusCode, exception.Message, - ((IWebApiException) exception).ShowMessage), - _ => HandleContext(ref context, StatusCodes.Status500InternalServerError, exception.Message) - }; - } - - private static Task HandleContext(ref HttpContext context, int? statusCode = null, string errorMessage = null, bool showMessage = false) - { - context.Response.Headers.Clear(); - context.Response.StatusCode = statusCode ?? StatusCodes.Status500InternalServerError; - - if (!showMessage || statusCode == StatusCodes.Status204NoContent || string.IsNullOrEmpty(errorMessage) ) return Task.CompletedTask; - - context.Response.ContentType = "application/json"; - return context.Response.WriteAsync(JsonSerializer.Serialize(new { error = errorMessage })); - } } } diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs index 7d3032b0..9730ac4a 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Application/Middleware/MiddlewareConfiguration.cs @@ -5,7 +5,6 @@ using Microsoft.Extensions.Options; using Devon4Net.Infrastructure.Common.Application.Options; using Devon4Net.Infrastructure.Common.Application.Middleware.KillSwicth; -using Devon4Net.Infrastructure.Common.Application.Middleware.Exception; using Devon4Net.Infrastructure.Common.Application.Middleware.Headers; using Devon4Net.Infrastructure.Common.Application.Middleware.Certificates; using Devon4Net.Infrastructure.Common.Application.Middleware.Logs; @@ -26,7 +25,6 @@ public static void SetupMiddleware(this IApplicationBuilder builder, IServiceCol if (killSwitch?.UseKillSwitch == true) builder.UseMiddleware(); if (certificates?.ClientCertificate?.EnableClientCertificateCheck == true || certificates?.ClientCertificate?.RequireClientCertificate== true) builder.UseMiddleware(); - builder.UseMiddleware(); builder.UseMiddleware(); } diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/HttpCustomRequestException.cs b/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/HttpCustomRequestException.cs index 4d5371a6..aa61aad4 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/HttpCustomRequestException.cs +++ b/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/HttpCustomRequestException.cs @@ -1,36 +1,44 @@ using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Infrastructure.Common.Exceptions { [Serializable] - public class HttpCustomRequestException : Exception, IWebApiException + public class HttpCustomRequestException : WebApiException { - public int StatusCode { get; } + /// + /// Gets the forced http status code to be fired on the exception manager. + /// + public override int StatusCode => StatusCodes.Status404NotFound; - public bool ShowMessage => false; + /// + /// Gets a value indicating whether show the message on the response?. + /// + public override bool ShowMessage => true; public HttpCustomRequestException() { - StatusCode = StatusCodes.Status500InternalServerError; } public HttpCustomRequestException(string message) : base(message) { - StatusCode = StatusCodes.Status500InternalServerError; } public HttpCustomRequestException(string message, int statusCode) : base(message) { - StatusCode = statusCode; } public HttpCustomRequestException(string message, Exception inner) : base(message, inner) { - StatusCode = StatusCodes.Status500InternalServerError; + } + + protected HttpCustomRequestException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { } } } diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/IWebApiException.cs b/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/IWebApiException.cs deleted file mode 100644 index d247fd4e..00000000 --- a/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/IWebApiException.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Runtime.Serialization; - -namespace Devon4Net.Infrastructure.Common.Exceptions -{ - /// Interface for webapi exceptions - public interface IWebApiException : ISerializable - { - /// Gets the status code. - /// The status code. - int StatusCode { get; } - /// Gets a value indicating whether [show message]. - /// - /// true if [show message]; otherwise, false. - bool ShowMessage { get; } - } -} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/WebApiException.cs b/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/WebApiException.cs new file mode 100644 index 00000000..bd2dd9c3 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Exceptions/WebApiException.cs @@ -0,0 +1,29 @@ +namespace Devon4Net.Infrastructure.Common.Exceptions +{ + /// Interface for webapi exceptions. + public abstract class WebApiException : Exception + { + protected WebApiException() + { + } + + protected WebApiException(string message) + : base(message) + { + } + + protected WebApiException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// Gets the status code. + /// The status code. + public virtual int StatusCode { get; } + + /// Gets a value indicating whether [show message]. + /// + /// true if [show message]; otherwise, false. + public virtual bool ShowMessage { get; } + } +} diff --git a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/AdminNotFoundException.cs b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/AdminNotFoundException.cs index 86637dbf..8b6d5bc1 100644 --- a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/AdminNotFoundException.cs +++ b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/AdminNotFoundException.cs @@ -1,5 +1,6 @@ using Devon4Net.Infrastructure.Common.Exceptions; using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Infrastructure.Kafka.Exceptions { @@ -7,14 +8,17 @@ namespace Devon4Net.Infrastructure.Kafka.Exceptions /// Custom exception AdminNotFoundException /// [Serializable] - public class AdminNotFoundException : Exception, IWebApiException + public class AdminNotFoundException : WebApiException { - public int StatusCode => StatusCodes.Status500InternalServerError; + /// + /// Gets the forced http status code to be fired on the exception manager. + /// + public override int StatusCode => StatusCodes.Status500InternalServerError; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; /// /// Initializes a new instance of the class. @@ -40,5 +44,10 @@ public AdminNotFoundException(string message) public AdminNotFoundException(string message, Exception innerException) : base(message, innerException) { } + + protected AdminNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { + } } } diff --git a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerException.cs b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerException.cs index 8e9a4826..a072fac2 100644 --- a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerException.cs +++ b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerException.cs @@ -1,5 +1,6 @@ using Devon4Net.Infrastructure.Common.Exceptions; using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Infrastructure.Kafka.Exceptions { @@ -7,14 +8,17 @@ namespace Devon4Net.Infrastructure.Kafka.Exceptions /// Custom exception ConsumerException /// [Serializable] - public class ConsumerException : Exception, IWebApiException + public class ConsumerException : WebApiException { - public int StatusCode => StatusCodes.Status500InternalServerError; + /// + /// Gets the forced http status code to be fired on the exception manager. + /// + public override int StatusCode => StatusCodes.Status500InternalServerError; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; /// /// Initializes a new instance of the class. @@ -40,5 +44,10 @@ public ConsumerException(string message) public ConsumerException(string message, Exception innerException) : base(message, innerException) { } + + protected ConsumerException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { + } } } diff --git a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerNotFoundException.cs b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerNotFoundException.cs index fd93e06c..512dd55a 100644 --- a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerNotFoundException.cs +++ b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ConsumerNotFoundException.cs @@ -1,5 +1,6 @@ using Devon4Net.Infrastructure.Common.Exceptions; using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Infrastructure.Kafka.Exceptions { @@ -7,14 +8,17 @@ namespace Devon4Net.Infrastructure.Kafka.Exceptions /// Custom exception ConsumerNotFoundException /// [Serializable] - public class ConsumerNotFoundException : Exception, IWebApiException + public class ConsumerNotFoundException : WebApiException { - public int StatusCode => StatusCodes.Status500InternalServerError; + /// + /// Gets the forced http status code to be fired on the exception manager. + /// + public override int StatusCode => StatusCodes.Status500InternalServerError; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; /// /// Initializes a new instance of the class. @@ -40,5 +44,10 @@ public ConsumerNotFoundException(string message) public ConsumerNotFoundException(string message, Exception innerException) : base(message, innerException) { } + + protected ConsumerNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { + } } } diff --git a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/DeliverMessageException.cs b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/DeliverMessageException.cs index 8577a733..c897188c 100644 --- a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/DeliverMessageException.cs +++ b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/DeliverMessageException.cs @@ -1,5 +1,6 @@ using Devon4Net.Infrastructure.Common.Exceptions; using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Infrastructure.Kafka.Exceptions { @@ -7,14 +8,17 @@ namespace Devon4Net.Infrastructure.Kafka.Exceptions /// Custom exception DeliverMessageException /// [Serializable] - public class DeliverMessageException : Exception, IWebApiException + public class DeliverMessageException : WebApiException { - public int StatusCode => StatusCodes.Status500InternalServerError; + /// + /// Gets the forced http status code to be fired on the exception manager. + /// + public override int StatusCode => StatusCodes.Status500InternalServerError; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; /// /// Initializes a new instance of the class. @@ -40,5 +44,10 @@ public DeliverMessageException(string message) public DeliverMessageException(string message, Exception innerException) : base(message, innerException) { } + + protected DeliverMessageException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { + } } } diff --git a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ProducerNotFoundException.cs b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ProducerNotFoundException.cs index 9eeadd20..6aa38da0 100644 --- a/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ProducerNotFoundException.cs +++ b/source/Modules/Devon4Net.Infrastructure.Kafka/Exceptions/ProducerNotFoundException.cs @@ -1,5 +1,6 @@ using Devon4Net.Infrastructure.Common.Exceptions; using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Infrastructure.Kafka.Exceptions { @@ -7,14 +8,17 @@ namespace Devon4Net.Infrastructure.Kafka.Exceptions /// Custom exception ProducerNotFoundException /// [Serializable] - public class ProducerNotFoundException : Exception, IWebApiException + public class ProducerNotFoundException : WebApiException { - public int StatusCode => StatusCodes.Status500InternalServerError; + /// + /// Gets the forced http status code to be fired on the exception manager. + /// + public override int StatusCode => StatusCodes.Status500InternalServerError; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; /// /// Initializes a new instance of the class. @@ -40,5 +44,10 @@ public ProducerNotFoundException(string message) public ProducerNotFoundException(string message, Exception innerException) : base(message, innerException) { } + + protected ProducerNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { + } } } diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs index b3b7326e..2eb54d0a 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Exceptions/EmployeeNotFoundException.cs @@ -8,17 +8,17 @@ namespace Devon4Net.Application.Exceptions; /// Custom exception EmployeeNotFoundException /// [Serializable] -public class EmployeeNotFoundException : Exception, IWebApiException +public class EmployeeNotFoundException : WebApiException { /// - /// The forced http status code to be fired on the exception manager + /// Gets the forced http status code to be fired on the exception manager. /// - public int StatusCode => StatusCodes.Status404NotFound; + public override int StatusCode => StatusCodes.Status404NotFound; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; public EmployeeNotFoundException() { @@ -44,12 +44,8 @@ public EmployeeNotFoundException(string message, Exception innerException) { } - /// - /// Initializes a new instance of the class with serialized data. - /// - /// - /// - protected EmployeeNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + protected EmployeeNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() { } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj b/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj index df3b2f09..afec5507 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj +++ b/source/Templates/CleanArchitecture/Devon4Net.IntegrationTests/Devon4Net.IntegrationTests.csproj @@ -15,7 +15,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj b/source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj index 055699ed..cd1f7cc0 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj +++ b/source/Templates/CleanArchitecture/Devon4Net.UnitTests/Devon4Net.UnitTests.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.crt b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.crt new file mode 100644 index 00000000..513d0680 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqTCCApGgAwIBAgIUFh7tIM1nq2XzSM4jy6Fu4yBTrX0wDQYJKoZIhvcNAQEL +BQAwZDELMAkGA1UEBhMCRVMxETAPBgNVBAgMCFZhbGVuY2lhMREwDwYDVQQHDAhW +YWxlbmNpYTEVMBMGA1UECgwMQ2VydGlmaWNhdGVzMRgwFgYDVQQDDA9sb2NhbGhv +c3QubG9jYWwwHhcNMjAwOTAzMTEwNjA5WhcNMjQwOTAyMTEwNjA5WjBkMQswCQYD +VQQGEwJFUzERMA8GA1UECAwIVmFsZW5jaWExETAPBgNVBAcMCFZhbGVuY2lhMRUw +EwYDVQQKDAxDZXJ0aWZpY2F0ZXMxGDAWBgNVBAMMD2xvY2FsaG9zdC5sb2NhbDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdeJfXptbcHGA8xVjTOjKfR +727UtXomEqc+9KO1jpT9e/FPcrfu0Jafvsfh3yPTd9pSDIqazqQxnX1Nh3EOiY8p +jUqq5ADUCbBypWCZlj0m/lME32SGCSnhVrxScDtYylGyBH1XmJuyb/umiEEh3uhk +98KuxdFj5IoVUQ61fR7AB1wpqRuyoKNjfrDaKtjtEyENZRRJ1mx8rZpuRgoOKkCF +1z/RzJuzimLB1+ZOErK7gMA67B3dS8SLa8W414vmB10ffB4RD9csHnQAapqDCIiX ++YQHTRU+YFkm6J+s8WX36F2FdOehIWnrSuV5VprJPkTBglyxigMWrdi0OtJ+kKMC +AwEAAaNTMFEwHQYDVR0OBBYEFDMeRVNIZlI9cWJio1knxEgEgwcdMB8GA1UdIwQY +MBaAFDMeRVNIZlI9cWJio1knxEgEgwcdMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggEBAKLFvRWe/630uD5bbwDu8aDTz4soDxgmZcMyi6/So2dF+XrD +D/Yq84bNpZhuUYelnDsxq6XZZMdQDztGZcX1G5kPabkW6kC2mb+KoPs2Be9ZJpu8 +aTHeOGKkgsWa05lAVQ5xSkDUpLGI8jSF3DLnRo0hKgKTe38iqErSMnOrPMR5drXw +rT35eOszU7PbhCcFWucGFH5uoFYfv709w8VeIB2dyq4po2FObGo//Zsy/n7xl+Oe +Fo8w0D/X2YsfSojqaSFLZl9t/3frJdiF1pYt6z6v911IvR086z7q0s0kmW8dxkVy +KCjhocdtf4iik3V8WENZPFtIReTIEn0eGEr/n7s= +-----END CERTIFICATE----- diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.pem b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.pem new file mode 100644 index 00000000..513d0680 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/RootCA.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqTCCApGgAwIBAgIUFh7tIM1nq2XzSM4jy6Fu4yBTrX0wDQYJKoZIhvcNAQEL +BQAwZDELMAkGA1UEBhMCRVMxETAPBgNVBAgMCFZhbGVuY2lhMREwDwYDVQQHDAhW +YWxlbmNpYTEVMBMGA1UECgwMQ2VydGlmaWNhdGVzMRgwFgYDVQQDDA9sb2NhbGhv +c3QubG9jYWwwHhcNMjAwOTAzMTEwNjA5WhcNMjQwOTAyMTEwNjA5WjBkMQswCQYD +VQQGEwJFUzERMA8GA1UECAwIVmFsZW5jaWExETAPBgNVBAcMCFZhbGVuY2lhMRUw +EwYDVQQKDAxDZXJ0aWZpY2F0ZXMxGDAWBgNVBAMMD2xvY2FsaG9zdC5sb2NhbDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdeJfXptbcHGA8xVjTOjKfR +727UtXomEqc+9KO1jpT9e/FPcrfu0Jafvsfh3yPTd9pSDIqazqQxnX1Nh3EOiY8p +jUqq5ADUCbBypWCZlj0m/lME32SGCSnhVrxScDtYylGyBH1XmJuyb/umiEEh3uhk +98KuxdFj5IoVUQ61fR7AB1wpqRuyoKNjfrDaKtjtEyENZRRJ1mx8rZpuRgoOKkCF +1z/RzJuzimLB1+ZOErK7gMA67B3dS8SLa8W414vmB10ffB4RD9csHnQAapqDCIiX ++YQHTRU+YFkm6J+s8WX36F2FdOehIWnrSuV5VprJPkTBglyxigMWrdi0OtJ+kKMC +AwEAAaNTMFEwHQYDVR0OBBYEFDMeRVNIZlI9cWJio1knxEgEgwcdMB8GA1UdIwQY +MBaAFDMeRVNIZlI9cWJio1knxEgEgwcdMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggEBAKLFvRWe/630uD5bbwDu8aDTz4soDxgmZcMyi6/So2dF+XrD +D/Yq84bNpZhuUYelnDsxq6XZZMdQDztGZcX1G5kPabkW6kC2mb+KoPs2Be9ZJpu8 +aTHeOGKkgsWa05lAVQ5xSkDUpLGI8jSF3DLnRo0hKgKTe38iqErSMnOrPMR5drXw +rT35eOszU7PbhCcFWucGFH5uoFYfv709w8VeIB2dyq4po2FObGo//Zsy/n7xl+Oe +Fo8w0D/X2YsfSojqaSFLZl9t/3frJdiF1pYt6z6v911IvR086z7q0s0kmW8dxkVy +KCjhocdtf4iik3V8WENZPFtIReTIEn0eGEr/n7s= +-----END CERTIFICATE----- diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/localhost.crt b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/localhost.crt new file mode 100644 index 00000000..9a08a599 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/localhost.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1DCCArygAwIBAgIJAMt7yDoYrBXfMA0GCSqGSIb3DQEBCwUAMGQxCzAJBgNV +BAYTAkVTMREwDwYDVQQIDAhWYWxlbmNpYTERMA8GA1UEBwwIVmFsZW5jaWExFTAT +BgNVBAoMDENlcnRpZmljYXRlczEYMBYGA1UEAwwPbG9jYWxob3N0LmxvY2FsMB4X +DTE5MTAxMDExMDUwM1oXDTIyMDczMDExMDUwM1owZDELMAkGA1UEBhMCRVMxETAP +BgNVBAgMCFZhbGVuY2lhMREwDwYDVQQHDAhWYWxlbmNpYTEVMBMGA1UECgwMQ2Vy +dGlmaWNhdGVzMRgwFgYDVQQDDA9sb2NhbGhvc3QubG9jYWwwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDAsevHKZ4/YgRuXWRbkJ6My6+3S37Zb7ajxVEa +KGUQkRyGElRJk8AbDJVYjBxdE30vM9OFdnuQk+kywWlAkC0yXZosEfPloCcGX3Oe +NAcs/4XVnYSlnfSaVUgG4LJdOtyNJgrWVC+yl1qyHW+M4uqjx1BVw3TkTBgozvOr ++eiIvyrDQmSB5jR/A+BejquSbLDMUv35n6q0PrNy6ZdO+AeB7X5Jm1a/PVDe017o +WcG8PBs7wAbd/GVnZo842HnTh0cPurtrTMwsKqpJWcV+5pBnnvJRkHc7jzg8fk4n +gAnmOWJYl1DySFJE9XGo3cB8xhYuuTB5bvyTXDT5DzTaEpNpAgMBAAGjgYgwgYUw +HwYDVR0jBBgwFoAUQvhY+l9vU1lwwKk2gO+nJ4plMZ0wCQYDVR0TBAIwADALBgNV +HQ8EBAMCBPAwSgYDVR0RBEMwQYIJbG9jYWxob3N0gg9sb2NhbGhvc3QubG9jYWyC +CTEyNy4wLjAuMYILZmFrZTEubG9jYWyCC2Zha2UyLmxvY2FsMA0GCSqGSIb3DQEB +CwUAA4IBAQC3LrullXQJSlkxLooPNd9LxWwjHiak3hCyvpQ3r09DCSd9JJSE+E5o +3N2TPOolmCDHZDAbUtUdIeHH3AUDRHKbEz2DACQg4fyb6ByL3Qe8L5NHGo3ELFfc +dmbXxM53J9fQ66Fm8kzWXoZlHdSboCuf8OlxdtRQZzJZA6pcwu/qx6DGc3/1K2iJ +PLajHC36p/fudSl41DJIZBdNYsd1NmlW48im+fOvcBCXVGzST2J3954u7CrtTffG +rYA9X/EQZ7n9y7tymuhYkMCDCKJNSkX1fu0tkoFskuxRJK3lKcGJ59o1VsNHR3Eb +1+n85nSqQrPQaa35DOEq6z6IWg3M/tby +-----END CERTIFICATE----- diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/localhost.pfx b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Certificates/localhost.pfx new file mode 100644 index 0000000000000000000000000000000000000000..b63d0382882fa701d193f8bf97f11d5e577656d6 GIT binary patch literal 2541 zcmY+^c{CJ^8U}F2%rJ=}W2fwlWyZdYC1n{|hRKp`vM&*#vLy^-3!y}sRlz}F{qd$CR%#hJRBqs zh=UN%;^zn)5dBXD&cgw(o<-^aTAH(`_)kNN;02%i_riHvFoGG#uvQpoFcHV-3ji?C zMBso76ULGOM z(1ze$>MCZ41F3G^w!IP9L`rw+3mS8c)N4HoQ_<0z$uCpC*7&e%8h(UnS4 zA%&Z27nYh`JL;@r7Uj#kI2$mM&^(L-#d_=dI@TmdW^*7<{2t4si3H;>B;WF{QS!Oz z$zhg$oY$MN;^S*lDI`%c{2Eq!x}|FX1`npY5vE{=mF(+l$}Qr~=x=}5*~&MzdOD)& zce?=^k+!;`d+d4AxvbPl$a`|%XMuZN5%?!N1=LNE(x$xrHyeRMGVfDQ85vC+oPQeL zx)ixr^FD>0*45SVlCfnQ8z*Os@bIQuBTpJ)lJ$>9<8t|PZ|+*Zd|ERA(C(EzVIzdF+l7`L9>fL zOm;2ARe4|^$(Wf#dvQftMM`mqkeKj?5jipFy&3rB@GxJ-1K4-&)lVln!0` z3Gw^2dP(sr330${gpU>ZP|SysZECeBg5DEmi)2}82d#P`^-Zb^k2_n4Rb`0P_^Rb+G}N>W%C1}8N}Lg{-R;@h=K|3 zij~B}owq3qty;^yDx~jK9uHcPiejFy9(t$2$ek%2_qr^0c83M`fr!=%Zrr4>C`_8R)OXTOsTe%8)dr7>MU1Bv5OPGV!tM$h#)>pR}2$Ssf*!+hc_xi6_HJ>7mW z+m^nUEt|+s8BPl20tGEn?k&|2xY#nDB*nRWP5LMR1dRdq^&khN$5Y1k_Bse0Nc(@_ z$iso;fH;uMSqwWnQ843w7&%7^IP+%f44U=-(}nJjE+T?CO=P;eA%AoM;(+~)Yj(XM zLYWcZ$o4?_WEJr_3$vr!%K;Yhw9+u{qf81a(Qa=W3J;T;?0Ms`7e+%cdX=JzeTYfA za3h@>25>KcE8bQd&Y@ea9`~9lr)DCvj9LErh4028^jk(#`1}^cnX%SCcp;0E?$<0W zK2W?VCG1Oq{7aQ^Wuag@BQ)mIgh^8kv$(cAQz^5fr&~eN%-T9;-LbuWTN1sGIS?i0 zI*|p0@5b*Ab6jZ3E!mKNp3k4}E-scS6wrLPL(+TZJ{#mYe$p2MTBTE35HkA9d4JkC zP2|~bO$8qZb&9!7_H)T-OMaG-tpQ^Y?`KsWPra8yVRt6+pD?drU}9}^7IyRF3k^=i z<9<_eA%`y~F{*MG(YHvkpz|tT5%&2p{&wX9c}qcjqIgDiQEJ-tL#nocX)rC@PbPE-QYYmGXxGJd&jd{h!)8b zBJX!6b+OvCtQr6k4__}QZ(CXD{Ql`yJXF0@9i0na_dV>q5wVGC@lMwY_f=&6go>LR z7YjmL{^BQ5y0?OMj|GKWJ@5mhR~SShUJYV(3)*p_R2^;9db&mA!jP0I>`Oz2@#9`< z$Ol-?j{szWgqqy@%Q|y~+|!xNAysUJ(<$@xZ1gVC(uUTgmoSHlRayU5Nr2L|gvb_1 zg^Q>8V^oPt>}$(>=+$W&3F`g5ksc_Mj^oGrV9aYHahfTA--Tb6v}H02soe(Rn?9$> zM*WHw3BjiutEPE=Ka>M(p#x7N72p);acF{1U0WvE%YC2NwXxukW#Njqk5?Q1?Y(ydwNT3}4?UY;06L%AT^J!LjoX#~net z3EW}fiUt||L!PwWm6nzx+aLn{bSsc9L0`pi$QTZ3hnfUmy*h2LQZ*+yT}IK6qg$V_ zaoKz~=qPK5iK%Avo`GLDY11TA?>Kf!ie=a&oBYg*=M&{2@eE40V<1u713dOdBB zkhB0<+epWolT~y^ER#iR=6w@TJPbVqc6}|cBS~Xr4E>doKpThQ)boujp(jK6JGlz2 z_IWott{cPNDxPk=tO%?+FEkd*1Drzh*2nK+N_PfkRXhA!GIFM4zxwhkKOjnDhNt8@ z?I5hZIhuZO7F1f%5ww-jtwMjYY$m0-uMYG~+3rS%*bV{wd_>NIx!$=_92N%pp{(rO z;0%jj*%at7b1I`S*_EU4Z+p(iYp1yn+_2vn9G$fhqF-x&QZ8uWYZMpt66>#9M CreateEmployee([FromBody] EmployeeDto employeeDto) { + throw new HttpCustomRequestException("message"); var command = new CreateEmployeeCommand(employeeDto.Name, employeeDto.Surname, employeeDto.Mail); return _mediator.CommandAsync(command); } - [HttpGet("GetEmployees")] + [HttpGet] public Task> GetAllEmployees() { var query = new GetAllEmployeesQuery(); return _mediator.QueryAsync(query); } - [HttpGet("GetEmployeeById")] - public Task GetAllEmployees([FromQuery] long id) + [HttpGet("{employeeId}")] + public Task GetAllEmployees([FromRoute] long employeeId) { - var query = new GetEmployeeByIdQuery(id); + var query = new GetEmployeeByIdQuery(employeeId); return _mediator.QueryAsync(query); } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Devon4Net.Presentation.csproj b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj similarity index 84% rename from source/Templates/CleanArchitecture/Devon4Net.Presentation/Devon4Net.Presentation.csproj rename to source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj index f7976898..156f5a7f 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Devon4Net.Presentation.csproj +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj @@ -7,7 +7,7 @@ - + @@ -17,7 +17,9 @@ - + + Always + diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Program.cs b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Program.cs similarity index 100% rename from source/Templates/CleanArchitecture/Devon4Net.Presentation/Program.cs rename to source/Templates/CleanArchitecture/Devon4Net.WebAPI/Program.cs diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/Properties/launchSettings.json b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Properties/launchSettings.json similarity index 100% rename from source/Templates/CleanArchitecture/Devon4Net.Presentation/Properties/launchSettings.json rename to source/Templates/CleanArchitecture/Devon4Net.WebAPI/Properties/launchSettings.json diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.Development.json b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/appsettings.Development.json similarity index 100% rename from source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.Development.json rename to source/Templates/CleanArchitecture/Devon4Net.WebAPI/appsettings.Development.json diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.json b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/appsettings.json similarity index 100% rename from source/Templates/CleanArchitecture/Devon4Net.Presentation/appsettings.json rename to source/Templates/CleanArchitecture/Devon4Net.WebAPI/appsettings.json diff --git a/source/Templates/CleanArchitecture/Devon4Net.Presentation/libman.json b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/libman.json similarity index 100% rename from source/Templates/CleanArchitecture/Devon4Net.Presentation/libman.json rename to source/Templates/CleanArchitecture/Devon4Net.WebAPI/libman.json diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Exceptions/EmployeeNotFoundException.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Exceptions/EmployeeNotFoundException.cs index b931fd1a..d411ef43 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Exceptions/EmployeeNotFoundException.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/EmployeeManagement/Exceptions/EmployeeNotFoundException.cs @@ -1,5 +1,5 @@ using Devon4Net.Infrastructure.Common.Exceptions; -using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Application.WebAPI.Business.EmployeeManagement.Exceptions { @@ -7,17 +7,17 @@ namespace Devon4Net.Application.WebAPI.Business.EmployeeManagement.Exceptions /// Custom exception EmployeeNotFoundException /// [Serializable] - public class EmployeeNotFoundException : Exception, IWebApiException + public class EmployeeNotFoundException : WebApiException { /// - /// The forced http status code to be fired on the exception manager + /// Gets the forced http status code to be fired on the exception manager. /// - public int StatusCode => StatusCodes.Status404NotFound; + public override int StatusCode => StatusCodes.Status404NotFound; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; /// /// Initializes a new instance of the class. @@ -43,5 +43,10 @@ public EmployeeNotFoundException(string message) public EmployeeNotFoundException(string message, Exception innerException) : base(message, innerException) { } + + protected EmployeeNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { + } } } diff --git a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Exceptions/TodoNotFoundException.cs b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Exceptions/TodoNotFoundException.cs index 6b0287f5..4d9f5443 100644 --- a/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Exceptions/TodoNotFoundException.cs +++ b/source/Templates/WebAPI/Devon4Net.Application.WebAPI/Business/MediatRManagement/Exceptions/TodoNotFoundException.cs @@ -1,5 +1,6 @@ using Devon4Net.Infrastructure.Common.Exceptions; using Microsoft.AspNetCore.Http; +using System.Runtime.Serialization; namespace Devon4Net.Application.WebAPI.Business.MediatRManagement.Exceptions { @@ -7,17 +8,17 @@ namespace Devon4Net.Application.WebAPI.Business.MediatRManagement.Exceptions /// Custom exception TodoNotFoundException /// [Serializable] - public class TodoNotFoundException : Exception, IWebApiException + public class TodoNotFoundException : WebApiException { /// - /// The forced http status code to be fired on the exception manager + /// Gets the forced http status code to be fired on the exception manager. /// - public int StatusCode => StatusCodes.Status204NoContent; + public override int StatusCode => StatusCodes.Status500InternalServerError; /// - /// Show the message on the response? + /// Gets a value indicating whether show the message on the response?. /// - public bool ShowMessage => true; + public override bool ShowMessage => true; /// /// Initializes a new instance of the class. @@ -43,5 +44,15 @@ public TodoNotFoundException(string message) public TodoNotFoundException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + protected TodoNotFoundException(SerializationInfo serializationInfo, StreamingContext streamingContext) + : base() + { + } } } diff --git a/source/devon4net.sln b/source/devon4net.sln index 3b489353..5d884294 100644 --- a/source/devon4net.sln +++ b/source/devon4net.sln @@ -105,14 +105,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Infrastructure", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.IntegrationTests", "Templates\CleanArchitecture\Devon4Net.IntegrationTests\Devon4Net.IntegrationTests.csproj", "{727B7E51-F913-4196-99E3-B9D01CDBA2E2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Presentation", "Templates\CleanArchitecture\Devon4Net.Presentation\Devon4Net.Presentation.csproj", "{93305326-BB9C-4F7D-B6FA-41B78100C4E4}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.UnitTests", "Templates\CleanArchitecture\Devon4Net.UnitTests\Devon4Net.UnitTests.csproj", "{4C685698-3083-4BB0-BFF4-6DA152115D2C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B55F2379-2A06-4761-B1E7-DAD1D14A93BC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3A21B19A-CDDB-4849-87F2-EBC575EA889F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.WebAPI", "Templates\CleanArchitecture\Devon4Net.WebAPI\Devon4Net.WebAPI.csproj", "{D853C05D-76B5-4D9D-B419-437ADD7C23FD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -417,14 +417,6 @@ Global {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Release|Any CPU.Build.0 = Release|Any CPU {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Release|x64.ActiveCfg = Release|Any CPU {727B7E51-F913-4196-99E3-B9D01CDBA2E2}.Release|x64.Build.0 = Release|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|x64.ActiveCfg = Debug|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Debug|x64.Build.0 = Debug|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|Any CPU.Build.0 = Release|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|x64.ActiveCfg = Release|Any CPU - {93305326-BB9C-4F7D-B6FA-41B78100C4E4}.Release|x64.Build.0 = Release|Any CPU {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Debug|Any CPU.Build.0 = Debug|Any CPU {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -433,6 +425,14 @@ Global {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Release|Any CPU.Build.0 = Release|Any CPU {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Release|x64.ActiveCfg = Release|Any CPU {4C685698-3083-4BB0-BFF4-6DA152115D2C}.Release|x64.Build.0 = Release|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Debug|x64.ActiveCfg = Debug|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Debug|x64.Build.0 = Debug|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Release|Any CPU.Build.0 = Release|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Release|x64.ActiveCfg = Release|Any CPU + {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -484,10 +484,10 @@ Global {5C1E97EE-D116-4237-A86C-496E123F429D} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} {A65EBB59-D598-46A0-B80C-0840D97E3A69} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} {727B7E51-F913-4196-99E3-B9D01CDBA2E2} = {3A21B19A-CDDB-4849-87F2-EBC575EA889F} - {93305326-BB9C-4F7D-B6FA-41B78100C4E4} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} {4C685698-3083-4BB0-BFF4-6DA152115D2C} = {3A21B19A-CDDB-4849-87F2-EBC575EA889F} {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} = {4EC816D6-3F55-41AD-B805-955F251C03EB} {3A21B19A-CDDB-4849-87F2-EBC575EA889F} = {4EC816D6-3F55-41AD-B805-955F251C03EB} + {D853C05D-76B5-4D9D-B419-437ADD7C23FD} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E2CBC81F-F2EE-4BD5-B567-AAF8057444D1} From 1ad1c54ea3a7a7031300a4c5d2f823f5961fa234 Mon Sep 17 00:00:00 2001 From: JosepFe Date: Wed, 14 Feb 2024 18:35:17 +0100 Subject: [PATCH 16/21] Modify domain entity to use private set Modify type for entity Id from long to Guid --- .../Devon4Net.Application/Dtos/EmployeeDto.cs | 2 +- .../GetEmployeeById/GetEmployeeByIdQuery.cs | 2 +- .../Ports/Projectors/IEmployeeProjector.cs | 2 +- .../Ports/Repositories/IEmployeeRepository.cs | 4 ++-- .../Devon4Net.Domain/Entities/Employee.cs | 21 ++++++++++++++----- .../Adapters/Projectors/EmployeeProjector.cs | 4 +++- .../Repositories/EmployeeRepository.cs | 6 +++--- .../Persistence/EmployeeContext.cs | 3 +++ .../Controllers/EmployeeController.cs | 3 +-- 9 files changed, 31 insertions(+), 16 deletions(-) diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs index 5bd1c4e0..d1db3e37 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Dtos/EmployeeDto.cs @@ -10,7 +10,7 @@ public class EmployeeDto /// /// the Id /// - public long Id { get; init; } + public Guid Id { get; init; } /// /// the Name diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs index 2cda535a..126ec820 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs @@ -3,4 +3,4 @@ namespace Devon4Net.Application.Features.Queries.GetEmployeeById; -public record GetEmployeeByIdQuery(long Id) : QueryBase; \ No newline at end of file +public record GetEmployeeByIdQuery(Guid Id) : QueryBase; \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs index a4f9738a..ec7bc391 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Projectors/IEmployeeProjector.cs @@ -4,7 +4,7 @@ namespace Devon4Net.Application.Ports.Projectors; public interface IEmployeeProjector { - public Task GetEmployeeById(long id); + public Task GetEmployeeById(Guid id); public Task> GetEmployees(); } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs index 78d01296..4b28420b 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Ports/Repositories/IEmployeeRepository.cs @@ -21,7 +21,7 @@ public interface IEmployeeRepository : IRepository /// /// /// - Task GetEmployeeById(long id); + Task GetEmployeeById(Guid id); /// /// Create @@ -37,5 +37,5 @@ public interface IEmployeeRepository : IRepository /// /// /// - Task DeleteEmployeeById(long id); + Task DeleteEmployeeById(Guid id); } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs b/source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs index a3857377..3611fd25 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Domain/Entities/Employee.cs @@ -1,27 +1,38 @@ -namespace Devon4Net.Domain.Entities; +using System.ComponentModel.DataAnnotations; + +namespace Devon4Net.Domain.Entities; /// /// Entity class for Employee /// public class Employee { + public Employee(string name, string surname, string mail) + { + Id = Guid.NewGuid(); + Name = name; + Surname = surname; + Mail = mail; + } + /// /// Id /// - public long Id { get; set; } + [Key] + public Guid Id { get; private set; } /// /// Name /// - public string Name { get; set; } + public string Name { get; private set; } /// /// Surname /// - public string Surname { get; set; } + public string Surname { get; private set; } /// /// mail /// - public string Mail { get; set; } + public string Mail { get; private set; } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs index 4b2264b9..d05c58f1 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs @@ -13,11 +13,12 @@ public EmployeeProjector(EmployeeContext context) : base(context) { } - public async Task GetEmployeeById(long id) + public async Task GetEmployeeById(Guid id) { var query = (IQueryable employeeQuery) => employeeQuery .Select(employee => new EmployeeDto { + Id = employee.Id, Name = employee.Name, Surname = employee.Surname, Mail = employee.Mail @@ -33,6 +34,7 @@ public Task> GetEmployees() { var query = (IQueryable employeeQuery) => employeeQuery.Select(employee => new EmployeeDto { + Id = employee.Id, Name = employee.Name, Surname = employee.Surname, Mail = employee.Mail diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs index c7439249..de75c283 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Repositories/EmployeeRepository.cs @@ -33,7 +33,7 @@ public Task> GetEmployee(Expression> predic /// /// /// - public Task GetEmployeeById(long id) + public Task GetEmployeeById(Guid id) { Devon4NetLogger.Debug($"GetEmployeeById method from repository EmployeeService with value : {id}"); return GetFirstOrDefault(t => t.Id == id); @@ -49,7 +49,7 @@ public Task GetEmployeeById(long id) public Task Create(string name, string surName, string mail) { Devon4NetLogger.Debug($"SetEmployee method from repository EmployeeService with value : {name}"); - return Create(new Employee { Name = name, Surname = surName, Mail = mail }); + return Create(new Employee(name, surName, mail)); } /// @@ -57,7 +57,7 @@ public Task Create(string name, string surName, string mail) /// /// /// - public async Task DeleteEmployeeById(long id) + public async Task DeleteEmployeeById(Guid id) { Devon4NetLogger.Debug($"DeleteEmployeeById method from repository EmployeeService with value : {id}"); var deleted = await Delete(t => t.Id == id).ConfigureAwait(false); diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs index 5533dc06..0727c9ad 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs @@ -30,6 +30,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity(entity => { + entity.Property(e => e.Id) + .IsRequired() + .HasMaxLength(255); entity.Property(e => e.Name) .IsRequired() .HasMaxLength(255); diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs index 21638102..d567999b 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs @@ -24,7 +24,6 @@ public EmployeeController(IMediatRHandler mediator) [HttpPost] public Task CreateEmployee([FromBody] EmployeeDto employeeDto) { - throw new HttpCustomRequestException("message"); var command = new CreateEmployeeCommand(employeeDto.Name, employeeDto.Surname, employeeDto.Mail); return _mediator.CommandAsync(command); } @@ -37,7 +36,7 @@ public Task> GetAllEmployees() } [HttpGet("{employeeId}")] - public Task GetAllEmployees([FromRoute] long employeeId) + public Task GetAllEmployees([FromRoute] Guid employeeId) { var query = new GetEmployeeByIdQuery(employeeId); return _mediator.QueryAsync(query); From e1123856ec6548b98af9a5db784d30269b3f35bd Mon Sep 17 00:00:00 2001 From: JosepFe Date: Sun, 19 May 2024 19:00:14 +0200 Subject: [PATCH 17/21] [Add] Docker compose project --- .gitignore | 1 + .../Templates/CleanArchitecture/.dockerignore | 30 ++++++++ .../Devon4Net.CleanArchitecture.sln | 76 +++++++++++++++++++ ...Net.CleanArchitecure.docker-compose.dcproj | 19 +++++ .../Devon4Net.WebAPI/Devon4Net.WebAPI.csproj | 2 + .../Devon4Net.WebAPI/Dockerfile | 57 ++++++++++++++ .../docker-compose.override.yml | 12 +++ .../CleanArchitecture/docker-compose.yml | 20 +++++ source/devon4net.sln | 11 +++ 9 files changed, 228 insertions(+) create mode 100644 source/Templates/CleanArchitecture/.dockerignore create mode 100644 source/Templates/CleanArchitecture/Devon4Net.CleanArchitecture.sln create mode 100644 source/Templates/CleanArchitecture/Devon4Net.CleanArchitecure.docker-compose.dcproj create mode 100644 source/Templates/CleanArchitecture/Devon4Net.WebAPI/Dockerfile create mode 100644 source/Templates/CleanArchitecture/docker-compose.override.yml create mode 100644 source/Templates/CleanArchitecture/docker-compose.yml diff --git a/.gitignore b/.gitignore index dfc747e3..4c071b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.suo *.user *.sln.docstates +*.env # Build results [Dd]ebug/ diff --git a/source/Templates/CleanArchitecture/.dockerignore b/source/Templates/CleanArchitecture/.dockerignore new file mode 100644 index 00000000..fe1152bd --- /dev/null +++ b/source/Templates/CleanArchitecture/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.CleanArchitecture.sln b/source/Templates/CleanArchitecture/Devon4Net.CleanArchitecture.sln new file mode 100644 index 00000000..eef1c8b2 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.CleanArchitecture.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34024.191 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C6718118-8896-49D4-948E-7B1BF8CD52CD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9BDC90DC-9BFB-4CD6-BD2B-023A756DBA0F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{91851FFC-D596-460D-8DB7-263ECDA77773}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "Devon4Net.CleanArchitecure.docker-compose", "Devon4Net.CleanArchitecure.docker-compose.dcproj", "{486A3585-A387-4BD4-BA66-8B991FB42F73}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Application", "Devon4Net.Application\Devon4Net.Application.csproj", "{9AB931AC-BFE6-47D7-8A6A-254D911E003A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Domain", "Devon4Net.Domain\Devon4Net.Domain.csproj", "{180F7F20-F88A-4A97-BCDD-874C4480BF8A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.Infrastructure", "Devon4Net.Infrastructure\Devon4Net.Infrastructure.csproj", "{F6B4D676-3775-4998-AE14-350929AFD2E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.IntegrationTests", "Devon4Net.IntegrationTests\Devon4Net.IntegrationTests.csproj", "{4BAFF196-7474-482D-B600-35096C00533D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.UnitTests", "Devon4Net.UnitTests\Devon4Net.UnitTests.csproj", "{B4C3DF5E-4739-44B7-846E-2E61801CD7C1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.WebAPI", "Devon4Net.WebAPI\Devon4Net.WebAPI.csproj", "{32FDA553-436A-4D4B-B13C-65AA6906B4E1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {486A3585-A387-4BD4-BA66-8B991FB42F73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {486A3585-A387-4BD4-BA66-8B991FB42F73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {486A3585-A387-4BD4-BA66-8B991FB42F73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {486A3585-A387-4BD4-BA66-8B991FB42F73}.Release|Any CPU.Build.0 = Release|Any CPU + {9AB931AC-BFE6-47D7-8A6A-254D911E003A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AB931AC-BFE6-47D7-8A6A-254D911E003A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AB931AC-BFE6-47D7-8A6A-254D911E003A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AB931AC-BFE6-47D7-8A6A-254D911E003A}.Release|Any CPU.Build.0 = Release|Any CPU + {180F7F20-F88A-4A97-BCDD-874C4480BF8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {180F7F20-F88A-4A97-BCDD-874C4480BF8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {180F7F20-F88A-4A97-BCDD-874C4480BF8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {180F7F20-F88A-4A97-BCDD-874C4480BF8A}.Release|Any CPU.Build.0 = Release|Any CPU + {F6B4D676-3775-4998-AE14-350929AFD2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6B4D676-3775-4998-AE14-350929AFD2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6B4D676-3775-4998-AE14-350929AFD2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6B4D676-3775-4998-AE14-350929AFD2E2}.Release|Any CPU.Build.0 = Release|Any CPU + {4BAFF196-7474-482D-B600-35096C00533D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BAFF196-7474-482D-B600-35096C00533D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BAFF196-7474-482D-B600-35096C00533D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BAFF196-7474-482D-B600-35096C00533D}.Release|Any CPU.Build.0 = Release|Any CPU + {B4C3DF5E-4739-44B7-846E-2E61801CD7C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4C3DF5E-4739-44B7-846E-2E61801CD7C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4C3DF5E-4739-44B7-846E-2E61801CD7C1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4C3DF5E-4739-44B7-846E-2E61801CD7C1}.Release|Any CPU.Build.0 = Release|Any CPU + {32FDA553-436A-4D4B-B13C-65AA6906B4E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32FDA553-436A-4D4B-B13C-65AA6906B4E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32FDA553-436A-4D4B-B13C-65AA6906B4E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32FDA553-436A-4D4B-B13C-65AA6906B4E1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {486A3585-A387-4BD4-BA66-8B991FB42F73} = {9BDC90DC-9BFB-4CD6-BD2B-023A756DBA0F} + {9AB931AC-BFE6-47D7-8A6A-254D911E003A} = {9BDC90DC-9BFB-4CD6-BD2B-023A756DBA0F} + {180F7F20-F88A-4A97-BCDD-874C4480BF8A} = {9BDC90DC-9BFB-4CD6-BD2B-023A756DBA0F} + {F6B4D676-3775-4998-AE14-350929AFD2E2} = {9BDC90DC-9BFB-4CD6-BD2B-023A756DBA0F} + {4BAFF196-7474-482D-B600-35096C00533D} = {91851FFC-D596-460D-8DB7-263ECDA77773} + {B4C3DF5E-4739-44B7-846E-2E61801CD7C1} = {91851FFC-D596-460D-8DB7-263ECDA77773} + {32FDA553-436A-4D4B-B13C-65AA6906B4E1} = {9BDC90DC-9BFB-4CD6-BD2B-023A756DBA0F} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {09065A63-6429-42AD-8ED3-28EF422E543A} + EndGlobalSection +EndGlobal diff --git a/source/Templates/CleanArchitecture/Devon4Net.CleanArchitecure.docker-compose.dcproj b/source/Templates/CleanArchitecture/Devon4Net.CleanArchitecure.docker-compose.dcproj new file mode 100644 index 00000000..d09a521b --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.CleanArchitecure.docker-compose.dcproj @@ -0,0 +1,19 @@ + + + + 6f9dad10-d077-4ca1-8baa-a42d093f934e + 2.1 + Linux + False + LaunchBrowser + devon4net.clean-architecture.web-api + {Scheme}://localhost:{ServicePort}/swagger + + + + docker-compose.yml + + + + + diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj index 156f5a7f..dd07587d 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj @@ -4,6 +4,8 @@ net8.0 enable enable + ..\.. + 83711c0b-b386-4d97-a3da-a96a5d6863cf diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Dockerfile b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Dockerfile new file mode 100644 index 00000000..22859815 --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Dockerfile @@ -0,0 +1,57 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +ARG USERNAME=${USER_NAME} +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +# Create the user +RUN groupadd --gid "$USER_GID" "$USERNAME" \ + && useradd --uid "$USER_UID" --gid "$USER_GID" -m "$USERNAME" \ + # [Optional] Add sudo support. Omit if you don't need to install software after connecting. + && apt-get update \ + && apt-get install --no-install-recommends -y sudo \ + && echo "$USERNAME" ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/"$USERNAME" \ + && chmod 0440 /etc/sudoers.d/"$USERNAME" \ + && apt-get clean +# ******************************************************** +# * Anything else you want to do like clean up goes here * +# ******************************************************** + +# [Optional] Set the default user. Omit if you want to keep the default as root. +USER $USERNAME +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG USERNAME +USER $USERNAME +ARG BUILD_CONFIGURATION=Release + +WORKDIR /src +COPY . . +# COPY ["Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj", "Templates/CleanArchitecture/Devon4Net.WebAPI/"] +# COPY ["Templates/CleanArchitecture/Devon4Net.Application/Devon4Net.Application.csproj", "Templates/CleanArchitecture/Devon4Net.Application/"] +# COPY ["Templates/CleanArchitecture/Devon4Net.Infrastructure/Devon4Net.Infrastructure.csproj", "Templates/CleanArchitecture/Devon4Net.Infrastructure/"] +# COPY ["Templates/CleanArchitecture/Devon4Net.Domain/Devon4Net.Domain.csproj", "Templates/CleanArchitecture/Devon4Net.Domain/"] +RUN dotnet restore "source/Templates/CleanArchitecture/Devon4Net.WebAPI/Devon4Net.WebAPI.csproj" + +# COPY . . +# COPY ./Templates/CleanArchitecture/Devon4Net.WebAPI/ /Templates/CleanArchitecture/Devon4Net.WebAPI/ +# COPY ./Templates/CleanArchitecture/Devon4Net.Application/ /Templates/CleanArchitecture/Devon4Net.Application/ +# COPY ./Templates/CleanArchitecture/Devon4Net.Infrastructure/ /Templates/CleanArchitecture/Devon4Net.Infrastructure/ +# COPY ./Templates/CleanArchitecture/Devon4Net.Domain/ /Templates/CleanArchitecture/Devon4Net.Domain/ +# COPY ["Modules", "Modules"] + +WORKDIR "/src/source/Templates/CleanArchitecture/Devon4Net.WebAPI" +RUN dotnet build "Devon4Net.WebAPI.csproj" -c ${BUILD_CONFIGURATION} --no-restore -o /app/build + +FROM build AS publish +USER $USERNAME +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Devon4Net.WebAPI.csproj" -c ${BUILD_CONFIGURATION} -o /app/publish /p:UseAppHost=false + +FROM base AS final +USER $USERNAME +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Devon4Net.WebAPI.dll"] diff --git a/source/Templates/CleanArchitecture/docker-compose.override.yml b/source/Templates/CleanArchitecture/docker-compose.override.yml new file mode 100644 index 00000000..4d6394e9 --- /dev/null +++ b/source/Templates/CleanArchitecture/docker-compose.override.yml @@ -0,0 +1,12 @@ +version: '3.4' + +services: + devon4net.clean-architecture.web-api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_Kestrel__Certificates__Default__Password=${PASSWORD_ENV_SEEDED} + - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx + volumes: + - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro + - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro + - ${USERPROFILE}\.aspnet\https:/https/ diff --git a/source/Templates/CleanArchitecture/docker-compose.yml b/source/Templates/CleanArchitecture/docker-compose.yml new file mode 100644 index 00000000..ef05112f --- /dev/null +++ b/source/Templates/CleanArchitecture/docker-compose.yml @@ -0,0 +1,20 @@ +version: '3.4' +services: + devon4net.clean-architecture.web-api: + build: + context: ../../../. + dockerfile: source/Templates/CleanArchitecture/Devon4Net.WebAPI/Dockerfile + args: + BUILD_CONFIGURATION: Release + USERNAME: ${USER_NAME} + ports: + - "80:8085" + - "443:9000" + environment: + - ASPNETCORE_URLS=https://+:443;http://+:80 + networks: + - mydevnetwork + +networks: + mydevnetwork: + driver: bridge \ No newline at end of file diff --git a/source/devon4net.sln b/source/devon4net.sln index 5d884294..f2a34627 100644 --- a/source/devon4net.sln +++ b/source/devon4net.sln @@ -113,6 +113,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3A21B19A-C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devon4Net.WebAPI", "Templates\CleanArchitecture\Devon4Net.WebAPI\Devon4Net.WebAPI.csproj", "{D853C05D-76B5-4D9D-B419-437ADD7C23FD}" EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "Devon4Net.CleanArchitecure.docker-compose", "Templates\CleanArchitecture\Devon4Net.CleanArchitecure.docker-compose.dcproj", "{6F9DAD10-D077-4CA1-8BAA-A42D093F934E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -433,6 +435,14 @@ Global {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Release|Any CPU.Build.0 = Release|Any CPU {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Release|x64.ActiveCfg = Release|Any CPU {D853C05D-76B5-4D9D-B419-437ADD7C23FD}.Release|x64.Build.0 = Release|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Debug|x64.ActiveCfg = Debug|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Debug|x64.Build.0 = Debug|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Release|Any CPU.Build.0 = Release|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Release|x64.ActiveCfg = Release|Any CPU + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -488,6 +498,7 @@ Global {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} = {4EC816D6-3F55-41AD-B805-955F251C03EB} {3A21B19A-CDDB-4849-87F2-EBC575EA889F} = {4EC816D6-3F55-41AD-B805-955F251C03EB} {D853C05D-76B5-4D9D-B419-437ADD7C23FD} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} + {6F9DAD10-D077-4CA1-8BAA-A42D093F934E} = {B55F2379-2A06-4761-B1E7-DAD1D14A93BC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E2CBC81F-F2EE-4BD5-B567-AAF8057444D1} From 655d983a5df7ee0798a6c17849398cad0c9b3e7f Mon Sep 17 00:00:00 2001 From: JosepFe Date: Sun, 30 Jun 2024 13:13:52 +0200 Subject: [PATCH 18/21] entity configuration file clean up context --- .../Persistence/EmployeeContext.cs | 42 +++---------------- .../EmployeeConfiguration.cs | 24 +++++++++++ .../Controllers/EmployeeController.cs | 3 +- 3 files changed, 31 insertions(+), 38 deletions(-) create mode 100644 source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EntityConfiguration/EmployeeConfiguration.cs diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs index 0727c9ad..fdb5c21e 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EmployeeContext.cs @@ -6,42 +6,12 @@ namespace Devon4Net.Infrastructure.Persistence; /// /// Employee database context definition /// -public class EmployeeContext : DbContext +public class EmployeeContext(DbContextOptions options) : DbContext(options) { - /// - /// Employee context definition - /// - /// - public EmployeeContext(DbContextOptions options) - : base(options) - { - } + private const string Schema = "employees"; - /// - /// DbSet - /// - public virtual DbSet Employee { get; set; } - - /// - /// Model rules definition - /// - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(entity => - { - entity.Property(e => e.Id) - .IsRequired() - .HasMaxLength(255); - entity.Property(e => e.Name) - .IsRequired() - .HasMaxLength(255); - entity.Property(e => e.Surname) - .IsRequired() - .HasMaxLength(255); - entity.Property(e => e.Mail) - .IsRequired() - .HasMaxLength(255); - }); - } + public DbSet Employee => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) => modelBuilder + .ApplyConfigurationsFromAssembly(typeof(EmployeeContext).Assembly).HasDefaultSchema(Schema); } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EntityConfiguration/EmployeeConfiguration.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EntityConfiguration/EmployeeConfiguration.cs new file mode 100644 index 00000000..c2a550cd --- /dev/null +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Persistence/EntityConfiguration/EmployeeConfiguration.cs @@ -0,0 +1,24 @@ +using Devon4Net.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Devon4Net.Infrastructure.Persistence.EntityConfiguration; + +public class EmployeeEntityTypeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.Property(e => e.Id) + .IsRequired() + .HasMaxLength(255); + builder.Property(e => e.Name) + .IsRequired() + .HasMaxLength(255); + builder.Property(e => e.Surname) + .IsRequired() + .HasMaxLength(255); + builder.Property(e => e.Mail) + .IsRequired() + .HasMaxLength(255); + } +} \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs index d567999b..69815304 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs @@ -3,11 +3,10 @@ using Devon4Net.Application.Features.Queries.GetAllEmployees; using Devon4Net.Application.Features.Queries.GetEmployeeById; using Devon4Net.Infrastructure.Common.Application.Attributes; -using Devon4Net.Infrastructure.Common.Exceptions; using Devon4Net.Infrastructure.MediatR.Handler; using Microsoft.AspNetCore.Mvc; -namespace Devon4Net.Presentation.Controllers; +namespace Devon4Net.WebAPI.Controllers; [ApiController] [Route("/employees")] From 0c698e577cec6f1634e7ac0263f17ff4dea72840 Mon Sep 17 00:00:00 2001 From: JosepFe Date: Tue, 2 Jul 2024 23:14:39 +0200 Subject: [PATCH 19/21] Added DevonResult and DevonError object to work with Result pattern --- .../Enumerators/LogType.cs | 11 -- .../Errors/DevonError.cs | 20 ++++ .../Helpers/DevonErrorHelper.cs | 11 ++ .../Models/DevonResult.cs | 109 ++++++++++++++++++ .../Models/IDevonResult.cs | 11 ++ source/Modules/nuget.sh | 30 +++++ .../GetEmployeeById/GetEmployeeByIdHandler.cs | 18 ++- .../GetEmployeeById/GetEmployeeByIdQuery.cs | 3 +- .../Adapters/Projectors/EmployeeProjector.cs | 2 +- .../Controllers/EmployeeController.cs | 5 +- 10 files changed, 201 insertions(+), 19 deletions(-) delete mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Enumerators/LogType.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Errors/DevonError.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Helpers/DevonErrorHelper.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Models/DevonResult.cs create mode 100644 source/Modules/Devon4Net.Infrastructure.Common/Models/IDevonResult.cs create mode 100644 source/Modules/nuget.sh diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Enumerators/LogType.cs b/source/Modules/Devon4Net.Infrastructure.Common/Enumerators/LogType.cs deleted file mode 100644 index 36831a6d..00000000 --- a/source/Modules/Devon4Net.Infrastructure.Common/Enumerators/LogType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Devon4Net.Infrastructure.Common.Enumerators -{ - public enum LogType - { - Debug = 0, - Info = 1, - Warning = 2, - Error = 3, - Fatal = 4 - } -} diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Errors/DevonError.cs b/source/Modules/Devon4Net.Infrastructure.Common/Errors/DevonError.cs new file mode 100644 index 00000000..4ef213a8 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Errors/DevonError.cs @@ -0,0 +1,20 @@ +using System.Net; + +namespace Devon4Net.Infrastructure.Common.Errors; + +public class DevonError +{ + public string Code { get; init; } + + public string Message { get; init; } + + public HttpStatusCode HttpStatus { get; private set; } + + public DevonError(string code, string message, HttpStatusCode? httpStatus = null) + { + Code = code; + Message = message; + HttpStatus = httpStatus ?? HttpStatusCode.InternalServerError; + } + +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Helpers/DevonErrorHelper.cs b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/DevonErrorHelper.cs new file mode 100644 index 00000000..d1dc57ad --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Helpers/DevonErrorHelper.cs @@ -0,0 +1,11 @@ +using System; +using Devon4Net.Infrastructure.Common.Errors; + +namespace Devon4Net.Infrastructure.Common.Helpers +{ + public static class DevonErrorHelper + { + public static int ToHigherHttpStatusCode(this IEnumerable errors) + => (int)errors.Max(x => x.HttpStatus); + } +} diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Models/DevonResult.cs b/source/Modules/Devon4Net.Infrastructure.Common/Models/DevonResult.cs new file mode 100644 index 00000000..3c594259 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Models/DevonResult.cs @@ -0,0 +1,109 @@ +using Devon4Net.Infrastructure.Common.Errors; +using Devon4Net.Infrastructure.Common.Helpers; +using Microsoft.AspNetCore.Mvc; +using System.Net; +using System.Text.Json.Serialization; + +namespace Devon4Net.Infrastructure.Common.Models; + +public class DevonResult : IDevonResult +{ + protected DevonResult() { } + + public DevonResult(T value) + { + Data = value; + } + + public static implicit operator T(DevonResult result) => result.Data; + public static implicit operator DevonResult(T value) => new DevonResult(value); + + [JsonInclude] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public T Data { get; init; } + + [JsonIgnore] + public Type ValueType => typeof(T); + + [JsonInclude] + public bool IsSuccess => !Errors.Any(); + + [JsonInclude] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IEnumerable Errors { get; protected set; } = []; + + public object GetData() + { + return Data; + } + + public static DevonResult Success(T value) + { + return new DevonResult(value); + } + + public static DevonResult Error(DevonError devonError) + { + return new DevonResult() { Errors = [devonError] }; + } + + public static DevonResult Error(IEnumerable devonErrors = null) + { + return new DevonResult() + { + Errors = devonErrors + }; + } + + public void AddError(DevonError error) + { + IEnumerable errors; + if (Errors != null) + { + errors = Errors.Append(error); + } + else + { + errors = []; + } + + Errors = errors; + } + + public IEnumerable? GetErrors() + { + return Errors; + } + + public bool ReturnedError(DevonError errorCode) + { + return Errors?.Any((DevonError myError) => myError.Code == $"{errorCode}") ?? false; + } + + public bool ReturnedError(IEnumerable errorCodes) + { + return errorCodes.Any((DevonError errorCode) => Errors?.Any((DevonError myError) => myError.Code == $"{errorCode}") ?? false); + } + + public IActionResult BuildResult(HttpStatusCode httpStatusCode = HttpStatusCode.OK) + { + if (IsSuccess) + { + T dataAsT = Data; + if(dataAsT == null) + { + return new NoContentResult(); + } + + return new OkObjectResult(dataAsT) + { + StatusCode = (int)httpStatusCode, + }; + + } + return new ObjectResult(Errors) + { + StatusCode = Errors.ToHigherHttpStatusCode(), + }; + } +} \ No newline at end of file diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Models/IDevonResult.cs b/source/Modules/Devon4Net.Infrastructure.Common/Models/IDevonResult.cs new file mode 100644 index 00000000..b6d6ba11 --- /dev/null +++ b/source/Modules/Devon4Net.Infrastructure.Common/Models/IDevonResult.cs @@ -0,0 +1,11 @@ +using Devon4Net.Infrastructure.Common.Errors; + +namespace Devon4Net.Infrastructure.Common.Models +{ + public interface IDevonResult + { + IEnumerable Errors { get; } + Type ValueType { get; } + object GetData(); + } +} diff --git a/source/Modules/nuget.sh b/source/Modules/nuget.sh new file mode 100644 index 00000000..42e0b312 --- /dev/null +++ b/source/Modules/nuget.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Directory where the NuGet packages will be stored +nugets_directory="$(pwd)/nugets" + +# Ask the user for the version input +read -p "Enter the new version: " new_version + +# Create the nugets directory if it doesn't exist +mkdir -p "$nugets_directory"/"$new_version" + +# Find all csproj files and build/pack their projects +find . -type f -name "*.csproj" | while read -r csproj_file; do + echo "Processing project: $csproj_file" + + # Update the version in the csproj file + sed -i "s#.*#$new_version#g" "$csproj_file" + + # Get the directory of the csproj file + project_dir=$(dirname "$csproj_file") + + # Build the project + dotnet build --configuration Release "$project_dir" + + # Pack the project + dotnet pack --configuration Release "$project_dir" + + # Move the generated nupkg files to the nugets directory + mv "$project_dir"/bin/Release/*"$new_version".nupkg "$nugets_directory"/"$new_version" +done \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs index 1bef01c2..a431ef36 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdHandler.cs @@ -1,11 +1,13 @@ using Devon4Net.Application.Dtos; using Devon4Net.Application.Ports.Projectors; using Devon4Net.Infrastructure.Common; +using Devon4Net.Infrastructure.Common.Errors; +using Devon4Net.Infrastructure.Common.Models; using MediatR; namespace Devon4Net.Application.Features.Queries.GetEmployeeById; -public class GetEmployeeByIdHandler : IRequestHandler +public class GetEmployeeByIdHandler : IRequestHandler> { private readonly IEmployeeProjector _employeeProjector; @@ -14,9 +16,17 @@ public GetEmployeeByIdHandler(IEmployeeProjector employeeProjector) _employeeProjector = employeeProjector; } - public async Task Handle(GetEmployeeByIdQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetEmployeeByIdQuery request, CancellationToken cancellationToken) { - Devon4NetLogger.Debug("Started GetAllEmployeesHandler"); - return await _employeeProjector.GetEmployeeById(request.Id); + Devon4NetLogger.Debug("Started GetEmployeeByIdHandler"); + var employee = await _employeeProjector.GetEmployeeById(request.Id); + + if (employee == null) + { + var error = new DevonError("123", "user not found", System.Net.HttpStatusCode.NotFound); + return DevonResult.Error(error); + } + + return employee; } } \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs index 126ec820..4699ebd4 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Application/Features/Queries/GetEmployeeById/GetEmployeeByIdQuery.cs @@ -1,6 +1,7 @@ using Devon4Net.Application.Dtos; +using Devon4Net.Infrastructure.Common.Models; using Devon4Net.Infrastructure.MediatR.Query; namespace Devon4Net.Application.Features.Queries.GetEmployeeById; -public record GetEmployeeByIdQuery(Guid Id) : QueryBase; \ No newline at end of file +public record GetEmployeeByIdQuery(Guid Id) : QueryBase>; \ No newline at end of file diff --git a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs index d05c58f1..7f450717 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.Infrastructure/Adapters/Projectors/EmployeeProjector.cs @@ -27,7 +27,7 @@ public async Task GetEmployeeById(Guid id) var employeeDtos = await GetProjection(query); - return employeeDtos.FirstOrDefault() ?? throw new EmployeeNotFoundException(); + return employeeDtos.FirstOrDefault(); } public Task> GetEmployees() diff --git a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs index 69815304..faf921c3 100644 --- a/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs +++ b/source/Templates/CleanArchitecture/Devon4Net.WebAPI/Controllers/EmployeeController.cs @@ -35,9 +35,10 @@ public Task> GetAllEmployees() } [HttpGet("{employeeId}")] - public Task GetAllEmployees([FromRoute] Guid employeeId) + public async Task GetEmployeeById([FromRoute] Guid employeeId) { var query = new GetEmployeeByIdQuery(employeeId); - return _mediator.QueryAsync(query); + var result = await _mediator.QueryAsync(query); + return result.BuildResult(); } } \ No newline at end of file From 6b402d4bcfd1b583a0c78f0e1a8ff24e7ecff4cc Mon Sep 17 00:00:00 2001 From: JosepFe Date: Sun, 15 Sep 2024 11:16:02 +0200 Subject: [PATCH 20/21] create github action --- .github/workflows/nuget-package.yml | 75 +++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/nuget-package.yml diff --git a/.github/workflows/nuget-package.yml b/.github/workflows/nuget-package.yml new file mode 100644 index 00000000..7f7b7a2c --- /dev/null +++ b/.github/workflows/nuget-package.yml @@ -0,0 +1,75 @@ +name: Build and Publish NuGet Package + +on: + workflow_dispatch: + inputs: + project: + description: 'Select the project to build' + required: true + default: 'Path/To/Project1.csproj' + options: + - 'Path/To/Project1.csproj' + - 'Path/To/Project2.csproj' + version_type: + description: 'Select the version type' + required: true + default: 'patch' + options: + - 'major' + - 'minor' + - 'patch' + - 'preview' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v2 + with: + dotnet-version: '7.x' + + - name: Get latest version from GitHub Packages + run: | + LATEST_VERSION=$(dotnet nuget list source --source "https://nuget.pkg.github.com//index.json" | grep "" | head -n 1 | awk '{print $2}') + echo "Latest version: $LATEST_VERSION" + echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_ENV + + - name: Determine new version + id: version + run: | + VERSION_TYPE="${{ github.event.inputs.version_type }}" + LATEST_VERSION="${{ env.LATEST_VERSION }}" + + # Extract major, minor, patch from latest version (assumes format major.minor.patch or major.minor.patch-preview) + MAJOR=$(echo $LATEST_VERSION | cut -d. -f1) + MINOR=$(echo $LATEST_VERSION | cut -d. -f2) + PATCH=$(echo $LATEST_VERSION | cut -d. -f3 | cut -d- -f1) # Removes preview suffix if present + + # Increment version based on user input + if [ "$VERSION_TYPE" == "major" ]; then + NEW_VERSION="$((MAJOR + 1)).0.0" + elif [ "$VERSION_TYPE" == "minor" ]; then + NEW_VERSION="$MAJOR.$((MINOR + 1)).0" + elif [ "$VERSION_TYPE" == "patch" ]; then + NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" + elif [ "$VERSION_TYPE" == "preview" ]; then + PREVIEW_SUFFIX="-preview" + NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))$PREVIEW_SUFFIX" + fi + + echo "New version: $NEW_VERSION" + echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV + + - name: Restore dependencies + run: dotnet restore ${{ github.event.inputs.project }} + + - name: Build project + run: dotnet build ${{ github.event.inputs.project }} --configuration Release --no-restore + + - name: Pack NuGet package + run: dotnet pack ${{ github.event.inputs.project }} --configuration Release --no-build --output ./nuget-packages /p:PackageVersion=${{ env.NEW_VERSION }} \ No newline at end of file From c8ead9ea665441f486bbd7ab6a9c8a6b7ed7644d Mon Sep 17 00:00:00 2001 From: JosepFe Date: Sun, 15 Sep 2024 11:39:04 +0200 Subject: [PATCH 21/21] removed deprecated project references --- .../Devon4Net.Infrastructure.AWS.Serverless.csproj | 4 ---- .../Devon4Net.Infrastructure.Common.csproj | 1 - .../Devon4Net.Infrastructure.Logger.csproj | 1 - 3 files changed, 6 deletions(-) diff --git a/source/Modules/Devon4Net.Infrastructure.AWS.Serverless/Devon4Net.Infrastructure.AWS.Serverless.csproj b/source/Modules/Devon4Net.Infrastructure.AWS.Serverless/Devon4Net.Infrastructure.AWS.Serverless.csproj index facff820..fceaa24a 100644 --- a/source/Modules/Devon4Net.Infrastructure.AWS.Serverless/Devon4Net.Infrastructure.AWS.Serverless.csproj +++ b/source/Modules/Devon4Net.Infrastructure.AWS.Serverless/Devon4Net.Infrastructure.AWS.Serverless.csproj @@ -35,10 +35,6 @@ True \ - - - - diff --git a/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj b/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj index da3ae94f..a3a3882d 100644 --- a/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj +++ b/source/Modules/Devon4Net.Infrastructure.Common/Devon4Net.Infrastructure.Common.csproj @@ -24,7 +24,6 @@ - diff --git a/source/Modules/Devon4Net.Infrastructure.Logger/Devon4Net.Infrastructure.Logger.csproj b/source/Modules/Devon4Net.Infrastructure.Logger/Devon4Net.Infrastructure.Logger.csproj index b36fc1ac..6ca76b7a 100644 --- a/source/Modules/Devon4Net.Infrastructure.Logger/Devon4Net.Infrastructure.Logger.csproj +++ b/source/Modules/Devon4Net.Infrastructure.Logger/Devon4Net.Infrastructure.Logger.csproj @@ -24,7 +24,6 @@ -