From e707711a88d982958dd5db9b9590bc76a4dd2e26 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 00:51:15 -0400 Subject: [PATCH 1/8] Add CSharpFunctionalExtensions, Update non-Microsoft packages --- api/Directory.Packages.props | 53 ++++++++++--------- .../PayrollProcessor.Core.Domain.Tests.csproj | 12 +++-- .../PayrollProcessor.Core.Domain.csproj | 1 + ...ollProcessor.Data.Persistence.Tests.csproj | 10 +++- ...ayrollProcessor.Functions.Api.Tests.csproj | 10 +++- .../PayrollProcessor.Web.Api.Tests.csproj | 10 +++- 6 files changed, 61 insertions(+), 35 deletions(-) diff --git a/api/Directory.Packages.props b/api/Directory.Packages.props index 90752ea..778b0c2 100644 --- a/api/Directory.Packages.props +++ b/api/Directory.Packages.props @@ -3,44 +3,45 @@ true - - - - - - - - - + + + + + + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + - - + + - + - - - + + + - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + \ No newline at end of file diff --git a/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj b/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj index 642932e..e04591d 100644 --- a/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj +++ b/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj @@ -11,9 +11,15 @@ - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj b/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj index 1f12a38..1ccb284 100644 --- a/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj +++ b/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj @@ -6,6 +6,7 @@ + diff --git a/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj b/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj index 5f5c775..b10b13f 100644 --- a/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj +++ b/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj @@ -11,8 +11,14 @@ - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj b/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj index 59c55c7..0e6b134 100644 --- a/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj +++ b/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj @@ -13,8 +13,14 @@ - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj b/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj index 4ca5c75..cf31340 100644 --- a/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj +++ b/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj @@ -10,8 +10,14 @@ - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 33917cb2367b652b2d2c5181a1cf5085b3d839aa Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 01:13:48 -0400 Subject: [PATCH 2/8] Add warnings as errors --- api/Directory.Build.props | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/Directory.Build.props b/api/Directory.Build.props index f49bdde..0ac63c6 100644 --- a/api/Directory.Build.props +++ b/api/Directory.Build.props @@ -2,6 +2,7 @@ latest enable - CS8600;CS8602;CS8603 + true + true From 0d40a888798af570e80d7653f3722ac5a46d12f6 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 01:14:02 -0400 Subject: [PATCH 3/8] Update editorconfig --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index ab4fa5d..b82269d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -112,7 +112,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:suggestion csharp_style_unused_value_expression_statement_preference = discard_variable:silent # 'using' directive preferences -csharp_using_directive_placement = outside_namespace:silent +csharp_using_directive_placement = outside_namespace:suggestion #### C# Formatting Rules #### From 12b1074392fcb2dd7e2227f58f0f1a2e488ebbcc Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 01:14:15 -0400 Subject: [PATCH 4/8] Update vscode workspace --- payroll-processor.code-workspace | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/payroll-processor.code-workspace b/payroll-processor.code-workspace index 1377e2b..ef548f8 100644 --- a/payroll-processor.code-workspace +++ b/payroll-processor.code-workspace @@ -52,12 +52,11 @@ */ "editor.codeActionsOnSave": {} }, - - "omnisharp.defaultLaunchSolution": "PayrollProcessor.sln", "omnisharp.autoStart": true, "omnisharp.enableEditorConfigSupport": true, "omnisharp.enableRoslynAnalyzers": true, - "omnisharp.useEditorFormattingSettings": true + "omnisharp.useEditorFormattingSettings": true, + "dotnet.defaultSolution": "PayrollProcessor.sln" }, "extensions": { "recommendations": [ From 6dcf8ec4d0235407bb1d0c5bea1195d6a66aa773 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 01:14:27 -0400 Subject: [PATCH 5/8] Add IStranglerCommandDispatcher WIP --- .../Commands/IStranglerCommandDispatcher.cs | 10 +++ .../Commands/IStranglerCommandHandler.cs | 13 +++ .../Commands/StranglerCommandDispatcher.cs | 84 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandDispatcher.cs create mode 100644 api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandHandler.cs create mode 100644 api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/StranglerCommandDispatcher.cs diff --git a/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandDispatcher.cs b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandDispatcher.cs new file mode 100644 index 0000000..00faf9b --- /dev/null +++ b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandDispatcher.cs @@ -0,0 +1,10 @@ +using System.Threading; +using CSharpFunctionalExtensions; + +namespace PayrollProcessor.Core.Domain.Intrastructure.Operations.Commands; +public interface IStranglerCommandDispatcher +{ + Result Dispatch(ICommand command, CancellationToken token = default); + + Result Dispatch(ICommand command, CancellationToken token = default); +} diff --git a/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandHandler.cs b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandHandler.cs new file mode 100644 index 0000000..94446a6 --- /dev/null +++ b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandHandler.cs @@ -0,0 +1,13 @@ +using System.Threading; +using CSharpFunctionalExtensions; + +namespace PayrollProcessor.Core.Domain.Intrastructure.Operations.Commands; +public interface IStranglerCommandHandler where TCommand : ICommand +{ + Result Execute(TCommand command, CancellationToken token); +} + +public interface IStranglerCommandHandler where TCommand : ICommand +{ + Result Execute(TCommand command, CancellationToken token); +} diff --git a/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/StranglerCommandDispatcher.cs b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/StranglerCommandDispatcher.cs new file mode 100644 index 0000000..3159c62 --- /dev/null +++ b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/StranglerCommandDispatcher.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using Ardalis.GuardClauses; +using CSharpFunctionalExtensions; +using PayrollProcessor.Core.Domain.Intrastructure.Operations.Factories; + +namespace PayrollProcessor.Core.Domain.Intrastructure.Operations.Commands; + +/// +/// TODO: Temporarily named after the Strangler Fig Pattern as this serves as an implementation of migrating to CSharpFunctionalExtensions from LanguageExt. +/// +public class StranglerCommandDispatcher : IStranglerCommandDispatcher +{ + private readonly ServiceProviderDelegate serviceProvider; + private static readonly ConcurrentDictionary commandHandlers = new ConcurrentDictionary(); + + public StranglerCommandDispatcher(ServiceProviderDelegate serviceProvider) + { + Guard.Against.Null(serviceProvider, nameof(serviceProvider)); + + this.serviceProvider = serviceProvider; + } + + public Result Dispatch(ICommand command, CancellationToken token = default) + { + Guard.Against.Null(command, nameof(command)); + + var commandType = command.GetType(); + + var handler = (StranglerCommandHandlerWrapper)commandHandlers + .GetOrAdd( + commandType, +#pragma warning disable CS8603 // Possible null reference return. + t => Activator + .CreateInstance(typeof(StranglerCommandHandlerWrapperImpl<>) + .MakeGenericType(commandType))); +#pragma warning restore CS8603 // Possible null reference return. + + return handler.Dispatch(command, serviceProvider, token); + } + + public Result Dispatch(ICommand command, CancellationToken token = default) + { + Guard.Against.Null(command, nameof(command)); + + var commandType = command.GetType(); + + var handler = (StranglerCommandHandlerWrapper)commandHandlers + .GetOrAdd( + commandType, +#pragma warning disable CS8603 // Possible null reference return. + t => Activator + .CreateInstance(typeof(StranglerCreateCommandHandlerWrapperImpl<,>) + .MakeGenericType(commandType, typeof(TResponse)))); +#pragma warning restore CS8603 // Possible null reference return. + + return handler.Dispatch(command, serviceProvider, token); + } +} + +internal abstract class StranglerCommandHandlerWrapper : HandlerBase +{ + public abstract Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token); +} + +internal class StranglerCommandHandlerWrapperImpl : StranglerCommandHandlerWrapper + where TCommand : ICommand +{ + public override Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token) => + GetHandler>(serviceProvider).Execute((TCommand)command, token); +} + +internal abstract class StranglerCommandHandlerWrapper : HandlerBase +{ + public abstract Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token); +} + +internal class StranglerCreateCommandHandlerWrapperImpl : StranglerCommandHandlerWrapper + where TCommand : ICommand +{ + public override Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token) => + GetHandler>(serviceProvider).Execute((TCommand)command, token); +} From 461247431171370b7be7ab0f55b029cf2e603ff4 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 01:35:00 -0400 Subject: [PATCH 6/8] Convert EmployeeCreate Endpoint to CSharpFunctionalExtensions --- .../Features/Employees/EmployeeCreate.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs b/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs index a199384..1fc1a05 100644 --- a/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs +++ b/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Ardalis.ApiEndpoints; using Ardalis.GuardClauses; +using CSharpFunctionalExtensions; using Microsoft.AspNetCore.Mvc; using PayrollProcessor.Core.Domain.Features.Employees; using PayrollProcessor.Core.Domain.Intrastructure.Identifiers; @@ -15,10 +16,10 @@ public class EmployeeCreate : EndpointBaseAsync .WithRequest .WithActionResult { - private readonly ICommandDispatcher dispatcher; + private readonly IStranglerCommandDispatcher dispatcher; private readonly IEntityIdGenerator generator; - public EmployeeCreate(ICommandDispatcher dispatcher, IEntityIdGenerator generator) + public EmployeeCreate(IStranglerCommandDispatcher dispatcher, IEntityIdGenerator generator) { Guard.Against.Null(dispatcher, nameof(dispatcher)); Guard.Against.Null(generator, nameof(generator)); @@ -50,11 +51,11 @@ public override Task> HandleAsync([FromBody] EmployeeCrea Title = request.Title }); - return dispatcher - .Dispatch(command) - .Match>( - employee => employee, - ex => new APIErrorResult(ex.Message)); + return Task.FromResult(dispatcher + .Dispatch(command, token) + .Match, Employee>( + onSuccess: employee => Ok(employee), + onFailure: ex => new APIErrorResult(ex))); } } From 43625c7770dd651eb550c23bd39f50ac7ea859d8 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 01:38:21 -0400 Subject: [PATCH 7/8] Remove Task.FromResult --- .../Features/Employees/EmployeeCreate.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs b/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs index 1fc1a05..447083c 100644 --- a/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs +++ b/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs @@ -12,7 +12,7 @@ namespace PayrollProcessor.Web.Api.Features.Employees; -public class EmployeeCreate : EndpointBaseAsync +public class EmployeeCreate : EndpointBaseSync .WithRequest .WithActionResult { @@ -35,7 +35,7 @@ public EmployeeCreate(IStranglerCommandDispatcher dispatcher, IEntityIdGenerator OperationId = "Employees.Create", Tags = new[] { "Employees" }) ] - public override Task> HandleAsync([FromBody] EmployeeCreateRequest request, CancellationToken token) + public override ActionResult Handle([FromBody] EmployeeCreateRequest request) { var command = new EmployeeCreateCommand( generator.Generate(), @@ -51,11 +51,11 @@ public override Task> HandleAsync([FromBody] EmployeeCrea Title = request.Title }); - return Task.FromResult(dispatcher - .Dispatch(command, token) + return dispatcher + .Dispatch(command) .Match, Employee>( onSuccess: employee => Ok(employee), - onFailure: ex => new APIErrorResult(ex))); + onFailure: ex => new APIErrorResult(ex)); } } From eb9c6d6c8d9ba19f5238be3df525f1b62b0f93a4 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Sun, 10 Sep 2023 02:00:30 -0400 Subject: [PATCH 8/8] config cleanup, readme fixes --- .all-contributorsrc | 2 +- README.md | 4 ++-- payroll-processor.code-workspace | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index e5642ee..c002b2f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -9,7 +9,7 @@ "login": "KyleMcMaster", "name": "Kyle McMaster", "avatar_url": "https://avatars1.githubusercontent.com/u/11415127?v=4", - "profile": "https://github.com/KyleMcMaster", + "profile": "https://www.KyleMcMaster.com", "contributions": [ "design", "code", diff --git a/README.md b/README.md index c09ba43..764c27c 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ Sample HRIS application where a list of employees and their payroll information ### Api and Functions -![dotnet core - build & test](https://github.com/KyleMcMaster/payroll-processor/workflows/dotnet%20core%20-%20build%20&%20test/badge.svg) +![dotnet core - build & test](https://github.com/KyleMcMaster/payroll-processor/workflows/.github/workflows/policy-dotnetcore.yml/badge.svg) ### Client -![.github/workflows/npm.yml](https://github.com/KyleMcMaster/payroll-processor/workflows/.github/workflows/npm.yml/badge.svg) +![client - build & test](https://github.com/KyleMcMaster/payroll-processor/workflows/.github/workflows/policy-npm.yml/badge.svg) [![Styled with Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io) ## Motivation diff --git a/payroll-processor.code-workspace b/payroll-processor.code-workspace index ef548f8..fa9ab3f 100644 --- a/payroll-processor.code-workspace +++ b/payroll-processor.code-workspace @@ -1,5 +1,9 @@ { "folders": [ + { + "path": ".", + "name": "root" + }, { "path": "docs", "name": "docs" @@ -15,10 +19,6 @@ { "path": "vue-client", "name": "vue" - }, - { - "path": ".", - "name": "root" } ], "settings": { @@ -32,11 +32,11 @@ "api": true, "client": true, "docs": true, + "vue-client": true, "**/bin": true, "**/obj": true, "**/dist": true }, - "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": {