From bc525d76f2e8edf5521b7aa06a79b548f1be469a Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 7 Jan 2026 11:55:43 -0300 Subject: [PATCH 001/161] add bff template and package lock --- .github/workflows/publish.yml | 28 +- .github/workflows/validate.yml | 26 +- GPreviatti.Template.Hexagonal.Solution.csproj | 2 +- templates/{ => Bff}/.editorconfig | 0 templates/{ => Bff}/.gitignore | 0 templates/Bff/.template.config/template.json | 19 + templates/{ => Bff}/Directory.Build.props | 1 + templates/Bff/Directory.Packages.props | 38 + templates/{ => Bff}/Dockerfile | 0 .../Bff/Hexagonal.Solution.Template.Bff.sln | 57 + templates/Bff/Readme.md | 52 + templates/Bff/docker-compose-load-tests.yml | 68 ++ templates/Bff/docker-compose.yml | 27 + .../Cache/HybridCacheService.cs | 44 + .../Common/DefaultConfigurations.cs | 7 + .../Infrastructure/Http/BaseHttpService.cs | 74 ++ .../Http/ServiceConfigurations.cs | 14 + .../src/Infrastructure/Infrastructure.csproj | 16 + .../InfrastructureDependencyInjection.cs | 159 +++ .../Bff/src/Infrastructure/packages.lock.json | 288 +++++ .../WebApp/Endpoints/EndpointExtensions.cs | 10 + .../src/WebApp/Endpoints/OrderEndpoints.cs | 68 ++ .../GrpcServices/GrpcServiceExtensions.cs | 11 + .../src/WebApp/GrpcServices/OrderService.cs | 59 + .../HealthChecks/HealthCheckExtensions.cs | 48 + .../ExceptionHandlingMiddleware.cs | 32 + templates/Bff/src/WebApp/Program.cs | 58 + .../src/WebApp/Properties/launchSettings.json | 0 .../{ => Bff}/src/WebApp/Protos/order.proto | 0 templates/Bff/src/WebApp/WebApp.csproj | 16 + templates/{ => Bff}/src/WebApp/WebApp.http | 0 .../{ => Bff}/src/WebApp/appsettings.json | 0 templates/Bff/src/WebApp/packages.lock.json | 224 ++++ .../tests/CommonTests/CommonTests.csproj | 0 .../Bff/tests/CommonTests/packages.lock.json | 57 + .../Common/CustomWebApplicationFactory.cs | 11 + .../IntegrationTests}/Fixtures/BaseFixture.cs | 0 .../tests/IntegrationTests/GlobalUsings.cs | 0 .../IntegrationTests/IntegrationTests.csproj | 27 + .../WebApp/Grpc/Common/ApiGrpcHelper.cs | 0 .../WebApp/Grpc/Orders/GetOrderGrpcTest.cs | 0 .../WebApp/Http/Common/ApiHelper.cs | 0 .../WebApp/Http/Common/BaseHttpFixture.cs | 0 .../WebApp/Http/Orders/CreateOrderTest.cs | 0 .../WebApp/Http/Orders/GetAllOrdersTest.cs | 0 .../WebApp/Http/Orders/GetOrderTest.cs | 0 .../tests/IntegrationTests/packages.lock.json | 714 +++++++++++ .../tests/LoadTests/protos/order.proto | 0 .../{ => Bff}/tests/LoadTests/scriptGrpc.js | 0 .../{ => Bff}/tests/LoadTests/scriptHttp.js | 0 templates/Full/.editorconfig | 197 ++++ .../prompts/create-migrations.prompt.md | 0 .../.github/prompts/crud-generator.prompt.md | 0 templates/Full/.gitignore | 482 ++++++++ .../{ => Full}/.template.config/template.json | 8 +- templates/Full/Directory.Build.props | 12 + templates/{ => Full}/Directory.Packages.props | 0 templates/Full/Dockerfile | 19 + .../Hexagonal.Solution.Template.Full.sln} | 0 templates/{ => Full}/Readme.md | 0 .../{ => Full}/docker-compose-load-tests.yml | 0 templates/{ => Full}/docker-compose.yml | 0 templates/{ => Full}/scripts/migrations.sql | 0 templates/{ => Full}/scripts/seeds.sql | 0 .../src/Application/Application.csproj | 0 .../ApplicationDependencyInjection.cs | 0 .../Constants/DefaultApplicationMessages.cs | 0 .../Common/Constants/DefaultConfigurations.cs | 0 .../Common/Constants/NotificationType.cs | 0 .../Common/Messages/BaseMessage.cs | 0 .../Messages/CreateNotificationMessage.cs | 0 .../Common/Repositories/IBaseRepository.cs | 0 .../Common/Requests/BaseRequest.cs | 0 .../Common/Requests/BaseResponse.cs | 0 .../Common/Services/IHybridCacheService.cs | 0 .../Common/Services/IProduceService.cs | 0 .../Common/UseCases/BaseInOutUseCase.cs | 0 .../Common/UseCases/BaseInUseCase.cs | 0 .../Common/UseCases/BaseOutUseCase.cs | 0 .../Common/UseCases/BaseUseCase.cs | 0 .../CreateNotificationUseCase.cs | 0 .../GetAllNotificationsUseCase.cs | 0 .../Notifications/GetNotificationUseCase.cs | 0 .../Notifications/NotificationDto.cs | 0 .../Application/Orders/CreateOrderUseCase.cs | 0 .../Application/Orders/GetAllOrdersUseCase.cs | 0 .../src/Application/Orders/GetOrderUseCase.cs | 0 .../src/Application/Orders/OrderDto.cs | 0 .../Full/src/Application/packages.lock.json | 76 ++ .../src/Domain/Common/DomainEntity.cs | 0 .../{ => Full}/src/Domain/Common/Result.cs | 0 templates/{ => Full}/src/Domain/Domain.csproj | 0 .../src/Domain/DomainDependencyInjection.cs | 0 .../src/Domain/Notifications/Notification.cs | 0 .../{ => Full}/src/Domain/Orders/Item.cs | 0 .../{ => Full}/src/Domain/Orders/Order.cs | 0 templates/Full/src/Domain/packages.lock.json | 21 + .../InfrastructureCacheDependencyInjection.cs | 0 .../Cache/Services/HybridCacheService.cs | 0 .../Common/BaseBackgroundService.cs | 0 .../Infrastructure/Data/Common/BaseMapping.cs | 0 .../Data/Common/BaseRepository.cs | 0 .../InfrastructureDataDependencyInjection.cs | 0 .../Data/Mapping/ItemDbMapping.cs | 0 .../Data/Mapping/NotificationDbMapping.cs | 0 .../Data/Mapping/OrderDbMapping.cs | 0 ...012134409_AddNotificationTable.Designer.cs | 0 .../20251012134409_AddNotificationTable.cs | 0 .../Migrations/MyDbContextModelSnapshot.cs | 0 .../src/Infrastructure/Data/MyDbContext.cs | 0 .../Infrastructure/Data/MyDbContextFactory.cs | 0 .../src/Infrastructure/Infrastructure.csproj | 0 .../InfrastructureDependencyInjection.cs | 0 .../Messaging/Consumers/BaseConsumer.cs | 0 .../Consumers/CreateNotificationConsumer.cs | 0 .../Messaging/MessagingDependencyInjection.cs | 0 .../Messaging/Producers/ProducerService.cs | 0 ...ructureOpenTelemetryDependencyInjection.cs | 0 .../src/Infrastructure/packages.lock.json | 822 +++++++++++++ .../WebApp/Endpoints/EndpointExtensions.cs | 0 .../src/WebApp/Endpoints/OrderEndpoints.cs | 0 .../GrpcServices/GrpcServiceExtensions.cs | 0 .../src/WebApp/GrpcServices/OrderService.cs | 0 .../HealthChecks/HealthCheckExtensions.cs | 0 .../ExceptionHandlingMiddleware.cs | 0 templates/{ => Full}/src/WebApp/Program.cs | 0 .../src/WebApp/Properties/launchSettings.json | 32 + templates/Full/src/WebApp/Protos/order.proto | 34 + templates/{ => Full}/src/WebApp/WebApp.csproj | 0 templates/Full/src/WebApp/WebApp.http | 63 + templates/Full/src/WebApp/appsettings.json | 7 + templates/Full/src/WebApp/packages.lock.json | 486 ++++++++ .../Full/tests/CommonTests/CommonTests.csproj | 7 + .../tests/CommonTests/Fixtures/BaseFixture.cs | 14 + .../Full/tests/CommonTests/packages.lock.json | 57 + .../Common/CustomWebApplicationFactory.cs | 0 .../IntegrationTests/Data/BaseDataFixture.cs | 0 .../Data/BaseRepositoryTest.cs | 0 .../tests/IntegrationTests/GlobalUsings.cs | 2 + .../IntegrationTests/IntegrationTests.csproj | 0 .../WebApp/Grpc/Common/ApiGrpcHelper.cs | 12 + .../WebApp/Grpc/Orders/GetOrderGrpcTest.cs | 70 ++ .../WebApp/Http/Common/ApiHelper.cs | 57 + .../WebApp/Http/Common/BaseHttpFixture.cs | 14 + .../WebApp/Http/Orders/CreateOrderTest.cs | 65 + .../WebApp/Http/Orders/GetAllOrdersTest.cs | 107 ++ .../WebApp/Http/Orders/GetOrderTest.cs | 68 ++ .../Messaging/Common/BaseMessagingFixture.cs | 0 .../Notifications/CreateNotificationTest.cs | 0 .../tests/IntegrationTests/packages.lock.json | 1042 +++++++++++++++++ .../Full/tests/LoadTests/protos/order.proto | 24 + templates/Full/tests/LoadTests/scriptGrpc.js | 52 + templates/Full/tests/LoadTests/scriptHttp.js | 53 + .../Common/BaseApplicationFixture.cs | 0 .../Common/RepositoryMockExtensions.cs | 0 .../CreateNotificationUseCaseTests.cs | 0 .../GetAllNotificationsUseCaseTests.cs | 0 .../GetNotificationUseCaseTest.cs | 0 .../Orders/CreateOrderUseCaseTests.cs | 0 .../Orders/GetAllOrdersUseCaseTest.cs | 0 .../Orders/GetOrderUseCaseTests.cs | 0 .../Architecture/ApplicationTests.cs | 0 .../UnitTests/Architecture/DomainTests.cs | 0 .../UnitTests/Domain/NotificationTests.cs | 0 .../tests/UnitTests/Domain/OrderTests.cs | 0 .../tests/UnitTests/GlobalUsings.cs | 0 .../tests/UnitTests/UnitTests.csproj | 0 .../Full/tests/UnitTests/packages.lock.json | 268 +++++ .../UnitTests/stryker-config-application.json | 0 .../UnitTests/stryker-config-domain.json | 0 170 files changed, 6405 insertions(+), 21 deletions(-) rename templates/{ => Bff}/.editorconfig (100%) rename templates/{ => Bff}/.gitignore (100%) create mode 100644 templates/Bff/.template.config/template.json rename templates/{ => Bff}/Directory.Build.props (83%) create mode 100644 templates/Bff/Directory.Packages.props rename templates/{ => Bff}/Dockerfile (100%) create mode 100644 templates/Bff/Hexagonal.Solution.Template.Bff.sln create mode 100644 templates/Bff/Readme.md create mode 100644 templates/Bff/docker-compose-load-tests.yml create mode 100644 templates/Bff/docker-compose.yml create mode 100644 templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs create mode 100644 templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs create mode 100644 templates/Bff/src/Infrastructure/Http/BaseHttpService.cs create mode 100644 templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs create mode 100644 templates/Bff/src/Infrastructure/Infrastructure.csproj create mode 100644 templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs create mode 100644 templates/Bff/src/Infrastructure/packages.lock.json create mode 100644 templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs create mode 100644 templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs create mode 100644 templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs create mode 100644 templates/Bff/src/WebApp/GrpcServices/OrderService.cs create mode 100644 templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs create mode 100644 templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs create mode 100644 templates/Bff/src/WebApp/Program.cs rename templates/{ => Bff}/src/WebApp/Properties/launchSettings.json (100%) rename templates/{ => Bff}/src/WebApp/Protos/order.proto (100%) create mode 100644 templates/Bff/src/WebApp/WebApp.csproj rename templates/{ => Bff}/src/WebApp/WebApp.http (100%) rename templates/{ => Bff}/src/WebApp/appsettings.json (100%) create mode 100644 templates/Bff/src/WebApp/packages.lock.json rename templates/{ => Bff}/tests/CommonTests/CommonTests.csproj (100%) create mode 100644 templates/Bff/tests/CommonTests/packages.lock.json create mode 100644 templates/Bff/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs rename templates/{tests/CommonTests => Bff/tests/IntegrationTests}/Fixtures/BaseFixture.cs (100%) rename templates/{ => Bff}/tests/IntegrationTests/GlobalUsings.cs (100%) create mode 100644 templates/Bff/tests/IntegrationTests/IntegrationTests.csproj rename templates/{ => Bff}/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs (100%) rename templates/{ => Bff}/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs (100%) rename templates/{ => Bff}/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs (100%) rename templates/{ => Bff}/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs (100%) rename templates/{ => Bff}/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs (100%) rename templates/{ => Bff}/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs (100%) rename templates/{ => Bff}/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs (100%) create mode 100644 templates/Bff/tests/IntegrationTests/packages.lock.json rename templates/{ => Bff}/tests/LoadTests/protos/order.proto (100%) rename templates/{ => Bff}/tests/LoadTests/scriptGrpc.js (100%) rename templates/{ => Bff}/tests/LoadTests/scriptHttp.js (100%) create mode 100644 templates/Full/.editorconfig rename templates/{ => Full}/.github/prompts/create-migrations.prompt.md (100%) rename templates/{ => Full}/.github/prompts/crud-generator.prompt.md (100%) create mode 100644 templates/Full/.gitignore rename templates/{ => Full}/.template.config/template.json (68%) create mode 100644 templates/Full/Directory.Build.props rename templates/{ => Full}/Directory.Packages.props (100%) create mode 100644 templates/Full/Dockerfile rename templates/{Hexagonal.Solution.Template.sln => Full/Hexagonal.Solution.Template.Full.sln} (100%) rename templates/{ => Full}/Readme.md (100%) rename templates/{ => Full}/docker-compose-load-tests.yml (100%) rename templates/{ => Full}/docker-compose.yml (100%) rename templates/{ => Full}/scripts/migrations.sql (100%) rename templates/{ => Full}/scripts/seeds.sql (100%) rename templates/{ => Full}/src/Application/Application.csproj (100%) rename templates/{ => Full}/src/Application/ApplicationDependencyInjection.cs (100%) rename templates/{ => Full}/src/Application/Common/Constants/DefaultApplicationMessages.cs (100%) rename templates/{ => Full}/src/Application/Common/Constants/DefaultConfigurations.cs (100%) rename templates/{ => Full}/src/Application/Common/Constants/NotificationType.cs (100%) rename templates/{ => Full}/src/Application/Common/Messages/BaseMessage.cs (100%) rename templates/{ => Full}/src/Application/Common/Messages/CreateNotificationMessage.cs (100%) rename templates/{ => Full}/src/Application/Common/Repositories/IBaseRepository.cs (100%) rename templates/{ => Full}/src/Application/Common/Requests/BaseRequest.cs (100%) rename templates/{ => Full}/src/Application/Common/Requests/BaseResponse.cs (100%) rename templates/{ => Full}/src/Application/Common/Services/IHybridCacheService.cs (100%) rename templates/{ => Full}/src/Application/Common/Services/IProduceService.cs (100%) rename templates/{ => Full}/src/Application/Common/UseCases/BaseInOutUseCase.cs (100%) rename templates/{ => Full}/src/Application/Common/UseCases/BaseInUseCase.cs (100%) rename templates/{ => Full}/src/Application/Common/UseCases/BaseOutUseCase.cs (100%) rename templates/{ => Full}/src/Application/Common/UseCases/BaseUseCase.cs (100%) rename templates/{ => Full}/src/Application/Notifications/CreateNotificationUseCase.cs (100%) rename templates/{ => Full}/src/Application/Notifications/GetAllNotificationsUseCase.cs (100%) rename templates/{ => Full}/src/Application/Notifications/GetNotificationUseCase.cs (100%) rename templates/{ => Full}/src/Application/Notifications/NotificationDto.cs (100%) rename templates/{ => Full}/src/Application/Orders/CreateOrderUseCase.cs (100%) rename templates/{ => Full}/src/Application/Orders/GetAllOrdersUseCase.cs (100%) rename templates/{ => Full}/src/Application/Orders/GetOrderUseCase.cs (100%) rename templates/{ => Full}/src/Application/Orders/OrderDto.cs (100%) create mode 100644 templates/Full/src/Application/packages.lock.json rename templates/{ => Full}/src/Domain/Common/DomainEntity.cs (100%) rename templates/{ => Full}/src/Domain/Common/Result.cs (100%) rename templates/{ => Full}/src/Domain/Domain.csproj (100%) rename templates/{ => Full}/src/Domain/DomainDependencyInjection.cs (100%) rename templates/{ => Full}/src/Domain/Notifications/Notification.cs (100%) rename templates/{ => Full}/src/Domain/Orders/Item.cs (100%) rename templates/{ => Full}/src/Domain/Orders/Order.cs (100%) create mode 100644 templates/Full/src/Domain/packages.lock.json rename templates/{ => Full}/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs (100%) rename templates/{ => Full}/src/Infrastructure/Cache/Services/HybridCacheService.cs (100%) rename templates/{ => Full}/src/Infrastructure/Common/BaseBackgroundService.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Common/BaseMapping.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Common/BaseRepository.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Mapping/ItemDbMapping.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Mapping/OrderDbMapping.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/MyDbContext.cs (100%) rename templates/{ => Full}/src/Infrastructure/Data/MyDbContextFactory.cs (100%) rename templates/{ => Full}/src/Infrastructure/Infrastructure.csproj (100%) rename templates/{ => Full}/src/Infrastructure/InfrastructureDependencyInjection.cs (100%) rename templates/{ => Full}/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs (100%) rename templates/{ => Full}/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs (100%) rename templates/{ => Full}/src/Infrastructure/Messaging/MessagingDependencyInjection.cs (100%) rename templates/{ => Full}/src/Infrastructure/Messaging/Producers/ProducerService.cs (100%) rename templates/{ => Full}/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs (100%) create mode 100644 templates/Full/src/Infrastructure/packages.lock.json rename templates/{ => Full}/src/WebApp/Endpoints/EndpointExtensions.cs (100%) rename templates/{ => Full}/src/WebApp/Endpoints/OrderEndpoints.cs (100%) rename templates/{ => Full}/src/WebApp/GrpcServices/GrpcServiceExtensions.cs (100%) rename templates/{ => Full}/src/WebApp/GrpcServices/OrderService.cs (100%) rename templates/{ => Full}/src/WebApp/HealthChecks/HealthCheckExtensions.cs (100%) rename templates/{ => Full}/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs (100%) rename templates/{ => Full}/src/WebApp/Program.cs (100%) create mode 100644 templates/Full/src/WebApp/Properties/launchSettings.json create mode 100644 templates/Full/src/WebApp/Protos/order.proto rename templates/{ => Full}/src/WebApp/WebApp.csproj (100%) create mode 100644 templates/Full/src/WebApp/WebApp.http create mode 100644 templates/Full/src/WebApp/appsettings.json create mode 100644 templates/Full/src/WebApp/packages.lock.json create mode 100644 templates/Full/tests/CommonTests/CommonTests.csproj create mode 100644 templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs create mode 100644 templates/Full/tests/CommonTests/packages.lock.json rename templates/{ => Full}/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs (100%) rename templates/{ => Full}/tests/IntegrationTests/Data/BaseDataFixture.cs (100%) rename templates/{ => Full}/tests/IntegrationTests/Data/BaseRepositoryTest.cs (100%) create mode 100644 templates/Full/tests/IntegrationTests/GlobalUsings.cs rename templates/{ => Full}/tests/IntegrationTests/IntegrationTests.csproj (100%) create mode 100644 templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs create mode 100644 templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs create mode 100644 templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs create mode 100644 templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs create mode 100644 templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs create mode 100644 templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs create mode 100644 templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs rename templates/{ => Full}/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs (100%) rename templates/{ => Full}/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs (100%) create mode 100644 templates/Full/tests/IntegrationTests/packages.lock.json create mode 100644 templates/Full/tests/LoadTests/protos/order.proto create mode 100644 templates/Full/tests/LoadTests/scriptGrpc.js create mode 100644 templates/Full/tests/LoadTests/scriptHttp.js rename templates/{ => Full}/tests/UnitTests/Application/Common/BaseApplicationFixture.cs (100%) rename templates/{ => Full}/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs (100%) rename templates/{ => Full}/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs (100%) rename templates/{ => Full}/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs (100%) rename templates/{ => Full}/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/Architecture/ApplicationTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/Architecture/DomainTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/Domain/NotificationTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/Domain/OrderTests.cs (100%) rename templates/{ => Full}/tests/UnitTests/GlobalUsings.cs (100%) rename templates/{ => Full}/tests/UnitTests/UnitTests.csproj (100%) create mode 100644 templates/Full/tests/UnitTests/packages.lock.json rename templates/{ => Full}/tests/UnitTests/stryker-config-application.json (100%) rename templates/{ => Full}/tests/UnitTests/stryker-config-domain.json (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cf3cd8a6..b8735b2c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,22 +4,34 @@ on: workflow_dispatch: jobs: - validate: + validate-full-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: - project: "hexagonal-solution-template" + project: "hexagonal-solution-template-full" organization: "gpreviatti" - unit_test_project_path: "./templates/tests/UnitTests/" + unit_test_project_path: "./Full/templates/tests/UnitTests/" unit_test_verbosity: 'd' dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" - domain_stryker_config_path: "./templates/tests/UnitTests/stryker-config-domain.json" - application_stryker_config_path: "./templates/tests/UnitTests/stryker-config-application.json" - integration_test_project_path: "./templates/tests/IntegrationTests" - docker_compose_file_path: "./templates/docker-compose.yml" + domain_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-domain.json" + application_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-application.json" + integration_test_project_path: "./Full/templates/tests/IntegrationTests" + docker_compose_file_path: "./Full/templates/docker-compose.yml" secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} + + validate-minimal-template: + uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 + with: + project: "hexagonal-solution-template-minimal" + organization: "gpreviatti" + dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" + integration_test_project_path: "./Minimal/templates/tests/IntegrationTests" + docker_compose_file_path: "./Minimal/templates/docker-compose.yml" + secrets: + sonar_token: ${{ secrets.SONAR_TOKEN }} + pack-and-publish: - needs: validate + needs: [validate-full-template, validate-minimal-template] uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-pack.yml@v1 with: dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 269e4843..44940c7a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -5,18 +5,28 @@ on: workflow_dispatch: jobs: - validate: + validate-full-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: - project: "hexagonal-solution-template" + project: "hexagonal-solution-template-full" organization: "gpreviatti" - unit_test_project_path: "./templates/tests/UnitTests/" + unit_test_project_path: "./Full/templates/tests/UnitTests/" unit_test_verbosity: 'd' dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" - domain_stryker_config_path: "./templates/tests/UnitTests/stryker-config-domain.json" - application_stryker_config_path: "./templates/tests/UnitTests/stryker-config-application.json" - integration_test_project_path: "./templates/tests/IntegrationTests" - docker_compose_file_path: "./templates/docker-compose.yml" + domain_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-domain.json" + application_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-application.json" + integration_test_project_path: "./Full/templates/tests/IntegrationTests" + docker_compose_file_path: "./Full/templates/docker-compose.yml" secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} - + + validate-minimal-template: + uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 + with: + project: "hexagonal-solution-template-minimal" + organization: "gpreviatti" + dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" + integration_test_project_path: "./Minimal/templates/tests/IntegrationTests" + docker_compose_file_path: "./Minimal/templates/docker-compose.yml" + secrets: + sonar_token: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/GPreviatti.Template.Hexagonal.Solution.csproj b/GPreviatti.Template.Hexagonal.Solution.csproj index c78eba8d..af673d66 100644 --- a/GPreviatti.Template.Hexagonal.Solution.csproj +++ b/GPreviatti.Template.Hexagonal.Solution.csproj @@ -4,7 +4,7 @@ GPreviatti.Template.Hexagonal.Solution Hexagonal architecture solution template Giovanni Brunno Previatti - Solution following Hexagonal architecture and best praticies. + Solution following Hexagonal architecture and best practices. template;hexagonal-architecture;clean-architecture;ddd;net8 net10.0 true diff --git a/templates/.editorconfig b/templates/Bff/.editorconfig similarity index 100% rename from templates/.editorconfig rename to templates/Bff/.editorconfig diff --git a/templates/.gitignore b/templates/Bff/.gitignore similarity index 100% rename from templates/.gitignore rename to templates/Bff/.gitignore diff --git a/templates/Bff/.template.config/template.json b/templates/Bff/.template.config/template.json new file mode 100644 index 00000000..8ab61a0a --- /dev/null +++ b/templates/Bff/.template.config/template.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json.schemastore.org/template", + "author": "Giovanni Brunno Previatti", + "identity": "Hexagonal.Solution.Template.Bff", + "name": "Hexagonal solution template Bff", + "description": "Solution template following hexagonal architecture structure best practices for backend for frontend (BFF) applications", + "shortName": "bff", + "sourceName": "Hexagonal.Solution.Template.Bff", + "classifications": [ + "common", + "template", + "hexagonal-architecture" + ], + "tags": { + "language": "C#", + "type": "project" + }, + "preferNameDirectory": true +} diff --git a/templates/Directory.Build.props b/templates/Bff/Directory.Build.props similarity index 83% rename from templates/Directory.Build.props rename to templates/Bff/Directory.Build.props index b46ddbca..df67cf0c 100644 --- a/templates/Directory.Build.props +++ b/templates/Bff/Directory.Build.props @@ -4,6 +4,7 @@ enable enable true + true diff --git a/templates/Bff/Directory.Packages.props b/templates/Bff/Directory.Packages.props new file mode 100644 index 00000000..703cea51 --- /dev/null +++ b/templates/Bff/Directory.Packages.props @@ -0,0 +1,38 @@ + + + true + true + $(NoWarn);NU1507 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/Dockerfile b/templates/Bff/Dockerfile similarity index 100% rename from templates/Dockerfile rename to templates/Bff/Dockerfile diff --git a/templates/Bff/Hexagonal.Solution.Template.Bff.sln b/templates/Bff/Hexagonal.Solution.Template.Bff.sln new file mode 100644 index 00000000..f2794c89 --- /dev/null +++ b/templates/Bff/Hexagonal.Solution.Template.Bff.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11222.16 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6A878B68-ADCF-470F-8273-DA5C32E2DB79}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D499CDEC-C117-4968-A519-21C64757C640}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApp", "src\WebApp\WebApp.csproj", "{EB1974DC-D58F-41A8-A965-CC772F2B8CEB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{EA853C22-3BDD-43C0-A150-8C77BBE41058}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{34744AD9-DAE2-429F-94C2-CCE51DFAD280}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props + docker-compose.yml = docker-compose.yml + scripts\migrations.sql = scripts\migrations.sql + scripts\seeds.sql = scripts\seeds.sql + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.Build.0 = Release|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.Build.0 = Release|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} + {EA853C22-3BDD-43C0-A150-8C77BBE41058} = {D499CDEC-C117-4968-A519-21C64757C640} + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3AD0BA6E-E14B-401F-B4F6-C7736B01643C} + EndGlobalSection +EndGlobal diff --git a/templates/Bff/Readme.md b/templates/Bff/Readme.md new file mode 100644 index 00000000..08f55d2f --- /dev/null +++ b/templates/Bff/Readme.md @@ -0,0 +1,52 @@ +# Hexagonal Architecture Solution Template + +This repository provides a template for building applications using the Hexagonal Architecture (also known as Ports and Adapters Architecture). The template includes a well-structured project layout, sample implementations, and best practices to help you get started quickly. + +## Project Structure + +- `src/`: Contains the source code for the application. + - `Infrastructure/`: Contains implementations for external systems (e.g., databases, messaging). + - `WebApp/`: Contains the web application layer (e.g., REST API, gRPC services). + +- `tests/`: Contains unit and integration tests for the application. + - `CommonTests/`: Contains shared test utilities and base classes. + - `IntegrationTests/`: Contains integration tests for the application. + - `LoadTests/`: Contains load tests for performance evaluation. + +## Helper Commands + +### Start docker compose + +```bash +docker-compose up -d +``` + +### Run load tests with full summary for HTTP script + +```bash +k6 run tests/LoadTests/scriptHttp.js --summary-mode=full +``` + +### Run load tests with full summary for gRPC script + +```bash +k6 run tests/LoadTests/scriptGrpc.js --summary-mode=full +``` + +### Run tests (except load tests) + +```bash +dotnet test +``` + +### Run unit tests + +```bash +dotnet test tests/UnitTests +``` + +### Run integration tests + +```bash +dotnet test tests/IntegrationTests +```s diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml new file mode 100644 index 00000000..189007f1 --- /dev/null +++ b/templates/Bff/docker-compose-load-tests.yml @@ -0,0 +1,68 @@ +name: hexagonal-solution-template-load-tests + +services: + k6: + image: grafana/k6:1.4.2 + depends_on: + webapp: + condition: service_healthy + ports: + - "6565:6565" + environment: + - WEBAPP_URL=http://webapp:5000 + - K6_SUMMARY_MODE=full + command: ["run", "/LoadTests/scriptHttp.js", ] + volumes: + - ./tests/LoadTests:/LoadTests + networks: + - hexagonal_solution_template_network + + webapp: + build: + context: . + dockerfile: ./Dockerfile + ports: + - "5000:5000" + depends_on: + redis: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/health" ] + interval: 30s + timeout: 10s + retries: 5 + start_period: 15s + environment: + - ASPNETCORE_ENVIRONMENT=Development + - LOGGING__LOGLEVEL__DEFAULT=Warning + - LOGGING__LOGLEVEL__MICROSOFT=Warning + - LOGGING__LOGLEVEL__MICROSOFT_HOSTING_LIFETIME=Warning + - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE=Warning + - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning + - ENABLE_SENSITIVE_DATA_LOGGING=false + - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp + - OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889 + - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://aspire-dashboard:18889 + - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://aspire-dashboard:18889 + - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://aspire-dashboard:18889 + - OTEL_EXPORTER_OTLP_PROTOCOL=grpc + - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests + - ConnectionStrings__OrderDb=Server=sqlserver,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true; + - ConnectionStrings__Redis=redis:6379 + - ConnectionStrings__RabbitMQ=amqp://guest:guest@rabbitmq:5672/ + networks: + - hexagonal_solution_template_network + + aspire-dashboard: + extends: + file: docker-compose.yml + service: aspire-dashboard + + redis: + extends: + file: docker-compose.yml + service: redis + +networks: + hexagonal_solution_template_network: + driver: bridge diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml new file mode 100644 index 00000000..0bad6fa3 --- /dev/null +++ b/templates/Bff/docker-compose.yml @@ -0,0 +1,27 @@ +name: hexagonal-solution-template + +services: + aspire-dashboard: + image: mcr.microsoft.com/dotnet/aspire-dashboard:13 + ports: + - "18888:18888" + - "18889:18889" + environment: + - DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true + + redis: + image: redis:8 + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 3s + timeout: 10s + retries: 5 + start_period: 15s + networks: + - hexagonal_solution_template_network + +networks: + hexagonal_solution_template_network: + driver: bridge diff --git a/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs b/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs new file mode 100644 index 00000000..6c265b0a --- /dev/null +++ b/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs @@ -0,0 +1,44 @@ +using Infrastructure.Common; +using Microsoft.Extensions.Caching.Hybrid; +using Microsoft.Extensions.Logging; + +namespace Infrastructure.Cache; + +public sealed class HybridCacheService(HybridCache cache, ILogger logger) +{ + private readonly HybridCache _cache = cache; + private readonly ILogger _logger = logger; + + public async ValueTask GetOrCreateAsync( + string key, + Func> factory, + CancellationToken cancellationToken + ) + { + _logger.LogDebug("[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Retrieving cache entry", key); + + var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); + + _logger.LogDebug("[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Cache entry retrieved", key); + + return result; + } + + public async ValueTask CreateAsync(string key, TResult value, CancellationToken cancellationToken) + { + _logger.LogDebug("[HybridCacheService] | [CreateAsync] | [{Key}] | Creating cache entry", key); + + await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); + + _logger.LogDebug("[HybridCacheService] | [CreateAsync] | [{Key}] | Cache entry created", key); + } + + public async ValueTask DeleteAsync(string key, CancellationToken cancellationToken) + { + _logger.LogDebug("[HybridCacheService] | [DeleteAsync] | [{Key}] | Deleting cache entry", key); + + await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); + + _logger.LogDebug("[HybridCacheService] | [DeleteAsync] | [{Key}] | Cache entry deleted", key); + } +} diff --git a/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs b/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs new file mode 100644 index 00000000..8f33aa80 --- /dev/null +++ b/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs @@ -0,0 +1,7 @@ +namespace Infrastructure.Common; + +public static class DefaultConfigurations +{ + public static string ApplicationName => "Hexagonal.Solution.Template.Bff"; + +} diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs new file mode 100644 index 00000000..1ed40a80 --- /dev/null +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -0,0 +1,74 @@ +using System.Diagnostics; +using System.Text.Json; +using Microsoft.Extensions.Logging; + +namespace Infrastructure.Http; + +public class BaseHttpService(HttpClient httpClient, ILogger logger) +{ + protected readonly HttpClient _httpClient = httpClient; + protected readonly ILogger _logger = logger; + protected readonly JsonSerializerOptions _jsonSerializerOptions = new(JsonSerializerDefaults.Web); + protected readonly Stopwatch _stopwatch = new(); + + public async Task SendAsync( + string requestUri, + HttpMethod method, + CancellationToken cancellationToken, + Dictionary? headers = null + ) + { + _stopwatch.Start(); + _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); + + var requestMessage = new HttpRequestMessage(method, requestUri); + + if (headers != null) foreach (var header in headers) + requestMessage.Headers.Add(header.Key, header.Value); + + var httpResponse = await _httpClient.SendAsync(requestMessage, cancellationToken); + httpResponse.EnsureSuccessStatusCode(); + + var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken); + + var result = await JsonSerializer.DeserializeAsync(stream, _jsonSerializerOptions, cancellationToken); + + _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); + + return result; + } + + public async Task SendAsync( + string requestUri, + HttpMethod method, + TRequest request, + CancellationToken cancellationToken, + Dictionary? headers = null + ) + { + _stopwatch.Start(); + _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); + + var memoryStream = new MemoryStream(); + await JsonSerializer.SerializeAsync(memoryStream, request, _jsonSerializerOptions, cancellationToken); + + memoryStream.Seek(0, SeekOrigin.Begin); + var requestMessage = new HttpRequestMessage(method, requestUri); + + if (headers != null) foreach (var header in headers) + requestMessage.Headers.Add(header.Key, header.Value); + + using var requestContent = new StreamContent(memoryStream); + requestMessage.Content = requestContent; + + using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + response.EnsureSuccessStatusCode(); + var content = await response.Content.ReadAsStreamAsync(cancellationToken); + + var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); + + _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); + + return result; + } +} diff --git a/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs b/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs new file mode 100644 index 00000000..02c8f680 --- /dev/null +++ b/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs @@ -0,0 +1,14 @@ +namespace Infrastructure.Http; + +public static class ServicesKeys +{ + public const string Orders = nameof(Orders); + public const string Payments = nameof(Payments); +} + +public sealed class ServiceConfigurations +{ + public string Name { get; set; } + public string Url { get; set; } + public object Authentication { get; set; } +} \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Infrastructure.csproj b/templates/Bff/src/Infrastructure/Infrastructure.csproj new file mode 100644 index 00000000..17ea259b --- /dev/null +++ b/templates/Bff/src/Infrastructure/Infrastructure.csproj @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs new file mode 100644 index 00000000..ed8827c3 --- /dev/null +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -0,0 +1,159 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Exporter; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using OpenTelemetry.Logs; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Configuration; +using Infrastructure.Cache; +using Infrastructure.Http; +using Infrastructure.Common; + +namespace Infrastructure; + +public static class InfrastructureDependencyInjection +{ + extension(WebApplicationBuilder builder) + { + public WebApplicationBuilder AddInfrastructure() + { + var configuration = builder.Configuration; + + builder.Services + .AddCache(configuration) + .AddHttpServices(configuration); + + builder.AddOpenTelemetry(); + + return builder; + } + + internal WebApplicationBuilder AddOpenTelemetry() + { + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower() == "grpc" + ? OtlpExportProtocol.Grpc + : OtlpExportProtocol.HttpProtobuf; + var exporterMetricsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"); + var exporterTracesEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"); + var exporterLogsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"); + + if ( + string.Equals(environment, "IntegrationTests", StringComparison.OrdinalIgnoreCase) || + string.IsNullOrWhiteSpace(exporterLogsEndpoint) || + string.IsNullOrWhiteSpace(exporterMetricsEndpoint) || + string.IsNullOrWhiteSpace(exporterTracesEndpoint) + ) + { + return builder; + } + + builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource.AddEnvironmentVariableDetector()) + .WithMetrics(metrics => metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddOtlpExporter(options => + { + options.Protocol = exporterProtocol; + options.Endpoint = new Uri(exporterMetricsEndpoint); + }) + ) + .WithTracing(tracing => tracing + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation(options => + { + options.RecordException = true; + }) + .AddEntityFrameworkCoreInstrumentation( + options => + { + options.SetDbStatementForText = true; + options.SetDbStatementForStoredProcedure = true; + } + ) + .AddRedisInstrumentation() + .AddGrpcClientInstrumentation() + .AddOtlpExporter(options => + { + options.Protocol = exporterProtocol; + options.Endpoint = new Uri(exporterTracesEndpoint!); + }) + ) + .WithLogging(logging => logging + .AddOtlpExporter(options => + { + options.Protocol = exporterProtocol; + options.Endpoint = new Uri(exporterLogsEndpoint!); + }) + ); + + builder.Services.AddLogging(logging => logging.AddOpenTelemetry(openTelemetryLoggerOptions => + { + openTelemetryLoggerOptions.IncludeScopes = true; + openTelemetryLoggerOptions.IncludeFormattedMessage = true; + })); + + return builder; + } + } + + extension(IServiceCollection services) + { + internal IServiceCollection AddCache(IConfiguration configuration) + { + services + .AddStackExchangeRedisCache(options => + { + options.Configuration = configuration.GetConnectionString("Redis") ?? throw new NullReferenceException("Redis connection string is not configured."); + options.Configuration += ",abortConnect=false,connectTimeout=5000,syncTimeout=5000"; + }) + .AddHybridCache(options => + { + options.DefaultEntryOptions = new() + { + Expiration = TimeSpan.FromMinutes(30), + LocalCacheExpiration = TimeSpan.FromMinutes(30) + }; + }); + + services.AddSingleton(); + + return services; + } + + internal IServiceCollection AddHttpServices(IConfiguration configuration) + { + + services.AddKeyedScoped(ServicesKeys.Orders, (serviceProvider, cancellationToken) => + { + var serviceConfiguration = configuration.GetSection("Services:Orders").Get() ?? throw new NullReferenceException("Orders service configuration is not configured."); + + var httpClientFactory = serviceProvider.GetRequiredService(); + var logger = serviceProvider.GetRequiredService>(); + var client = httpClientFactory.CreateClient(ServicesKeys.Orders); + client.BaseAddress = new Uri(serviceConfiguration.Url) ?? throw new NullReferenceException("Orders service address is not configured."); + client.DefaultRequestHeaders.Add("Accept", "application/json"); + + return new(client, logger); + }); + + services.AddKeyedScoped(ServicesKeys.Payments, (serviceProvider, cancellationToken) => + { + var serviceConfiguration = configuration.GetSection("Services:Payments").Get() ?? throw new NullReferenceException("Payments service configuration is not configured."); + + var httpClientFactory = serviceProvider.GetRequiredService(); + var logger = serviceProvider.GetRequiredService>(); + var client = httpClientFactory.CreateClient(ServicesKeys.Payments); + client.BaseAddress = new Uri(serviceConfiguration.Url) ?? throw new NullReferenceException("Payments service address is not configured."); + client.DefaultRequestHeaders.Add("Accept", "application/json"); + + return new(client, logger); + }); + + return services; + } + } +} diff --git a/templates/Bff/src/Infrastructure/packages.lock.json b/templates/Bff/src/Infrastructure/packages.lock.json new file mode 100644 index 00000000..16a71f14 --- /dev/null +++ b/templates/Bff/src/Infrastructure/packages.lock.json @@ -0,0 +1,288 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.Extensions.Caching.Hybrid": { + "type": "Direct", + "requested": "[10.1.0, )", + "resolved": "10.1.0", + "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.StackExchangeRedis": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "StackExchange.Redis": "2.7.27" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Extensions.Hosting": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "dependencies": { + "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Instrumentation.AspNetCore": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "type": "Direct", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.GrpcNetClient": { + "type": "Direct", + "requested": "[1.12.0-beta.1, )", + "resolved": "1.12.0-beta.1", + "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "dependencies": { + "OpenTelemetry": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Http": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.StackExchangeRedis": { + "type": "Direct", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "dependencies": { + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", + "StackExchange.Redis": "[2.6.122, 3.0.0)" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "H4SWETCh/cC5L1WtWchHR6LntGk3rDTTznZMssr4cL8IbDmMWBxY+MOGDc/ASnqNolLKPIWHWeuC1ddiL/iNPw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "d2kDKnCsJvY7mBVhcjPSp9BkJk48DsaHPg5u+Oy4f8XaOqnEedRy/USyvnpHL92wpJ6DrTPy7htppUUzskbCXQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "tMF9wNh+hlyYDWB8mrFCQHQmWHlRosol1b/N2Jrefy1bFLnuTlgSYmPyHNmz8xVQgs7DpXytBRWxGhG+mSTp0g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "SfK89ytD61S7DgzorFljSkUeluC1ncn6dtZgwc0ot39f/BEYWBl5jpgvodxduoYAs1d9HG8faCDRZxE95UMo2A==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "/ppSdehKk3fuXjlqCDgSOtjRK/pSHU8eWgzSHfHdwVm5BP4Dgejehkw+PtxKG2j98qTDEHDst2Y99aNsmJldmw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "KrN6TGFwCwqOkLLk/idW/XtDQh+8In+CL9T4M1Dx+5ScsjTq4TlVbal8q532m82UYrMr6RiQJF2HvYCN0QwVsA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "j8zcwhS6bYB6FEfaY3nYSgHdpiL2T+/V3xjpHtslVAegyI1JUbB9yAt/BFdvZdsNbY0Udm4xFtvfT/hUwcOOOg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Configuration.Binder": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "tL9cSl3maS5FPzp/3MtlZI21ExWhni0nnUCF8HY4npTsINw45n9SNDbkKXBMtFyUFGSsQep25fHIDN4f/Vp3AQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Configuration.Binder": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "OpenTelemetry": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Configuration": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + } + }, + "OpenTelemetry.Api": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + }, + "OpenTelemetry.Api.ProviderBuilderExtensions": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "OpenTelemetry.Api": "1.14.0" + } + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.27", + "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + } + } + } +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs b/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs new file mode 100644 index 00000000..064bc2d3 --- /dev/null +++ b/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs @@ -0,0 +1,10 @@ +namespace WebApp.Endpoints; + +internal static class EndpointExtensions +{ + public static WebApplication MapEndpoints(this WebApplication app) + { + // app.MapOrderEndpoints(); + return app; + } +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs new file mode 100644 index 00000000..429d268a --- /dev/null +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -0,0 +1,68 @@ +using Infrastructure.Http; +using Microsoft.AspNetCore.Mvc; +using Infrastructure.Cache; +using Infrastructure.Common; + +namespace WebApp.Endpoints; + +internal static class OrderEndpoints +{ + public static WebApplication MapOrderEndpoints(this WebApplication app) + { + var cache = app.Services.GetRequiredService(); + var httpService = app.Services.GetRequiredKeyedService(ServicesKeys.Orders); + + var ordersGroup = app.MapGroup("/orders") + .WithTags("Orders"); + + ordersGroup.MapGet("/{id}", async ( + [FromHeader] Guid correlationId, + [FromRoute] int id, + CancellationToken cancellationToken, + [FromHeader] bool cacheEnabled = true + ) => { + var response = cacheEnabled switch + { + true => await cache.GetOrCreateAsync( + $"{nameof(OrderEndpoints)}-{id}", + async (cancellationToken) => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, new() + { + { "CorrelationId", correlationId.ToString() } + }), + cancellationToken + ), + false or _ => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, new() + { + { "CorrelationId", correlationId.ToString() } + }), + }; + + return response?.Success ? Results.Ok(response) : Results.NotFound(response); + }); + + ordersGroup.MapPost("/", async ( + [FromBody] dynamic request, + CancellationToken cancellationToken + ) => + { + var response = await httpService.SendAsync("orders", request, cancellationToken); + + if (!response.Success || response.Data == null) + return Results.BadRequest(response); + + return Results.Created($"/orders/{response.Data.Id}", response); + }); + + ordersGroup.MapPost("/paginated", async ( + [FromBody] dynamic request, + CancellationToken cancellationToken + ) => + { + var response = await httpService.SendAsync("orders/paginated", request, cancellationToken); + + return response.Success ? Results.Ok(response) : Results.BadRequest(response); + }); + + return app; + } +} diff --git a/templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs b/templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs new file mode 100644 index 00000000..92bc81f1 --- /dev/null +++ b/templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs @@ -0,0 +1,11 @@ +namespace WebApp.GrpcServices; + +internal static class GrpcServiceExtensions +{ + public static WebApplication MapGrpcServices(this WebApplication app) + { + // app.MapGrpcService(); + + return app; + } +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/GrpcServices/OrderService.cs b/templates/Bff/src/WebApp/GrpcServices/OrderService.cs new file mode 100644 index 00000000..f374b991 --- /dev/null +++ b/templates/Bff/src/WebApp/GrpcServices/OrderService.cs @@ -0,0 +1,59 @@ +// using System.Globalization; +// using Application.Common.Requests; +// using Application.Common.Services; +// using Application.Common.UseCases; +// using Grpc.Core; +// using GrpcOrder; +// using static GrpcOrder.OrderService; +// using GetOrderRequest = Application.Orders.GetOrderRequest; +// using OrderDto = Application.Orders.OrderDto; + +// namespace WebApp.GrpcServices; + +// public class OrderService( +// IBaseInOutUseCase> useCase, +// IHybridCacheService cache +// ) : OrderServiceBase +// { +// private readonly IBaseInOutUseCase> _useCase = useCase; +// private readonly IHybridCacheService _cache = cache; + +// public override async Task Get( +// GrpcOrder.GetOrderRequest request, +// ServerCallContext context +// ) +// { +// var response = await _cache.GetOrCreateAsync( +// $"{nameof(OrderService)}-{request.Id}", +// async cancellationToken => +// { +// var correlationId = Guid.TryParse(request.CorrelationId, out var guid) ? guid : Guid.Empty; +// return await _useCase.HandleAsync(new(correlationId, request.Id), cancellationToken); +// }, +// context.CancellationToken +// ); + +// if (!response.Success || response.Data == null) +// return new() { Success = false, Message = response.Message }; + +// OrderReply orderReply = new() +// { +// Success = true, +// Message = string.Empty, +// Data = new() +// { +// Id = response.Data.Id, +// Total = double.TryParse(response.Data.Total.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var total) ? total : 0.0 +// } +// }; + +// orderReply.Data.Items?.AddRange(response.Data.Items?.Select(i => new GrpcOrder.ItemDto +// { +// Id = i.Id, +// Name = i.Name, +// Value = double.TryParse(i.Value.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : 0.0 +// })); + +// return orderReply; +// } +// } diff --git a/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs b/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs new file mode 100644 index 00000000..e9380df8 --- /dev/null +++ b/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs @@ -0,0 +1,48 @@ +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace WebApp.HealthChecks; + +internal static class HealthCheckExtensions +{ + public static IServiceCollection AddCustomHealthChecks( + this IServiceCollection services, + IConfiguration configuration + ) + { + services + .AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy()) + .AddRedis( + configuration.GetConnectionString("Redis")!, + name: "Redis", + tags: ["services"] + ); + + return services; + } + + public static IApplicationBuilder UseCustomHealthChecks( + this IApplicationBuilder app + ) + { + app.UseHealthChecks("/health", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + + app.UseHealthChecks("/live", new HealthCheckOptions + { + Predicate = r => r.Name.Contains("self") + }); + + app.UseHealthChecks("/ready", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("services") + }); + + return app; + } +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs new file mode 100644 index 00000000..361fdfb9 --- /dev/null +++ b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -0,0 +1,32 @@ +using System.Net; + +namespace WebApp.Middlewares; + +internal sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger) +{ + private readonly RequestDelegate _next = next; + private readonly ILogger _logger = logger; + private readonly string _className = nameof(ExceptionHandlingMiddleware); + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private async Task HandleExceptionAsync(HttpContext context, Exception exception) + { + _logger.LogError(exception, "[{ClassName}] | [{Method}] | {Message}", _className, nameof(HandleExceptionAsync), exception.Message); + + context.Response.ContentType = "application/json"; + context.Response.StatusCode = (int) HttpStatusCode.BadRequest; + } +} + +public record ExceptionResponse(HttpStatusCode StatusCode, string Description); diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs new file mode 100644 index 00000000..274a8b4a --- /dev/null +++ b/templates/Bff/src/WebApp/Program.cs @@ -0,0 +1,58 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Infrastructure; +using Microsoft.AspNetCore.Http.Json; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using WebApp.Endpoints; +using WebApp.GrpcServices; +using WebApp.HealthChecks; +using WebApp.Middlewares; + +namespace WebApp; + +#pragma warning disable S1118 // Utility classes should not have public constructors +public sealed class Program +#pragma warning restore S1118 // Utility classes should not have public constructors +{ + private static async Task Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddGrpc(); + + builder.Services.AddCustomHealthChecks(builder.Configuration); + + builder.Services.AddResponseCompression(options => + { + options.EnableForHttps = true; + }); + + builder.Services.Configure(options => + { + options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; + options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.SerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; + options.SerializerOptions.PropertyNameCaseInsensitive = true; + }); + + builder.AddInfrastructure(); + + builder.WebHost.ConfigureKestrel(options => + options.ConfigureEndpointDefaults(listenOptions => + listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3 + )); + + var app = builder.Build(); + + app.UseHttpsRedirection(); + + app.MapEndpoints() + .MapGrpcServices() + .UseCustomHealthChecks() + .UseResponseCompression() + .UseMiddleware(); + + await app.RunAsync(); + } +} diff --git a/templates/src/WebApp/Properties/launchSettings.json b/templates/Bff/src/WebApp/Properties/launchSettings.json similarity index 100% rename from templates/src/WebApp/Properties/launchSettings.json rename to templates/Bff/src/WebApp/Properties/launchSettings.json diff --git a/templates/src/WebApp/Protos/order.proto b/templates/Bff/src/WebApp/Protos/order.proto similarity index 100% rename from templates/src/WebApp/Protos/order.proto rename to templates/Bff/src/WebApp/Protos/order.proto diff --git a/templates/Bff/src/WebApp/WebApp.csproj b/templates/Bff/src/WebApp/WebApp.csproj new file mode 100644 index 00000000..1a2be00c --- /dev/null +++ b/templates/Bff/src/WebApp/WebApp.csproj @@ -0,0 +1,16 @@ + + + false + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/src/WebApp/WebApp.http b/templates/Bff/src/WebApp/WebApp.http similarity index 100% rename from templates/src/WebApp/WebApp.http rename to templates/Bff/src/WebApp/WebApp.http diff --git a/templates/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json similarity index 100% rename from templates/src/WebApp/appsettings.json rename to templates/Bff/src/WebApp/appsettings.json diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json new file mode 100644 index 00000000..1df939df --- /dev/null +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -0,0 +1,224 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "AspNetCore.HealthChecks.Redis": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", + "dependencies": { + "StackExchange.Redis": "2.7.4" + } + }, + "AspNetCore.HealthChecks.UI.Client": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "1Ub3Wvvbz7CMuFNWgLEc9qqQibiMoovDML/WHrwr5J83RPgtI20giCR92s/ipLgu7IIuqw+W/y7WpIeHqAICxg==", + "dependencies": { + "AspNetCore.HealthChecks.UI.Core": "9.0.0" + } + }, + "Grpc.AspNetCore": { + "type": "Direct", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } + }, + "AspNetCore.HealthChecks.UI.Core": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==" + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.31.1", + "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" + }, + "Grpc.AspNetCore.Server": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "diSC/ZeNdSdxHdYSOpYwuSBBDYpuNVtJQFJfiBB0WrYOQ4lVMmdxuUZJcViahQyo8pCvS3Mueo5lqFxwwMF/iw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.AspNetCore.Server.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "y5KGO1GO0N2L/hCCMR05mmoK8j+v8rKvZ+9nothAxKx2Tf2CwV8f4TM5K0GkKfDsp4vrc4lm90MU6E+DeN7YIw==", + "dependencies": { + "Grpc.AspNetCore.Server": "2.76.0", + "Grpc.Net.ClientFactory": "2.76.0" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "cSxC2tdnFdXXuBgIn1pjc4YBx7LXTCp4M0qn+SMBS35VWZY+cEQYLWTBDDhdBH1HzU7BV+ncVZlniGQHMpRJKQ==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "K1oldmqw2+Gn69nGRzZLhqSiUZwelX1GrBu/cUl9wNf1C0uB61vFS6JcxUUv9P8VoUJhFsmV44JA6lI2EUt4xw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "XI+kO69L9AV8B9N0UQOmH911r6MOEp9huHiavEsY56DJYuzJ9KAxNGy37dpV6CLbgCaN2uKmpOsZ9Pao6bmpVQ==", + "dependencies": { + "Grpc.Net.Client": "2.76.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "bZpiMVYgvpB44/wBh1RotrkqC7bg2FOasLri2GhR3hMKyzsiTxCoDE49YjPrJeFc4RW0wS8u+EInI09sjxVFRA==", + "dependencies": { + "Grpc.Core.Api": "2.76.0" + } + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + }, + "OpenTelemetry": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + } + }, + "OpenTelemetry.Api": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + }, + "OpenTelemetry.Api.ProviderBuilderExtensions": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "dependencies": { + "OpenTelemetry.Api": "1.14.0" + } + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.27", + "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==", + "dependencies": { + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "infrastructure": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", + "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )" + } + }, + "Microsoft.Extensions.Caching.Hybrid": { + "type": "CentralTransitive", + "requested": "[10.1.0, )", + "resolved": "10.1.0", + "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==" + }, + "Microsoft.Extensions.Caching.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "dependencies": { + "StackExchange.Redis": "2.7.27" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Extensions.Hosting": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Instrumentation.AspNetCore": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.GrpcNetClient": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.1, )", + "resolved": "1.12.0-beta.1", + "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "dependencies": { + "OpenTelemetry": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Http": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", + "StackExchange.Redis": "[2.6.122, 3.0.0)" + } + } + } + } +} \ No newline at end of file diff --git a/templates/tests/CommonTests/CommonTests.csproj b/templates/Bff/tests/CommonTests/CommonTests.csproj similarity index 100% rename from templates/tests/CommonTests/CommonTests.csproj rename to templates/Bff/tests/CommonTests/CommonTests.csproj diff --git a/templates/Bff/tests/CommonTests/packages.lock.json b/templates/Bff/tests/CommonTests/packages.lock.json new file mode 100644 index 00000000..7d97c3a9 --- /dev/null +++ b/templates/Bff/tests/CommonTests/packages.lock.json @@ -0,0 +1,57 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "AutoFixture": { + "type": "Direct", + "requested": "[4.18.1, )", + "resolved": "4.18.1", + "contentHash": "BmWZDY4fkrYOyd5/CTBOeXbzsNwV8kI4kDi/Ty1Y5F+WDHBVKxzfWlBE4RSicvZ+EOi2XDaN5uwdrHsItLW6Kw==", + "dependencies": { + "Fare": "[2.1.1, 3.0.0)" + } + }, + "Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "xunit.extensibility.core": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "Fare": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "HaI8puqA66YU7/9cK4Sgbs1taUTP1Ssa4QT2PIzqJ7GvAbN1QgkjbRsjH+FSbMh1MJdvS0CIwQNLtFT+KF6KpA==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + } + } + } +} \ No newline at end of file diff --git a/templates/Bff/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs b/templates/Bff/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs new file mode 100644 index 00000000..37aa31f5 --- /dev/null +++ b/templates/Bff/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc.Testing; +using WebApp; + +namespace IntegrationTests.Common; + +[CollectionDefinition("WebApplicationFactoryCollectionDefinition")] +public sealed class WebApplicationFactoryCollectionDefinition : IClassFixture>; + +public class CustomWebApplicationFactory : WebApplicationFactory, IDisposable where TProgram : class +{ +} diff --git a/templates/tests/CommonTests/Fixtures/BaseFixture.cs b/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs similarity index 100% rename from templates/tests/CommonTests/Fixtures/BaseFixture.cs rename to templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs diff --git a/templates/tests/IntegrationTests/GlobalUsings.cs b/templates/Bff/tests/IntegrationTests/GlobalUsings.cs similarity index 100% rename from templates/tests/IntegrationTests/GlobalUsings.cs rename to templates/Bff/tests/IntegrationTests/GlobalUsings.cs diff --git a/templates/Bff/tests/IntegrationTests/IntegrationTests.csproj b/templates/Bff/tests/IntegrationTests/IntegrationTests.csproj new file mode 100644 index 00000000..2be9c611 --- /dev/null +++ b/templates/Bff/tests/IntegrationTests/IntegrationTests.csproj @@ -0,0 +1,27 @@ + + + false + true + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + \ No newline at end of file diff --git a/templates/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs diff --git a/templates/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs diff --git a/templates/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs diff --git a/templates/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs diff --git a/templates/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs diff --git a/templates/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs diff --git a/templates/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json new file mode 100644 index 00000000..01b71e45 --- /dev/null +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -0,0 +1,714 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.AspNetCore.Mvc.Testing": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "84aSUoB++qrL9mlkAT1ybV9KQ5bv7sbpx2B5uo9se0ryYjNPIeiuknVy7r0FOwRk8T58PYybhIBa7WOkdMgOZQ==", + "dependencies": { + "Microsoft.AspNetCore.TestHost": "10.0.1", + "Microsoft.Extensions.DependencyModel": "10.0.1", + "Microsoft.Extensions.Hosting": "10.0.1" + } + }, + "Microsoft.AspNetCore.TestHost": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Vvos4CyBam5dCsH3eD1c9MQI4ESWwzNSJsToFz4i6NmfPsaySzNSiv0QYRmSAAIBXb8GXxPmuy42TkIrw2xCzQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", + "dependencies": { + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" + }, + "AspNetCore.HealthChecks.UI.Core": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" + } + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.31.1", + "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" + }, + "Grpc.AspNetCore.Server": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "diSC/ZeNdSdxHdYSOpYwuSBBDYpuNVtJQFJfiBB0WrYOQ4lVMmdxuUZJcViahQyo8pCvS3Mueo5lqFxwwMF/iw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.AspNetCore.Server.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "y5KGO1GO0N2L/hCCMR05mmoK8j+v8rKvZ+9nothAxKx2Tf2CwV8f4TM5K0GkKfDsp4vrc4lm90MU6E+DeN7YIw==", + "dependencies": { + "Grpc.AspNetCore.Server": "2.76.0", + "Grpc.Net.ClientFactory": "2.76.0" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "cSxC2tdnFdXXuBgIn1pjc4YBx7LXTCp4M0qn+SMBS35VWZY+cEQYLWTBDDhdBH1HzU7BV+ncVZlniGQHMpRJKQ==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "K1oldmqw2+Gn69nGRzZLhqSiUZwelX1GrBu/cUl9wNf1C0uB61vFS6JcxUUv9P8VoUJhFsmV44JA6lI2EUt4xw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "XI+kO69L9AV8B9N0UQOmH911r6MOEp9huHiavEsY56DJYuzJ9KAxNGy37dpV6CLbgCaN2uKmpOsZ9Pao6bmpVQ==", + "dependencies": { + "Grpc.Net.Client": "2.76.0", + "Microsoft.Extensions.Http": "8.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "bZpiMVYgvpB44/wBh1RotrkqC7bg2FOasLri2GhR3hMKyzsiTxCoDE49YjPrJeFc4RW0wS8u+EInI09sjxVFRA==", + "dependencies": { + "Grpc.Core.Api": "2.76.0" + } + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "s5cxcdtIig66YT3J+7iHflMuorznK8kXuwBBPHMp4KImx5ZGE3FRa1Nj9fI/xMwFV+KzUMjqZ2MhOedPH8LiBQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "csD8Eps3HQ3yc0x6NhgTV+aIFKSs3qVlVCtFnMHz/JOjnv7eEj/qSXKXiK9LzBzB1qSfAVqFnB5iaX2nUmagIQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "N/6GiwiZFCBFZDk3vg1PhHW3zMqqu5WWpmeZAA9VTXv7Q8pr8NZR/EQsH0DjzqydDksJtY6EQBsu81d5okQOlA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "0zW3eYBJlRctmgqk5s0kFIi5o5y2g80mvGCD8bkYxREPQlKUnr0ndU/Sop+UDIhyWN0fIi4RW63vo7BKTi7ncA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "ULEJ0nkaW90JYJGkFujPcJtADXcJpXiSOLbokPcWJZ8iDbtDINifEYAUVqZVr81IDNTrRFul6O8RolOKOsgFPg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "IiWPd4j8JLNjSkyXl5hvJwX2ZENDVQVPDHYgZmYdw8+YkY2xp9iQt0vjdnAQZLpo/ipeW1xgOqfSBEnivKWPYQ==" + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks": { + "type": "Transitive", + "resolved": "8.0.11", + "contentHash": "zLgN22Zp9pk8RHlwssRTexw4+a6wqOnKWN+VejdPn5Yhjql4XiBhkFo35Nu8mmqHIk/UEmmCnMGLWq75aFfkOw==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.11", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { + "type": "Transitive", + "resolved": "8.0.11", + "contentHash": "So3JUdRxozRjvQ3cxU6F3nI/i4emDnjane6yMYcJhvTTTu29ltlIdoXjkFGRceIWz8yKvuEpzXItZ0x5GvN2nQ==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "4bxzGXIzZnz0Bf7czQ72jGvpOqJsRW/44PS7YLFXTTnu6cNcPvmSREDvBoH0ZVP2hAbMfL4sUoCUn54k70jPWw==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "49dFvGJjLSwGn76eHnP1gBvCJkL8HRYpCrG0DCvsP6wRpEQRLN2Fq8rTxbP+6jS7jmYKCnSVO5C65v4mT3rzeA==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "0jjfjQSOFZlHhwOoIQw0WyzxtkDMYdnPY3iFrOLasxYq/5J4FDt1HWT8TktBclOVjFY1FOOkoOc99X7AhbqSIw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.Configuration.CommandLine": "10.0.1", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.Configuration.UserSecrets": "10.0.1", + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Logging.Console": "10.0.1", + "Microsoft.Extensions.Logging.Debug": "10.0.1", + "Microsoft.Extensions.Logging.EventLog": "10.0.1", + "Microsoft.Extensions.Logging.EventSource": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Http": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "VqfTvbX9C6BA0VeIlpzPlljnNsXxiI5CdUHb9ksWERH94WQ6ft3oLGUAa4xKcDGu4xF+rIZ8wj7IOAd6/q7vGw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Zp9MM+jFCa7oktIug62V9eNygpkf+6kFVatF+UC/ODeUwIr5givYKy8fYSSI9sWdxqDqv63y1x0mm2VjOl8GOw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "System.Diagnostics.EventLog": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "WnFvZP+Y+lfeNFKPK/+mBpaCC7EeBDlobrQOqnP7rrw/+vE7yu8Rjczum1xbC0F/8cAHafog84DMp9200akMNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.0.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "OpenTelemetry": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Configuration": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + } + }, + "OpenTelemetry.Api": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + }, + "OpenTelemetry.Api.ProviderBuilderExtensions": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "OpenTelemetry.Api": "1.14.0" + } + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.27", + "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "xfaHEHVDkMOOZR5S6ZGezD0+vekdH1Nx/9Ih8/rOqOGSOk1fxiN3u94bYkBW/wigj0Uw2Wt3vvRj9mtYdgwEjw==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "infrastructure": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", + "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )" + } + }, + "webapp": { + "type": "Project", + "dependencies": { + "AspNetCore.HealthChecks.Redis": "[9.0.0, )", + "AspNetCore.HealthChecks.UI.Client": "[9.0.0, )", + "Grpc.AspNetCore": "[2.76.0, )", + "Infrastructure": "[1.0.0, )" + } + }, + "AspNetCore.HealthChecks.Redis": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11", + "StackExchange.Redis": "2.7.4" + } + }, + "AspNetCore.HealthChecks.UI.Client": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "1Ub3Wvvbz7CMuFNWgLEc9qqQibiMoovDML/WHrwr5J83RPgtI20giCR92s/ipLgu7IIuqw+W/y7WpIeHqAICxg==", + "dependencies": { + "AspNetCore.HealthChecks.UI.Core": "9.0.0" + } + }, + "Grpc.AspNetCore": { + "type": "CentralTransitive", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } + }, + "Microsoft.Extensions.Caching.Hybrid": { + "type": "CentralTransitive", + "requested": "[10.1.0, )", + "resolved": "10.1.0", + "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "StackExchange.Redis": "2.7.27" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Extensions.Hosting": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "dependencies": { + "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Instrumentation.AspNetCore": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.GrpcNetClient": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.1, )", + "resolved": "1.12.0-beta.1", + "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "dependencies": { + "OpenTelemetry": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Http": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "dependencies": { + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", + "StackExchange.Redis": "[2.6.122, 3.0.0)" + } + }, + "xunit.extensibility.core": { + "type": "CentralTransitive", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + } + } + } +} \ No newline at end of file diff --git a/templates/tests/LoadTests/protos/order.proto b/templates/Bff/tests/LoadTests/protos/order.proto similarity index 100% rename from templates/tests/LoadTests/protos/order.proto rename to templates/Bff/tests/LoadTests/protos/order.proto diff --git a/templates/tests/LoadTests/scriptGrpc.js b/templates/Bff/tests/LoadTests/scriptGrpc.js similarity index 100% rename from templates/tests/LoadTests/scriptGrpc.js rename to templates/Bff/tests/LoadTests/scriptGrpc.js diff --git a/templates/tests/LoadTests/scriptHttp.js b/templates/Bff/tests/LoadTests/scriptHttp.js similarity index 100% rename from templates/tests/LoadTests/scriptHttp.js rename to templates/Bff/tests/LoadTests/scriptHttp.js diff --git a/templates/Full/.editorconfig b/templates/Full/.editorconfig new file mode 100644 index 00000000..c4f02cf9 --- /dev/null +++ b/templates/Full/.editorconfig @@ -0,0 +1,197 @@ +# editorconfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +# Generated code +[*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}] +generated_code = true + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Types: use keywords instead of BCL types, and permit var only when the type is clear +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# static fields should have s_ prefix +dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code style defaults +csharp_using_directive_placement = outside_namespace:suggestion +dotnet_sort_system_directives_first = true +csharp_prefer_braces = true:silent +csharp_preserve_single_line_blocks = true:none +csharp_preserve_single_line_statements = false:none +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = false:none +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_namespace_declarations = file_scoped:warning +dotnet_style_readonly_field = true:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +csharp_prefer_simple_default_expression = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = true:silent +csharp_style_expression_bodied_operators = true:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Other features +csharp_style_prefer_index_operator = false:none +csharp_style_prefer_range_operator = false:none +csharp_style_pattern_local_over_anonymous_function = false:none + +# Space preferences +csharp_space_after_cast = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# S3216: "ConfigureAwait(false)" should be used +dotnet_diagnostic.S3216.severity = none + +# S2360: Optional parameters should not be used +dotnet_diagnostic.S2360.severity = none + +# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. +dotnet_diagnostic.CS8618.severity = none + +# CA1873: Avoid potentially expensive logging +dotnet_diagnostic.CA1873.severity = none + +# Xml project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] +indent_size = 2 + +[*.{csproj,vbproj,proj,nativeproj,locproj}] +charset = utf-8 + +# Xml build files +[*.builds] +indent_size = 2 + +# Xml files +[*.{xml,stylecop,resx,ruleset}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +# YAML config files +[*.{yml,yaml}] +indent_size = 2 + +# Shell scripts +[*.sh] +end_of_line = lf +[*.{cmd,bat}] +end_of_line = crlf + +[*.{sql,json}] +insert_final_newline = false diff --git a/templates/.github/prompts/create-migrations.prompt.md b/templates/Full/.github/prompts/create-migrations.prompt.md similarity index 100% rename from templates/.github/prompts/create-migrations.prompt.md rename to templates/Full/.github/prompts/create-migrations.prompt.md diff --git a/templates/.github/prompts/crud-generator.prompt.md b/templates/Full/.github/prompts/crud-generator.prompt.md similarity index 100% rename from templates/.github/prompts/crud-generator.prompt.md rename to templates/Full/.github/prompts/crud-generator.prompt.md diff --git a/templates/Full/.gitignore b/templates/Full/.gitignore new file mode 100644 index 00000000..ecced320 --- /dev/null +++ b/templates/Full/.gitignore @@ -0,0 +1,482 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.vspscc +*.vssscc +.builds +*.pidb +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp + +!Logs \ No newline at end of file diff --git a/templates/.template.config/template.json b/templates/Full/.template.config/template.json similarity index 68% rename from templates/.template.config/template.json rename to templates/Full/.template.config/template.json index 0415d7fe..a298e2f0 100644 --- a/templates/.template.config/template.json +++ b/templates/Full/.template.config/template.json @@ -1,11 +1,11 @@ { "$schema": "http://json.schemastore.org/template", "author": "Giovanni Brunno Previatti", - "identity": "Hexagonal.Solution.Template", - "name": "Hexagonal solution template", + "identity": "Hexagonal.Solution.Template.Full", + "name": "Hexagonal solution template Full", "description": "Solution template following hexagonal architecture structure best practices", - "shortName": "hexagonal-solution", - "sourceName": "Hexagonal.Solution.Template", + "shortName": "full", + "sourceName": "Hexagonal.Solution.Template.Full", "classifications": [ "common", "template", diff --git a/templates/Full/Directory.Build.props b/templates/Full/Directory.Build.props new file mode 100644 index 00000000..df67cf0c --- /dev/null +++ b/templates/Full/Directory.Build.props @@ -0,0 +1,12 @@ + + + net10.0 + enable + enable + true + true + + + + + \ No newline at end of file diff --git a/templates/Directory.Packages.props b/templates/Full/Directory.Packages.props similarity index 100% rename from templates/Directory.Packages.props rename to templates/Full/Directory.Packages.props diff --git a/templates/Full/Dockerfile b/templates/Full/Dockerfile new file mode 100644 index 00000000..9f46f6c3 --- /dev/null +++ b/templates/Full/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build-env +WORKDIR /app + +COPY . . + +WORKDIR /app/src/WebApp +RUN dotnet restore \ + && dotnet publish -c Release -o /app/out + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 +WORKDIR /app +COPY --from=build-env /app/out . + +RUN apt-get update \ + && apt-get install -y curl + +EXPOSE 5000 + +ENTRYPOINT ["dotnet", "WebApp.dll", "--urls", "http://+:5000"] diff --git a/templates/Hexagonal.Solution.Template.sln b/templates/Full/Hexagonal.Solution.Template.Full.sln similarity index 100% rename from templates/Hexagonal.Solution.Template.sln rename to templates/Full/Hexagonal.Solution.Template.Full.sln diff --git a/templates/Readme.md b/templates/Full/Readme.md similarity index 100% rename from templates/Readme.md rename to templates/Full/Readme.md diff --git a/templates/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml similarity index 100% rename from templates/docker-compose-load-tests.yml rename to templates/Full/docker-compose-load-tests.yml diff --git a/templates/docker-compose.yml b/templates/Full/docker-compose.yml similarity index 100% rename from templates/docker-compose.yml rename to templates/Full/docker-compose.yml diff --git a/templates/scripts/migrations.sql b/templates/Full/scripts/migrations.sql similarity index 100% rename from templates/scripts/migrations.sql rename to templates/Full/scripts/migrations.sql diff --git a/templates/scripts/seeds.sql b/templates/Full/scripts/seeds.sql similarity index 100% rename from templates/scripts/seeds.sql rename to templates/Full/scripts/seeds.sql diff --git a/templates/src/Application/Application.csproj b/templates/Full/src/Application/Application.csproj similarity index 100% rename from templates/src/Application/Application.csproj rename to templates/Full/src/Application/Application.csproj diff --git a/templates/src/Application/ApplicationDependencyInjection.cs b/templates/Full/src/Application/ApplicationDependencyInjection.cs similarity index 100% rename from templates/src/Application/ApplicationDependencyInjection.cs rename to templates/Full/src/Application/ApplicationDependencyInjection.cs diff --git a/templates/src/Application/Common/Constants/DefaultApplicationMessages.cs b/templates/Full/src/Application/Common/Constants/DefaultApplicationMessages.cs similarity index 100% rename from templates/src/Application/Common/Constants/DefaultApplicationMessages.cs rename to templates/Full/src/Application/Common/Constants/DefaultApplicationMessages.cs diff --git a/templates/src/Application/Common/Constants/DefaultConfigurations.cs b/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs similarity index 100% rename from templates/src/Application/Common/Constants/DefaultConfigurations.cs rename to templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs diff --git a/templates/src/Application/Common/Constants/NotificationType.cs b/templates/Full/src/Application/Common/Constants/NotificationType.cs similarity index 100% rename from templates/src/Application/Common/Constants/NotificationType.cs rename to templates/Full/src/Application/Common/Constants/NotificationType.cs diff --git a/templates/src/Application/Common/Messages/BaseMessage.cs b/templates/Full/src/Application/Common/Messages/BaseMessage.cs similarity index 100% rename from templates/src/Application/Common/Messages/BaseMessage.cs rename to templates/Full/src/Application/Common/Messages/BaseMessage.cs diff --git a/templates/src/Application/Common/Messages/CreateNotificationMessage.cs b/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs similarity index 100% rename from templates/src/Application/Common/Messages/CreateNotificationMessage.cs rename to templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs diff --git a/templates/src/Application/Common/Repositories/IBaseRepository.cs b/templates/Full/src/Application/Common/Repositories/IBaseRepository.cs similarity index 100% rename from templates/src/Application/Common/Repositories/IBaseRepository.cs rename to templates/Full/src/Application/Common/Repositories/IBaseRepository.cs diff --git a/templates/src/Application/Common/Requests/BaseRequest.cs b/templates/Full/src/Application/Common/Requests/BaseRequest.cs similarity index 100% rename from templates/src/Application/Common/Requests/BaseRequest.cs rename to templates/Full/src/Application/Common/Requests/BaseRequest.cs diff --git a/templates/src/Application/Common/Requests/BaseResponse.cs b/templates/Full/src/Application/Common/Requests/BaseResponse.cs similarity index 100% rename from templates/src/Application/Common/Requests/BaseResponse.cs rename to templates/Full/src/Application/Common/Requests/BaseResponse.cs diff --git a/templates/src/Application/Common/Services/IHybridCacheService.cs b/templates/Full/src/Application/Common/Services/IHybridCacheService.cs similarity index 100% rename from templates/src/Application/Common/Services/IHybridCacheService.cs rename to templates/Full/src/Application/Common/Services/IHybridCacheService.cs diff --git a/templates/src/Application/Common/Services/IProduceService.cs b/templates/Full/src/Application/Common/Services/IProduceService.cs similarity index 100% rename from templates/src/Application/Common/Services/IProduceService.cs rename to templates/Full/src/Application/Common/Services/IProduceService.cs diff --git a/templates/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs similarity index 100% rename from templates/src/Application/Common/UseCases/BaseInOutUseCase.cs rename to templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs diff --git a/templates/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs similarity index 100% rename from templates/src/Application/Common/UseCases/BaseInUseCase.cs rename to templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs diff --git a/templates/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs similarity index 100% rename from templates/src/Application/Common/UseCases/BaseOutUseCase.cs rename to templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs diff --git a/templates/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs similarity index 100% rename from templates/src/Application/Common/UseCases/BaseUseCase.cs rename to templates/Full/src/Application/Common/UseCases/BaseUseCase.cs diff --git a/templates/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs similarity index 100% rename from templates/src/Application/Notifications/CreateNotificationUseCase.cs rename to templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs diff --git a/templates/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs similarity index 100% rename from templates/src/Application/Notifications/GetAllNotificationsUseCase.cs rename to templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs diff --git a/templates/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs similarity index 100% rename from templates/src/Application/Notifications/GetNotificationUseCase.cs rename to templates/Full/src/Application/Notifications/GetNotificationUseCase.cs diff --git a/templates/src/Application/Notifications/NotificationDto.cs b/templates/Full/src/Application/Notifications/NotificationDto.cs similarity index 100% rename from templates/src/Application/Notifications/NotificationDto.cs rename to templates/Full/src/Application/Notifications/NotificationDto.cs diff --git a/templates/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs similarity index 100% rename from templates/src/Application/Orders/CreateOrderUseCase.cs rename to templates/Full/src/Application/Orders/CreateOrderUseCase.cs diff --git a/templates/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs similarity index 100% rename from templates/src/Application/Orders/GetAllOrdersUseCase.cs rename to templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs diff --git a/templates/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs similarity index 100% rename from templates/src/Application/Orders/GetOrderUseCase.cs rename to templates/Full/src/Application/Orders/GetOrderUseCase.cs diff --git a/templates/src/Application/Orders/OrderDto.cs b/templates/Full/src/Application/Orders/OrderDto.cs similarity index 100% rename from templates/src/Application/Orders/OrderDto.cs rename to templates/Full/src/Application/Orders/OrderDto.cs diff --git a/templates/Full/src/Application/packages.lock.json b/templates/Full/src/Application/packages.lock.json new file mode 100644 index 00000000..11025338 --- /dev/null +++ b/templates/Full/src/Application/packages.lock.json @@ -0,0 +1,76 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "FluentValidation": { + "type": "Direct", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "EPpkIe1yh1a0OXyC100oOA8WMbZvqUu5plwhvYcb7oSELfyUZzfxV48BLhvs3kKo4NwG7MGLNgy1RJiYtT8Dpw==" + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "Direct", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "D0VXh4dtjjX2aQizuaa0g6R8X3U1JaVqJPfGCvLwZX9t/O2h7tkpbitbadQMfwcgSPdDbI2vDxuwRMv/Uf9dHA==", + "dependencies": { + "FluentValidation": "12.1.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "domain": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + } + } + } +} \ No newline at end of file diff --git a/templates/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs similarity index 100% rename from templates/src/Domain/Common/DomainEntity.cs rename to templates/Full/src/Domain/Common/DomainEntity.cs diff --git a/templates/src/Domain/Common/Result.cs b/templates/Full/src/Domain/Common/Result.cs similarity index 100% rename from templates/src/Domain/Common/Result.cs rename to templates/Full/src/Domain/Common/Result.cs diff --git a/templates/src/Domain/Domain.csproj b/templates/Full/src/Domain/Domain.csproj similarity index 100% rename from templates/src/Domain/Domain.csproj rename to templates/Full/src/Domain/Domain.csproj diff --git a/templates/src/Domain/DomainDependencyInjection.cs b/templates/Full/src/Domain/DomainDependencyInjection.cs similarity index 100% rename from templates/src/Domain/DomainDependencyInjection.cs rename to templates/Full/src/Domain/DomainDependencyInjection.cs diff --git a/templates/src/Domain/Notifications/Notification.cs b/templates/Full/src/Domain/Notifications/Notification.cs similarity index 100% rename from templates/src/Domain/Notifications/Notification.cs rename to templates/Full/src/Domain/Notifications/Notification.cs diff --git a/templates/src/Domain/Orders/Item.cs b/templates/Full/src/Domain/Orders/Item.cs similarity index 100% rename from templates/src/Domain/Orders/Item.cs rename to templates/Full/src/Domain/Orders/Item.cs diff --git a/templates/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs similarity index 100% rename from templates/src/Domain/Orders/Order.cs rename to templates/Full/src/Domain/Orders/Order.cs diff --git a/templates/Full/src/Domain/packages.lock.json b/templates/Full/src/Domain/packages.lock.json new file mode 100644 index 00000000..e50bae1d --- /dev/null +++ b/templates/Full/src/Domain/packages.lock.json @@ -0,0 +1,21 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.Extensions.DependencyInjection": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + } + } + } +} \ No newline at end of file diff --git a/templates/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs b/templates/Full/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs similarity index 100% rename from templates/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs rename to templates/Full/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs diff --git a/templates/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs similarity index 100% rename from templates/src/Infrastructure/Cache/Services/HybridCacheService.cs rename to templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs diff --git a/templates/src/Infrastructure/Common/BaseBackgroundService.cs b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs similarity index 100% rename from templates/src/Infrastructure/Common/BaseBackgroundService.cs rename to templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs diff --git a/templates/src/Infrastructure/Data/Common/BaseMapping.cs b/templates/Full/src/Infrastructure/Data/Common/BaseMapping.cs similarity index 100% rename from templates/src/Infrastructure/Data/Common/BaseMapping.cs rename to templates/Full/src/Infrastructure/Data/Common/BaseMapping.cs diff --git a/templates/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs similarity index 100% rename from templates/src/Infrastructure/Data/Common/BaseRepository.cs rename to templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs diff --git a/templates/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs b/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs similarity index 100% rename from templates/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs rename to templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs diff --git a/templates/src/Infrastructure/Data/Mapping/ItemDbMapping.cs b/templates/Full/src/Infrastructure/Data/Mapping/ItemDbMapping.cs similarity index 100% rename from templates/src/Infrastructure/Data/Mapping/ItemDbMapping.cs rename to templates/Full/src/Infrastructure/Data/Mapping/ItemDbMapping.cs diff --git a/templates/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs b/templates/Full/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs similarity index 100% rename from templates/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs rename to templates/Full/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs diff --git a/templates/src/Infrastructure/Data/Mapping/OrderDbMapping.cs b/templates/Full/src/Infrastructure/Data/Mapping/OrderDbMapping.cs similarity index 100% rename from templates/src/Infrastructure/Data/Mapping/OrderDbMapping.cs rename to templates/Full/src/Infrastructure/Data/Mapping/OrderDbMapping.cs diff --git a/templates/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs b/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs similarity index 100% rename from templates/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs rename to templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs diff --git a/templates/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs b/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs similarity index 100% rename from templates/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs rename to templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs diff --git a/templates/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs similarity index 100% rename from templates/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs rename to templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs diff --git a/templates/src/Infrastructure/Data/MyDbContext.cs b/templates/Full/src/Infrastructure/Data/MyDbContext.cs similarity index 100% rename from templates/src/Infrastructure/Data/MyDbContext.cs rename to templates/Full/src/Infrastructure/Data/MyDbContext.cs diff --git a/templates/src/Infrastructure/Data/MyDbContextFactory.cs b/templates/Full/src/Infrastructure/Data/MyDbContextFactory.cs similarity index 100% rename from templates/src/Infrastructure/Data/MyDbContextFactory.cs rename to templates/Full/src/Infrastructure/Data/MyDbContextFactory.cs diff --git a/templates/src/Infrastructure/Infrastructure.csproj b/templates/Full/src/Infrastructure/Infrastructure.csproj similarity index 100% rename from templates/src/Infrastructure/Infrastructure.csproj rename to templates/Full/src/Infrastructure/Infrastructure.csproj diff --git a/templates/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Full/src/Infrastructure/InfrastructureDependencyInjection.cs similarity index 100% rename from templates/src/Infrastructure/InfrastructureDependencyInjection.cs rename to templates/Full/src/Infrastructure/InfrastructureDependencyInjection.cs diff --git a/templates/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs similarity index 100% rename from templates/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs rename to templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs diff --git a/templates/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs similarity index 100% rename from templates/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs rename to templates/Full/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs diff --git a/templates/src/Infrastructure/Messaging/MessagingDependencyInjection.cs b/templates/Full/src/Infrastructure/Messaging/MessagingDependencyInjection.cs similarity index 100% rename from templates/src/Infrastructure/Messaging/MessagingDependencyInjection.cs rename to templates/Full/src/Infrastructure/Messaging/MessagingDependencyInjection.cs diff --git a/templates/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs similarity index 100% rename from templates/src/Infrastructure/Messaging/Producers/ProducerService.cs rename to templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs diff --git a/templates/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs similarity index 100% rename from templates/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs rename to templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json new file mode 100644 index 00000000..aa77f7ab --- /dev/null +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -0,0 +1,822 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.EntityFrameworkCore": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "0VcVx+hIo7j6bCjTlgPB1D4vHyLdkY3pVmlcsvRR8APEr0vRQ+Nj2Q3qYXTUeHgp8gdBxQFDVCfcAXknevD2KQ==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Build.Framework": "17.14.28", + "Microsoft.Build.Tasks.Core": "17.14.28", + "Microsoft.Build.Utilities.Core": "17.14.28", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.14.0", + "Microsoft.CodeAnalysis.Workspaces.MSBuild": "4.14.0", + "Microsoft.EntityFrameworkCore.Relational": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyModel": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Mono.TextTemplating": "3.0.0", + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Uz/M/Y2UPWJ+wJV8JGb7gp5H6Q8oE+/Fz/YZ5osBNDIxrlT+/I7AraVKxErSlz/nZq5JVy+jL1znnIcgI0G/Hw==", + "dependencies": { + "Microsoft.Data.SqlClient": "6.1.1", + "Microsoft.EntityFrameworkCore.Relational": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Hybrid": { + "type": "Direct", + "requested": "[10.1.0, )", + "resolved": "10.1.0", + "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.StackExchangeRedis": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "StackExchange.Redis": "2.7.27" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Extensions.Hosting": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "dependencies": { + "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Instrumentation.AspNetCore": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "type": "Direct", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.GrpcNetClient": { + "type": "Direct", + "requested": "[1.12.0-beta.1, )", + "resolved": "1.12.0-beta.1", + "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "dependencies": { + "OpenTelemetry": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Http": { + "type": "Direct", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.StackExchangeRedis": { + "type": "Direct", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "dependencies": { + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", + "StackExchange.Redis": "[2.6.122, 3.0.0)" + } + }, + "RabbitMQ.Client": { + "type": "Direct", + "requested": "[7.2.0, )", + "resolved": "7.2.0", + "contentHash": "PPQ7cF7lwbhqC4up6en1bTUZlz06YqQwJecOJzsguTtyhNA7oL5uNDZIx/h6ZfcyPZV4V3DYKSCxfm4RUFLcbA==", + "dependencies": { + "System.Threading.RateLimiting": "8.0.0" + } + }, + "RabbitMQ.Client.OpenTelemetry": { + "type": "Direct", + "requested": "[1.0.0-rc.2, )", + "resolved": "1.0.0-rc.2", + "contentHash": "fVjEZ8DsLDw3EEp/Q5XGFhk7Rvluh32n+38kyBcCdZu/F7cOuj6ETo+pYMhXHtcKGk856sEEOaEWR+alsETv9w==", + "dependencies": { + "OpenTelemetry.Api": "1.9.0", + "RabbitMQ.Client": "7.2.0" + } + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", + "dependencies": { + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" + } + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.Cryptography": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" + }, + "Microsoft.Build": { + "type": "Transitive", + "resolved": "17.7.2", + "contentHash": "AmWnumxsMiRycFfE3kq/XnFFTAoPpCWl3UuiKQWCa5Z0+hBKVoiydzS2iXJGd3x+jry+qaTR9GzoezjV9NFT5A==", + "dependencies": { + "Microsoft.Build.Framework": "17.7.2", + "Microsoft.NET.StringTools": "17.7.2", + "System.Configuration.ConfigurationManager": "7.0.0", + "System.Reflection.MetadataLoadContext": "7.0.0", + "System.Security.Permissions": "7.0.0" + } + }, + "Microsoft.Build.Framework": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "wRcyTzGV0LRAtFdrddtioh59Ky4/zbvyraP0cQkDzRSRkhgAQb0K88D/JNC6VHLIXanRi3mtV1jU0uQkBwmiVg==" + }, + "Microsoft.Build.Tasks.Core": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "jk3O0tXp9QWPXhLJ7Pl8wm/eGtGgA1++vwHGWEmnwMU6eP//ghtcCUpQh9CQMwEKGDnH0aJf285V1s8yiSlKfQ==", + "dependencies": { + "Microsoft.Build.Framework": "17.14.28", + "Microsoft.Build.Utilities.Core": "17.14.28", + "Microsoft.NET.StringTools": "17.14.28", + "System.CodeDom": "9.0.0", + "System.Configuration.ConfigurationManager": "9.0.0", + "System.Diagnostics.EventLog": "9.0.0", + "System.Formats.Nrbf": "9.0.0", + "System.Resources.Extensions": "9.0.0", + "System.Security.Cryptography.Pkcs": "9.0.0", + "System.Security.Cryptography.ProtectedData": "9.0.0", + "System.Security.Cryptography.Xml": "9.0.0" + } + }, + "Microsoft.Build.Utilities.Core": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "rhSdPo8QfLXXWM+rY0x0z1G4KK4ZhMoIbHROyDj8MUBFab9nvHR0NaMnjzOgXldhmD2zi2ir8d6xCatNzlhF5g==", + "dependencies": { + "Microsoft.Build.Framework": "17.14.28", + "Microsoft.NET.StringTools": "17.14.28", + "System.Configuration.ConfigurationManager": "9.0.0", + "System.Diagnostics.EventLog": "9.0.0", + "System.Security.Cryptography.ProtectedData": "9.0.0" + } + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "QkgCEM4qJo6gdtblXtNgHqtykS61fxW+820hx5JN6n9DD4mQtqNB+6fPeJ3GQWg6jkkGz6oG9yZq7H3Gf0zwYw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.CSharp": "[4.14.0]", + "Microsoft.CodeAnalysis.Common": "[4.14.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.14.0]", + "System.Composition": "9.0.0" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "wNVK9JrqjqDC/WgBUFV6henDfrW87NPfo98nzah/+M/G1D6sBOPtXwqce3UQNn+6AjTnmkHYN1WV9XmTlPemTw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]", + "System.Composition": "9.0.0" + } + }, + "Microsoft.CodeAnalysis.Workspaces.MSBuild": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "YU7Sguzm1Cuhi2U6S0DRKcVpqAdBd2QmatpyE0KqYMJogJ9E27KHOWGUzAOjsyjAM7sNaUk+a8VPz24knDseFw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Build": "17.7.2", + "Microsoft.Build.Framework": "17.7.2", + "Microsoft.Build.Tasks.Core": "17.7.2", + "Microsoft.Build.Utilities.Core": "17.7.2", + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.14.0]", + "Microsoft.Extensions.DependencyInjection": "9.0.0", + "Microsoft.Extensions.Logging": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "Microsoft.Extensions.Primitives": "9.0.0", + "Newtonsoft.Json": "13.0.3", + "System.CodeDom": "7.0.0", + "System.Composition": "9.0.0", + "System.Configuration.ConfigurationManager": "9.0.0", + "System.Diagnostics.EventLog": "9.0.0", + "System.Resources.Extensions": "9.0.0", + "System.Security.Cryptography.Pkcs": "7.0.2", + "System.Security.Cryptography.ProtectedData": "9.0.0", + "System.Security.Cryptography.Xml": "7.0.1", + "System.Security.Permissions": "9.0.0", + "System.Windows.Extensions": "9.0.0" + } + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.1", + "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", + "dependencies": { + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", + "Microsoft.Bcl.Cryptography": "9.0.4", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.Extensions.Caching.Memory": "9.0.4", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "9.0.4", + "System.Security.Cryptography.Pkcs": "9.0.4" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "8/5kYGwKN6wtc89QqcPTOZDAJSMX8MzKCf5OmYjIfAHWTfsUEpGKYrdtfNk4X36rQ0BiU3n57Y4rbtnerzJN0Q==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "H4SWETCh/cC5L1WtWchHR6LntGk3rDTTznZMssr4cL8IbDmMWBxY+MOGDc/ASnqNolLKPIWHWeuC1ddiL/iNPw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "tMF9wNh+hlyYDWB8mrFCQHQmWHlRosol1b/N2Jrefy1bFLnuTlgSYmPyHNmz8xVQgs7DpXytBRWxGhG+mSTp0g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "IiWPd4j8JLNjSkyXl5hvJwX2ZENDVQVPDHYgZmYdw8+YkY2xp9iQt0vjdnAQZLpo/ipeW1xgOqfSBEnivKWPYQ==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "SfK89ytD61S7DgzorFljSkUeluC1ncn6dtZgwc0ot39f/BEYWBl5jpgvodxduoYAs1d9HG8faCDRZxE95UMo2A==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "/ppSdehKk3fuXjlqCDgSOtjRK/pSHU8eWgzSHfHdwVm5BP4Dgejehkw+PtxKG2j98qTDEHDst2Y99aNsmJldmw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "KrN6TGFwCwqOkLLk/idW/XtDQh+8In+CL9T4M1Dx+5ScsjTq4TlVbal8q532m82UYrMr6RiQJF2HvYCN0QwVsA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "j8zcwhS6bYB6FEfaY3nYSgHdpiL2T+/V3xjpHtslVAegyI1JUbB9yAt/BFdvZdsNbY0Udm4xFtvfT/hUwcOOOg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Configuration.Binder": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "tL9cSl3maS5FPzp/3MtlZI21ExWhni0nnUCF8HY4npTsINw45n9SNDbkKXBMtFyUFGSsQep25fHIDN4f/Vp3AQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Configuration.Binder": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.35.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", + "dependencies": { + "Microsoft.Identity.Client": "4.73.1", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "DMIeWDlxe0Wz0DIhJZ2FMoGQAN2yrGZOi5jjFhRYHWR5ONd0CS6IpAHlRnA7uA/5BF+BADvgsETxW2XrPiFc1A==" + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "YqueG52R/Xej4VVbKuRIodjiAhV0HR/XVbLbNrJhCZnzjnSjgMJ/dCdV0akQQxavX6hp/LC6rqLGLcXeQYU7XA==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "OpenTelemetry": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Configuration": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + } + }, + "OpenTelemetry.Api": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + }, + "OpenTelemetry.Api.ProviderBuilderExtensions": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "OpenTelemetry.Api": "1.14.0" + } + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.27", + "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" + } + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "oTE5IfuMoET8yaZP/vdvy9xO47guAv/rOhe4DODuFBN3ySprcQOlXqO3j+e/H/YpKKR5sglrxRaZ2HYOhNJrqA==" + }, + "System.Composition": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "3Djj70fFTraOarSKmRnmRy/zm4YurICm+kiCtI0dYRqGJnLX6nJ+G3WYuFJ173cAPax/gh96REcbNiVqcrypFQ==", + "dependencies": { + "System.Composition.AttributedModel": "9.0.0", + "System.Composition.Convention": "9.0.0", + "System.Composition.Hosting": "9.0.0", + "System.Composition.Runtime": "9.0.0", + "System.Composition.TypedParts": "9.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "iri00l/zIX9g4lHMY+Nz0qV1n40+jFYAmgsaiNn16xvt2RDwlqByNG4wgblagnDYxm3YSQQ0jLlC/7Xlk9CzyA==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "+vuqVP6xpi582XIjJi6OCsIxuoTZfR0M7WWufk3uGDeCl3wGW6KnpylUJ3iiXdPByPE0vR5TjJgR6hDLez4FQg==", + "dependencies": { + "System.Composition.AttributedModel": "9.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "OFqSeFeJYr7kHxDfaViGM1ymk7d4JxK//VSoNF9Ux0gpqkLsauDZpu89kTHHNdCWfSljbFcvAafGyBoY094btQ==", + "dependencies": { + "System.Composition.Runtime": "9.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "w1HOlQY1zsOWYussjFGZCEYF2UZXgvoYnS94NIu2CBnAGMbXFAX8PY8c92KwUItPmowal68jnVLBCzdrWLeEKA==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "aRZlojCCGEHDKqh43jaDgaVpYETsgd7Nx4g1zwLKMtv4iTo0627715ajEFNpEEBTgLmvZuv8K0EVxc3sM4NWJA==", + "dependencies": { + "System.Composition.AttributedModel": "9.0.0", + "System.Composition.Hosting": "9.0.0", + "System.Composition.Runtime": "9.0.0" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", + "dependencies": { + "System.Diagnostics.EventLog": "9.0.4", + "System.Security.Cryptography.ProtectedData": "9.0.4" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "getRQEXD8idlpb1KW56XuxImMy0FKp2WJPDf3Qr0kI/QKxxJSftqfDFVo0DZ3HCJRLU73qHSruv5q2l5O47jQQ==" + }, + "System.Formats.Nrbf": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "F/6tNE+ckmdFeSQAyQo26bQOqfPFKEfZcuqnp4kBE6/7jP26diP+QTHCJJ6vpEfaY6bLy+hBLiIQUSxSmNwLkA==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Reflection.MetadataLoadContext": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "z9PvtMJra5hK8n+g0wmPtaG7HQRZpTmIPRw5Z0LEemlcdQMHuTD5D7OAY/fZuuz1L9db++QOcDF0gJTLpbMtZQ==" + }, + "System.Resources.Extensions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "tvhuT1D2OwPROdL1kRWtaTJliQo0WdyhvwDpd8RM997G7m3Hya5nhbYhNTS75x6Vu+ypSOgL5qxDCn8IROtCxw==", + "dependencies": { + "System.Formats.Nrbf": "9.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" + }, + "System.Security.Cryptography.Xml": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "GQZn5wFd+pyOfwWaCbqxG7trQ5ox01oR8kYgWflgtux4HiUNihGEgG2TktRWyH+9bw7NoEju1D41H/upwQeFQw==", + "dependencies": { + "System.Security.Cryptography.Pkcs": "9.0.0" + } + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "H2VFD4SFVxieywNxn9/epb63/IOcPPfA0WOtfkljzNfu7GCcHIBQNuwP6zGCEIi7Ci/oj8aLPUNK9sYImMFf4Q==", + "dependencies": { + "System.Windows.Extensions": "9.0.0" + } + }, + "System.Threading.RateLimiting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" + }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "U9msthvnH2Fsw7xwAvIhNHOdnIjOQTwOc8Vd0oGOsiRcGMGoBFlUD6qtYawRUoQdKH9ysxesZ9juFElt1Jw/7A==" + }, + "application": { + "type": "Project", + "dependencies": { + "Domain": "[1.0.0, )", + "FluentValidation": "[12.1.1, )", + "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", + "Microsoft.Extensions.Logging": "[10.0.1, )" + } + }, + "domain": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + } + }, + "FluentValidation": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "EPpkIe1yh1a0OXyC100oOA8WMbZvqUu5plwhvYcb7oSELfyUZzfxV48BLhvs3kKo4NwG7MGLNgy1RJiYtT8Dpw==" + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "D0VXh4dtjjX2aQizuaa0g6R8X3U1JaVqJPfGCvLwZX9t/O2h7tkpbitbadQMfwcgSPdDbI2vDxuwRMv/Uf9dHA==", + "dependencies": { + "FluentValidation": "12.1.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + } + } + } +} \ No newline at end of file diff --git a/templates/src/WebApp/Endpoints/EndpointExtensions.cs b/templates/Full/src/WebApp/Endpoints/EndpointExtensions.cs similarity index 100% rename from templates/src/WebApp/Endpoints/EndpointExtensions.cs rename to templates/Full/src/WebApp/Endpoints/EndpointExtensions.cs diff --git a/templates/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Full/src/WebApp/Endpoints/OrderEndpoints.cs similarity index 100% rename from templates/src/WebApp/Endpoints/OrderEndpoints.cs rename to templates/Full/src/WebApp/Endpoints/OrderEndpoints.cs diff --git a/templates/src/WebApp/GrpcServices/GrpcServiceExtensions.cs b/templates/Full/src/WebApp/GrpcServices/GrpcServiceExtensions.cs similarity index 100% rename from templates/src/WebApp/GrpcServices/GrpcServiceExtensions.cs rename to templates/Full/src/WebApp/GrpcServices/GrpcServiceExtensions.cs diff --git a/templates/src/WebApp/GrpcServices/OrderService.cs b/templates/Full/src/WebApp/GrpcServices/OrderService.cs similarity index 100% rename from templates/src/WebApp/GrpcServices/OrderService.cs rename to templates/Full/src/WebApp/GrpcServices/OrderService.cs diff --git a/templates/src/WebApp/HealthChecks/HealthCheckExtensions.cs b/templates/Full/src/WebApp/HealthChecks/HealthCheckExtensions.cs similarity index 100% rename from templates/src/WebApp/HealthChecks/HealthCheckExtensions.cs rename to templates/Full/src/WebApp/HealthChecks/HealthCheckExtensions.cs diff --git a/templates/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs similarity index 100% rename from templates/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs rename to templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs diff --git a/templates/src/WebApp/Program.cs b/templates/Full/src/WebApp/Program.cs similarity index 100% rename from templates/src/WebApp/Program.cs rename to templates/Full/src/WebApp/Program.cs diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json new file mode 100644 index 00000000..58992051 --- /dev/null +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Development": { + "commandName": "Project", + "dotnetRunMessages": true, + "sqlDebugging": true, + "launchBrowser": false, + "applicationUrl": "https://*:7175;http://*:5010", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "LOGGING__LOGLEVEL__DEFAULT": "Debug", + "LOGGING__LOGLEVEL__MICROSOFT": "Information", + "LOGGING__LOGLEVEL__MICROSOFT_HOSTING_LIFETIME": "Information", + "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE": "Information", + "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND": "Information", + "ENABLE_SENSITIVE_DATA_LOGGING": "true", + "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:18889", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:18889", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:18889", + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:18889", + "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", + "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", + "ConnectionStrings__OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", + "ConnectionStrings__Redis": "127.0.0.1:6379", + "ConnectionStrings__RabbitMQ": "amqp://guest:guest@127.0.0.1:5672/" + + } + } + } +} diff --git a/templates/Full/src/WebApp/Protos/order.proto b/templates/Full/src/WebApp/Protos/order.proto new file mode 100644 index 00000000..96761cd8 --- /dev/null +++ b/templates/Full/src/WebApp/Protos/order.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; + +option csharp_namespace = "GrpcOrder"; + +package order; + +service OrderService { + rpc Get (GetOrderRequest) returns (OrderReply); +} + +message GetOrderRequest { + int32 id = 1; + string correlationId = 2; +} + +message ItemDto { + int32 id = 1; + string name = 2; + string description = 3; + double value = 4; +} + +message OrderDto { + int32 id = 1; + string description = 2; + double total = 3; + repeated ItemDto items = 4; +} + +message OrderReply { + bool success = 1; + string message = 2; + OrderDto data = 3; +} \ No newline at end of file diff --git a/templates/src/WebApp/WebApp.csproj b/templates/Full/src/WebApp/WebApp.csproj similarity index 100% rename from templates/src/WebApp/WebApp.csproj rename to templates/Full/src/WebApp/WebApp.csproj diff --git a/templates/Full/src/WebApp/WebApp.http b/templates/Full/src/WebApp/WebApp.http new file mode 100644 index 00000000..f9a0963b --- /dev/null +++ b/templates/Full/src/WebApp/WebApp.http @@ -0,0 +1,63 @@ +@WebApp_HostAddress = https://localhost:7175 + +### +GET {{WebApp_HostAddress}}/health HTTP/2 +Accept: application/json + +### +GET {{WebApp_HostAddress}}/live HTTP/2 + +### +GET {{WebApp_HostAddress}}/ready HTTP/2 + +### +GET {{WebApp_HostAddress}}/orders/1 HTTP/2 +CorrelationId: {{$guid}} +CacheEnabled: false +Accept-Encoding: gzip, deflate +Accept: application/json + +### +POST {{WebApp_HostAddress}}/orders HTTP/2 +Content-Type: application/json + +{ + "CorrelationId": "{{$guid}}", + "Description": "John's computer", + "Items": [ + { + "Name": "Computer", + "Description": "Surface 2", + "Value": 1000 + }, + { + "Name": "Mouse", + "Description": "Microsoft mouse", + "Value": 99 + } + ] +} + +### +POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 +Content-Type: application/json + +{ + "CorrelationId": "{{$guid}}", + "Page": 1, + "PageSize": 1 +} + +### +POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 +Content-Type: application/json + +{ + "CorrelationId": "{{$guid}}", + "Page": 1, + "PageSize": 10, + "SearchByValues": { + "Description": "client" + } +} + diff --git a/templates/Full/src/WebApp/appsettings.json b/templates/Full/src/WebApp/appsettings.json new file mode 100644 index 00000000..770f1bb3 --- /dev/null +++ b/templates/Full/src/WebApp/appsettings.json @@ -0,0 +1,7 @@ +{ + "ConnectionStrings": { + "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", + "Redis": "localhost:6379", + "RabbitMq": "amqp://guest:guest@localhost:5672/" + } +} \ No newline at end of file diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json new file mode 100644 index 00000000..5e3ca9e0 --- /dev/null +++ b/templates/Full/src/WebApp/packages.lock.json @@ -0,0 +1,486 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "AspNetCore.HealthChecks.Rabbitmq": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "7WSQ7EwioA5niakzzLtGVcZMEOh+42fSwrI24vnNsT7gZuVGOViNekyz38G6wBPYKcpL/lUkMdg3ZaCiZTi/Dw==", + "dependencies": { + "RabbitMQ.Client": "7.0.0" + } + }, + "AspNetCore.HealthChecks.Redis": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", + "dependencies": { + "StackExchange.Redis": "2.7.4" + } + }, + "AspNetCore.HealthChecks.SqlServer": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "UxCf65iCF2nU1u7AcB320abjL4CRg5swCgJECY6mKk1j5lrGMfVtskWwriGs1T29pYdRig9Vra3SPnP+4G82pA==", + "dependencies": { + "Microsoft.Data.SqlClient": "5.2.2" + } + }, + "AspNetCore.HealthChecks.UI.Client": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "1Ub3Wvvbz7CMuFNWgLEc9qqQibiMoovDML/WHrwr5J83RPgtI20giCR92s/ipLgu7IIuqw+W/y7WpIeHqAICxg==", + "dependencies": { + "AspNetCore.HealthChecks.UI.Core": "9.0.0" + } + }, + "Grpc.AspNetCore": { + "type": "Direct", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } + }, + "AspNetCore.HealthChecks.UI.Core": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==" + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", + "dependencies": { + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" + } + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.31.1", + "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" + }, + "Grpc.AspNetCore.Server": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "diSC/ZeNdSdxHdYSOpYwuSBBDYpuNVtJQFJfiBB0WrYOQ4lVMmdxuUZJcViahQyo8pCvS3Mueo5lqFxwwMF/iw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.AspNetCore.Server.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "y5KGO1GO0N2L/hCCMR05mmoK8j+v8rKvZ+9nothAxKx2Tf2CwV8f4TM5K0GkKfDsp4vrc4lm90MU6E+DeN7YIw==", + "dependencies": { + "Grpc.AspNetCore.Server": "2.76.0", + "Grpc.Net.ClientFactory": "2.76.0" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "cSxC2tdnFdXXuBgIn1pjc4YBx7LXTCp4M0qn+SMBS35VWZY+cEQYLWTBDDhdBH1HzU7BV+ncVZlniGQHMpRJKQ==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "K1oldmqw2+Gn69nGRzZLhqSiUZwelX1GrBu/cUl9wNf1C0uB61vFS6JcxUUv9P8VoUJhFsmV44JA6lI2EUt4xw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "XI+kO69L9AV8B9N0UQOmH911r6MOEp9huHiavEsY56DJYuzJ9KAxNGy37dpV6CLbgCaN2uKmpOsZ9Pao6bmpVQ==", + "dependencies": { + "Grpc.Net.Client": "2.76.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "bZpiMVYgvpB44/wBh1RotrkqC7bg2FOasLri2GhR3hMKyzsiTxCoDE49YjPrJeFc4RW0wS8u+EInI09sjxVFRA==", + "dependencies": { + "Grpc.Core.Api": "2.76.0" + } + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.Cryptography": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.1", + "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", + "dependencies": { + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", + "Microsoft.Bcl.Cryptography": "9.0.4", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "9.0.4", + "System.Security.Cryptography.Pkcs": "9.0.4" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "8/5kYGwKN6wtc89QqcPTOZDAJSMX8MzKCf5OmYjIfAHWTfsUEpGKYrdtfNk4X36rQ0BiU3n57Y4rbtnerzJN0Q==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.1" + } + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.35.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", + "dependencies": { + "Microsoft.Identity.Client": "4.73.1", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "OpenTelemetry": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + } + }, + "OpenTelemetry.Api": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + }, + "OpenTelemetry.Api.ProviderBuilderExtensions": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "dependencies": { + "OpenTelemetry.Api": "1.14.0" + } + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.27", + "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==", + "dependencies": { + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", + "dependencies": { + "System.Memory.Data": "8.0.1" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", + "dependencies": { + "System.Security.Cryptography.ProtectedData": "9.0.4" + } + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" + }, + "application": { + "type": "Project", + "dependencies": { + "Domain": "[1.0.0, )", + "FluentValidation": "[12.1.1, )", + "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )" + } + }, + "domain": { + "type": "Project" + }, + "infrastructure": { + "type": "Project", + "dependencies": { + "Application": "[1.0.0, )", + "Microsoft.EntityFrameworkCore": "[10.0.1, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.1, )", + "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", + "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )", + "RabbitMQ.Client": "[7.2.0, )", + "RabbitMQ.Client.OpenTelemetry": "[1.0.0-rc.2, )" + } + }, + "FluentValidation": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "EPpkIe1yh1a0OXyC100oOA8WMbZvqUu5plwhvYcb7oSELfyUZzfxV48BLhvs3kKo4NwG7MGLNgy1RJiYtT8Dpw==" + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "D0VXh4dtjjX2aQizuaa0g6R8X3U1JaVqJPfGCvLwZX9t/O2h7tkpbitbadQMfwcgSPdDbI2vDxuwRMv/Uf9dHA==", + "dependencies": { + "FluentValidation": "12.1.1" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Uz/M/Y2UPWJ+wJV8JGb7gp5H6Q8oE+/Fz/YZ5osBNDIxrlT+/I7AraVKxErSlz/nZq5JVy+jL1znnIcgI0G/Hw==", + "dependencies": { + "Microsoft.Data.SqlClient": "6.1.1", + "Microsoft.EntityFrameworkCore.Relational": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Hybrid": { + "type": "CentralTransitive", + "requested": "[10.1.0, )", + "resolved": "10.1.0", + "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==" + }, + "Microsoft.Extensions.Caching.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "dependencies": { + "StackExchange.Redis": "2.7.27" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Extensions.Hosting": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Instrumentation.AspNetCore": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.GrpcNetClient": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.1, )", + "resolved": "1.12.0-beta.1", + "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "dependencies": { + "OpenTelemetry": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Http": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", + "StackExchange.Redis": "[2.6.122, 3.0.0)" + } + }, + "RabbitMQ.Client": { + "type": "CentralTransitive", + "requested": "[7.2.0, )", + "resolved": "7.2.0", + "contentHash": "PPQ7cF7lwbhqC4up6en1bTUZlz06YqQwJecOJzsguTtyhNA7oL5uNDZIx/h6ZfcyPZV4V3DYKSCxfm4RUFLcbA==" + }, + "RabbitMQ.Client.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[1.0.0-rc.2, )", + "resolved": "1.0.0-rc.2", + "contentHash": "fVjEZ8DsLDw3EEp/Q5XGFhk7Rvluh32n+38kyBcCdZu/F7cOuj6ETo+pYMhXHtcKGk856sEEOaEWR+alsETv9w==", + "dependencies": { + "OpenTelemetry.Api": "1.9.0", + "RabbitMQ.Client": "7.2.0" + } + } + } + } +} \ No newline at end of file diff --git a/templates/Full/tests/CommonTests/CommonTests.csproj b/templates/Full/tests/CommonTests/CommonTests.csproj new file mode 100644 index 00000000..3da95e90 --- /dev/null +++ b/templates/Full/tests/CommonTests/CommonTests.csproj @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs b/templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs new file mode 100644 index 00000000..0b8da73e --- /dev/null +++ b/templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs @@ -0,0 +1,14 @@ +using AutoFixture; + +namespace CommonTests.Fixtures; +public class BaseFixture +{ + public Fixture autoFixture = new(); + + public CancellationToken cancellationToken = CancellationToken.None; + + public BaseFixture() + { + autoFixture.Behaviors.Add(new OmitOnRecursionBehavior()); + } +} diff --git a/templates/Full/tests/CommonTests/packages.lock.json b/templates/Full/tests/CommonTests/packages.lock.json new file mode 100644 index 00000000..7d97c3a9 --- /dev/null +++ b/templates/Full/tests/CommonTests/packages.lock.json @@ -0,0 +1,57 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "AutoFixture": { + "type": "Direct", + "requested": "[4.18.1, )", + "resolved": "4.18.1", + "contentHash": "BmWZDY4fkrYOyd5/CTBOeXbzsNwV8kI4kDi/Ty1Y5F+WDHBVKxzfWlBE4RSicvZ+EOi2XDaN5uwdrHsItLW6Kw==", + "dependencies": { + "Fare": "[2.1.1, 3.0.0)" + } + }, + "Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "xunit.extensibility.core": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "Fare": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "HaI8puqA66YU7/9cK4Sgbs1taUTP1Ssa4QT2PIzqJ7GvAbN1QgkjbRsjH+FSbMh1MJdvS0CIwQNLtFT+KF6KpA==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + } + } + } +} \ No newline at end of file diff --git a/templates/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs b/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs similarity index 100% rename from templates/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs rename to templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs diff --git a/templates/tests/IntegrationTests/Data/BaseDataFixture.cs b/templates/Full/tests/IntegrationTests/Data/BaseDataFixture.cs similarity index 100% rename from templates/tests/IntegrationTests/Data/BaseDataFixture.cs rename to templates/Full/tests/IntegrationTests/Data/BaseDataFixture.cs diff --git a/templates/tests/IntegrationTests/Data/BaseRepositoryTest.cs b/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs similarity index 100% rename from templates/tests/IntegrationTests/Data/BaseRepositoryTest.cs rename to templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs diff --git a/templates/Full/tests/IntegrationTests/GlobalUsings.cs b/templates/Full/tests/IntegrationTests/GlobalUsings.cs new file mode 100644 index 00000000..dc5775ea --- /dev/null +++ b/templates/Full/tests/IntegrationTests/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using AutoFixture; diff --git a/templates/tests/IntegrationTests/IntegrationTests.csproj b/templates/Full/tests/IntegrationTests/IntegrationTests.csproj similarity index 100% rename from templates/tests/IntegrationTests/IntegrationTests.csproj rename to templates/Full/tests/IntegrationTests/IntegrationTests.csproj diff --git a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs new file mode 100644 index 00000000..b9c4f292 --- /dev/null +++ b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs @@ -0,0 +1,12 @@ +using Grpc.Net.Client; + +namespace IntegrationTests.WebApp.Grpc.Common; +public sealed class ApiGrpcHelper(HttpClient httpClient) +{ + public HttpClient httpClient = httpClient; + + public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(httpClient.BaseAddress!, new GrpcChannelOptions + { + HttpClient = httpClient + }); +} diff --git a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs new file mode 100644 index 00000000..f4ea7772 --- /dev/null +++ b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs @@ -0,0 +1,70 @@ +using CommonTests.Fixtures; +using Grpc.Net.Client; +using GrpcOrder; +using IntegrationTests.Common; +using IntegrationTests.WebApp.Grpc.Common; +using WebApp; + +namespace IntegrationTests.WebApp.Grpc.Orders; + +[Collection("WebApplicationFactoryCollectionDefinition")] +public class GetOrderGrpcTest : BaseFixture +{ + public CustomWebApplicationFactory customWebApplicationFactory; + + public ApiGrpcHelper apiGrpcHelper; + private readonly GrpcChannel _grpcChannel; + private readonly OrderService.OrderServiceClient _service; + + public GetOrderGrpcTest(CustomWebApplicationFactory customWebApplicationFactory) + { + this.customWebApplicationFactory = customWebApplicationFactory; + apiGrpcHelper = new(this.customWebApplicationFactory.CreateClient()); + _grpcChannel = apiGrpcHelper.AsGrpcClientChannel(); + _service = new(_grpcChannel); + } + + [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] + public async Task Given_A_Valid_Request_Then_Pass() + { + // Arrange + GetOrderRequest request = new() + { + CorrelationId = Guid.NewGuid().ToString(), + Id = 1 + }; + + // Act + var response = await _service.GetAsync(request); + + // Assert + Assert.NotNull(response); + Assert.True(response.Success); + Assert.True(string.IsNullOrEmpty(response.Message)); + Assert.NotNull(response.Data); + Assert.Equal(1, response.Data.Id); + Assert.NotNull(response.Data.Items); + Assert.NotEmpty(response.Data.Items); + Assert.Equal(1000.0, response.Data.Total); + } + + [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] + public async Task Given_A_Invalid_Request_Then_Fails() + { + // Arrange + GetOrderRequest request = new() + { + CorrelationId = Guid.NewGuid().ToString(), + Id = 999 + }; + + // Act + var response = await _service.GetAsync(request); + + // Assert + Assert.NotNull(response); + Assert.False(response.Success); + Assert.False(string.IsNullOrEmpty(response.Message)); + Assert.Null(response.Data); + } +} diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs new file mode 100644 index 00000000..d2962430 --- /dev/null +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs @@ -0,0 +1,57 @@ +using System.Text; +using System.Text.Json; +using Grpc.Net.Client; + +namespace IntegrationTests.WebApp.Http.Common; +public sealed class ApiHelper(HttpClient httpClient) +{ + public HttpClient httpClient = httpClient; + + private static readonly JsonSerializerOptions _jsonSerializerOptions = new() + { + PropertyNameCaseInsensitive = true + }; + + public void AddHeaders(Dictionary headers) + { + foreach (var header in headers) + { + if (httpClient.DefaultRequestHeaders.Contains(header.Key)) + { + httpClient.DefaultRequestHeaders.Remove(header.Key); + } + + httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); + } + } + + public async Task GetAsync(string resourceUrl) => + await httpClient.GetAsync(resourceUrl); + + public async Task PostAsync(string resourceUrl, dynamic dataClass) => + await httpClient.PostAsync(resourceUrl, SerializeRequest(dataClass)); + + public async Task PutAsync(string resourceUrl, dynamic data) => + await httpClient.PutAsync(resourceUrl, SerializeRequest(data)); + + public async Task DeleteAsync(string resourceUrl) => + await httpClient.DeleteAsync(resourceUrl); + + public StringContent SerializeRequest(dynamic data) + { + var json = JsonSerializer.Serialize(data); + return new StringContent(json, Encoding.UTF8, "application/json"); + } + + public async Task DeSerializeResponse(HttpResponseMessage response) + { + var content = await response.Content.ReadAsStreamAsync(); + + return JsonSerializer.Deserialize(content, _jsonSerializerOptions); + } + + public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(httpClient.BaseAddress!, new GrpcChannelOptions + { + HttpClient = httpClient + }); +} diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs new file mode 100644 index 00000000..64636bb8 --- /dev/null +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs @@ -0,0 +1,14 @@ +using CommonTests.Fixtures; +using IntegrationTests.Common; +using WebApp; + +namespace IntegrationTests.WebApp.Http.Common; + +public class BaseHttpFixture : BaseFixture +{ + public ApiHelper apiHelper; + public string resourceUrl = string.Empty; + + public void SetApiHelper(CustomWebApplicationFactory customWebApplicationFactory) => + apiHelper = new(customWebApplicationFactory.CreateClient()); +} \ No newline at end of file diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs new file mode 100644 index 00000000..cf9af870 --- /dev/null +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs @@ -0,0 +1,65 @@ +using System.Net; +using Application.Common.Requests; +using Application.Orders; +using IntegrationTests.Common; +using IntegrationTests.WebApp.Http.Common; +using WebApp; + +namespace IntegrationTests.WebApp.Http.Orders; + +public class CreateOrderTestFixture : BaseHttpFixture +{ + public CreateOrderRequest SetValidRequest() => autoFixture.Create(); + + public CreateOrderRequest SetInvalidRequest() => autoFixture + .Build() + .With(r => r.Description, string.Empty) + .Create(); +} + +[Collection("WebApplicationFactoryCollectionDefinition")] +public sealed class CreateOrderTest : IClassFixture +{ + private readonly CreateOrderTestFixture _fixture; + + public CreateOrderTest(CustomWebApplicationFactory customWebApplicationFactory, CreateOrderTestFixture fixture) + { + _fixture = fixture; + _fixture.SetApiHelper(customWebApplicationFactory); + _fixture.resourceUrl = "orders"; + } + + [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] + public async Task Given_A_Valid_Request_Then_Pass() + { + // Arrange + var request = _fixture.SetValidRequest(); + + // Act + var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.Created, result.StatusCode); + Assert.True(response!.Success); + Assert.NotNull(response.Data); + } + + [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] + public async Task Given_A_Invalid_Request_Then_Fails() + { + // Arrange + var request = _fixture.SetInvalidRequest(); + + // Act + var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + // Assert + Assert.NotNull(response); + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); + Assert.False(response.Success); + Assert.Null(response.Data); + } +} diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs new file mode 100644 index 00000000..42d98d43 --- /dev/null +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs @@ -0,0 +1,107 @@ +using System.Net; +using Application.Common.Requests; +using Application.Orders; +using IntegrationTests.Common; +using IntegrationTests.WebApp.Http.Common; +using WebApp; + +namespace IntegrationTests.WebApp.Http.Orders; + +public class GetAllOrdersTestFixture : BaseHttpFixture +{ + public BasePaginatedRequest SetValidRequest() => + new(Guid.NewGuid(), 1, 10); + + public BasePaginatedRequest SetInvalidPageRequest() => + new(Guid.NewGuid(), 0, 10); + + public BasePaginatedRequest SetInvalidPageSizeRequest() => + new(Guid.NewGuid(), 1, 0); +} + +[Collection("WebApplicationFactoryCollectionDefinition")] +public class GetAllOrdersTest : IClassFixture +{ + private readonly GetAllOrdersTestFixture _fixture; + public GetAllOrdersTest(CustomWebApplicationFactory customWebApplicationFactory, GetAllOrdersTestFixture fixture) + { + _fixture = fixture; + _fixture.SetApiHelper(customWebApplicationFactory); + _fixture.resourceUrl = "orders/paginated"; + } + + [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] + public async Task Given_A_Valid_Request_Then_Pass() + { + // Arrange + var request = _fixture.SetValidRequest(); + + // Act + var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.OK, result.StatusCode); + Assert.True(response!.Success); + Assert.NotNull(response.Data); + Assert.True(response.TotalPages >= 0); + Assert.True(response.TotalRecords >= 0); + } + + [Fact(DisplayName = nameof(Given_An_Invalid_Page_Request_Then_Fails))] + public async Task Given_An_Invalid_Page_Request_Then_Fails() + { + // Arrange + var request = _fixture.SetInvalidPageRequest(); + + // Act + var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); + Assert.False(response!.Success); + Assert.Contains("Page must be greater than 0", response.Message); + } + + [Fact(DisplayName = nameof(Given_An_Invalid_PageSize_Request_Then_Fails))] + public async Task Given_An_Invalid_PageSize_Request_Then_Fails() + { + // Arrange + var request = _fixture.SetInvalidPageSizeRequest(); + + // Act + var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); + Assert.False(response!.Success); + Assert.Contains("PageSize must be greater than 0", response.Message); + } + + [Fact(DisplayName = nameof(Given_An_Valid_Request_When_Pass_Search_By_Values_Filter_Then_Pass))] + public async Task Given_An_Valid_Request_When_Pass_Search_By_Values_Filter_Then_Pass() + { + // Arrange + var request = new BasePaginatedRequest( + Guid.NewGuid(), 1, 10, + SearchByValues: new Dictionary { { "Description", "client" } } + ); + + // Act + var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.OK, result.StatusCode); + Assert.True(response!.Success); + Assert.NotNull(response.Data); + Assert.True(response.TotalPages >= 0); + Assert.True(response.TotalRecords >= 0); + } +} diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs new file mode 100644 index 00000000..cde446ae --- /dev/null +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs @@ -0,0 +1,68 @@ +using System.Net; +using Application.Common.Requests; +using Application.Orders; +using IntegrationTests.Common; +using IntegrationTests.WebApp.Http.Common; +using WebApp; + +namespace IntegrationTests.WebApp.Http.Orders; + +[Collection("WebApplicationFactoryCollectionDefinition")] +public class GetOrderTest : IClassFixture +{ + private readonly BaseHttpFixture _fixture; + public GetOrderTest(CustomWebApplicationFactory customWebApplicationFactory, BaseHttpFixture fixture) + { + _fixture = fixture; + _fixture.SetApiHelper(customWebApplicationFactory); + _fixture.resourceUrl = "orders/{0}"; + } + + [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] + public async Task Given_A_Valid_Request_Then_Pass() + { + // Arrange + var id = 1; + var url = string.Format(_fixture.resourceUrl, id); + _fixture.apiHelper.AddHeaders(new Dictionary + { + { "CorrelationId", Guid.NewGuid().ToString() } + }); + + // Act + var result = await _fixture.apiHelper.GetAsync(url); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var data = response?.Data; + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.OK, result.StatusCode); + Assert.True(response!.Success); + Assert.NotNull(data); + Assert.Equal(id, data.Id); + Assert.NotNull(data.Items); + Assert.NotEmpty(data.Items); + } + + [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] + public async Task Given_A_Invalid_Request_Then_Fails() + { + // Arrange + var id = 9999999; + var url = string.Format(_fixture.resourceUrl, id); + _fixture.apiHelper.AddHeaders(new Dictionary + { + { "CorrelationId", Guid.NewGuid().ToString() } + }); + + // Act + var result = await _fixture.apiHelper.GetAsync(url); + var response = await _fixture.apiHelper.DeSerializeResponse>(result); + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.NotFound, result.StatusCode); + Assert.False(response!.Success); + Assert.Null(response.Data); + } +} diff --git a/templates/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs rename to templates/Full/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs diff --git a/templates/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs similarity index 100% rename from templates/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs rename to templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json new file mode 100644 index 00000000..c64d0489 --- /dev/null +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -0,0 +1,1042 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.AspNetCore.Mvc.Testing": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "84aSUoB++qrL9mlkAT1ybV9KQ5bv7sbpx2B5uo9se0ryYjNPIeiuknVy7r0FOwRk8T58PYybhIBa7WOkdMgOZQ==", + "dependencies": { + "Microsoft.AspNetCore.TestHost": "10.0.1", + "Microsoft.Extensions.DependencyModel": "10.0.1", + "Microsoft.Extensions.Hosting": "10.0.1" + } + }, + "Microsoft.AspNetCore.TestHost": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Vvos4CyBam5dCsH3eD1c9MQI4ESWwzNSJsToFz4i6NmfPsaySzNSiv0QYRmSAAIBXb8GXxPmuy42TkIrw2xCzQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", + "dependencies": { + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" + }, + "AspNetCore.HealthChecks.UI.Core": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" + } + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", + "dependencies": { + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" + } + }, + "Fare": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "HaI8puqA66YU7/9cK4Sgbs1taUTP1Ssa4QT2PIzqJ7GvAbN1QgkjbRsjH+FSbMh1MJdvS0CIwQNLtFT+KF6KpA==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.31.1", + "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" + }, + "Grpc.AspNetCore.Server": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "diSC/ZeNdSdxHdYSOpYwuSBBDYpuNVtJQFJfiBB0WrYOQ4lVMmdxuUZJcViahQyo8pCvS3Mueo5lqFxwwMF/iw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.AspNetCore.Server.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "y5KGO1GO0N2L/hCCMR05mmoK8j+v8rKvZ+9nothAxKx2Tf2CwV8f4TM5K0GkKfDsp4vrc4lm90MU6E+DeN7YIw==", + "dependencies": { + "Grpc.AspNetCore.Server": "2.76.0", + "Grpc.Net.ClientFactory": "2.76.0" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "cSxC2tdnFdXXuBgIn1pjc4YBx7LXTCp4M0qn+SMBS35VWZY+cEQYLWTBDDhdBH1HzU7BV+ncVZlniGQHMpRJKQ==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "K1oldmqw2+Gn69nGRzZLhqSiUZwelX1GrBu/cUl9wNf1C0uB61vFS6JcxUUv9P8VoUJhFsmV44JA6lI2EUt4xw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "XI+kO69L9AV8B9N0UQOmH911r6MOEp9huHiavEsY56DJYuzJ9KAxNGy37dpV6CLbgCaN2uKmpOsZ9Pao6bmpVQ==", + "dependencies": { + "Grpc.Net.Client": "2.76.0", + "Microsoft.Extensions.Http": "8.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "bZpiMVYgvpB44/wBh1RotrkqC7bg2FOasLri2GhR3hMKyzsiTxCoDE49YjPrJeFc4RW0wS8u+EInI09sjxVFRA==", + "dependencies": { + "Grpc.Core.Api": "2.76.0" + } + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.Cryptography": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.1", + "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", + "dependencies": { + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", + "Microsoft.Bcl.Cryptography": "9.0.4", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.Extensions.Caching.Memory": "9.0.4", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "9.0.4", + "System.Security.Cryptography.Pkcs": "9.0.4" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "8/5kYGwKN6wtc89QqcPTOZDAJSMX8MzKCf5OmYjIfAHWTfsUEpGKYrdtfNk4X36rQ0BiU3n57Y4rbtnerzJN0Q==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "s5cxcdtIig66YT3J+7iHflMuorznK8kXuwBBPHMp4KImx5ZGE3FRa1Nj9fI/xMwFV+KzUMjqZ2MhOedPH8LiBQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "csD8Eps3HQ3yc0x6NhgTV+aIFKSs3qVlVCtFnMHz/JOjnv7eEj/qSXKXiK9LzBzB1qSfAVqFnB5iaX2nUmagIQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "N/6GiwiZFCBFZDk3vg1PhHW3zMqqu5WWpmeZAA9VTXv7Q8pr8NZR/EQsH0DjzqydDksJtY6EQBsu81d5okQOlA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "0zW3eYBJlRctmgqk5s0kFIi5o5y2g80mvGCD8bkYxREPQlKUnr0ndU/Sop+UDIhyWN0fIi4RW63vo7BKTi7ncA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "ULEJ0nkaW90JYJGkFujPcJtADXcJpXiSOLbokPcWJZ8iDbtDINifEYAUVqZVr81IDNTrRFul6O8RolOKOsgFPg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "IiWPd4j8JLNjSkyXl5hvJwX2ZENDVQVPDHYgZmYdw8+YkY2xp9iQt0vjdnAQZLpo/ipeW1xgOqfSBEnivKWPYQ==" + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks": { + "type": "Transitive", + "resolved": "8.0.11", + "contentHash": "zLgN22Zp9pk8RHlwssRTexw4+a6wqOnKWN+VejdPn5Yhjql4XiBhkFo35Nu8mmqHIk/UEmmCnMGLWq75aFfkOw==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.11", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { + "type": "Transitive", + "resolved": "8.0.11", + "contentHash": "So3JUdRxozRjvQ3cxU6F3nI/i4emDnjane6yMYcJhvTTTu29ltlIdoXjkFGRceIWz8yKvuEpzXItZ0x5GvN2nQ==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "4bxzGXIzZnz0Bf7czQ72jGvpOqJsRW/44PS7YLFXTTnu6cNcPvmSREDvBoH0ZVP2hAbMfL4sUoCUn54k70jPWw==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "49dFvGJjLSwGn76eHnP1gBvCJkL8HRYpCrG0DCvsP6wRpEQRLN2Fq8rTxbP+6jS7jmYKCnSVO5C65v4mT3rzeA==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "0jjfjQSOFZlHhwOoIQw0WyzxtkDMYdnPY3iFrOLasxYq/5J4FDt1HWT8TktBclOVjFY1FOOkoOc99X7AhbqSIw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.Configuration.CommandLine": "10.0.1", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.Configuration.UserSecrets": "10.0.1", + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Logging.Console": "10.0.1", + "Microsoft.Extensions.Logging.Debug": "10.0.1", + "Microsoft.Extensions.Logging.EventLog": "10.0.1", + "Microsoft.Extensions.Logging.EventSource": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Http": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "VqfTvbX9C6BA0VeIlpzPlljnNsXxiI5CdUHb9ksWERH94WQ6ft3oLGUAa4xKcDGu4xF+rIZ8wj7IOAd6/q7vGw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Zp9MM+jFCa7oktIug62V9eNygpkf+6kFVatF+UC/ODeUwIr5givYKy8fYSSI9sWdxqDqv63y1x0mm2VjOl8GOw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "System.Diagnostics.EventLog": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "WnFvZP+Y+lfeNFKPK/+mBpaCC7EeBDlobrQOqnP7rrw/+vE7yu8Rjczum1xbC0F/8cAHafog84DMp9200akMNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.35.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", + "dependencies": { + "Microsoft.Identity.Client": "4.73.1", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.0.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "OpenTelemetry": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Configuration": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + } + }, + "OpenTelemetry.Api": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + }, + "OpenTelemetry.Api.ProviderBuilderExtensions": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "OpenTelemetry.Api": "1.14.0" + } + }, + "Pipelines.Sockets.Unofficial": { + "type": "Transitive", + "resolved": "2.2.8", + "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" + }, + "StackExchange.Redis": { + "type": "Transitive", + "resolved": "2.7.27", + "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Pipelines.Sockets.Unofficial": "2.2.8" + } + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", + "dependencies": { + "System.Diagnostics.EventLog": "9.0.4", + "System.Security.Cryptography.ProtectedData": "9.0.4" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "xfaHEHVDkMOOZR5S6ZGezD0+vekdH1Nx/9Ih8/rOqOGSOk1fxiN3u94bYkBW/wigj0Uw2Wt3vvRj9mtYdgwEjw==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" + }, + "System.Threading.RateLimiting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "application": { + "type": "Project", + "dependencies": { + "Domain": "[1.0.0, )", + "FluentValidation": "[12.1.1, )", + "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", + "Microsoft.Extensions.Logging": "[10.0.1, )" + } + }, + "commontests": { + "type": "Project", + "dependencies": { + "AutoFixture": "[4.18.1, )", + "Newtonsoft.Json": "[13.0.4, )", + "xunit.extensibility.core": "[2.9.3, )" + } + }, + "domain": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + } + }, + "infrastructure": { + "type": "Project", + "dependencies": { + "Application": "[1.0.0, )", + "Microsoft.EntityFrameworkCore": "[10.0.1, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.1, )", + "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", + "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )", + "RabbitMQ.Client": "[7.2.0, )", + "RabbitMQ.Client.OpenTelemetry": "[1.0.0-rc.2, )" + } + }, + "webapp": { + "type": "Project", + "dependencies": { + "AspNetCore.HealthChecks.RabbitMQ": "[9.0.0, )", + "AspNetCore.HealthChecks.Redis": "[9.0.0, )", + "AspNetCore.HealthChecks.SqlServer": "[9.0.0, )", + "AspNetCore.HealthChecks.UI.Client": "[9.0.0, )", + "Grpc.AspNetCore": "[2.76.0, )", + "Infrastructure": "[1.0.0, )" + } + }, + "AspNetCore.HealthChecks.Rabbitmq": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "7WSQ7EwioA5niakzzLtGVcZMEOh+42fSwrI24vnNsT7gZuVGOViNekyz38G6wBPYKcpL/lUkMdg3ZaCiZTi/Dw==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11", + "RabbitMQ.Client": "7.0.0" + } + }, + "AspNetCore.HealthChecks.Redis": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11", + "StackExchange.Redis": "2.7.4" + } + }, + "AspNetCore.HealthChecks.SqlServer": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "UxCf65iCF2nU1u7AcB320abjL4CRg5swCgJECY6mKk1j5lrGMfVtskWwriGs1T29pYdRig9Vra3SPnP+4G82pA==", + "dependencies": { + "Microsoft.Data.SqlClient": "5.2.2", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" + } + }, + "AspNetCore.HealthChecks.UI.Client": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "1Ub3Wvvbz7CMuFNWgLEc9qqQibiMoovDML/WHrwr5J83RPgtI20giCR92s/ipLgu7IIuqw+W/y7WpIeHqAICxg==", + "dependencies": { + "AspNetCore.HealthChecks.UI.Core": "9.0.0" + } + }, + "AutoFixture": { + "type": "CentralTransitive", + "requested": "[4.18.1, )", + "resolved": "4.18.1", + "contentHash": "BmWZDY4fkrYOyd5/CTBOeXbzsNwV8kI4kDi/Ty1Y5F+WDHBVKxzfWlBE4RSicvZ+EOi2XDaN5uwdrHsItLW6Kw==", + "dependencies": { + "Fare": "[2.1.1, 3.0.0)" + } + }, + "FluentValidation": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "EPpkIe1yh1a0OXyC100oOA8WMbZvqUu5plwhvYcb7oSELfyUZzfxV48BLhvs3kKo4NwG7MGLNgy1RJiYtT8Dpw==" + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "D0VXh4dtjjX2aQizuaa0g6R8X3U1JaVqJPfGCvLwZX9t/O2h7tkpbitbadQMfwcgSPdDbI2vDxuwRMv/Uf9dHA==", + "dependencies": { + "FluentValidation": "12.1.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" + } + }, + "Grpc.AspNetCore": { + "type": "CentralTransitive", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "Uz/M/Y2UPWJ+wJV8JGb7gp5H6Q8oE+/Fz/YZ5osBNDIxrlT+/I7AraVKxErSlz/nZq5JVy+jL1znnIcgI0G/Hw==", + "dependencies": { + "Microsoft.Data.SqlClient": "6.1.1", + "Microsoft.EntityFrameworkCore.Relational": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Hybrid": { + "type": "CentralTransitive", + "requested": "[10.1.0, )", + "resolved": "10.1.0", + "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "StackExchange.Redis": "2.7.27" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "dependencies": { + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Extensions.Hosting": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "dependencies": { + "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", + "OpenTelemetry": "1.14.0" + } + }, + "OpenTelemetry.Instrumentation.AspNetCore": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.GrpcNetClient": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.1, )", + "resolved": "1.12.0-beta.1", + "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "dependencies": { + "OpenTelemetry": "[1.12.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Http": { + "type": "CentralTransitive", + "requested": "[1.14.0, )", + "resolved": "1.14.0", + "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.StackExchangeRedis": { + "type": "CentralTransitive", + "requested": "[1.12.0-beta.2, )", + "resolved": "1.12.0-beta.2", + "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "dependencies": { + "Microsoft.Extensions.Options": "9.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", + "StackExchange.Redis": "[2.6.122, 3.0.0)" + } + }, + "RabbitMQ.Client": { + "type": "CentralTransitive", + "requested": "[7.2.0, )", + "resolved": "7.2.0", + "contentHash": "PPQ7cF7lwbhqC4up6en1bTUZlz06YqQwJecOJzsguTtyhNA7oL5uNDZIx/h6ZfcyPZV4V3DYKSCxfm4RUFLcbA==", + "dependencies": { + "System.Threading.RateLimiting": "8.0.0" + } + }, + "RabbitMQ.Client.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[1.0.0-rc.2, )", + "resolved": "1.0.0-rc.2", + "contentHash": "fVjEZ8DsLDw3EEp/Q5XGFhk7Rvluh32n+38kyBcCdZu/F7cOuj6ETo+pYMhXHtcKGk856sEEOaEWR+alsETv9w==", + "dependencies": { + "OpenTelemetry.Api": "1.9.0", + "RabbitMQ.Client": "7.2.0" + } + }, + "xunit.extensibility.core": { + "type": "CentralTransitive", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + } + } + } +} \ No newline at end of file diff --git a/templates/Full/tests/LoadTests/protos/order.proto b/templates/Full/tests/LoadTests/protos/order.proto new file mode 100644 index 00000000..53b2838d --- /dev/null +++ b/templates/Full/tests/LoadTests/protos/order.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package order; + +service OrderService { + rpc Get (GetOrderRequest) returns (OrderReply); +} + +message GetOrderRequest { + int32 id = 1; + string correlationId = 2; +} + +message OrderDto { + int32 id = 1; + string description = 2; + double total = 3; +} + +message OrderReply { + bool success = 1; + string message = 2; + OrderDto data = 3; +} \ No newline at end of file diff --git a/templates/Full/tests/LoadTests/scriptGrpc.js b/templates/Full/tests/LoadTests/scriptGrpc.js new file mode 100644 index 00000000..457d6bcf --- /dev/null +++ b/templates/Full/tests/LoadTests/scriptGrpc.js @@ -0,0 +1,52 @@ +import grpc from 'k6/net/grpc'; +import { check, sleep } from 'k6'; +import { Counter, Trend, Rate } from 'k6/metrics'; + +export const options = { + scenarios: { + get_order: { + exec: 'getOrder', + executor: 'constant-vus', + vus: 10, + duration: '60s', + gracefulStop: '10s' + } + }, + thresholds: { + grpc_req_duration: ['p(50) < 50', 'p(95) < 100', 'p(99.9) < 500'], + get_order_response_time: ['p(95) < 100'], + get_order_success_rate: ['rate>0.95'], + get_order_requests_total: ['count>500'] + } +}; + +const webappUrl = __ENV.WEBAPP_GRPC_URL || 'localhost:7175'; + +const client = new grpc.Client(); +client.load([], './protos/order.proto'); + +const getOrderRequestsCounter = new Counter('get_order_requests_total'); +const getOrderResponseTime = new Trend('get_order_response_time'); +const getOrderSuccessRate = new Rate('get_order_success_rate'); +export function getOrder() { + getOrderRequestsCounter.add(1); + + client.connect(webappUrl, { plaintext: false }); + + const startTime = Date.now(); + const request = { id: 1, correlationId: crypto.randomUUID() }; + const response = client.invoke('order.OrderService/Get', request); + const duration = Date.now() - startTime; + + getOrderResponseTime.add(duration); + + const checkResults = check(response, { + 'status is OK': (r) => r && r.status === grpc.StatusOK, + 'success': (r) => r.message && r.message.success === true, + }); + + getOrderSuccessRate.add(checkResults); + + client.close(); + sleep(1); +} diff --git a/templates/Full/tests/LoadTests/scriptHttp.js b/templates/Full/tests/LoadTests/scriptHttp.js new file mode 100644 index 00000000..f36a1164 --- /dev/null +++ b/templates/Full/tests/LoadTests/scriptHttp.js @@ -0,0 +1,53 @@ +import http, { get } from 'k6/http'; +import { check, sleep } from 'k6'; +import { Counter, Trend, Rate } from 'k6/metrics'; + +export const options = { + scenarios: { + get_order: { + exec: 'getOrder', + executor: 'constant-vus', + vus: 10, + duration: '60s', + gracefulStop: '10s' + } + }, + thresholds: { + http_req_duration: ['p(50) < 100', 'p(95) < 500', 'p(99.9) < 1000'], + http_req_failed: ['rate<0.1'], + get_order_response_time: ['p(95) < 500'], + get_order_success_rate: ['rate>0.95'], + get_order_requests_total: ['count>500'] + }, +}; + +const webappUrl = __ENV.WEBAPP_URL || 'https://localhost:7175'; + +const headers = { + headers: { + 'correlationId': crypto.randomUUID(), + 'Accept': 'application/json', + 'Accept-Encoding': 'gzip, deflate', + 'CacheEnabled': 'false' + } +}; + +const getOrderRequestsCounter = new Counter('get_order_requests_total'); +const getOrderResponseTime = new Trend('get_order_response_time'); +const getOrderSuccessRate = new Rate('get_order_success_rate'); +export function getOrder() { + getOrderRequestsCounter.add(1); + + const res = http.get(`${webappUrl}/orders/1`, headers); + + getOrderResponseTime.add(res.timings.duration); + + const checkResults = check(res, { + 'status is 200': (r) => r.status === 200, + 'content type is JSON': (r) => r.headers['Content-Type'] === 'application/json; charset=utf-8', + }); + + getOrderSuccessRate.add(checkResults); + + sleep(1); +} diff --git a/templates/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs similarity index 100% rename from templates/tests/UnitTests/Application/Common/BaseApplicationFixture.cs rename to templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs diff --git a/templates/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs b/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs similarity index 100% rename from templates/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs rename to templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs diff --git a/templates/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs similarity index 100% rename from templates/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs rename to templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs diff --git a/templates/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs similarity index 100% rename from templates/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs rename to templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs diff --git a/templates/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs similarity index 100% rename from templates/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs rename to templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs diff --git a/templates/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs similarity index 100% rename from templates/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs rename to templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs diff --git a/templates/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs similarity index 100% rename from templates/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs rename to templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs diff --git a/templates/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs similarity index 100% rename from templates/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs rename to templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs diff --git a/templates/tests/UnitTests/Architecture/ApplicationTests.cs b/templates/Full/tests/UnitTests/Architecture/ApplicationTests.cs similarity index 100% rename from templates/tests/UnitTests/Architecture/ApplicationTests.cs rename to templates/Full/tests/UnitTests/Architecture/ApplicationTests.cs diff --git a/templates/tests/UnitTests/Architecture/DomainTests.cs b/templates/Full/tests/UnitTests/Architecture/DomainTests.cs similarity index 100% rename from templates/tests/UnitTests/Architecture/DomainTests.cs rename to templates/Full/tests/UnitTests/Architecture/DomainTests.cs diff --git a/templates/tests/UnitTests/Domain/NotificationTests.cs b/templates/Full/tests/UnitTests/Domain/NotificationTests.cs similarity index 100% rename from templates/tests/UnitTests/Domain/NotificationTests.cs rename to templates/Full/tests/UnitTests/Domain/NotificationTests.cs diff --git a/templates/tests/UnitTests/Domain/OrderTests.cs b/templates/Full/tests/UnitTests/Domain/OrderTests.cs similarity index 100% rename from templates/tests/UnitTests/Domain/OrderTests.cs rename to templates/Full/tests/UnitTests/Domain/OrderTests.cs diff --git a/templates/tests/UnitTests/GlobalUsings.cs b/templates/Full/tests/UnitTests/GlobalUsings.cs similarity index 100% rename from templates/tests/UnitTests/GlobalUsings.cs rename to templates/Full/tests/UnitTests/GlobalUsings.cs diff --git a/templates/tests/UnitTests/UnitTests.csproj b/templates/Full/tests/UnitTests/UnitTests.csproj similarity index 100% rename from templates/tests/UnitTests/UnitTests.csproj rename to templates/Full/tests/UnitTests/UnitTests.csproj diff --git a/templates/Full/tests/UnitTests/packages.lock.json b/templates/Full/tests/UnitTests/packages.lock.json new file mode 100644 index 00000000..5ce4b748 --- /dev/null +++ b/templates/Full/tests/UnitTests/packages.lock.json @@ -0,0 +1,268 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "ILogger.Moq": { + "type": "Direct", + "requested": "[2.0.0, )", + "resolved": "2.0.0", + "contentHash": "GY6xsOTwcxJY/PxK3blmX7OP+pnFmO/ngLQOy8NXn09+di1By9IVKyexvP6QlHL3c1rr0IH9lz/SZVZup2BjAQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "Moq": "4.18.0" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[18.0.1, )", + "resolved": "18.0.1", + "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", + "dependencies": { + "Microsoft.CodeCoverage": "18.0.1", + "Microsoft.TestPlatform.TestHost": "18.0.1" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.72, )", + "resolved": "4.20.72", + "contentHash": "EA55cjyNn8eTNWrgrdZJH5QLFp2L43oxl1tlkoYUKIE9pRwL784OWiTXeCV5ApS+AMYEAlt7Fo03A2XfouvHmQ==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "NetArchTest.Rules": { + "type": "Direct", + "requested": "[1.3.2, )", + "resolved": "1.3.2", + "contentHash": "puPyNXkwJq8/UwXhHV8NrzNzkQl4IxEbcP+3PU0xLRiOedsVpaSdpwHhvOZfI0VwTcRvawCNxYQcSRbD4RUg4w==", + "dependencies": { + "Mono.Cecil": "0.11.3" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.5, )", + "resolved": "3.1.5", + "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Fare": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "HaI8puqA66YU7/9cK4Sgbs1taUTP1Ssa4QT2PIzqJ7GvAbN1QgkjbRsjH+FSbMh1MJdvS0CIwQNLtFT+KF6KpA==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "18.0.1", + "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.0.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.11.3", + "contentHash": "DNYE+io5XfEE8+E+5padThTPHJARJHbz1mhbhMPNrrWGKVKKqj/KEeLvbawAmbIcT73NuxLV7itHZaYCZcVWGg==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "application": { + "type": "Project", + "dependencies": { + "Domain": "[1.0.0, )", + "FluentValidation": "[12.1.1, )", + "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", + "Microsoft.Extensions.Logging": "[10.0.1, )" + } + }, + "commontests": { + "type": "Project", + "dependencies": { + "AutoFixture": "[4.18.1, )", + "Newtonsoft.Json": "[13.0.4, )", + "xunit.extensibility.core": "[2.9.3, )" + } + }, + "domain": { + "type": "Project", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + } + }, + "AutoFixture": { + "type": "CentralTransitive", + "requested": "[4.18.1, )", + "resolved": "4.18.1", + "contentHash": "BmWZDY4fkrYOyd5/CTBOeXbzsNwV8kI4kDi/Ty1Y5F+WDHBVKxzfWlBE4RSicvZ+EOi2XDaN5uwdrHsItLW6Kw==", + "dependencies": { + "Fare": "[2.1.1, 3.0.0)" + } + }, + "FluentValidation": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "EPpkIe1yh1a0OXyC100oOA8WMbZvqUu5plwhvYcb7oSELfyUZzfxV48BLhvs3kKo4NwG7MGLNgy1RJiYtT8Dpw==" + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "CentralTransitive", + "requested": "[12.1.1, )", + "resolved": "12.1.1", + "contentHash": "D0VXh4dtjjX2aQizuaa0g6R8X3U1JaVqJPfGCvLwZX9t/O2h7tkpbitbadQMfwcgSPdDbI2vDxuwRMv/Uf9dHA==", + "dependencies": { + "FluentValidation": "12.1.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "xunit.extensibility.core": { + "type": "CentralTransitive", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + } + } + } +} \ No newline at end of file diff --git a/templates/tests/UnitTests/stryker-config-application.json b/templates/Full/tests/UnitTests/stryker-config-application.json similarity index 100% rename from templates/tests/UnitTests/stryker-config-application.json rename to templates/Full/tests/UnitTests/stryker-config-application.json diff --git a/templates/tests/UnitTests/stryker-config-domain.json b/templates/Full/tests/UnitTests/stryker-config-domain.json similarity index 100% rename from templates/tests/UnitTests/stryker-config-domain.json rename to templates/Full/tests/UnitTests/stryker-config-domain.json From 92465a6e31bcb7005f869da310f9e13583e4c336 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 7 Jan 2026 18:01:09 -0300 Subject: [PATCH 002/161] feat: update http service configurations and add appsettings --- .../Common/DefaultConfigurations.cs | 1 - .../Infrastructure/Http/BaseHttpService.cs | 44 +++++------------- .../Http/ServiceConfigurations.cs | 9 +--- .../src/Infrastructure/Http/ServicesKeys.cs | 7 +++ .../InfrastructureDependencyInjection.cs | 45 ++++++++++--------- .../src/WebApp/Endpoints/OrderEndpoints.cs | 25 ++++------- .../src/WebApp/Properties/launchSettings.json | 8 ++-- .../src/WebApp/appsettings.Development.json | 20 +++++++++ templates/Bff/src/WebApp/appsettings.json | 15 ++++++- 9 files changed, 89 insertions(+), 85 deletions(-) create mode 100644 templates/Bff/src/Infrastructure/Http/ServicesKeys.cs create mode 100644 templates/Bff/src/WebApp/appsettings.Development.json diff --git a/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs b/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs index 8f33aa80..4dee5d30 100644 --- a/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs +++ b/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs @@ -3,5 +3,4 @@ namespace Infrastructure.Common; public static class DefaultConfigurations { public static string ApplicationName => "Hexagonal.Solution.Template.Bff"; - } diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 1ed40a80..7a7e4bb9 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -15,6 +15,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log string requestUri, HttpMethod method, CancellationToken cancellationToken, + dynamic? request = null, Dictionary? headers = null ) { @@ -23,46 +24,23 @@ public class BaseHttpService(HttpClient httpClient, ILogger log var requestMessage = new HttpRequestMessage(method, requestUri); - if (headers != null) foreach (var header in headers) - requestMessage.Headers.Add(header.Key, header.Value); - - var httpResponse = await _httpClient.SendAsync(requestMessage, cancellationToken); - httpResponse.EnsureSuccessStatusCode(); - - var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken); - - var result = await JsonSerializer.DeserializeAsync(stream, _jsonSerializerOptions, cancellationToken); - - _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); - - return result; - } - - public async Task SendAsync( - string requestUri, - HttpMethod method, - TRequest request, - CancellationToken cancellationToken, - Dictionary? headers = null - ) - { - _stopwatch.Start(); - _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); - - var memoryStream = new MemoryStream(); - await JsonSerializer.SerializeAsync(memoryStream, request, _jsonSerializerOptions, cancellationToken); + if (request != null) + { + var memoryStream = new MemoryStream(); + await JsonSerializer.SerializeAsync(memoryStream, request, _jsonSerializerOptions, cancellationToken); - memoryStream.Seek(0, SeekOrigin.Begin); - var requestMessage = new HttpRequestMessage(method, requestUri); + memoryStream.Seek(0, SeekOrigin.Begin); + using var requestContent = new StreamContent(memoryStream); + requestMessage.Content = requestContent; + } if (headers != null) foreach (var header in headers) requestMessage.Headers.Add(header.Key, header.Value); - using var requestContent = new StreamContent(memoryStream); - requestMessage.Content = requestContent; - using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + response.EnsureSuccessStatusCode(); + var content = await response.Content.ReadAsStreamAsync(cancellationToken); var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); diff --git a/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs b/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs index 02c8f680..e02b515b 100644 --- a/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs +++ b/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs @@ -1,14 +1,9 @@ namespace Infrastructure.Http; -public static class ServicesKeys -{ - public const string Orders = nameof(Orders); - public const string Payments = nameof(Payments); -} - public sealed class ServiceConfigurations { public string Name { get; set; } - public string Url { get; set; } + public string BaseAddress { get; set; } public object Authentication { get; set; } + public object Headers { get; set; } } \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Http/ServicesKeys.cs b/templates/Bff/src/Infrastructure/Http/ServicesKeys.cs new file mode 100644 index 00000000..75cf3f9d --- /dev/null +++ b/templates/Bff/src/Infrastructure/Http/ServicesKeys.cs @@ -0,0 +1,7 @@ +namespace Infrastructure.Http; + +public enum ServicesKeys +{ + Orders, + Payments +} diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index ed8827c3..ce460547 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -23,7 +23,7 @@ public WebApplicationBuilder AddInfrastructure() builder.Services .AddCache(configuration) - .AddHttpServices(configuration); + .AddHttp(configuration); builder.AddOpenTelemetry(); @@ -124,34 +124,37 @@ internal IServiceCollection AddCache(IConfiguration configuration) return services; } - internal IServiceCollection AddHttpServices(IConfiguration configuration) + internal IServiceCollection AddHttp(IConfiguration configuration) { + var httpConfigurations = configuration.GetSection("Http").Get>() + ?? throw new NullReferenceException("Http services configuration is not configured."); - services.AddKeyedScoped(ServicesKeys.Orders, (serviceProvider, cancellationToken) => + var serviceKeys = Enum.GetValues(); + + foreach (var serviceKey in serviceKeys) { - var serviceConfiguration = configuration.GetSection("Services:Orders").Get() ?? throw new NullReferenceException("Orders service configuration is not configured."); + var serviceName = serviceKey.ToString(); - var httpClientFactory = serviceProvider.GetRequiredService(); - var logger = serviceProvider.GetRequiredService>(); - var client = httpClientFactory.CreateClient(ServicesKeys.Orders); - client.BaseAddress = new Uri(serviceConfiguration.Url) ?? throw new NullReferenceException("Orders service address is not configured."); - client.DefaultRequestHeaders.Add("Accept", "application/json"); + var serviceConfiguration = httpConfigurations.FirstOrDefault(x => + string.Equals(x.Name, serviceName, StringComparison.OrdinalIgnoreCase)) + ?? throw new NullReferenceException($"{serviceName} service configuration is not configured."); - return new(client, logger); - }); + services.AddKeyedScoped(serviceKey, (serviceProvider, _) => + { + var httpClientFactory = serviceProvider.GetRequiredService(); + var logger = serviceProvider.GetRequiredService>(); + var client = httpClientFactory.CreateClient(serviceKey.ToString()); - services.AddKeyedScoped(ServicesKeys.Payments, (serviceProvider, cancellationToken) => - { - var serviceConfiguration = configuration.GetSection("Services:Payments").Get() ?? throw new NullReferenceException("Payments service configuration is not configured."); + client.BaseAddress = new Uri(serviceConfiguration.BaseAddress) + ?? throw new NullReferenceException($"{serviceName} service address is not configured."); - var httpClientFactory = serviceProvider.GetRequiredService(); - var logger = serviceProvider.GetRequiredService>(); - var client = httpClientFactory.CreateClient(ServicesKeys.Payments); - client.BaseAddress = new Uri(serviceConfiguration.Url) ?? throw new NullReferenceException("Payments service address is not configured."); - client.DefaultRequestHeaders.Add("Accept", "application/json"); + if (serviceConfiguration.Headers is Dictionary headers && headers.Count > 0) + foreach (var header in headers) + client.DefaultRequestHeaders.Add(header.Key, header.Value); - return new(client, logger); - }); + return new BaseHttpService(client, logger); + }); + } return services; } diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 429d268a..463e9bbd 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -1,7 +1,6 @@ using Infrastructure.Http; using Microsoft.AspNetCore.Mvc; using Infrastructure.Cache; -using Infrastructure.Common; namespace WebApp.Endpoints; @@ -10,10 +9,12 @@ internal static class OrderEndpoints public static WebApplication MapOrderEndpoints(this WebApplication app) { var cache = app.Services.GetRequiredService(); - var httpService = app.Services.GetRequiredKeyedService(ServicesKeys.Orders); + + var serviceKey = ServicesKeys.Orders.ToString(); - var ordersGroup = app.MapGroup("/orders") - .WithTags("Orders"); + var httpService = app.Services.GetRequiredKeyedService(serviceKey); + + var ordersGroup = app.MapGroup(serviceKey).WithTags(serviceKey); ordersGroup.MapGet("/{id}", async ( [FromHeader] Guid correlationId, @@ -25,13 +26,13 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) { true => await cache.GetOrCreateAsync( $"{nameof(OrderEndpoints)}-{id}", - async (cancellationToken) => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, new() + async (cancellationToken) => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() { { "CorrelationId", correlationId.ToString() } }), cancellationToken ), - false or _ => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, new() + false or _ => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() { { "CorrelationId", correlationId.ToString() } }), @@ -45,7 +46,7 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) CancellationToken cancellationToken ) => { - var response = await httpService.SendAsync("orders", request, cancellationToken); + var response = await httpService.SendAsync("orders", HttpMethod.Post, cancellationToken, request); if (!response.Success || response.Data == null) return Results.BadRequest(response); @@ -53,16 +54,6 @@ CancellationToken cancellationToken return Results.Created($"/orders/{response.Data.Id}", response); }); - ordersGroup.MapPost("/paginated", async ( - [FromBody] dynamic request, - CancellationToken cancellationToken - ) => - { - var response = await httpService.SendAsync("orders/paginated", request, cancellationToken); - - return response.Success ? Results.Ok(response) : Results.BadRequest(response); - }); - return app; } } diff --git a/templates/Bff/src/WebApp/Properties/launchSettings.json b/templates/Bff/src/WebApp/Properties/launchSettings.json index 58992051..4772c572 100644 --- a/templates/Bff/src/WebApp/Properties/launchSettings.json +++ b/templates/Bff/src/WebApp/Properties/launchSettings.json @@ -6,7 +6,7 @@ "dotnetRunMessages": true, "sqlDebugging": true, "launchBrowser": false, - "applicationUrl": "https://*:7175;http://*:5010", + "applicationUrl": "https://*:7176;http://*:5011", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "LOGGING__LOGLEVEL__DEFAULT": "Debug", @@ -15,16 +15,14 @@ "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE": "Information", "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND": "Information", "ENABLE_SENSITIVE_DATA_LOGGING": "true", - "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", + "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp.Bff", "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:18889", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:18889", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:18889", "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:18889", "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", - "ConnectionStrings__OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", - "ConnectionStrings__Redis": "127.0.0.1:6379", - "ConnectionStrings__RabbitMQ": "amqp://guest:guest@127.0.0.1:5672/" + "ConnectionStrings__Redis": "127.0.0.1:6379" } } diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json new file mode 100644 index 00000000..b3be136c --- /dev/null +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -0,0 +1,20 @@ +{ + "ConnectionStrings": { + "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", + "Redis": "localhost:6379", + "RabbitMq": "amqp://guest:guest@localhost:5672/" + }, + "Http": [ + { + "Name": "Orders", + "BaseAddress": "http://localhost:5001", + "Headers": { + "Accept": "application/json" + } + }, + { + "Name": "Payments", + "BaseAddress": "http://localhost:5002" + } + ] +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index 770f1bb3..b3be136c 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -3,5 +3,18 @@ "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", "Redis": "localhost:6379", "RabbitMq": "amqp://guest:guest@localhost:5672/" - } + }, + "Http": [ + { + "Name": "Orders", + "BaseAddress": "http://localhost:5001", + "Headers": { + "Accept": "application/json" + } + }, + { + "Name": "Payments", + "BaseAddress": "http://localhost:5002" + } + ] } \ No newline at end of file From b1b753b82249e1fd26ff78c4ef6508d5bd88e5e9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 8 Jan 2026 07:23:54 -0300 Subject: [PATCH 003/161] feat: add mock API with gRPC and HTTP endpoints --- templates/Bff/Directory.Build.props | 1 + templates/Bff/mocks/MockApi/Dockerfile | 19 +++++ .../MockApi/Endpoints/EndpointExtensions.cs | 10 +++ .../mocks/MockApi/Endpoints/OrderEndpoints.cs | 57 ++++++++++++++ .../GrpcServices/GrpcServiceExtensions.cs | 11 +++ .../MockApi/GrpcServices/OrderService.cs | 59 +++++++++++++++ templates/Bff/mocks/MockApi/MockApi.csproj | 8 ++ templates/Bff/mocks/MockApi/MockApi.http | 63 ++++++++++++++++ templates/Bff/mocks/MockApi/Program.cs | 47 ++++++++++++ .../MockApi/Properties/launchSettings.json | 16 ++++ .../Bff/mocks/MockApi/Protos/order.proto | 34 +++++++++ templates/Bff/mocks/MockApi/appsettings.json | 20 +++++ .../Bff/mocks/MockApi/packages.lock.json | 74 +++++++++++++++++++ templates/Bff/src/WebApp/WebApp.csproj | 3 - templates/Full/Directory.Build.props | 1 + templates/Full/src/WebApp/WebApp.csproj | 3 - 16 files changed, 420 insertions(+), 6 deletions(-) create mode 100644 templates/Bff/mocks/MockApi/Dockerfile create mode 100644 templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs create mode 100644 templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs create mode 100644 templates/Bff/mocks/MockApi/GrpcServices/GrpcServiceExtensions.cs create mode 100644 templates/Bff/mocks/MockApi/GrpcServices/OrderService.cs create mode 100644 templates/Bff/mocks/MockApi/MockApi.csproj create mode 100644 templates/Bff/mocks/MockApi/MockApi.http create mode 100644 templates/Bff/mocks/MockApi/Program.cs create mode 100644 templates/Bff/mocks/MockApi/Properties/launchSettings.json create mode 100644 templates/Bff/mocks/MockApi/Protos/order.proto create mode 100644 templates/Bff/mocks/MockApi/appsettings.json create mode 100644 templates/Bff/mocks/MockApi/packages.lock.json diff --git a/templates/Bff/Directory.Build.props b/templates/Bff/Directory.Build.props index df67cf0c..a31e77ed 100644 --- a/templates/Bff/Directory.Build.props +++ b/templates/Bff/Directory.Build.props @@ -5,6 +5,7 @@ enable true true + false diff --git a/templates/Bff/mocks/MockApi/Dockerfile b/templates/Bff/mocks/MockApi/Dockerfile new file mode 100644 index 00000000..9f46f6c3 --- /dev/null +++ b/templates/Bff/mocks/MockApi/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build-env +WORKDIR /app + +COPY . . + +WORKDIR /app/src/WebApp +RUN dotnet restore \ + && dotnet publish -c Release -o /app/out + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 +WORKDIR /app +COPY --from=build-env /app/out . + +RUN apt-get update \ + && apt-get install -y curl + +EXPOSE 5000 + +ENTRYPOINT ["dotnet", "WebApp.dll", "--urls", "http://+:5000"] diff --git a/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs b/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs new file mode 100644 index 00000000..04dca36d --- /dev/null +++ b/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs @@ -0,0 +1,10 @@ +namespace MockApi.Endpoints; + +internal static class EndpointExtensions +{ + public static WebApplication MapEndpoints(this WebApplication app) + { + // app.MapOrderEndpoints(); + return app; + } +} \ No newline at end of file diff --git a/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs b/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs new file mode 100644 index 00000000..4ac61842 --- /dev/null +++ b/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs @@ -0,0 +1,57 @@ +// using Microsoft.AspNetCore.Mvc; + +// namespace MockApi.Endpoints; + +// internal static class OrderEndpoints +// { +// public static WebApplication MapOrderEndpoints(this WebApplication app) +// { +// var cache = app.Services.GetRequiredService(); + +// var serviceKey = ServicesKeys.Orders.ToString(); + +// var httpService = app.Services.GetRequiredKeyedService(serviceKey); + +// var ordersGroup = app.MapGroup(serviceKey).WithTags(serviceKey); + +// ordersGroup.MapGet("/{id}", async ( +// [FromHeader] Guid correlationId, +// [FromRoute] int id, +// CancellationToken cancellationToken, +// [FromHeader] bool cacheEnabled = true +// ) => { +// var response = cacheEnabled switch +// { +// true => await cache.GetOrCreateAsync( +// $"{nameof(OrderEndpoints)}-{id}", +// async (cancellationToken) => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() +// { +// { "CorrelationId", correlationId.ToString() } +// }), +// cancellationToken +// ), +// false or _ => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() +// { +// { "CorrelationId", correlationId.ToString() } +// }), +// }; + +// return response?.Success ? Results.Ok(response) : Results.NotFound(response); +// }); + +// ordersGroup.MapPost("/", async ( +// [FromBody] dynamic request, +// CancellationToken cancellationToken +// ) => +// { +// var response = await httpService.SendAsync("orders", HttpMethod.Post, cancellationToken, request); + +// if (!response.Success || response.Data == null) +// return Results.BadRequest(response); + +// return Results.Created($"/orders/{response.Data.Id}", response); +// }); + +// return app; +// } +// } diff --git a/templates/Bff/mocks/MockApi/GrpcServices/GrpcServiceExtensions.cs b/templates/Bff/mocks/MockApi/GrpcServices/GrpcServiceExtensions.cs new file mode 100644 index 00000000..e45b669c --- /dev/null +++ b/templates/Bff/mocks/MockApi/GrpcServices/GrpcServiceExtensions.cs @@ -0,0 +1,11 @@ +namespace MockApi.GrpcServices; + +internal static class GrpcServiceExtensions +{ + public static WebApplication MapGrpcServices(this WebApplication app) + { + // app.MapGrpcService(); + + return app; + } +} \ No newline at end of file diff --git a/templates/Bff/mocks/MockApi/GrpcServices/OrderService.cs b/templates/Bff/mocks/MockApi/GrpcServices/OrderService.cs new file mode 100644 index 00000000..366400fa --- /dev/null +++ b/templates/Bff/mocks/MockApi/GrpcServices/OrderService.cs @@ -0,0 +1,59 @@ +// using System.Globalization; +// using Application.Common.Requests; +// using Application.Common.Services; +// using Application.Common.UseCases; +// using Grpc.Core; +// using GrpcOrder; +// using static GrpcOrder.OrderService; +// using GetOrderRequest = Application.Orders.GetOrderRequest; +// using OrderDto = Application.Orders.OrderDto; + +// namespace MockApi.GrpcServices; + +// public class OrderService( +// IBaseInOutUseCase> useCase, +// IHybridCacheService cache +// ) : OrderServiceBase +// { +// private readonly IBaseInOutUseCase> _useCase = useCase; +// private readonly IHybridCacheService _cache = cache; + +// public override async Task Get( +// GrpcOrder.GetOrderRequest request, +// ServerCallContext context +// ) +// { +// var response = await _cache.GetOrCreateAsync( +// $"{nameof(OrderService)}-{request.Id}", +// async cancellationToken => +// { +// var correlationId = Guid.TryParse(request.CorrelationId, out var guid) ? guid : Guid.Empty; +// return await _useCase.HandleAsync(new(correlationId, request.Id), cancellationToken); +// }, +// context.CancellationToken +// ); + +// if (!response.Success || response.Data == null) +// return new() { Success = false, Message = response.Message }; + +// OrderReply orderReply = new() +// { +// Success = true, +// Message = string.Empty, +// Data = new() +// { +// Id = response.Data.Id, +// Total = double.TryParse(response.Data.Total.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var total) ? total : 0.0 +// } +// }; + +// orderReply.Data.Items?.AddRange(response.Data.Items?.Select(i => new GrpcOrder.ItemDto +// { +// Id = i.Id, +// Name = i.Name, +// Value = double.TryParse(i.Value.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : 0.0 +// })); + +// return orderReply; +// } +// } diff --git a/templates/Bff/mocks/MockApi/MockApi.csproj b/templates/Bff/mocks/MockApi/MockApi.csproj new file mode 100644 index 00000000..5f1faddf --- /dev/null +++ b/templates/Bff/mocks/MockApi/MockApi.csproj @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/templates/Bff/mocks/MockApi/MockApi.http b/templates/Bff/mocks/MockApi/MockApi.http new file mode 100644 index 00000000..f9a0963b --- /dev/null +++ b/templates/Bff/mocks/MockApi/MockApi.http @@ -0,0 +1,63 @@ +@WebApp_HostAddress = https://localhost:7175 + +### +GET {{WebApp_HostAddress}}/health HTTP/2 +Accept: application/json + +### +GET {{WebApp_HostAddress}}/live HTTP/2 + +### +GET {{WebApp_HostAddress}}/ready HTTP/2 + +### +GET {{WebApp_HostAddress}}/orders/1 HTTP/2 +CorrelationId: {{$guid}} +CacheEnabled: false +Accept-Encoding: gzip, deflate +Accept: application/json + +### +POST {{WebApp_HostAddress}}/orders HTTP/2 +Content-Type: application/json + +{ + "CorrelationId": "{{$guid}}", + "Description": "John's computer", + "Items": [ + { + "Name": "Computer", + "Description": "Surface 2", + "Value": 1000 + }, + { + "Name": "Mouse", + "Description": "Microsoft mouse", + "Value": 99 + } + ] +} + +### +POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 +Content-Type: application/json + +{ + "CorrelationId": "{{$guid}}", + "Page": 1, + "PageSize": 1 +} + +### +POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 +Content-Type: application/json + +{ + "CorrelationId": "{{$guid}}", + "Page": 1, + "PageSize": 10, + "SearchByValues": { + "Description": "client" + } +} + diff --git a/templates/Bff/mocks/MockApi/Program.cs b/templates/Bff/mocks/MockApi/Program.cs new file mode 100644 index 00000000..22b821d0 --- /dev/null +++ b/templates/Bff/mocks/MockApi/Program.cs @@ -0,0 +1,47 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Http.Json; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using MockApi.Endpoints; +using MockApi.GrpcServices; + +namespace MockApi; + +public sealed class Program +{ + private static async Task Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddGrpc(); + + builder.Services.AddResponseCompression(options => + { + options.EnableForHttps = true; + }); + + builder.Services.Configure(options => + { + options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; + options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.SerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; + options.SerializerOptions.PropertyNameCaseInsensitive = true; + }); + + builder.WebHost.ConfigureKestrel(options => + options.ConfigureEndpointDefaults(listenOptions => + listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3 + )); + + var app = builder.Build(); + + app.UseHttpsRedirection(); + + app.MapEndpoints() + .MapGrpcServices() + .UseResponseCompression(); + + await app.RunAsync(); + } +} diff --git a/templates/Bff/mocks/MockApi/Properties/launchSettings.json b/templates/Bff/mocks/MockApi/Properties/launchSettings.json new file mode 100644 index 00000000..5d85f37d --- /dev/null +++ b/templates/Bff/mocks/MockApi/Properties/launchSettings.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Development": { + "commandName": "Project", + "dotnetRunMessages": true, + "sqlDebugging": true, + "launchBrowser": false, + "applicationUrl": "https://*:7177;http://*:5012", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + + } + } + } +} diff --git a/templates/Bff/mocks/MockApi/Protos/order.proto b/templates/Bff/mocks/MockApi/Protos/order.proto new file mode 100644 index 00000000..96761cd8 --- /dev/null +++ b/templates/Bff/mocks/MockApi/Protos/order.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; + +option csharp_namespace = "GrpcOrder"; + +package order; + +service OrderService { + rpc Get (GetOrderRequest) returns (OrderReply); +} + +message GetOrderRequest { + int32 id = 1; + string correlationId = 2; +} + +message ItemDto { + int32 id = 1; + string name = 2; + string description = 3; + double value = 4; +} + +message OrderDto { + int32 id = 1; + string description = 2; + double total = 3; + repeated ItemDto items = 4; +} + +message OrderReply { + bool success = 1; + string message = 2; + OrderDto data = 3; +} \ No newline at end of file diff --git a/templates/Bff/mocks/MockApi/appsettings.json b/templates/Bff/mocks/MockApi/appsettings.json new file mode 100644 index 00000000..b3be136c --- /dev/null +++ b/templates/Bff/mocks/MockApi/appsettings.json @@ -0,0 +1,20 @@ +{ + "ConnectionStrings": { + "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", + "Redis": "localhost:6379", + "RabbitMq": "amqp://guest:guest@localhost:5672/" + }, + "Http": [ + { + "Name": "Orders", + "BaseAddress": "http://localhost:5001", + "Headers": { + "Accept": "application/json" + } + }, + { + "Name": "Payments", + "BaseAddress": "http://localhost:5002" + } + ] +} \ No newline at end of file diff --git a/templates/Bff/mocks/MockApi/packages.lock.json b/templates/Bff/mocks/MockApi/packages.lock.json new file mode 100644 index 00000000..5f23af35 --- /dev/null +++ b/templates/Bff/mocks/MockApi/packages.lock.json @@ -0,0 +1,74 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Grpc.AspNetCore": { + "type": "Direct", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.31.1", + "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" + }, + "Grpc.AspNetCore.Server": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "diSC/ZeNdSdxHdYSOpYwuSBBDYpuNVtJQFJfiBB0WrYOQ4lVMmdxuUZJcViahQyo8pCvS3Mueo5lqFxwwMF/iw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.AspNetCore.Server.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "y5KGO1GO0N2L/hCCMR05mmoK8j+v8rKvZ+9nothAxKx2Tf2CwV8f4TM5K0GkKfDsp4vrc4lm90MU6E+DeN7YIw==", + "dependencies": { + "Grpc.AspNetCore.Server": "2.76.0", + "Grpc.Net.ClientFactory": "2.76.0" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "cSxC2tdnFdXXuBgIn1pjc4YBx7LXTCp4M0qn+SMBS35VWZY+cEQYLWTBDDhdBH1HzU7BV+ncVZlniGQHMpRJKQ==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "K1oldmqw2+Gn69nGRzZLhqSiUZwelX1GrBu/cUl9wNf1C0uB61vFS6JcxUUv9P8VoUJhFsmV44JA6lI2EUt4xw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "XI+kO69L9AV8B9N0UQOmH911r6MOEp9huHiavEsY56DJYuzJ9KAxNGy37dpV6CLbgCaN2uKmpOsZ9Pao6bmpVQ==", + "dependencies": { + "Grpc.Net.Client": "2.76.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "bZpiMVYgvpB44/wBh1RotrkqC7bg2FOasLri2GhR3hMKyzsiTxCoDE49YjPrJeFc4RW0wS8u+EInI09sjxVFRA==", + "dependencies": { + "Grpc.Core.Api": "2.76.0" + } + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + } + } + } +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/WebApp.csproj b/templates/Bff/src/WebApp/WebApp.csproj index 1a2be00c..6c9b8e5a 100644 --- a/templates/Bff/src/WebApp/WebApp.csproj +++ b/templates/Bff/src/WebApp/WebApp.csproj @@ -1,7 +1,4 @@  - - false - diff --git a/templates/Full/Directory.Build.props b/templates/Full/Directory.Build.props index df67cf0c..a31e77ed 100644 --- a/templates/Full/Directory.Build.props +++ b/templates/Full/Directory.Build.props @@ -5,6 +5,7 @@ enable true true + false diff --git a/templates/Full/src/WebApp/WebApp.csproj b/templates/Full/src/WebApp/WebApp.csproj index 2a7223cb..53d8f64a 100644 --- a/templates/Full/src/WebApp/WebApp.csproj +++ b/templates/Full/src/WebApp/WebApp.csproj @@ -1,7 +1,4 @@  - - false - From 4630db1ae4aec61214f576b32222dc488657f298 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 8 Jan 2026 07:36:40 -0300 Subject: [PATCH 004/161] feat: add mock API service and update related configurations --- templates/Bff/docker-compose.yml | 9 ++ templates/Bff/mocks/MockApi/Dockerfile | 9 +- .../MockApi/Endpoints/EndpointExtensions.cs | 2 +- .../mocks/MockApi/Endpoints/OrderEndpoints.cs | 115 +++++++++--------- .../MockApi/Properties/launchSettings.json | 1 - .../src/WebApp/appsettings.Development.json | 4 +- templates/Bff/src/WebApp/appsettings.json | 8 +- 7 files changed, 77 insertions(+), 71 deletions(-) diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index 0bad6fa3..5461d094 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -9,6 +9,15 @@ services: environment: - DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true + mockApi: + build: + context: ./mocks/MockApi + dockerfile: Dockerfile + ports: + - "5012:5012" + networks: + - hexagonal_solution_template_network + redis: image: redis:8 ports: diff --git a/templates/Bff/mocks/MockApi/Dockerfile b/templates/Bff/mocks/MockApi/Dockerfile index 9f46f6c3..99494472 100644 --- a/templates/Bff/mocks/MockApi/Dockerfile +++ b/templates/Bff/mocks/MockApi/Dockerfile @@ -3,7 +3,7 @@ WORKDIR /app COPY . . -WORKDIR /app/src/WebApp +WORKDIR /app/src/MockApi RUN dotnet restore \ && dotnet publish -c Release -o /app/out @@ -11,9 +11,6 @@ FROM mcr.microsoft.com/dotnet/aspnet:10.0 WORKDIR /app COPY --from=build-env /app/out . -RUN apt-get update \ - && apt-get install -y curl +EXPOSE 5012 -EXPOSE 5000 - -ENTRYPOINT ["dotnet", "WebApp.dll", "--urls", "http://+:5000"] +ENTRYPOINT ["dotnet", "MockApi.dll", "--urls", "http://+:5012"] diff --git a/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs b/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs index 04dca36d..5a4165a1 100644 --- a/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs +++ b/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs @@ -4,7 +4,7 @@ internal static class EndpointExtensions { public static WebApplication MapEndpoints(this WebApplication app) { - // app.MapOrderEndpoints(); + app.MapOrderEndpoints(); return app; } } \ No newline at end of file diff --git a/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs b/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs index 4ac61842..20e85758 100644 --- a/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs @@ -1,57 +1,58 @@ -// using Microsoft.AspNetCore.Mvc; - -// namespace MockApi.Endpoints; - -// internal static class OrderEndpoints -// { -// public static WebApplication MapOrderEndpoints(this WebApplication app) -// { -// var cache = app.Services.GetRequiredService(); - -// var serviceKey = ServicesKeys.Orders.ToString(); - -// var httpService = app.Services.GetRequiredKeyedService(serviceKey); - -// var ordersGroup = app.MapGroup(serviceKey).WithTags(serviceKey); - -// ordersGroup.MapGet("/{id}", async ( -// [FromHeader] Guid correlationId, -// [FromRoute] int id, -// CancellationToken cancellationToken, -// [FromHeader] bool cacheEnabled = true -// ) => { -// var response = cacheEnabled switch -// { -// true => await cache.GetOrCreateAsync( -// $"{nameof(OrderEndpoints)}-{id}", -// async (cancellationToken) => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() -// { -// { "CorrelationId", correlationId.ToString() } -// }), -// cancellationToken -// ), -// false or _ => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() -// { -// { "CorrelationId", correlationId.ToString() } -// }), -// }; - -// return response?.Success ? Results.Ok(response) : Results.NotFound(response); -// }); - -// ordersGroup.MapPost("/", async ( -// [FromBody] dynamic request, -// CancellationToken cancellationToken -// ) => -// { -// var response = await httpService.SendAsync("orders", HttpMethod.Post, cancellationToken, request); - -// if (!response.Success || response.Data == null) -// return Results.BadRequest(response); - -// return Results.Created($"/orders/{response.Data.Id}", response); -// }); - -// return app; -// } -// } +using Microsoft.AspNetCore.Mvc; + +namespace MockApi.Endpoints; + +internal static class OrderEndpoints +{ + public static WebApplication MapOrderEndpoints(this WebApplication app) + { + var ordersGroup = app.MapGroup("orders").WithTags("orders"); + + ordersGroup.MapGet("/{id}", async ( + [FromHeader] Guid correlationId, + [FromRoute] int id, + CancellationToken cancellationToken, + [FromHeader] bool cacheEnabled = true + ) => { + return Results.Ok(new + { + Success = true, + Message = string.Empty, + Data = new + { + Id = id, + Total = 100.0, + Items = new[] + { + new { Id = 1, Name = "Item 1", Value = 50.0 }, + new { Id = 2, Name = "Item 2", Value = 50.0 } + } + } + }); + }); + + ordersGroup.MapPost("/", async ( + [FromBody] dynamic request, + CancellationToken cancellationToken + ) => + { + return Results.Created($"/orders/1", new + { + Success = true, + Message = string.Empty, + Data = new + { + Id = 1, + Total = 100.0, + Items = new[] + { + new { Id = 1, Name = "Item 1", Value = 50.0 }, + new { Id = 2, Name = "Item 2", Value = 50.0 } + } + } + }); + }); + + return app; + } +} diff --git a/templates/Bff/mocks/MockApi/Properties/launchSettings.json b/templates/Bff/mocks/MockApi/Properties/launchSettings.json index 5d85f37d..6c998bc4 100644 --- a/templates/Bff/mocks/MockApi/Properties/launchSettings.json +++ b/templates/Bff/mocks/MockApi/Properties/launchSettings.json @@ -4,7 +4,6 @@ "Development": { "commandName": "Project", "dotnetRunMessages": true, - "sqlDebugging": true, "launchBrowser": false, "applicationUrl": "https://*:7177;http://*:5012", "environmentVariables": { diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index b3be136c..7b1f5003 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -7,14 +7,14 @@ "Http": [ { "Name": "Orders", - "BaseAddress": "http://localhost:5001", + "BaseAddress": "http://localhost:5012", "Headers": { "Accept": "application/json" } }, { "Name": "Payments", - "BaseAddress": "http://localhost:5002" + "BaseAddress": "http://localhost:5012" } ] } \ No newline at end of file diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index b3be136c..0fcb0a36 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -1,20 +1,20 @@ { "ConnectionStrings": { "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", - "Redis": "localhost:6379", - "RabbitMq": "amqp://guest:guest@localhost:5672/" + "Redis": "redis:6379", + "RabbitMq": "amqp://guest:guest@rabbitmq:5672/" }, "Http": [ { "Name": "Orders", - "BaseAddress": "http://localhost:5001", + "BaseAddress": "http://mockApi:5012", "Headers": { "Accept": "application/json" } }, { "Name": "Payments", - "BaseAddress": "http://localhost:5002" + "BaseAddress": "http://mockApi:5012" } ] } \ No newline at end of file From 3043417ed64323ea3859357977f6b11aa8374b53 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 9 Jan 2026 07:30:28 -0300 Subject: [PATCH 005/161] refactor: simplify Program.cs by removing unnecessary code --- templates/Bff/mocks/MockApi/Program.cs | 65 +++++++++----------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/templates/Bff/mocks/MockApi/Program.cs b/templates/Bff/mocks/MockApi/Program.cs index 22b821d0..2e563336 100644 --- a/templates/Bff/mocks/MockApi/Program.cs +++ b/templates/Bff/mocks/MockApi/Program.cs @@ -1,47 +1,28 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Microsoft.AspNetCore.Http.Json; -using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.AspNetCore.Server.Kestrel.Core; using MockApi.Endpoints; using MockApi.GrpcServices; -namespace MockApi; +var builder = WebApplication.CreateBuilder(args); -public sealed class Program +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddGrpc(); + +builder.Services.AddResponseCompression(options => { - private static async Task Main(string[] args) - { - var builder = WebApplication.CreateBuilder(args); - - builder.Services.AddEndpointsApiExplorer(); - builder.Services.AddGrpc(); - - builder.Services.AddResponseCompression(options => - { - options.EnableForHttps = true; - }); - - builder.Services.Configure(options => - { - options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; - options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; - options.SerializerOptions.ReadCommentHandling = JsonCommentHandling.Skip; - options.SerializerOptions.PropertyNameCaseInsensitive = true; - }); - - builder.WebHost.ConfigureKestrel(options => - options.ConfigureEndpointDefaults(listenOptions => - listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3 - )); - - var app = builder.Build(); - - app.UseHttpsRedirection(); - - app.MapEndpoints() - .MapGrpcServices() - .UseResponseCompression(); - - await app.RunAsync(); - } -} + options.EnableForHttps = true; +}); + +builder.WebHost.ConfigureKestrel(options => + options.ConfigureEndpointDefaults(listenOptions => + listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3 +)); + +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.MapEndpoints() + .MapGrpcServices() + .UseResponseCompression(); + +await app.RunAsync(); From 30da48f268f7f799b31b2442a680a85458af6c5c Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 9 Jan 2026 07:31:35 -0300 Subject: [PATCH 006/161] feat: add Microsoft.AspNetCore.OpenApi and Scalar.AspNetCore --- templates/Bff/Directory.Packages.props | 2 ++ .../mocks/MockApi/Endpoints/OrderEndpoints.cs | 6 ++--- .../src/Infrastructure/Infrastructure.csproj | 3 --- .../InfrastructureDependencyInjection.cs | 4 +-- .../WebApp/Endpoints/EndpointExtensions.cs | 2 +- .../src/WebApp/Endpoints/OrderEndpoints.cs | 27 ++++++++++++------- templates/Bff/src/WebApp/Program.cs | 5 ++++ templates/Bff/src/WebApp/WebApp.csproj | 2 ++ templates/Bff/src/WebApp/packages.lock.json | 20 ++++++++++++++ .../tests/IntegrationTests/packages.lock.json | 24 ++++++++++++++++- 10 files changed, 74 insertions(+), 21 deletions(-) diff --git a/templates/Bff/Directory.Packages.props b/templates/Bff/Directory.Packages.props index 703cea51..b8fe2916 100644 --- a/templates/Bff/Directory.Packages.props +++ b/templates/Bff/Directory.Packages.props @@ -13,6 +13,7 @@ + @@ -29,6 +30,7 @@ + diff --git a/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs b/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs index 20e85758..f00f9fd3 100644 --- a/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs @@ -9,10 +9,8 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) var ordersGroup = app.MapGroup("orders").WithTags("orders"); ordersGroup.MapGet("/{id}", async ( - [FromHeader] Guid correlationId, [FromRoute] int id, - CancellationToken cancellationToken, - [FromHeader] bool cacheEnabled = true + CancellationToken cancellationToken ) => { return Results.Ok(new { @@ -32,7 +30,7 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) }); ordersGroup.MapPost("/", async ( - [FromBody] dynamic request, + [FromBody] object request, CancellationToken cancellationToken ) => { diff --git a/templates/Bff/src/Infrastructure/Infrastructure.csproj b/templates/Bff/src/Infrastructure/Infrastructure.csproj index 17ea259b..d477fd2a 100644 --- a/templates/Bff/src/Infrastructure/Infrastructure.csproj +++ b/templates/Bff/src/Infrastructure/Infrastructure.csproj @@ -1,7 +1,4 @@  - - - diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index ce460547..b2ceddbe 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -139,7 +139,7 @@ internal IServiceCollection AddHttp(IConfiguration configuration) string.Equals(x.Name, serviceName, StringComparison.OrdinalIgnoreCase)) ?? throw new NullReferenceException($"{serviceName} service configuration is not configured."); - services.AddKeyedScoped(serviceKey, (serviceProvider, _) => + services.AddKeyedScoped(serviceKey, (serviceProvider, _) => { var httpClientFactory = serviceProvider.GetRequiredService(); var logger = serviceProvider.GetRequiredService>(); @@ -152,7 +152,7 @@ internal IServiceCollection AddHttp(IConfiguration configuration) foreach (var header in headers) client.DefaultRequestHeaders.Add(header.Key, header.Value); - return new BaseHttpService(client, logger); + return new(client, logger); }); } diff --git a/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs b/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs index 064bc2d3..c14e93de 100644 --- a/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs +++ b/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs @@ -4,7 +4,7 @@ internal static class EndpointExtensions { public static WebApplication MapEndpoints(this WebApplication app) { - // app.MapOrderEndpoints(); + app.MapOrderEndpoints(); return app; } } \ No newline at end of file diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 463e9bbd..0c59946e 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -8,18 +8,16 @@ internal static class OrderEndpoints { public static WebApplication MapOrderEndpoints(this WebApplication app) { - var cache = app.Services.GetRequiredService(); - var serviceKey = ServicesKeys.Orders.ToString(); - var httpService = app.Services.GetRequiredKeyedService(serviceKey); - var ordersGroup = app.MapGroup(serviceKey).WithTags(serviceKey); ordersGroup.MapGet("/{id}", async ( - [FromHeader] Guid correlationId, + [FromKeyedServices(ServicesKeys.Orders)] BaseHttpService httpService, [FromRoute] int id, + [FromServices] HybridCacheService cache, CancellationToken cancellationToken, + [FromHeader] Guid? correlationId = null, [FromHeader] bool cacheEnabled = true ) => { var response = cacheEnabled switch @@ -28,13 +26,13 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) $"{nameof(OrderEndpoints)}-{id}", async (cancellationToken) => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() { - { "CorrelationId", correlationId.ToString() } + { "CorrelationId", (correlationId ?? Guid.NewGuid()).ToString() } }), cancellationToken ), false or _ => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() { - { "CorrelationId", correlationId.ToString() } + { "CorrelationId", (correlationId ?? Guid.NewGuid()).ToString() } }), }; @@ -42,16 +40,25 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) }); ordersGroup.MapPost("/", async ( - [FromBody] dynamic request, + [FromBody] object request, + [FromKeyedServices(ServicesKeys.Orders)] BaseHttpService httpService, CancellationToken cancellationToken ) => { var response = await httpService.SendAsync("orders", HttpMethod.Post, cancellationToken, request); - if (!response.Success || response.Data == null) + if (response == null) + return Results.BadRequest(new { Success = false, Message = "No response from service" }); + + bool success = response.Success ?? false; + if (!success || response.Data == null) return Results.BadRequest(response); - return Results.Created($"/orders/{response.Data.Id}", response); + var data = response.Data; + + var id = data?.Id?.ToString() ?? "unknown"; + + return Results.Created($"/orders/{id}", response); }); return app; diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs index 274a8b4a..c4b661df 100644 --- a/templates/Bff/src/WebApp/Program.cs +++ b/templates/Bff/src/WebApp/Program.cs @@ -3,6 +3,7 @@ using Infrastructure; using Microsoft.AspNetCore.Http.Json; using Microsoft.AspNetCore.Server.Kestrel.Core; +using Scalar.AspNetCore; using WebApp.Endpoints; using WebApp.GrpcServices; using WebApp.HealthChecks; @@ -20,6 +21,7 @@ private static async Task Main(string[] args) builder.Services.AddEndpointsApiExplorer(); builder.Services.AddGrpc(); + builder.Services.AddOpenApi(); builder.Services.AddCustomHealthChecks(builder.Configuration); @@ -45,6 +47,9 @@ private static async Task Main(string[] args) var app = builder.Build(); + app.MapOpenApi(); + app.MapScalarApiReference(); + app.UseHttpsRedirection(); app.MapEndpoints() diff --git a/templates/Bff/src/WebApp/WebApp.csproj b/templates/Bff/src/WebApp/WebApp.csproj index 6c9b8e5a..8d531ee6 100644 --- a/templates/Bff/src/WebApp/WebApp.csproj +++ b/templates/Bff/src/WebApp/WebApp.csproj @@ -3,6 +3,8 @@ + + diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json index 1df939df..f5b2327c 100644 --- a/templates/Bff/src/WebApp/packages.lock.json +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -31,6 +31,21 @@ "Grpc.Tools": "2.76.0" } }, + "Microsoft.AspNetCore.OpenApi": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "dependencies": { + "Microsoft.OpenApi": "2.0.0" + } + }, + "Scalar.AspNetCore": { + "type": "Direct", + "requested": "[2.12.4, )", + "resolved": "2.12.4", + "contentHash": "z2AsZCSuota9x0o/E1pptMD0hH65cJFaAIiuVmm34hntZy2zR9LusRQkUvWIStf+jwBSHzzFcli88kDlrcNJ3w==" + }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", @@ -92,6 +107,11 @@ "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" }, + "Microsoft.OpenApi": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "GGYLfzV/G/ct80OZ45JxnWP7NvMX1BCugn/lX7TH5o0lcVaviavsLMTxmFV2AybXWjbi3h6FF1vgZiTK6PXndw==" + }, "OpenTelemetry": { "type": "Transitive", "resolved": "1.14.0", diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json index 01b71e45..3a4d214e 100644 --- a/templates/Bff/tests/IntegrationTests/packages.lock.json +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -438,6 +438,11 @@ "resolved": "10.0.1", "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" }, + "Microsoft.OpenApi": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "GGYLfzV/G/ct80OZ45JxnWP7NvMX1BCugn/lX7TH5o0lcVaviavsLMTxmFV2AybXWjbi3h6FF1vgZiTK6PXndw==" + }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", "resolved": "18.0.1", @@ -547,7 +552,9 @@ "AspNetCore.HealthChecks.Redis": "[9.0.0, )", "AspNetCore.HealthChecks.UI.Client": "[9.0.0, )", "Grpc.AspNetCore": "[2.76.0, )", - "Infrastructure": "[1.0.0, )" + "Infrastructure": "[1.0.0, )", + "Microsoft.AspNetCore.OpenApi": "[10.0.1, )", + "Scalar.AspNetCore": "[2.12.4, )" } }, "AspNetCore.HealthChecks.Redis": { @@ -580,6 +587,15 @@ "Grpc.Tools": "2.76.0" } }, + "Microsoft.AspNetCore.OpenApi": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "dependencies": { + "Microsoft.OpenApi": "2.0.0" + } + }, "Microsoft.Extensions.Caching.Hybrid": { "type": "CentralTransitive", "requested": "[10.1.0, )", @@ -700,6 +716,12 @@ "StackExchange.Redis": "[2.6.122, 3.0.0)" } }, + "Scalar.AspNetCore": { + "type": "CentralTransitive", + "requested": "[2.12.4, )", + "resolved": "2.12.4", + "contentHash": "z2AsZCSuota9x0o/E1pptMD0hH65cJFaAIiuVmm34hntZy2zR9LusRQkUvWIStf+jwBSHzzFcli88kDlrcNJ3w==" + }, "xunit.extensibility.core": { "type": "CentralTransitive", "requested": "[2.9.3, )", From 93107448a884def7c8dedb86dde3717f8f0c59ab Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 13:20:47 -0300 Subject: [PATCH 007/161] feat: refactor bff service and update order endpoints --- templates/Bff/.codacy/codacy.yaml | 15 +++++ templates/Bff/Directory.Build.props | 5 ++ templates/Bff/Dockerfile | 4 +- .../MockApi/Dockerfile => Dockerfile.MockApi} | 3 + .../Bff/Hexagonal.Solution.Template.Bff.sln | 43 +++++++++++++ templates/Bff/docker-compose-load-tests.yml | 16 +++-- templates/Bff/docker-compose.yml | 4 +- .../mocks/MockApi/Endpoints/OrderEndpoints.cs | 56 ----------------- templates/Bff/mocks/MockApi/MockApi.http | 63 ------------------- templates/Bff/mocks/MockApi/appsettings.json | 20 ------ .../Bff/src/Contracts/Common/BaseRequest.cs | 11 ++++ .../Bff/src/Contracts/Common/BaseResponse.cs | 43 +++++++++++++ templates/Bff/src/Contracts/Contracts.csproj | 5 ++ .../Contracts/Orders/CreateOrderRequest.cs | 6 ++ .../Bff/src/Contracts/Orders/OrderDto.cs | 16 +++++ .../Contracts}/Protos/order.proto | 0 .../Bff/src/Contracts/packages.lock.json | 6 ++ .../Infrastructure/Http/BaseHttpService.cs | 47 ++++++++++---- .../src/Infrastructure/Infrastructure.csproj | 3 + .../Bff/src/Infrastructure/packages.lock.json | 3 + .../MockApi/Endpoints/EndpointExtensions.cs | 0 .../src/MockApi/Endpoints/OrderEndpoints.cs | 32 ++++++++++ .../GrpcServices/GrpcServiceExtensions.cs | 0 .../MockApi/GrpcServices/OrderService.cs | 0 .../Bff/{mocks => src}/MockApi/MockApi.csproj | 4 ++ templates/Bff/src/MockApi/MockApi.http | 29 +++++++++ .../Bff/{mocks => src}/MockApi/Program.cs | 0 .../MockApi/Properties/launchSettings.json | 0 .../{mocks => src}/MockApi/packages.lock.json | 33 ++++++++++ .../src/WebApp/Endpoints/OrderEndpoints.cs | 33 +++++++--- templates/Bff/src/WebApp/Protos/order.proto | 34 ---------- templates/Bff/src/WebApp/WebApp.csproj | 3 - templates/Bff/src/WebApp/WebApp.http | 25 +------- templates/Bff/src/WebApp/packages.lock.json | 4 ++ .../tests/IntegrationTests/packages.lock.json | 4 ++ templates/Full/Directory.Build.props | 7 ++- 36 files changed, 342 insertions(+), 235 deletions(-) create mode 100644 templates/Bff/.codacy/codacy.yaml rename templates/Bff/{mocks/MockApi/Dockerfile => Dockerfile.MockApi} (86%) delete mode 100644 templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs delete mode 100644 templates/Bff/mocks/MockApi/MockApi.http delete mode 100644 templates/Bff/mocks/MockApi/appsettings.json create mode 100644 templates/Bff/src/Contracts/Common/BaseRequest.cs create mode 100644 templates/Bff/src/Contracts/Common/BaseResponse.cs create mode 100644 templates/Bff/src/Contracts/Contracts.csproj create mode 100644 templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs create mode 100644 templates/Bff/src/Contracts/Orders/OrderDto.cs rename templates/Bff/{mocks/MockApi => src/Contracts}/Protos/order.proto (100%) create mode 100644 templates/Bff/src/Contracts/packages.lock.json rename templates/Bff/{mocks => src}/MockApi/Endpoints/EndpointExtensions.cs (100%) create mode 100644 templates/Bff/src/MockApi/Endpoints/OrderEndpoints.cs rename templates/Bff/{mocks => src}/MockApi/GrpcServices/GrpcServiceExtensions.cs (100%) rename templates/Bff/{mocks => src}/MockApi/GrpcServices/OrderService.cs (100%) rename templates/Bff/{mocks => src}/MockApi/MockApi.csproj (58%) create mode 100644 templates/Bff/src/MockApi/MockApi.http rename templates/Bff/{mocks => src}/MockApi/Program.cs (100%) rename templates/Bff/{mocks => src}/MockApi/Properties/launchSettings.json (100%) rename templates/Bff/{mocks => src}/MockApi/packages.lock.json (69%) delete mode 100644 templates/Bff/src/WebApp/Protos/order.proto diff --git a/templates/Bff/.codacy/codacy.yaml b/templates/Bff/.codacy/codacy.yaml new file mode 100644 index 00000000..15365c77 --- /dev/null +++ b/templates/Bff/.codacy/codacy.yaml @@ -0,0 +1,15 @@ +runtimes: + - dart@3.7.2 + - go@1.22.3 + - java@17.0.10 + - node@22.2.0 + - python@3.11.11 +tools: + - dartanalyzer@3.7.2 + - eslint@8.57.0 + - lizard@1.17.31 + - pmd@7.11.0 + - pylint@3.3.6 + - revive@1.7.0 + - semgrep@1.78.0 + - trivy@0.66.0 diff --git a/templates/Bff/Directory.Build.props b/templates/Bff/Directory.Build.props index a31e77ed..0d37b829 100644 --- a/templates/Bff/Directory.Build.props +++ b/templates/Bff/Directory.Build.props @@ -9,5 +9,10 @@ + + + + Never + \ No newline at end of file diff --git a/templates/Bff/Dockerfile b/templates/Bff/Dockerfile index 9f46f6c3..9b7e6a8d 100644 --- a/templates/Bff/Dockerfile +++ b/templates/Bff/Dockerfile @@ -14,6 +14,6 @@ COPY --from=build-env /app/out . RUN apt-get update \ && apt-get install -y curl -EXPOSE 5000 +EXPOSE 5011 -ENTRYPOINT ["dotnet", "WebApp.dll", "--urls", "http://+:5000"] +ENTRYPOINT ["dotnet", "WebApp.dll", "--urls", "http://+:5011"] diff --git a/templates/Bff/mocks/MockApi/Dockerfile b/templates/Bff/Dockerfile.MockApi similarity index 86% rename from templates/Bff/mocks/MockApi/Dockerfile rename to templates/Bff/Dockerfile.MockApi index 99494472..3fdacafd 100644 --- a/templates/Bff/mocks/MockApi/Dockerfile +++ b/templates/Bff/Dockerfile.MockApi @@ -11,6 +11,9 @@ FROM mcr.microsoft.com/dotnet/aspnet:10.0 WORKDIR /app COPY --from=build-env /app/out . +RUN apt-get update \ + && apt-get install -y curl + EXPOSE 5012 ENTRYPOINT ["dotnet", "MockApi.dll", "--urls", "http://+:5012"] diff --git a/templates/Bff/Hexagonal.Solution.Template.Bff.sln b/templates/Bff/Hexagonal.Solution.Template.Bff.sln index f2794c89..12c4198f 100644 --- a/templates/Bff/Hexagonal.Solution.Template.Bff.sln +++ b/templates/Bff/Hexagonal.Solution.Template.Bff.sln @@ -24,24 +24,66 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution scripts\seeds.sql = scripts\seeds.sql EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contracts", "src\Contracts\Contracts.csproj", "{92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x64.ActiveCfg = Debug|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x64.Build.0 = Debug|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x86.ActiveCfg = Debug|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x86.Build.0 = Debug|Any CPU {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.Build.0 = Release|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x64.ActiveCfg = Release|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x64.Build.0 = Release|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x86.ActiveCfg = Release|Any CPU + {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x86.Build.0 = Release|Any CPU {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x64.ActiveCfg = Debug|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x64.Build.0 = Debug|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x86.ActiveCfg = Debug|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x86.Build.0 = Debug|Any CPU {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.Build.0 = Release|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x64.ActiveCfg = Release|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x64.Build.0 = Release|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x86.ActiveCfg = Release|Any CPU + {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x86.Build.0 = Release|Any CPU {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x64.ActiveCfg = Debug|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x64.Build.0 = Debug|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x86.ActiveCfg = Debug|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x86.Build.0 = Debug|Any CPU {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.Build.0 = Release|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x64.ActiveCfg = Release|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x64.Build.0 = Release|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x86.ActiveCfg = Release|Any CPU + {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x86.Build.0 = Release|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x64.ActiveCfg = Debug|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x64.Build.0 = Debug|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x86.ActiveCfg = Debug|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x86.Build.0 = Debug|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|Any CPU.Build.0 = Release|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x64.ActiveCfg = Release|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x64.Build.0 = Release|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x86.ActiveCfg = Release|Any CPU + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,6 +92,7 @@ Global {EB1974DC-D58F-41A8-A965-CC772F2B8CEB} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} {EA853C22-3BDD-43C0-A150-8C77BBE41058} = {D499CDEC-C117-4968-A519-21C64757C640} {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} + {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3AD0BA6E-E14B-401F-B4F6-C7736B01643C} diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml index 189007f1..17ecbb5b 100644 --- a/templates/Bff/docker-compose-load-tests.yml +++ b/templates/Bff/docker-compose-load-tests.yml @@ -17,7 +17,7 @@ services: networks: - hexagonal_solution_template_network - webapp: + bff: build: context: . dockerfile: ./Dockerfile @@ -36,23 +36,21 @@ services: - ASPNETCORE_ENVIRONMENT=Development - LOGGING__LOGLEVEL__DEFAULT=Warning - LOGGING__LOGLEVEL__MICROSOFT=Warning - - LOGGING__LOGLEVEL__MICROSOFT_HOSTING_LIFETIME=Warning - - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE=Warning - - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning - - ENABLE_SENSITIVE_DATA_LOGGING=false - - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp + - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.Bff - OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - - ConnectionStrings__OrderDb=Server=sqlserver,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true; - - ConnectionStrings__Redis=redis:6379 - - ConnectionStrings__RabbitMQ=amqp://guest:guest@rabbitmq:5672/ networks: - hexagonal_solution_template_network + mockApi: + extends: + file: docker-compose.yml + service: mockApi + aspire-dashboard: extends: file: docker-compose.yml diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index 5461d094..238bb673 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -11,8 +11,8 @@ services: mockApi: build: - context: ./mocks/MockApi - dockerfile: Dockerfile + context: . + dockerfile: Dockerfile.MockApi ports: - "5012:5012" networks: diff --git a/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs b/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs deleted file mode 100644 index f00f9fd3..00000000 --- a/templates/Bff/mocks/MockApi/Endpoints/OrderEndpoints.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace MockApi.Endpoints; - -internal static class OrderEndpoints -{ - public static WebApplication MapOrderEndpoints(this WebApplication app) - { - var ordersGroup = app.MapGroup("orders").WithTags("orders"); - - ordersGroup.MapGet("/{id}", async ( - [FromRoute] int id, - CancellationToken cancellationToken - ) => { - return Results.Ok(new - { - Success = true, - Message = string.Empty, - Data = new - { - Id = id, - Total = 100.0, - Items = new[] - { - new { Id = 1, Name = "Item 1", Value = 50.0 }, - new { Id = 2, Name = "Item 2", Value = 50.0 } - } - } - }); - }); - - ordersGroup.MapPost("/", async ( - [FromBody] object request, - CancellationToken cancellationToken - ) => - { - return Results.Created($"/orders/1", new - { - Success = true, - Message = string.Empty, - Data = new - { - Id = 1, - Total = 100.0, - Items = new[] - { - new { Id = 1, Name = "Item 1", Value = 50.0 }, - new { Id = 2, Name = "Item 2", Value = 50.0 } - } - } - }); - }); - - return app; - } -} diff --git a/templates/Bff/mocks/MockApi/MockApi.http b/templates/Bff/mocks/MockApi/MockApi.http deleted file mode 100644 index f9a0963b..00000000 --- a/templates/Bff/mocks/MockApi/MockApi.http +++ /dev/null @@ -1,63 +0,0 @@ -@WebApp_HostAddress = https://localhost:7175 - -### -GET {{WebApp_HostAddress}}/health HTTP/2 -Accept: application/json - -### -GET {{WebApp_HostAddress}}/live HTTP/2 - -### -GET {{WebApp_HostAddress}}/ready HTTP/2 - -### -GET {{WebApp_HostAddress}}/orders/1 HTTP/2 -CorrelationId: {{$guid}} -CacheEnabled: false -Accept-Encoding: gzip, deflate -Accept: application/json - -### -POST {{WebApp_HostAddress}}/orders HTTP/2 -Content-Type: application/json - -{ - "CorrelationId": "{{$guid}}", - "Description": "John's computer", - "Items": [ - { - "Name": "Computer", - "Description": "Surface 2", - "Value": 1000 - }, - { - "Name": "Mouse", - "Description": "Microsoft mouse", - "Value": 99 - } - ] -} - -### -POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 -Content-Type: application/json - -{ - "CorrelationId": "{{$guid}}", - "Page": 1, - "PageSize": 1 -} - -### -POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 -Content-Type: application/json - -{ - "CorrelationId": "{{$guid}}", - "Page": 1, - "PageSize": 10, - "SearchByValues": { - "Description": "client" - } -} - diff --git a/templates/Bff/mocks/MockApi/appsettings.json b/templates/Bff/mocks/MockApi/appsettings.json deleted file mode 100644 index b3be136c..00000000 --- a/templates/Bff/mocks/MockApi/appsettings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "ConnectionStrings": { - "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", - "Redis": "localhost:6379", - "RabbitMq": "amqp://guest:guest@localhost:5672/" - }, - "Http": [ - { - "Name": "Orders", - "BaseAddress": "http://localhost:5001", - "Headers": { - "Accept": "application/json" - } - }, - { - "Name": "Payments", - "BaseAddress": "http://localhost:5002" - } - ] -} \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Common/BaseRequest.cs b/templates/Bff/src/Contracts/Common/BaseRequest.cs new file mode 100644 index 00000000..c8af47c7 --- /dev/null +++ b/templates/Bff/src/Contracts/Common/BaseRequest.cs @@ -0,0 +1,11 @@ +namespace Contracts.Common; +public record BaseRequest(Guid CorrelationId); + +public record BasePaginatedRequest( + Guid CorrelationId, + int Page = 1, + int PageSize = 10, + string? SortBy = null, + bool SortDescending = false, + Dictionary? SearchByValues = null +) : BaseRequest(CorrelationId); \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Common/BaseResponse.cs b/templates/Bff/src/Contracts/Common/BaseResponse.cs new file mode 100644 index 00000000..68377696 --- /dev/null +++ b/templates/Bff/src/Contracts/Common/BaseResponse.cs @@ -0,0 +1,43 @@ +namespace Contracts.Common; + +public record BaseResponse +{ + public BaseResponse() { } + + public BaseResponse(bool success, string? message = null) + { + Success = success; + Message = message; + } + + public bool Success { get; set; } + public string? Message { get; set; } +} + +public record BaseResponse : BaseResponse where TData : class +{ + public BaseResponse() { } + + public BaseResponse(bool success, TData? data = null, string? message = null) : base(success, message) => Data = data; + + public TData? Data { get; set; } + +} + +public sealed record BasePaginatedResponse : BaseResponse> +{ + public BasePaginatedResponse() { } + + public BasePaginatedResponse( + bool success, int totalPages, int totalRecords, + IEnumerable? data = null, string? message = null + ) : base(success, data, message) + { + TotalPages = totalPages; + TotalRecords = totalRecords; + } + + public int TotalPages { get; set; } + public int TotalRecords { get; set; } + +} diff --git a/templates/Bff/src/Contracts/Contracts.csproj b/templates/Bff/src/Contracts/Contracts.csproj new file mode 100644 index 00000000..57e84256 --- /dev/null +++ b/templates/Bff/src/Contracts/Contracts.csproj @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs b/templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs new file mode 100644 index 00000000..72a22705 --- /dev/null +++ b/templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs @@ -0,0 +1,6 @@ +using Contracts.Common; + +namespace Contracts.Orders; + +public sealed record CreateOrderRequest(Guid CorrelationId, string Description, CreateOrderItemRequest[] Items) : BaseRequest(CorrelationId); +public sealed record CreateOrderItemRequest(string Name, string Description, decimal Value); \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Orders/OrderDto.cs b/templates/Bff/src/Contracts/Orders/OrderDto.cs new file mode 100644 index 00000000..e66660b2 --- /dev/null +++ b/templates/Bff/src/Contracts/Orders/OrderDto.cs @@ -0,0 +1,16 @@ +namespace Contracts.Orders; +public sealed record OrderDto +{ + public int Id { get; set; } + public string Description { get; set; } + public decimal Total { get; set; } + public IReadOnlyCollection? Items { get; set; } +}; + +public sealed record ItemDto +{ + public int Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public decimal Value { get; set; } +}; diff --git a/templates/Bff/mocks/MockApi/Protos/order.proto b/templates/Bff/src/Contracts/Protos/order.proto similarity index 100% rename from templates/Bff/mocks/MockApi/Protos/order.proto rename to templates/Bff/src/Contracts/Protos/order.proto diff --git a/templates/Bff/src/Contracts/packages.lock.json b/templates/Bff/src/Contracts/packages.lock.json new file mode 100644 index 00000000..6afd6786 --- /dev/null +++ b/templates/Bff/src/Contracts/packages.lock.json @@ -0,0 +1,6 @@ +{ + "version": 2, + "dependencies": { + "net10.0": {} + } +} \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 7a7e4bb9..9d21c143 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -11,28 +11,53 @@ public class BaseHttpService(HttpClient httpClient, ILogger log protected readonly JsonSerializerOptions _jsonSerializerOptions = new(JsonSerializerDefaults.Web); protected readonly Stopwatch _stopwatch = new(); - public async Task SendAsync( + public async Task SendAsync( string requestUri, HttpMethod method, CancellationToken cancellationToken, - dynamic? request = null, + TRequest request, Dictionary? headers = null - ) + ) where TRequest : class where TResponse : class { _stopwatch.Start(); _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); var requestMessage = new HttpRequestMessage(method, requestUri); - if (request != null) - { - var memoryStream = new MemoryStream(); - await JsonSerializer.SerializeAsync(memoryStream, request, _jsonSerializerOptions, cancellationToken); + var memoryStream = new MemoryStream(); + await JsonSerializer.SerializeAsync(memoryStream, request, _jsonSerializerOptions, cancellationToken); - memoryStream.Seek(0, SeekOrigin.Begin); - using var requestContent = new StreamContent(memoryStream); - requestMessage.Content = requestContent; - } + memoryStream.Seek(0, SeekOrigin.Begin); + using var requestContent = new StreamContent(memoryStream); + requestMessage.Content = requestContent; + + if (headers != null) foreach (var header in headers) + requestMessage.Headers.Add(header.Key, header.Value); + + using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + + response.EnsureSuccessStatusCode(); + + var content = await response.Content.ReadAsStreamAsync(cancellationToken); + + var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); + + _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); + + return result; + } + + public async Task SendAsync( + string requestUri, + HttpMethod method, + CancellationToken cancellationToken, + Dictionary? headers = null + ) where TResponse : class + { + _stopwatch.Start(); + _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); + + var requestMessage = new HttpRequestMessage(method, requestUri); if (headers != null) foreach (var header in headers) requestMessage.Headers.Add(header.Key, header.Value); diff --git a/templates/Bff/src/Infrastructure/Infrastructure.csproj b/templates/Bff/src/Infrastructure/Infrastructure.csproj index d477fd2a..6e7d97bb 100644 --- a/templates/Bff/src/Infrastructure/Infrastructure.csproj +++ b/templates/Bff/src/Infrastructure/Infrastructure.csproj @@ -10,4 +10,7 @@ + + + \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/packages.lock.json b/templates/Bff/src/Infrastructure/packages.lock.json index 16a71f14..a11b6fb6 100644 --- a/templates/Bff/src/Infrastructure/packages.lock.json +++ b/templates/Bff/src/Infrastructure/packages.lock.json @@ -263,6 +263,9 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, + "contracts": { + "type": "Project" + }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", "requested": "[10.0.1, )", diff --git a/templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs b/templates/Bff/src/MockApi/Endpoints/EndpointExtensions.cs similarity index 100% rename from templates/Bff/mocks/MockApi/Endpoints/EndpointExtensions.cs rename to templates/Bff/src/MockApi/Endpoints/EndpointExtensions.cs diff --git a/templates/Bff/src/MockApi/Endpoints/OrderEndpoints.cs b/templates/Bff/src/MockApi/Endpoints/OrderEndpoints.cs new file mode 100644 index 00000000..59aa2174 --- /dev/null +++ b/templates/Bff/src/MockApi/Endpoints/OrderEndpoints.cs @@ -0,0 +1,32 @@ +using AutoFixture; +using Contracts.Common; +using Contracts.Orders; +using Microsoft.AspNetCore.Mvc; + +namespace MockApi.Endpoints; + +internal static class OrderEndpoints +{ + public static WebApplication MapOrderEndpoints(this WebApplication app) + { + var ordersGroup = app.MapGroup("orders").WithTags("orders"); + var autoFixture = new Fixture(); + + ordersGroup.MapGet("/{id}", async ( + [FromRoute] int id, + CancellationToken cancellationToken + ) => id switch + { + 1 => Results.Ok(autoFixture.Create>()), + 2 => Results.Ok(autoFixture.Create>()), + _ => Results.NotFound(autoFixture.Create()) + }); + + ordersGroup.MapPost("/", async ( + [FromBody] CreateOrderRequest request, + CancellationToken cancellationToken + ) => Results.Created($"/orders/1", autoFixture.Create>())); + + return app; + } +} diff --git a/templates/Bff/mocks/MockApi/GrpcServices/GrpcServiceExtensions.cs b/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs similarity index 100% rename from templates/Bff/mocks/MockApi/GrpcServices/GrpcServiceExtensions.cs rename to templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs diff --git a/templates/Bff/mocks/MockApi/GrpcServices/OrderService.cs b/templates/Bff/src/MockApi/GrpcServices/OrderService.cs similarity index 100% rename from templates/Bff/mocks/MockApi/GrpcServices/OrderService.cs rename to templates/Bff/src/MockApi/GrpcServices/OrderService.cs diff --git a/templates/Bff/mocks/MockApi/MockApi.csproj b/templates/Bff/src/MockApi/MockApi.csproj similarity index 58% rename from templates/Bff/mocks/MockApi/MockApi.csproj rename to templates/Bff/src/MockApi/MockApi.csproj index 5f1faddf..9a36506e 100644 --- a/templates/Bff/mocks/MockApi/MockApi.csproj +++ b/templates/Bff/src/MockApi/MockApi.csproj @@ -1,8 +1,12 @@  + + + + \ No newline at end of file diff --git a/templates/Bff/src/MockApi/MockApi.http b/templates/Bff/src/MockApi/MockApi.http new file mode 100644 index 00000000..7a50ba9e --- /dev/null +++ b/templates/Bff/src/MockApi/MockApi.http @@ -0,0 +1,29 @@ +@WebApp_HostAddress = https://localhost:7177 + +### +GET {{WebApp_HostAddress}}/orders/1 HTTP/2 +CorrelationId: {{$guid}} +CacheEnabled: false +Accept-Encoding: gzip, deflate +Accept: application/json + +### +POST {{WebApp_HostAddress}}/orders HTTP/2 +Content-Type: application/json + +{ + "CorrelationId": "{{$guid}}", + "Description": "John's computer", + "Items": [ + { + "Name": "Computer", + "Description": "Surface 2", + "Value": 1000 + }, + { + "Name": "Mouse", + "Description": "Microsoft mouse", + "Value": 99 + } + ] +} diff --git a/templates/Bff/mocks/MockApi/Program.cs b/templates/Bff/src/MockApi/Program.cs similarity index 100% rename from templates/Bff/mocks/MockApi/Program.cs rename to templates/Bff/src/MockApi/Program.cs diff --git a/templates/Bff/mocks/MockApi/Properties/launchSettings.json b/templates/Bff/src/MockApi/Properties/launchSettings.json similarity index 100% rename from templates/Bff/mocks/MockApi/Properties/launchSettings.json rename to templates/Bff/src/MockApi/Properties/launchSettings.json diff --git a/templates/Bff/mocks/MockApi/packages.lock.json b/templates/Bff/src/MockApi/packages.lock.json similarity index 69% rename from templates/Bff/mocks/MockApi/packages.lock.json rename to templates/Bff/src/MockApi/packages.lock.json index 5f23af35..6ccd2207 100644 --- a/templates/Bff/mocks/MockApi/packages.lock.json +++ b/templates/Bff/src/MockApi/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { "net10.0": { + "AutoFixture": { + "type": "Direct", + "requested": "[4.18.1, )", + "resolved": "4.18.1", + "contentHash": "BmWZDY4fkrYOyd5/CTBOeXbzsNwV8kI4kDi/Ty1Y5F+WDHBVKxzfWlBE4RSicvZ+EOi2XDaN5uwdrHsItLW6Kw==", + "dependencies": { + "Fare": "[2.1.1, 3.0.0)" + } + }, "Grpc.AspNetCore": { "type": "Direct", "requested": "[2.76.0, )", @@ -13,6 +22,14 @@ "Grpc.Tools": "2.76.0" } }, + "Fare": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "HaI8puqA66YU7/9cK4Sgbs1taUTP1Ssa4QT2PIzqJ7GvAbN1QgkjbRsjH+FSbMh1MJdvS0CIwQNLtFT+KF6KpA==", + "dependencies": { + "NETStandard.Library": "1.6.1" + } + }, "Google.Protobuf": { "type": "Transitive", "resolved": "3.31.1", @@ -68,6 +85,22 @@ "type": "Transitive", "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "contracts": { + "type": "Project" } } } diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 0c59946e..181fd01f 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -1,6 +1,8 @@ using Infrastructure.Http; using Microsoft.AspNetCore.Mvc; using Infrastructure.Cache; +using Contracts.Orders; +using Contracts.Common; namespace WebApp.Endpoints; @@ -13,6 +15,7 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) var ordersGroup = app.MapGroup(serviceKey).WithTags(serviceKey); ordersGroup.MapGet("/{id}", async ( + [FromKeyedServices(ServicesKeys.Orders)] BaseHttpService httpService, [FromRoute] int id, [FromServices] HybridCacheService cache, @@ -24,42 +27,52 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) { true => await cache.GetOrCreateAsync( $"{nameof(OrderEndpoints)}-{id}", - async (cancellationToken) => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() + async (cancellationToken) => await httpService.SendAsync>($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() { { "CorrelationId", (correlationId ?? Guid.NewGuid()).ToString() } }), cancellationToken ), - false or _ => await httpService.SendAsync($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() + false or _ => await httpService.SendAsync>($"/orders/{id}", HttpMethod.Get, cancellationToken, headers: new() { { "CorrelationId", (correlationId ?? Guid.NewGuid()).ToString() } }), }; - return response?.Success ? Results.Ok(response) : Results.NotFound(response); - }); + return response != null && response.Success ? Results.Ok(response) : Results.NotFound(response); + }) + .Produces>(StatusCodes.Status200OK) + .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status500InternalServerError) + .WithDescription("Gets an order by its identifier") + .WithName("GetOrderById"); ordersGroup.MapPost("/", async ( - [FromBody] object request, + [FromBody] CreateOrderRequest request, [FromKeyedServices(ServicesKeys.Orders)] BaseHttpService httpService, CancellationToken cancellationToken ) => { - var response = await httpService.SendAsync("orders", HttpMethod.Post, cancellationToken, request); + var response = await httpService.SendAsync>("orders", HttpMethod.Post, cancellationToken, request); if (response == null) return Results.BadRequest(new { Success = false, Message = "No response from service" }); - bool success = response.Success ?? false; - if (!success || response.Data == null) + if (!response.Success || response.Data == null) return Results.BadRequest(response); var data = response.Data; - var id = data?.Id?.ToString() ?? "unknown"; + var id = data?.Id.ToString() ?? "unknown"; return Results.Created($"/orders/{id}", response); - }); + }) + .Produces>(StatusCodes.Status201Created) + .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status500InternalServerError) + .WithDescription("Creates a new order") + .WithName("CreateOrder"); return app; } diff --git a/templates/Bff/src/WebApp/Protos/order.proto b/templates/Bff/src/WebApp/Protos/order.proto deleted file mode 100644 index 96761cd8..00000000 --- a/templates/Bff/src/WebApp/Protos/order.proto +++ /dev/null @@ -1,34 +0,0 @@ -syntax = "proto3"; - -option csharp_namespace = "GrpcOrder"; - -package order; - -service OrderService { - rpc Get (GetOrderRequest) returns (OrderReply); -} - -message GetOrderRequest { - int32 id = 1; - string correlationId = 2; -} - -message ItemDto { - int32 id = 1; - string name = 2; - string description = 3; - double value = 4; -} - -message OrderDto { - int32 id = 1; - string description = 2; - double total = 3; - repeated ItemDto items = 4; -} - -message OrderReply { - bool success = 1; - string message = 2; - OrderDto data = 3; -} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/WebApp.csproj b/templates/Bff/src/WebApp/WebApp.csproj index 8d531ee6..dfe06e50 100644 --- a/templates/Bff/src/WebApp/WebApp.csproj +++ b/templates/Bff/src/WebApp/WebApp.csproj @@ -9,7 +9,4 @@ - - - \ No newline at end of file diff --git a/templates/Bff/src/WebApp/WebApp.http b/templates/Bff/src/WebApp/WebApp.http index f9a0963b..bd5f79d2 100644 --- a/templates/Bff/src/WebApp/WebApp.http +++ b/templates/Bff/src/WebApp/WebApp.http @@ -1,4 +1,4 @@ -@WebApp_HostAddress = https://localhost:7175 +@WebApp_HostAddress = https://localhost:7176 ### GET {{WebApp_HostAddress}}/health HTTP/2 @@ -38,26 +38,3 @@ Content-Type: application/json ] } -### -POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 -Content-Type: application/json - -{ - "CorrelationId": "{{$guid}}", - "Page": 1, - "PageSize": 1 -} - -### -POST {{WebApp_HostAddress}}/orders/paginated HTTP/2 -Content-Type: application/json - -{ - "CorrelationId": "{{$guid}}", - "Page": 1, - "PageSize": 10, - "SearchByValues": { - "Description": "client" - } -} - diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json index f5b2327c..5336db6e 100644 --- a/templates/Bff/src/WebApp/packages.lock.json +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -146,9 +146,13 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, + "contracts": { + "type": "Project" + }, "infrastructure": { "type": "Project", "dependencies": { + "Contracts": "[1.0.0, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json index 3a4d214e..1bf950e9 100644 --- a/templates/Bff/tests/IntegrationTests/packages.lock.json +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -532,9 +532,13 @@ "xunit.extensibility.core": "[2.9.3]" } }, + "contracts": { + "type": "Project" + }, "infrastructure": { "type": "Project", "dependencies": { + "Contracts": "[1.0.0, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", diff --git a/templates/Full/Directory.Build.props b/templates/Full/Directory.Build.props index a31e77ed..1a826ab3 100644 --- a/templates/Full/Directory.Build.props +++ b/templates/Full/Directory.Build.props @@ -7,7 +7,12 @@ true false - + + + + + Never + \ No newline at end of file From 24becf3bb0764be8a7fa3cc62da6912bf5281afa Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 13:22:56 -0300 Subject: [PATCH 008/161] feat: update solution structure and remove obsolete files --- templates/Bff/Directory.Build.props | 5 - .../Bff/Hexagonal.Solution.Template.Bff.sln | 100 ------------------ .../Bff/Hexagonal.Solution.Template.Bff.slnx | 25 +++++ .../Full/Hexagonal.Solution.Template.Full.sln | 85 --------------- .../Hexagonal.Solution.Template.Full.slnx | 22 ++++ 5 files changed, 47 insertions(+), 190 deletions(-) delete mode 100644 templates/Bff/Hexagonal.Solution.Template.Bff.sln create mode 100644 templates/Bff/Hexagonal.Solution.Template.Bff.slnx delete mode 100644 templates/Full/Hexagonal.Solution.Template.Full.sln create mode 100644 templates/Full/Hexagonal.Solution.Template.Full.slnx diff --git a/templates/Bff/Directory.Build.props b/templates/Bff/Directory.Build.props index 0d37b829..a31e77ed 100644 --- a/templates/Bff/Directory.Build.props +++ b/templates/Bff/Directory.Build.props @@ -9,10 +9,5 @@ - - - - Never - \ No newline at end of file diff --git a/templates/Bff/Hexagonal.Solution.Template.Bff.sln b/templates/Bff/Hexagonal.Solution.Template.Bff.sln deleted file mode 100644 index 12c4198f..00000000 --- a/templates/Bff/Hexagonal.Solution.Template.Bff.sln +++ /dev/null @@ -1,100 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.3.11222.16 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6A878B68-ADCF-470F-8273-DA5C32E2DB79}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D499CDEC-C117-4968-A519-21C64757C640}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApp", "src\WebApp\WebApp.csproj", "{EB1974DC-D58F-41A8-A965-CC772F2B8CEB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{EA853C22-3BDD-43C0-A150-8C77BBE41058}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{34744AD9-DAE2-429F-94C2-CCE51DFAD280}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - Directory.Build.props = Directory.Build.props - Directory.Packages.props = Directory.Packages.props - docker-compose.yml = docker-compose.yml - scripts\migrations.sql = scripts\migrations.sql - scripts\seeds.sql = scripts\seeds.sql - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contracts", "src\Contracts\Contracts.csproj", "{92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x64.ActiveCfg = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x64.Build.0 = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x86.ActiveCfg = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|x86.Build.0 = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.Build.0 = Release|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x64.ActiveCfg = Release|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x64.Build.0 = Release|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x86.ActiveCfg = Release|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|x86.Build.0 = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x64.ActiveCfg = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x64.Build.0 = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x86.ActiveCfg = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|x86.Build.0 = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.Build.0 = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x64.ActiveCfg = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x64.Build.0 = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x86.ActiveCfg = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|x86.Build.0 = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x64.ActiveCfg = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x64.Build.0 = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x86.ActiveCfg = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|x86.Build.0 = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.Build.0 = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x64.ActiveCfg = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x64.Build.0 = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x86.ActiveCfg = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|x86.Build.0 = Release|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x64.ActiveCfg = Debug|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x64.Build.0 = Debug|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x86.ActiveCfg = Debug|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Debug|x86.Build.0 = Debug|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|Any CPU.Build.0 = Release|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x64.ActiveCfg = Release|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x64.Build.0 = Release|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x86.ActiveCfg = Release|Any CPU - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} - {EA853C22-3BDD-43C0-A150-8C77BBE41058} = {D499CDEC-C117-4968-A519-21C64757C640} - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} - {92DD0E92-44A9-481F-B8AD-DF3AC7CECFBE} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3AD0BA6E-E14B-401F-B4F6-C7736B01643C} - EndGlobalSection -EndGlobal diff --git a/templates/Bff/Hexagonal.Solution.Template.Bff.slnx b/templates/Bff/Hexagonal.Solution.Template.Bff.slnx new file mode 100644 index 00000000..f46845ee --- /dev/null +++ b/templates/Bff/Hexagonal.Solution.Template.Bff.slnx @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/Full/Hexagonal.Solution.Template.Full.sln b/templates/Full/Hexagonal.Solution.Template.Full.sln deleted file mode 100644 index 7988e323..00000000 --- a/templates/Full/Hexagonal.Solution.Template.Full.sln +++ /dev/null @@ -1,85 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.3.11222.16 d18.3 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6A878B68-ADCF-470F-8273-DA5C32E2DB79}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D499CDEC-C117-4968-A519-21C64757C640}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "src\Domain\Domain.csproj", "{E2990ADA-8398-4769-98E9-EA105130137D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "src\Application\Application.csproj", "{3F9D43F2-9555-4771-A81B-9EBDE1A2C7CF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonTests", "tests\CommonTests\CommonTests.csproj", "{A1376FF6-1F7E-4D68-967A-121572141BA0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApp", "src\WebApp\WebApp.csproj", "{EB1974DC-D58F-41A8-A965-CC772F2B8CEB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "tests\UnitTests\UnitTests.csproj", "{5E16731E-1160-4F78-811C-037EE40C5AC0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{EA853C22-3BDD-43C0-A150-8C77BBE41058}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{34744AD9-DAE2-429F-94C2-CCE51DFAD280}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - Directory.Build.props = Directory.Build.props - Directory.Packages.props = Directory.Packages.props - docker-compose.yml = docker-compose.yml - scripts\migrations.sql = scripts\migrations.sql - scripts\seeds.sql = scripts\seeds.sql - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E2990ADA-8398-4769-98E9-EA105130137D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E2990ADA-8398-4769-98E9-EA105130137D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E2990ADA-8398-4769-98E9-EA105130137D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E2990ADA-8398-4769-98E9-EA105130137D}.Release|Any CPU.Build.0 = Release|Any CPU - {3F9D43F2-9555-4771-A81B-9EBDE1A2C7CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3F9D43F2-9555-4771-A81B-9EBDE1A2C7CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3F9D43F2-9555-4771-A81B-9EBDE1A2C7CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3F9D43F2-9555-4771-A81B-9EBDE1A2C7CF}.Release|Any CPU.Build.0 = Release|Any CPU - {A1376FF6-1F7E-4D68-967A-121572141BA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1376FF6-1F7E-4D68-967A-121572141BA0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1376FF6-1F7E-4D68-967A-121572141BA0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1376FF6-1F7E-4D68-967A-121572141BA0}.Release|Any CPU.Build.0 = Release|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB}.Release|Any CPU.Build.0 = Release|Any CPU - {5E16731E-1160-4F78-811C-037EE40C5AC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E16731E-1160-4F78-811C-037EE40C5AC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E16731E-1160-4F78-811C-037EE40C5AC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E16731E-1160-4F78-811C-037EE40C5AC0}.Release|Any CPU.Build.0 = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA853C22-3BDD-43C0-A150-8C77BBE41058}.Release|Any CPU.Build.0 = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {E2990ADA-8398-4769-98E9-EA105130137D} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} - {3F9D43F2-9555-4771-A81B-9EBDE1A2C7CF} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} - {A1376FF6-1F7E-4D68-967A-121572141BA0} = {D499CDEC-C117-4968-A519-21C64757C640} - {EB1974DC-D58F-41A8-A965-CC772F2B8CEB} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} - {5E16731E-1160-4F78-811C-037EE40C5AC0} = {D499CDEC-C117-4968-A519-21C64757C640} - {EA853C22-3BDD-43C0-A150-8C77BBE41058} = {D499CDEC-C117-4968-A519-21C64757C640} - {8C2C5A8B-5CC4-48B3-8266-EBFB68DFC51C} = {6A878B68-ADCF-470F-8273-DA5C32E2DB79} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3AD0BA6E-E14B-401F-B4F6-C7736B01643C} - EndGlobalSection -EndGlobal diff --git a/templates/Full/Hexagonal.Solution.Template.Full.slnx b/templates/Full/Hexagonal.Solution.Template.Full.slnx new file mode 100644 index 00000000..d86e4772 --- /dev/null +++ b/templates/Full/Hexagonal.Solution.Template.Full.slnx @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + From 30240b7169b5b98ff818a49c509c353dee7fc1f2 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 13:24:35 -0300 Subject: [PATCH 009/161] feat: add additional order endpoints to mock API service --- templates/Bff/src/MockApi/MockApi.http | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/templates/Bff/src/MockApi/MockApi.http b/templates/Bff/src/MockApi/MockApi.http index 7a50ba9e..a61bd2b1 100644 --- a/templates/Bff/src/MockApi/MockApi.http +++ b/templates/Bff/src/MockApi/MockApi.http @@ -7,6 +7,21 @@ CacheEnabled: false Accept-Encoding: gzip, deflate Accept: application/json +### +GET {{WebApp_HostAddress}}/orders/2 HTTP/2 +CorrelationId: {{$guid}} +CacheEnabled: false +Accept-Encoding: gzip, deflate +Accept: application/json + +### +GET {{WebApp_HostAddress}}/orders/4 HTTP/2 +CorrelationId: {{$guid}} +CacheEnabled: false +Accept-Encoding: gzip, deflate +Accept: application/json + + ### POST {{WebApp_HostAddress}}/orders HTTP/2 Content-Type: application/json From aa722858fc6466c9afb9853817ebe923a5f002cb Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 13:36:10 -0300 Subject: [PATCH 010/161] feat: standardize mock-api naming in configuration files --- templates/Bff/docker-compose-load-tests.yml | 2 +- templates/Bff/docker-compose.yml | 2 +- .../Bff/src/Infrastructure/Http/BaseHttpService.cs | 12 ++++-------- .../InfrastructureDependencyInjection.cs | 2 ++ templates/Bff/src/WebApp/WebApp.http | 8 ++++++++ templates/Bff/src/WebApp/appsettings.json | 7 +++++-- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml index 17ecbb5b..93cec545 100644 --- a/templates/Bff/docker-compose-load-tests.yml +++ b/templates/Bff/docker-compose-load-tests.yml @@ -46,7 +46,7 @@ services: networks: - hexagonal_solution_template_network - mockApi: + mock-api: extends: file: docker-compose.yml service: mockApi diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index 238bb673..49bc7f93 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -9,7 +9,7 @@ services: environment: - DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true - mockApi: + mock-api: build: context: . dockerfile: Dockerfile.MockApi diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 9d21c143..af39ef3a 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -22,9 +22,9 @@ public class BaseHttpService(HttpClient httpClient, ILogger log _stopwatch.Start(); _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); - var requestMessage = new HttpRequestMessage(method, requestUri); + HttpRequestMessage requestMessage = new(method, requestUri); - var memoryStream = new MemoryStream(); + using MemoryStream memoryStream = new(); await JsonSerializer.SerializeAsync(memoryStream, request, _jsonSerializerOptions, cancellationToken); memoryStream.Seek(0, SeekOrigin.Begin); @@ -36,11 +36,9 @@ public class BaseHttpService(HttpClient httpClient, ILogger log using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); - response.EnsureSuccessStatusCode(); - var content = await response.Content.ReadAsStreamAsync(cancellationToken); - var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); + var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); @@ -63,12 +61,10 @@ public class BaseHttpService(HttpClient httpClient, ILogger log requestMessage.Headers.Add(header.Key, header.Value); using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); - - response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStreamAsync(cancellationToken); - var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); + var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index b2ceddbe..53a01e3e 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -126,6 +126,8 @@ internal IServiceCollection AddCache(IConfiguration configuration) internal IServiceCollection AddHttp(IConfiguration configuration) { + services.AddHttpClient(); + var httpConfigurations = configuration.GetSection("Http").Get>() ?? throw new NullReferenceException("Http services configuration is not configured."); diff --git a/templates/Bff/src/WebApp/WebApp.http b/templates/Bff/src/WebApp/WebApp.http index bd5f79d2..70b3239b 100644 --- a/templates/Bff/src/WebApp/WebApp.http +++ b/templates/Bff/src/WebApp/WebApp.http @@ -17,6 +17,14 @@ CacheEnabled: false Accept-Encoding: gzip, deflate Accept: application/json +### +GET {{WebApp_HostAddress}}/orders/4 HTTP/2 +CorrelationId: {{$guid}} +CacheEnabled: false +Accept-Encoding: gzip, deflate +Accept: application/json + + ### POST {{WebApp_HostAddress}}/orders HTTP/2 Content-Type: application/json diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index 0fcb0a36..8bf462a3 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -7,14 +7,17 @@ "Http": [ { "Name": "Orders", - "BaseAddress": "http://mockApi:5012", + "BaseAddress": "http://mock-api:5012", "Headers": { "Accept": "application/json" } }, { "Name": "Payments", - "BaseAddress": "http://mockApi:5012" + "BaseAddress": "http://mock-api:5012", + "Headers": { + "Accept": "application/json" + } } ] } \ No newline at end of file From 4268734316df0a4cad67051cbfea5c8f09161d2e Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 13:36:54 -0300 Subject: [PATCH 011/161] feat: update docker-compose names for load tests and bff --- templates/Bff/docker-compose-load-tests.yml | 2 +- templates/Bff/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml index 93cec545..0eaf5988 100644 --- a/templates/Bff/docker-compose-load-tests.yml +++ b/templates/Bff/docker-compose-load-tests.yml @@ -1,4 +1,4 @@ -name: hexagonal-solution-template-load-tests +name: hexagonal-solution-template-bff-load-tests services: k6: diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index 49bc7f93..e879eb96 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -1,4 +1,4 @@ -name: hexagonal-solution-template +name: hexagonal-solution-template-bff services: aspire-dashboard: From fd6bcda8ccfc8c1fc941962791ea8ae5cbabd856 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 14:24:32 -0300 Subject: [PATCH 012/161] feat: enhance http service with content type and logging --- .../Infrastructure/Http/BaseHttpService.cs | 24 +++- .../src/WebApp/Endpoints/OrderEndpoints.cs | 13 +-- templates/Bff/src/WebApp/appsettings.json | 6 +- .../IntegrationTests/Fixtures/BaseFixture.cs | 10 +- .../tests/IntegrationTests/GlobalUsings.cs | 1 - .../WebApp/Grpc/ApiGrpcHelper.cs | 12 ++ .../WebApp/Grpc/Common/ApiGrpcHelper.cs | 12 -- .../WebApp/Grpc/GetOrderGrpcTest.cs | 70 ++++++++++++ .../WebApp/Grpc/Orders/GetOrderGrpcTest.cs | 70 ------------ .../WebApp/Http/{Common => }/ApiHelper.cs | 7 +- .../Http/{Common => }/BaseHttpFixture.cs | 4 +- .../IntegrationTests/WebApp/Http/OrderTest.cs | 96 ++++++++++++++++ .../WebApp/Http/Orders/CreateOrderTest.cs | 65 ----------- .../WebApp/Http/Orders/GetAllOrdersTest.cs | 107 ------------------ .../WebApp/Http/Orders/GetOrderTest.cs | 68 ----------- 15 files changed, 216 insertions(+), 349 deletions(-) create mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Grpc/ApiGrpcHelper.cs delete mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs create mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Grpc/GetOrderGrpcTest.cs delete mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs rename templates/Bff/tests/IntegrationTests/WebApp/Http/{Common => }/ApiHelper.cs (90%) rename templates/Bff/tests/IntegrationTests/WebApp/Http/{Common => }/BaseHttpFixture.cs (81%) create mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs delete mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs delete mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs delete mode 100644 templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index af39ef3a..38eaba76 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -16,7 +16,8 @@ public class BaseHttpService(HttpClient httpClient, ILogger log HttpMethod method, CancellationToken cancellationToken, TRequest request, - Dictionary? headers = null + Dictionary? headers = null, + string contentType = "application/json" ) where TRequest : class where TResponse : class { _stopwatch.Start(); @@ -29,12 +30,22 @@ public class BaseHttpService(HttpClient httpClient, ILogger log memoryStream.Seek(0, SeekOrigin.Begin); using var requestContent = new StreamContent(memoryStream); + requestContent.Headers.ContentType = new(contentType); requestMessage.Content = requestContent; if (headers != null) foreach (var header in headers) requestMessage.Headers.Add(header.Key, header.Value); - using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, cancellationToken); + + if (!response.IsSuccessStatusCode) + { + _logger.LogWarning( + "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms", + method, requestUri, response.ReasonPhrase, response.StatusCode, _stopwatch.ElapsedMilliseconds + ); + return null; + } var content = await response.Content.ReadAsStreamAsync(cancellationToken); @@ -61,6 +72,15 @@ public class BaseHttpService(HttpClient httpClient, ILogger log requestMessage.Headers.Add(header.Key, header.Value); using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + + if (!response.IsSuccessStatusCode) + { + _logger.LogWarning( + "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms", + method, requestUri, response.ReasonPhrase, response.StatusCode, _stopwatch.ElapsedMilliseconds + ); + return null; + } var content = await response.Content.ReadAsStreamAsync(cancellationToken); diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 181fd01f..4a0e314b 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -46,7 +46,7 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithDescription("Gets an order by its identifier") - .WithName("GetOrderById"); + .WithName("GetById"); ordersGroup.MapPost("/", async ( [FromBody] CreateOrderRequest request, @@ -57,14 +57,9 @@ CancellationToken cancellationToken var response = await httpService.SendAsync>("orders", HttpMethod.Post, cancellationToken, request); if (response == null) - return Results.BadRequest(new { Success = false, Message = "No response from service" }); + return Results.BadRequest(); - if (!response.Success || response.Data == null) - return Results.BadRequest(response); - - var data = response.Data; - - var id = data?.Id.ToString() ?? "unknown"; + var id = response.Data?.Id.ToString() ?? "unknown"; return Results.Created($"/orders/{id}", response); }) @@ -72,7 +67,7 @@ CancellationToken cancellationToken .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithDescription("Creates a new order") - .WithName("CreateOrder"); + .WithName("Create"); return app; } diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index 8bf462a3..c73b50a3 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -9,14 +9,16 @@ "Name": "Orders", "BaseAddress": "http://mock-api:5012", "Headers": { - "Accept": "application/json" + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate, br" } }, { "Name": "Payments", "BaseAddress": "http://mock-api:5012", "Headers": { - "Accept": "application/json" + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate, br" } } ] diff --git a/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs b/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs index 0b8da73e..608a3ab6 100644 --- a/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs +++ b/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs @@ -1,14 +1,6 @@ -using AutoFixture; - -namespace CommonTests.Fixtures; +namespace IntegrationTests.Fixtures; public class BaseFixture { - public Fixture autoFixture = new(); public CancellationToken cancellationToken = CancellationToken.None; - - public BaseFixture() - { - autoFixture.Behaviors.Add(new OmitOnRecursionBehavior()); - } } diff --git a/templates/Bff/tests/IntegrationTests/GlobalUsings.cs b/templates/Bff/tests/IntegrationTests/GlobalUsings.cs index dc5775ea..9df1d421 100644 --- a/templates/Bff/tests/IntegrationTests/GlobalUsings.cs +++ b/templates/Bff/tests/IntegrationTests/GlobalUsings.cs @@ -1,2 +1 @@ global using Xunit; -global using AutoFixture; diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Grpc/ApiGrpcHelper.cs b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/ApiGrpcHelper.cs new file mode 100644 index 00000000..2d284095 --- /dev/null +++ b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/ApiGrpcHelper.cs @@ -0,0 +1,12 @@ +// using Grpc.Net.Client; + +// namespace IntegrationTests.WebApp.Grpc; +// public sealed class ApiGrpcHelper(HttpClient httpClient) +// { +// public HttpClient httpClient = httpClient; + +// public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(httpClient.BaseAddress!, new GrpcChannelOptions +// { +// HttpClient = httpClient +// }); +// } diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs deleted file mode 100644 index b9c4f292..00000000 --- a/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Grpc.Net.Client; - -namespace IntegrationTests.WebApp.Grpc.Common; -public sealed class ApiGrpcHelper(HttpClient httpClient) -{ - public HttpClient httpClient = httpClient; - - public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(httpClient.BaseAddress!, new GrpcChannelOptions - { - HttpClient = httpClient - }); -} diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Grpc/GetOrderGrpcTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/GetOrderGrpcTest.cs new file mode 100644 index 00000000..84c76b37 --- /dev/null +++ b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/GetOrderGrpcTest.cs @@ -0,0 +1,70 @@ +// using Grpc.Net.Client; +// using GrpcOrder; +// using IntegrationTests.Common; +// using IntegrationTests.Fixtures; +// using IntegrationTests.WebApp.Grpc.Common; +// using WebApp; + +// namespace IntegrationTests.WebApp.Grpc.Orders; + +// [Collection("WebApplicationFactoryCollectionDefinition")] +// public class GetOrderGrpcTest : BaseFixture +// { +// public CustomWebApplicationFactory customWebApplicationFactory; + +// public ApiGrpcHelper apiGrpcHelper; +// private readonly GrpcChannel _grpcChannel; +// private readonly OrderService.OrderServiceClient _service; + +// public GetOrderGrpcTest(CustomWebApplicationFactory customWebApplicationFactory) +// { +// this.customWebApplicationFactory = customWebApplicationFactory; +// apiGrpcHelper = new(this.customWebApplicationFactory.CreateClient()); +// _grpcChannel = apiGrpcHelper.AsGrpcClientChannel(); +// _service = new(_grpcChannel); +// } + +// [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] +// public async Task Given_A_Valid_Request_Then_Pass() +// { +// // Arrange +// GetOrderRequest request = new() +// { +// CorrelationId = Guid.NewGuid().ToString(), +// Id = 1 +// }; + +// // Act +// var response = await _service.GetAsync(request); + +// // Assert +// Assert.NotNull(response); +// Assert.True(response.Success); +// Assert.True(string.IsNullOrEmpty(response.Message)); +// Assert.NotNull(response.Data); +// Assert.Equal(1, response.Data.Id); +// Assert.NotNull(response.Data.Items); +// Assert.NotEmpty(response.Data.Items); +// Assert.Equal(1000.0, response.Data.Total); +// } + +// [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] +// public async Task Given_A_Invalid_Request_Then_Fails() +// { +// // Arrange +// GetOrderRequest request = new() +// { +// CorrelationId = Guid.NewGuid().ToString(), +// Id = 999 +// }; + +// // Act +// var response = await _service.GetAsync(request); + +// // Assert +// Assert.NotNull(response); +// Assert.False(response.Success); +// Assert.False(string.IsNullOrEmpty(response.Message)); +// Assert.Null(response.Data); +// } +// } diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs deleted file mode 100644 index f4ea7772..00000000 --- a/templates/Bff/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using CommonTests.Fixtures; -using Grpc.Net.Client; -using GrpcOrder; -using IntegrationTests.Common; -using IntegrationTests.WebApp.Grpc.Common; -using WebApp; - -namespace IntegrationTests.WebApp.Grpc.Orders; - -[Collection("WebApplicationFactoryCollectionDefinition")] -public class GetOrderGrpcTest : BaseFixture -{ - public CustomWebApplicationFactory customWebApplicationFactory; - - public ApiGrpcHelper apiGrpcHelper; - private readonly GrpcChannel _grpcChannel; - private readonly OrderService.OrderServiceClient _service; - - public GetOrderGrpcTest(CustomWebApplicationFactory customWebApplicationFactory) - { - this.customWebApplicationFactory = customWebApplicationFactory; - apiGrpcHelper = new(this.customWebApplicationFactory.CreateClient()); - _grpcChannel = apiGrpcHelper.AsGrpcClientChannel(); - _service = new(_grpcChannel); - } - - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() - { - // Arrange - GetOrderRequest request = new() - { - CorrelationId = Guid.NewGuid().ToString(), - Id = 1 - }; - - // Act - var response = await _service.GetAsync(request); - - // Assert - Assert.NotNull(response); - Assert.True(response.Success); - Assert.True(string.IsNullOrEmpty(response.Message)); - Assert.NotNull(response.Data); - Assert.Equal(1, response.Data.Id); - Assert.NotNull(response.Data.Items); - Assert.NotEmpty(response.Data.Items); - Assert.Equal(1000.0, response.Data.Total); - } - - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() - { - // Arrange - GetOrderRequest request = new() - { - CorrelationId = Guid.NewGuid().ToString(), - Id = 999 - }; - - // Act - var response = await _service.GetAsync(request); - - // Assert - Assert.NotNull(response); - Assert.False(response.Success); - Assert.False(string.IsNullOrEmpty(response.Message)); - Assert.Null(response.Data); - } -} diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/ApiHelper.cs similarity index 90% rename from templates/Bff/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Http/ApiHelper.cs index d2962430..367fe5f2 100644 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs +++ b/templates/Bff/tests/IntegrationTests/WebApp/Http/ApiHelper.cs @@ -2,7 +2,7 @@ using System.Text.Json; using Grpc.Net.Client; -namespace IntegrationTests.WebApp.Http.Common; +namespace IntegrationTests.WebApp.Http; public sealed class ApiHelper(HttpClient httpClient) { public HttpClient httpClient = httpClient; @@ -43,8 +43,11 @@ public StringContent SerializeRequest(dynamic data) return new StringContent(json, Encoding.UTF8, "application/json"); } - public async Task DeSerializeResponse(HttpResponseMessage response) + public static async Task DeSerializeResponse(HttpResponseMessage response) { + if (response.Content == null) + return default; + var content = await response.Content.ReadAsStreamAsync(); return JsonSerializer.Deserialize(content, _jsonSerializerOptions); diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/BaseHttpFixture.cs similarity index 81% rename from templates/Bff/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs rename to templates/Bff/tests/IntegrationTests/WebApp/Http/BaseHttpFixture.cs index 64636bb8..cb793f25 100644 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs +++ b/templates/Bff/tests/IntegrationTests/WebApp/Http/BaseHttpFixture.cs @@ -1,8 +1,8 @@ -using CommonTests.Fixtures; using IntegrationTests.Common; +using IntegrationTests.Fixtures; using WebApp; -namespace IntegrationTests.WebApp.Http.Common; +namespace IntegrationTests.WebApp.Http; public class BaseHttpFixture : BaseFixture { diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs new file mode 100644 index 00000000..a8e7bb81 --- /dev/null +++ b/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs @@ -0,0 +1,96 @@ +using System.Net; +using Contracts.Common; +using Contracts.Orders; +using IntegrationTests.Common; +using WebApp; + +namespace IntegrationTests.WebApp.Http; + +[Collection("WebApplicationFactoryCollectionDefinition")] +public sealed class OrderTest : IClassFixture +{ + private readonly BaseHttpFixture _fixture; + public OrderTest(CustomWebApplicationFactory customWebApplicationFactory, BaseHttpFixture fixture) + { + _fixture = fixture; + _fixture.SetApiHelper(customWebApplicationFactory); + _fixture.resourceUrl = "orders/{0}"; + } + + [Fact(DisplayName = nameof(Given_A_Get_By_Id_Valid_Request_Then_Pass))] + public async Task Given_A_Get_By_Id_Valid_Request_Then_Pass() + { + // Arrange + var id = 1; + var url = string.Format(_fixture.resourceUrl, id); + _fixture.apiHelper.AddHeaders(new Dictionary + { + { "CorrelationId", Guid.NewGuid().ToString() }, + { "CacheEnabled", "false" } + }); + + // Act + var result = await _fixture.apiHelper.GetAsync(url); + var response = await ApiHelper.DeSerializeResponse>(result); + var data = response?.Data; + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.OK, result.StatusCode); + Assert.True(response!.Success); + Assert.NotNull(data); + Assert.NotNull(data.Items); + Assert.NotEmpty(data.Items); + } + + [Fact(DisplayName = nameof(Given_A_Get_By_Id_Invalid_Request_Then_Fails))] + public async Task Given_A_Get_By_Id_Invalid_Request_Then_Fails() + { + // Arrange + var id = 9999999; + var url = string.Format(_fixture.resourceUrl, id); + _fixture.apiHelper.AddHeaders(new Dictionary + { + { "CorrelationId", Guid.NewGuid().ToString() }, + { "CacheEnabled", "false" } + }); + + // Act + var result = await _fixture.apiHelper.GetAsync(url); + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.NotFound, result.StatusCode); + Assert.False(result.IsSuccessStatusCode); + } + + [Fact(DisplayName = nameof(Given_A_Valid_Create_Request_Then_Pass))] + public async Task Given_A_Valid_Create_Request_Then_Pass() + { + // Arrange + var url = string.Format(_fixture.resourceUrl, string.Empty); + CreateOrderRequest request = new(Guid.NewGuid(), "Test Order", + [ + new("Item 1", "Description 1", 500.0m), + new("Item 2", "Description 2", 500.0m) + ]); + + _fixture.apiHelper.AddHeaders(new Dictionary + { + { "CorrelationId", Guid.NewGuid().ToString() } + }); + + // Act + var result = await _fixture.apiHelper.PostAsync(url, request); + var response = await ApiHelper.DeSerializeResponse>(result); + var data = response?.Data; + + // Assert + Assert.NotNull(result); + Assert.Equal(HttpStatusCode.Created, result.StatusCode); + Assert.True(response!.Success); + Assert.NotNull(data); + Assert.NotNull(data.Items); + Assert.NotEmpty(data.Items); + } +} diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs deleted file mode 100644 index cf9af870..00000000 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Net; -using Application.Common.Requests; -using Application.Orders; -using IntegrationTests.Common; -using IntegrationTests.WebApp.Http.Common; -using WebApp; - -namespace IntegrationTests.WebApp.Http.Orders; - -public class CreateOrderTestFixture : BaseHttpFixture -{ - public CreateOrderRequest SetValidRequest() => autoFixture.Create(); - - public CreateOrderRequest SetInvalidRequest() => autoFixture - .Build() - .With(r => r.Description, string.Empty) - .Create(); -} - -[Collection("WebApplicationFactoryCollectionDefinition")] -public sealed class CreateOrderTest : IClassFixture -{ - private readonly CreateOrderTestFixture _fixture; - - public CreateOrderTest(CustomWebApplicationFactory customWebApplicationFactory, CreateOrderTestFixture fixture) - { - _fixture = fixture; - _fixture.SetApiHelper(customWebApplicationFactory); - _fixture.resourceUrl = "orders"; - } - - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() - { - // Arrange - var request = _fixture.SetValidRequest(); - - // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - - // Assert - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.Created, result.StatusCode); - Assert.True(response!.Success); - Assert.NotNull(response.Data); - } - - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() - { - // Arrange - var request = _fixture.SetInvalidRequest(); - - // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - // Assert - Assert.NotNull(response); - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); - Assert.False(response.Success); - Assert.Null(response.Data); - } -} diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs deleted file mode 100644 index 42d98d43..00000000 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System.Net; -using Application.Common.Requests; -using Application.Orders; -using IntegrationTests.Common; -using IntegrationTests.WebApp.Http.Common; -using WebApp; - -namespace IntegrationTests.WebApp.Http.Orders; - -public class GetAllOrdersTestFixture : BaseHttpFixture -{ - public BasePaginatedRequest SetValidRequest() => - new(Guid.NewGuid(), 1, 10); - - public BasePaginatedRequest SetInvalidPageRequest() => - new(Guid.NewGuid(), 0, 10); - - public BasePaginatedRequest SetInvalidPageSizeRequest() => - new(Guid.NewGuid(), 1, 0); -} - -[Collection("WebApplicationFactoryCollectionDefinition")] -public class GetAllOrdersTest : IClassFixture -{ - private readonly GetAllOrdersTestFixture _fixture; - public GetAllOrdersTest(CustomWebApplicationFactory customWebApplicationFactory, GetAllOrdersTestFixture fixture) - { - _fixture = fixture; - _fixture.SetApiHelper(customWebApplicationFactory); - _fixture.resourceUrl = "orders/paginated"; - } - - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() - { - // Arrange - var request = _fixture.SetValidRequest(); - - // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - - // Assert - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - Assert.True(response!.Success); - Assert.NotNull(response.Data); - Assert.True(response.TotalPages >= 0); - Assert.True(response.TotalRecords >= 0); - } - - [Fact(DisplayName = nameof(Given_An_Invalid_Page_Request_Then_Fails))] - public async Task Given_An_Invalid_Page_Request_Then_Fails() - { - // Arrange - var request = _fixture.SetInvalidPageRequest(); - - // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - - // Assert - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); - Assert.False(response!.Success); - Assert.Contains("Page must be greater than 0", response.Message); - } - - [Fact(DisplayName = nameof(Given_An_Invalid_PageSize_Request_Then_Fails))] - public async Task Given_An_Invalid_PageSize_Request_Then_Fails() - { - // Arrange - var request = _fixture.SetInvalidPageSizeRequest(); - - // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - - // Assert - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); - Assert.False(response!.Success); - Assert.Contains("PageSize must be greater than 0", response.Message); - } - - [Fact(DisplayName = nameof(Given_An_Valid_Request_When_Pass_Search_By_Values_Filter_Then_Pass))] - public async Task Given_An_Valid_Request_When_Pass_Search_By_Values_Filter_Then_Pass() - { - // Arrange - var request = new BasePaginatedRequest( - Guid.NewGuid(), 1, 10, - SearchByValues: new Dictionary { { "Description", "client" } } - ); - - // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - - // Assert - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - Assert.True(response!.Success); - Assert.NotNull(response.Data); - Assert.True(response.TotalPages >= 0); - Assert.True(response.TotalRecords >= 0); - } -} diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs deleted file mode 100644 index cde446ae..00000000 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Net; -using Application.Common.Requests; -using Application.Orders; -using IntegrationTests.Common; -using IntegrationTests.WebApp.Http.Common; -using WebApp; - -namespace IntegrationTests.WebApp.Http.Orders; - -[Collection("WebApplicationFactoryCollectionDefinition")] -public class GetOrderTest : IClassFixture -{ - private readonly BaseHttpFixture _fixture; - public GetOrderTest(CustomWebApplicationFactory customWebApplicationFactory, BaseHttpFixture fixture) - { - _fixture = fixture; - _fixture.SetApiHelper(customWebApplicationFactory); - _fixture.resourceUrl = "orders/{0}"; - } - - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() - { - // Arrange - var id = 1; - var url = string.Format(_fixture.resourceUrl, id); - _fixture.apiHelper.AddHeaders(new Dictionary - { - { "CorrelationId", Guid.NewGuid().ToString() } - }); - - // Act - var result = await _fixture.apiHelper.GetAsync(url); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - var data = response?.Data; - - // Assert - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.OK, result.StatusCode); - Assert.True(response!.Success); - Assert.NotNull(data); - Assert.Equal(id, data.Id); - Assert.NotNull(data.Items); - Assert.NotEmpty(data.Items); - } - - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() - { - // Arrange - var id = 9999999; - var url = string.Format(_fixture.resourceUrl, id); - _fixture.apiHelper.AddHeaders(new Dictionary - { - { "CorrelationId", Guid.NewGuid().ToString() } - }); - - // Act - var result = await _fixture.apiHelper.GetAsync(url); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); - - // Assert - Assert.NotNull(result); - Assert.Equal(HttpStatusCode.NotFound, result.StatusCode); - Assert.False(response!.Success); - Assert.Null(response.Data); - } -} From a90b70ccc47d3474ecf177562f32e5432fcffb39 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 14:27:35 -0300 Subject: [PATCH 013/161] CMGO-53 feat: add headers for Orders and Payments in appsettings --- templates/Bff/src/WebApp/appsettings.Development.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index 7b1f5003..984777fd 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -9,12 +9,17 @@ "Name": "Orders", "BaseAddress": "http://localhost:5012", "Headers": { - "Accept": "application/json" + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate, br" } }, { "Name": "Payments", - "BaseAddress": "http://localhost:5012" + "BaseAddress": "http://localhost:5012", + "Headers": { + "Accept": "application/json", + "Accept-Encoding": "gzip, deflate, br" + } } ] } \ No newline at end of file From dec808cd959b10b034274bd52be4c382e5ae33f1 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 14:40:55 -0300 Subject: [PATCH 014/161] feat: simplify response compression and update order tests --- templates/Bff/src/MockApi/Program.cs | 7 +------ templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs | 2 +- templates/Bff/src/WebApp/Program.cs | 8 +------- .../Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs | 7 ------- 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/templates/Bff/src/MockApi/Program.cs b/templates/Bff/src/MockApi/Program.cs index 2e563336..3319f47a 100644 --- a/templates/Bff/src/MockApi/Program.cs +++ b/templates/Bff/src/MockApi/Program.cs @@ -4,13 +4,8 @@ var builder = WebApplication.CreateBuilder(args); -builder.Services.AddEndpointsApiExplorer(); builder.Services.AddGrpc(); - -builder.Services.AddResponseCompression(options => -{ - options.EnableForHttps = true; -}); +builder.Services.AddResponseCompression(); builder.WebHost.ConfigureKestrel(options => options.ConfigureEndpointDefaults(listenOptions => diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 4a0e314b..8136ae5c 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -39,7 +39,7 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) }), }; - return response != null && response.Success ? Results.Ok(response) : Results.NotFound(response); + return response != null ? Results.Ok(response) : Results.NotFound(response); }) .Produces>(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound) diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs index c4b661df..d183e707 100644 --- a/templates/Bff/src/WebApp/Program.cs +++ b/templates/Bff/src/WebApp/Program.cs @@ -22,14 +22,8 @@ private static async Task Main(string[] args) builder.Services.AddEndpointsApiExplorer(); builder.Services.AddGrpc(); builder.Services.AddOpenApi(); - builder.Services.AddCustomHealthChecks(builder.Configuration); - - builder.Services.AddResponseCompression(options => - { - options.EnableForHttps = true; - }); - + builder.Services.AddResponseCompression(); builder.Services.Configure(options => { options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs index a8e7bb81..c1894497 100644 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs +++ b/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs @@ -37,7 +37,6 @@ public async Task Given_A_Get_By_Id_Valid_Request_Then_Pass() // Assert Assert.NotNull(result); Assert.Equal(HttpStatusCode.OK, result.StatusCode); - Assert.True(response!.Success); Assert.NotNull(data); Assert.NotNull(data.Items); Assert.NotEmpty(data.Items); @@ -75,11 +74,6 @@ public async Task Given_A_Valid_Create_Request_Then_Pass() new("Item 2", "Description 2", 500.0m) ]); - _fixture.apiHelper.AddHeaders(new Dictionary - { - { "CorrelationId", Guid.NewGuid().ToString() } - }); - // Act var result = await _fixture.apiHelper.PostAsync(url, request); var response = await ApiHelper.DeSerializeResponse>(result); @@ -88,7 +82,6 @@ public async Task Given_A_Valid_Create_Request_Then_Pass() // Assert Assert.NotNull(result); Assert.Equal(HttpStatusCode.Created, result.StatusCode); - Assert.True(response!.Success); Assert.NotNull(data); Assert.NotNull(data.Items); Assert.NotEmpty(data.Items); From f638a4f923229ef7dde53d227703c6df920a6a0c Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 14:42:20 -0300 Subject: [PATCH 015/161] feat: simplify response compression configuration in Program.cs --- templates/Full/Directory.Build.props | 7 +------ templates/Full/src/WebApp/Program.cs | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/templates/Full/Directory.Build.props b/templates/Full/Directory.Build.props index 1a826ab3..a31e77ed 100644 --- a/templates/Full/Directory.Build.props +++ b/templates/Full/Directory.Build.props @@ -7,12 +7,7 @@ true false - + - - - - Never - \ No newline at end of file diff --git a/templates/Full/src/WebApp/Program.cs b/templates/Full/src/WebApp/Program.cs index 0ac11494..cf3cc085 100644 --- a/templates/Full/src/WebApp/Program.cs +++ b/templates/Full/src/WebApp/Program.cs @@ -22,13 +22,8 @@ private static async Task Main(string[] args) builder.Services.AddEndpointsApiExplorer(); builder.Services.AddGrpc(); - builder.Services.AddCustomHealthChecks(builder.Configuration); - - builder.Services.AddResponseCompression(options => - { - options.EnableForHttps = true; - }); + builder.Services.AddResponseCompression(); builder.Services.Configure(options => { From fdda1b783f570bbdb904bf41ff9f75cabae4f5da Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 10 Jan 2026 15:01:00 -0300 Subject: [PATCH 016/161] feat(53): add Microsoft.Extensions.Http.Polly package and configuration --- templates/Bff/Directory.Packages.props | 1 + .../src/Infrastructure/Infrastructure.csproj | 1 + .../InfrastructureDependencyInjection.cs | 28 ++++-- .../Bff/src/Infrastructure/packages.lock.json | 91 ++++++++++++++----- templates/Bff/src/WebApp/packages.lock.json | 24 +++++ .../tests/IntegrationTests/packages.lock.json | 41 +++++++-- 6 files changed, 146 insertions(+), 40 deletions(-) diff --git a/templates/Bff/Directory.Packages.props b/templates/Bff/Directory.Packages.props index b8fe2916..f6b5350f 100644 --- a/templates/Bff/Directory.Packages.props +++ b/templates/Bff/Directory.Packages.props @@ -18,6 +18,7 @@ + diff --git a/templates/Bff/src/Infrastructure/Infrastructure.csproj b/templates/Bff/src/Infrastructure/Infrastructure.csproj index 6e7d97bb..0c798058 100644 --- a/templates/Bff/src/Infrastructure/Infrastructure.csproj +++ b/templates/Bff/src/Infrastructure/Infrastructure.csproj @@ -2,6 +2,7 @@ + diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 53a01e3e..320058ab 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -9,7 +9,8 @@ using Microsoft.Extensions.Configuration; using Infrastructure.Cache; using Infrastructure.Http; -using Infrastructure.Common; +using Polly; +using Polly.Extensions.Http; namespace Infrastructure; @@ -126,8 +127,6 @@ internal IServiceCollection AddCache(IConfiguration configuration) internal IServiceCollection AddHttp(IConfiguration configuration) { - services.AddHttpClient(); - var httpConfigurations = configuration.GetSection("Http").Get>() ?? throw new NullReferenceException("Http services configuration is not configured."); @@ -137,22 +136,27 @@ internal IServiceCollection AddHttp(IConfiguration configuration) { var serviceName = serviceKey.ToString(); - var serviceConfiguration = httpConfigurations.FirstOrDefault(x => - string.Equals(x.Name, serviceName, StringComparison.OrdinalIgnoreCase)) + var serviceConfiguration = httpConfigurations.FirstOrDefault(x => + string.Equals(x.Name, serviceName, StringComparison.OrdinalIgnoreCase)) ?? throw new NullReferenceException($"{serviceName} service configuration is not configured."); - services.AddKeyedScoped(serviceKey, (serviceProvider, _) => + services.AddHttpClient(serviceName, client => { - var httpClientFactory = serviceProvider.GetRequiredService(); - var logger = serviceProvider.GetRequiredService>(); - var client = httpClientFactory.CreateClient(serviceKey.ToString()); - client.BaseAddress = new Uri(serviceConfiguration.BaseAddress) ?? throw new NullReferenceException($"{serviceName} service address is not configured."); if (serviceConfiguration.Headers is Dictionary headers && headers.Count > 0) foreach (var header in headers) client.DefaultRequestHeaders.Add(header.Key, header.Value); + }) + .SetHandlerLifetime(TimeSpan.FromMinutes(5)) + .AddPolicyHandler(GetRetryPolicy()); + + services.AddKeyedScoped(serviceKey, (serviceProvider, _) => + { + var httpClientFactory = serviceProvider.GetRequiredService(); + var logger = serviceProvider.GetRequiredService>(); + var client = httpClientFactory.CreateClient(serviceName); return new(client, logger); }); @@ -160,5 +164,9 @@ internal IServiceCollection AddHttp(IConfiguration configuration) return services; } + + internal static IAsyncPolicy GetRetryPolicy() => HttpPolicyExtensions + .HandleTransientHttpError() + .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); } } diff --git a/templates/Bff/src/Infrastructure/packages.lock.json b/templates/Bff/src/Infrastructure/packages.lock.json index a11b6fb6..3664a4ea 100644 --- a/templates/Bff/src/Infrastructure/packages.lock.json +++ b/templates/Bff/src/Infrastructure/packages.lock.json @@ -26,6 +26,17 @@ "StackExchange.Redis": "2.7.27" } }, + "Microsoft.Extensions.Http.Polly": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "J7xjjT+r6qxDcID37DJE6n45sReV43hOE/d87AnNJX5UPTv2qxKWGAyoKG3cjO1p0UCPhOVf6zlPIriDyJmNrQ==", + "dependencies": { + "Microsoft.Extensions.Http": "10.0.1", + "Polly": "7.2.4", + "Polly.Extensions.Http": "3.0.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "Direct", "requested": "[1.14.0, )", @@ -118,28 +129,28 @@ }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "H4SWETCh/cC5L1WtWchHR6LntGk3rDTTznZMssr4cL8IbDmMWBxY+MOGDc/ASnqNolLKPIWHWeuC1ddiL/iNPw==", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", - "Microsoft.Extensions.Primitives": "10.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "d2kDKnCsJvY7mBVhcjPSp9BkJk48DsaHPg5u+Oy4f8XaOqnEedRy/USyvnpHL92wpJ6DrTPy7htppUUzskbCXQ==", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.0" + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "tMF9wNh+hlyYDWB8mrFCQHQmWHlRosol1b/N2Jrefy1bFLnuTlgSYmPyHNmz8xVQgs7DpXytBRWxGhG+mSTp0g==", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.0" + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { @@ -147,13 +158,23 @@ "resolved": "10.0.1", "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + } + }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "SfK89ytD61S7DgzorFljSkUeluC1ncn6dtZgwc0ot39f/BEYWBl5jpgvodxduoYAs1d9HG8faCDRZxE95UMo2A==", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", - "Microsoft.Extensions.Options": "10.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.FileProviders.Abstractions": { @@ -176,6 +197,19 @@ "Microsoft.Extensions.Logging.Abstractions": "10.0.0" } }, + "Microsoft.Extensions.Http": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "ZXJup9ReE1Ot3M8jqcw1b/lnc8USxyYS3cyLsssU39u04TES9JNGviWUGIvP3K7mMU3TF7kQl2aS0SmVwegflw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", "resolved": "10.0.1", @@ -210,14 +244,14 @@ }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "tL9cSl3maS5FPzp/3MtlZI21ExWhni0nnUCF8HY4npTsINw45n9SNDbkKXBMtFyUFGSsQep25fHIDN4f/Vp3AQ==", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", - "Microsoft.Extensions.Configuration.Binder": "10.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", - "Microsoft.Extensions.Options": "10.0.0", - "Microsoft.Extensions.Primitives": "10.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" } }, "Microsoft.Extensions.Primitives": { @@ -254,6 +288,19 @@ "resolved": "2.2.8", "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.4", + "contentHash": "bw00Ck5sh6ekduDE3mnCo1ohzuad946uslCDEENu3091+6UKnBuKLo4e+yaNcCzXxOZCXWY2gV4a35+K1d4LDA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, "StackExchange.Redis": { "type": "Transitive", "resolved": "2.7.27", diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json index 5336db6e..b8da38a7 100644 --- a/templates/Bff/src/WebApp/packages.lock.json +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -138,6 +138,19 @@ "resolved": "2.2.8", "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.4", + "contentHash": "bw00Ck5sh6ekduDE3mnCo1ohzuad946uslCDEENu3091+6UKnBuKLo4e+yaNcCzXxOZCXWY2gV4a35+K1d4LDA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, "StackExchange.Redis": { "type": "Transitive", "resolved": "2.7.27", @@ -155,6 +168,7 @@ "Contracts": "[1.0.0, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "Microsoft.Extensions.Http.Polly": "[10.0.1, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", @@ -179,6 +193,16 @@ "StackExchange.Redis": "2.7.27" } }, + "Microsoft.Extensions.Http.Polly": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "J7xjjT+r6qxDcID37DJE6n45sReV43hOE/d87AnNJX5UPTv2qxKWGAyoKG3cjO1p0UCPhOVf6zlPIriDyJmNrQ==", + "dependencies": { + "Polly": "7.2.4", + "Polly.Extensions.Http": "3.0.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "CentralTransitive", "requested": "[1.14.0, )", diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json index 1bf950e9..c2bdf58c 100644 --- a/templates/Bff/tests/IntegrationTests/packages.lock.json +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -332,15 +332,15 @@ }, "Microsoft.Extensions.Http": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", + "resolved": "10.0.1", + "contentHash": "ZXJup9ReE1Ot3M8jqcw1b/lnc8USxyYS3cyLsssU39u04TES9JNGviWUGIvP3K7mMU3TF7kQl2aS0SmVwegflw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "Microsoft.Extensions.Diagnostics": "8.0.0", - "Microsoft.Extensions.Logging": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "Microsoft.Extensions.Options": "8.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" } }, "Microsoft.Extensions.Logging.Abstractions": { @@ -486,6 +486,19 @@ "resolved": "2.2.8", "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==" }, + "Polly": { + "type": "Transitive", + "resolved": "7.2.4", + "contentHash": "bw00Ck5sh6ekduDE3mnCo1ohzuad946uslCDEENu3091+6UKnBuKLo4e+yaNcCzXxOZCXWY2gV4a35+K1d4LDA==" + }, + "Polly.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==", + "dependencies": { + "Polly": "7.1.0" + } + }, "StackExchange.Redis": { "type": "Transitive", "resolved": "2.7.27", @@ -541,6 +554,7 @@ "Contracts": "[1.0.0, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "Microsoft.Extensions.Http.Polly": "[10.0.1, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", @@ -633,6 +647,17 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" } }, + "Microsoft.Extensions.Http.Polly": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "J7xjjT+r6qxDcID37DJE6n45sReV43hOE/d87AnNJX5UPTv2qxKWGAyoKG3cjO1p0UCPhOVf6zlPIriDyJmNrQ==", + "dependencies": { + "Microsoft.Extensions.Http": "10.0.1", + "Polly": "7.2.4", + "Polly.Extensions.Http": "3.0.0" + } + }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", "requested": "[10.0.1, )", From 17528fb50294c03cf4bd5b757e999759f4929035 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 14 Jan 2026 15:38:41 -0300 Subject: [PATCH 017/161] feat: enhance request and response DTOs with detailed summaries --- .../Bff/src/Contracts/Common/BaseRequest.cs | 14 +++++ .../Bff/src/Contracts/Common/BaseResponse.cs | 56 +++++++++++++++++++ templates/Bff/src/Contracts/Contracts.csproj | 3 + .../Contracts/Orders/CreateOrderRequest.cs | 19 ++++++- .../Bff/src/Contracts/Orders/OrderDto.cs | 37 ++++++++++++ 5 files changed, 128 insertions(+), 1 deletion(-) diff --git a/templates/Bff/src/Contracts/Common/BaseRequest.cs b/templates/Bff/src/Contracts/Common/BaseRequest.cs index c8af47c7..80440f48 100644 --- a/templates/Bff/src/Contracts/Common/BaseRequest.cs +++ b/templates/Bff/src/Contracts/Common/BaseRequest.cs @@ -1,6 +1,20 @@ namespace Contracts.Common; + +/// +/// Base request structure +/// +/// The unique identifier for correlating requests public record BaseRequest(Guid CorrelationId); +/// +/// Base paginated request structure +/// +/// The unique identifier for correlating requests +/// The page number to retrieve +/// The number of items per page +/// The field to sort by +/// Indicates whether the sorting is in descending order +/// A dictionary of search criteria public record BasePaginatedRequest( Guid CorrelationId, int Page = 1, diff --git a/templates/Bff/src/Contracts/Common/BaseResponse.cs b/templates/Bff/src/Contracts/Common/BaseResponse.cs index 68377696..440f33c6 100644 --- a/templates/Bff/src/Contracts/Common/BaseResponse.cs +++ b/templates/Bff/src/Contracts/Common/BaseResponse.cs @@ -1,33 +1,82 @@ namespace Contracts.Common; +/// +/// Base response structure +/// public record BaseResponse { + /// + /// Base response structure + /// public BaseResponse() { } + /// + /// Base response structure + /// + /// Indicates whether the response is successful + /// Optional message providing additional information public BaseResponse(bool success, string? message = null) { Success = success; Message = message; } + /// + /// Indicates whether the response is successful + /// public bool Success { get; set; } + + /// + /// Optional message providing additional information + /// public string? Message { get; set; } } +/// +/// Generic base response structure with data +/// +/// The type of the data included in the response public record BaseResponse : BaseResponse where TData : class { + /// + /// Generic base response structure with data + /// public BaseResponse() { } + /// + /// Generic base response structure with data + /// + /// Indicates whether the response is successful + /// The data included in the response + /// Optional message providing additional information public BaseResponse(bool success, TData? data = null, string? message = null) : base(success, message) => Data = data; + /// + /// The data included in the response + /// public TData? Data { get; set; } } +/// +/// Generic base paginated response structure with data +/// +/// public sealed record BasePaginatedResponse : BaseResponse> { + /// + /// Generic base paginated response structure with data + /// public BasePaginatedResponse() { } + /// + /// Generic base paginated response structure with data + /// + /// Indicates whether the response is successful + /// The total number of pages available + /// The total number of records available + /// The data included in the response + /// Optional message providing additional information public BasePaginatedResponse( bool success, int totalPages, int totalRecords, IEnumerable? data = null, string? message = null @@ -37,7 +86,14 @@ public BasePaginatedResponse( TotalRecords = totalRecords; } + /// + /// The total number of pages available + /// public int TotalPages { get; set; } + + /// + /// The total number of records available + /// public int TotalRecords { get; set; } } diff --git a/templates/Bff/src/Contracts/Contracts.csproj b/templates/Bff/src/Contracts/Contracts.csproj index 57e84256..7daf16e2 100644 --- a/templates/Bff/src/Contracts/Contracts.csproj +++ b/templates/Bff/src/Contracts/Contracts.csproj @@ -1,4 +1,7 @@ + + true + diff --git a/templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs b/templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs index 72a22705..3a67a0da 100644 --- a/templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs +++ b/templates/Bff/src/Contracts/Orders/CreateOrderRequest.cs @@ -2,5 +2,22 @@ namespace Contracts.Orders; -public sealed record CreateOrderRequest(Guid CorrelationId, string Description, CreateOrderItemRequest[] Items) : BaseRequest(CorrelationId); +/// +/// Request to create a new order +/// +/// The unique identifier for the request +/// A description of the order +/// The items included in the order +public sealed record CreateOrderRequest( + Guid CorrelationId, + string Description, + CreateOrderItemRequest[] Items +) : BaseRequest(CorrelationId); + +/// +/// An item included in the order +/// +/// The name of the item +/// A description of the item +/// The value of the item public sealed record CreateOrderItemRequest(string Name, string Description, decimal Value); \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Orders/OrderDto.cs b/templates/Bff/src/Contracts/Orders/OrderDto.cs index e66660b2..f6593638 100644 --- a/templates/Bff/src/Contracts/Orders/OrderDto.cs +++ b/templates/Bff/src/Contracts/Orders/OrderDto.cs @@ -1,16 +1,53 @@ namespace Contracts.Orders; + +/// +/// Data transfer object representing an order +/// public sealed record OrderDto { + /// + /// The unique identifier of the order + /// public int Id { get; set; } + + /// + /// A description of the order + /// public string Description { get; set; } + + /// + /// The total value of the order + /// public decimal Total { get; set; } + + /// + /// The items included in the order + /// public IReadOnlyCollection? Items { get; set; } }; +/// +/// Data transfer object representing an item in an order +/// public sealed record ItemDto { + /// + /// The unique identifier of the item + /// public int Id { get; set; } + + /// + /// The name of the item + /// public string Name { get; set; } + + /// + /// A description of the item + /// public string Description { get; set; } + + /// + /// The value of the item + /// public decimal Value { get; set; } }; From f683c3a8058fc18f5d5989707d6dcdb89908a3bf Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 14 Jan 2026 15:40:54 -0300 Subject: [PATCH 018/161] docs: add example values to Total and Value properties --- templates/Bff/src/Contracts/Orders/OrderDto.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/Bff/src/Contracts/Orders/OrderDto.cs b/templates/Bff/src/Contracts/Orders/OrderDto.cs index f6593638..591c252e 100644 --- a/templates/Bff/src/Contracts/Orders/OrderDto.cs +++ b/templates/Bff/src/Contracts/Orders/OrderDto.cs @@ -18,6 +18,7 @@ public sealed record OrderDto /// /// The total value of the order /// + /// 99.99 public decimal Total { get; set; } /// @@ -49,5 +50,6 @@ public sealed record ItemDto /// /// The value of the item /// + /// 99.99 public decimal Value { get; set; } }; From 093fb6c3bd75d4eb806f405168974a2434ed1371 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 20 Jan 2026 18:22:34 -0300 Subject: [PATCH 019/161] feat(53): update order.proto with improved message structure and fields --- templates/Bff/.codacy/codacy.yaml | 15 ------------ .../Bff/src/Contracts/Protos/order.proto | 23 +++++++++++++++++-- 2 files changed, 21 insertions(+), 17 deletions(-) delete mode 100644 templates/Bff/.codacy/codacy.yaml diff --git a/templates/Bff/.codacy/codacy.yaml b/templates/Bff/.codacy/codacy.yaml deleted file mode 100644 index 15365c77..00000000 --- a/templates/Bff/.codacy/codacy.yaml +++ /dev/null @@ -1,15 +0,0 @@ -runtimes: - - dart@3.7.2 - - go@1.22.3 - - java@17.0.10 - - node@22.2.0 - - python@3.11.11 -tools: - - dartanalyzer@3.7.2 - - eslint@8.57.0 - - lizard@1.17.31 - - pmd@7.11.0 - - pylint@3.3.6 - - revive@1.7.0 - - semgrep@1.78.0 - - trivy@0.66.0 diff --git a/templates/Bff/src/Contracts/Protos/order.proto b/templates/Bff/src/Contracts/Protos/order.proto index 96761cd8..9ac9e17a 100644 --- a/templates/Bff/src/Contracts/Protos/order.proto +++ b/templates/Bff/src/Contracts/Protos/order.proto @@ -2,33 +2,52 @@ syntax = "proto3"; option csharp_namespace = "GrpcOrder"; -package order; +package Protos; +// Service for managing orders service OrderService { + // Retrieves an order by its ID rpc Get (GetOrderRequest) returns (OrderReply); } +// Request message for getting an order message GetOrderRequest { + // Unique identifier for the order int32 id = 1; - string correlationId = 2; + // Correlation ID for tracking requests + string correlation_id = 2; } +// Data transfer object for an item message ItemDto { + // Unique identifier for the item int32 id = 1; + // Name of the item string name = 2; + // Description of the item string description = 3; + // Price of the item double value = 4; } +// Data transfer object for an order message OrderDto { + // Unique identifier for the order int32 id = 1; + // Description of the order string description = 2; + // Total amount for the order double total = 3; + // List of items in the order repeated ItemDto items = 4; } +// Reply message for order retrieval message OrderReply { + // Indicates if the operation was successful bool success = 1; + // Message providing additional information string message = 2; + // The order data OrderDto data = 3; } \ No newline at end of file From d46e6cab98fb5f2b67b4e7d12fe09b73fa5ba32a Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 20 Jan 2026 18:49:11 -0300 Subject: [PATCH 020/161] feat(53): add health checks and related packages to the project --- templates/Bff/Directory.Packages.props | 1 + templates/Bff/docker-compose.yml | 6 ++++ .../HealthChecks/HealthCheckExtensions.cs | 35 +++++++++++++++++++ templates/Bff/src/MockApi/MockApi.csproj | 1 + templates/Bff/src/MockApi/Program.cs | 5 ++- templates/Bff/src/MockApi/packages.lock.json | 14 ++++++++ .../HealthChecks/HealthCheckExtensions.cs | 20 ++++++++++- templates/Bff/src/WebApp/WebApp.csproj | 1 + templates/Bff/src/WebApp/packages.lock.json | 6 ++++ .../tests/IntegrationTests/packages.lock.json | 11 ++++++ 10 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 templates/Bff/src/MockApi/HealthChecks/HealthCheckExtensions.cs diff --git a/templates/Bff/Directory.Packages.props b/templates/Bff/Directory.Packages.props index f6b5350f..9e19c223 100644 --- a/templates/Bff/Directory.Packages.props +++ b/templates/Bff/Directory.Packages.props @@ -7,6 +7,7 @@ + diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index e879eb96..69607e62 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -15,6 +15,12 @@ services: dockerfile: Dockerfile.MockApi ports: - "5012:5012" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5012/health"] + interval: 3s + timeout: 10s + retries: 5 + start_period: 15s networks: - hexagonal_solution_template_network diff --git a/templates/Bff/src/MockApi/HealthChecks/HealthCheckExtensions.cs b/templates/Bff/src/MockApi/HealthChecks/HealthCheckExtensions.cs new file mode 100644 index 00000000..3ef70958 --- /dev/null +++ b/templates/Bff/src/MockApi/HealthChecks/HealthCheckExtensions.cs @@ -0,0 +1,35 @@ +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace MockApi.HealthChecks; + +internal static class HealthCheckExtensions +{ + public static IServiceCollection AddCustomHealthChecks(this IServiceCollection services) + { + services + .AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy()); + + return services; + } + + public static IApplicationBuilder UseCustomHealthChecks( + this IApplicationBuilder app + ) + { + app.UseHealthChecks("/health", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + + app.UseHealthChecks("/live", new HealthCheckOptions + { + Predicate = r => r.Name.Contains("self") + }); + + return app; + } +} \ No newline at end of file diff --git a/templates/Bff/src/MockApi/MockApi.csproj b/templates/Bff/src/MockApi/MockApi.csproj index 9a36506e..321e875c 100644 --- a/templates/Bff/src/MockApi/MockApi.csproj +++ b/templates/Bff/src/MockApi/MockApi.csproj @@ -2,6 +2,7 @@ + diff --git a/templates/Bff/src/MockApi/Program.cs b/templates/Bff/src/MockApi/Program.cs index 3319f47a..107400c8 100644 --- a/templates/Bff/src/MockApi/Program.cs +++ b/templates/Bff/src/MockApi/Program.cs @@ -1,11 +1,13 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using MockApi.Endpoints; using MockApi.GrpcServices; +using MockApi.HealthChecks; var builder = WebApplication.CreateBuilder(args); builder.Services.AddGrpc(); builder.Services.AddResponseCompression(); +builder.Services.AddCustomHealthChecks(); builder.WebHost.ConfigureKestrel(options => options.ConfigureEndpointDefaults(listenOptions => @@ -18,6 +20,7 @@ app.MapEndpoints() .MapGrpcServices() - .UseResponseCompression(); + .UseResponseCompression() + .UseCustomHealthChecks(); await app.RunAsync(); diff --git a/templates/Bff/src/MockApi/packages.lock.json b/templates/Bff/src/MockApi/packages.lock.json index 6ccd2207..4a86fc4e 100644 --- a/templates/Bff/src/MockApi/packages.lock.json +++ b/templates/Bff/src/MockApi/packages.lock.json @@ -2,6 +2,15 @@ "version": 2, "dependencies": { "net10.0": { + "AspNetCore.HealthChecks.UI.Client": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "1Ub3Wvvbz7CMuFNWgLEc9qqQibiMoovDML/WHrwr5J83RPgtI20giCR92s/ipLgu7IIuqw+W/y7WpIeHqAICxg==", + "dependencies": { + "AspNetCore.HealthChecks.UI.Core": "9.0.0" + } + }, "AutoFixture": { "type": "Direct", "requested": "[4.18.1, )", @@ -22,6 +31,11 @@ "Grpc.Tools": "2.76.0" } }, + "AspNetCore.HealthChecks.UI.Core": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==" + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", diff --git a/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs b/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs index e9380df8..7523b2bb 100644 --- a/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs +++ b/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs @@ -1,4 +1,5 @@ using HealthChecks.UI.Client; +using Infrastructure.Http; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; @@ -11,7 +12,7 @@ public static IServiceCollection AddCustomHealthChecks( IConfiguration configuration ) { - services + var healthChecksBuilder = services .AddHealthChecks() .AddCheck("self", () => HealthCheckResult.Healthy()) .AddRedis( @@ -20,6 +21,23 @@ IConfiguration configuration tags: ["services"] ); + var serviceKeys = Enum.GetValues(); + foreach (var serviceKey in serviceKeys) + { + var baseAddress = configuration.GetSection("Http") + .GetChildren() + .FirstOrDefault(x => x["Name"] == serviceKey.ToString())?["BaseAddress"]; + + if (!string.IsNullOrEmpty(baseAddress)) + { + healthChecksBuilder.AddUrlGroup( + new Uri($"{baseAddress}/health"), + name: serviceKey.ToString(), + tags: ["services"] + ); + } + } + return services; } diff --git a/templates/Bff/src/WebApp/WebApp.csproj b/templates/Bff/src/WebApp/WebApp.csproj index dfe06e50..f5df78ec 100644 --- a/templates/Bff/src/WebApp/WebApp.csproj +++ b/templates/Bff/src/WebApp/WebApp.csproj @@ -2,6 +2,7 @@ + diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json index b8da38a7..4487b49f 100644 --- a/templates/Bff/src/WebApp/packages.lock.json +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -20,6 +20,12 @@ "AspNetCore.HealthChecks.UI.Core": "9.0.0" } }, + "AspNetCore.HealthChecks.Uris": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "XYdNlA437KeF8p9qOpZFyNqAN+c0FXt/JjTvzH/Qans0q0O3pPE8KPnn39ucQQjR/Roum1vLTP3kXiUs8VHyuA==" + }, "Grpc.AspNetCore": { "type": "Direct", "requested": "[2.76.0, )", diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json index c2bdf58c..ae5301f8 100644 --- a/templates/Bff/tests/IntegrationTests/packages.lock.json +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -569,6 +569,7 @@ "dependencies": { "AspNetCore.HealthChecks.Redis": "[9.0.0, )", "AspNetCore.HealthChecks.UI.Client": "[9.0.0, )", + "AspNetCore.HealthChecks.Uris": "[9.0.0, )", "Grpc.AspNetCore": "[2.76.0, )", "Infrastructure": "[1.0.0, )", "Microsoft.AspNetCore.OpenApi": "[10.0.1, )", @@ -594,6 +595,16 @@ "AspNetCore.HealthChecks.UI.Core": "9.0.0" } }, + "AspNetCore.HealthChecks.Uris": { + "type": "CentralTransitive", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "XYdNlA437KeF8p9qOpZFyNqAN+c0FXt/JjTvzH/Qans0q0O3pPE8KPnn39ucQQjR/Roum1vLTP3kXiUs8VHyuA==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11", + "Microsoft.Extensions.Http": "8.0.0" + } + }, "Grpc.AspNetCore": { "type": "CentralTransitive", "requested": "[2.76.0, )", From 411ddd93093c468953da41e55c7df29b90ceacf3 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 20 Jan 2026 19:10:38 -0300 Subject: [PATCH 021/161] feat(53): add health check and rate limiting extensions with config --- .../Http/ServiceConfigurations.cs | 1 + .../HealthCheckExtensions.cs | 2 +- templates/Bff/src/MockApi/Program.cs | 2 +- .../src/WebApp/Endpoints/OrderEndpoints.cs | 4 +- .../HealthCheckExtensions.cs | 2 +- .../WebApp/Extensions/RateLimitExtensions.cs | 44 +++++++++++++++++++ templates/Bff/src/WebApp/Program.cs | 5 ++- .../src/WebApp/appsettings.Development.json | 6 ++- templates/Bff/src/WebApp/appsettings.json | 6 ++- 9 files changed, 63 insertions(+), 9 deletions(-) rename templates/Bff/src/MockApi/{HealthChecks => Extensions}/HealthCheckExtensions.cs (96%) rename templates/Bff/src/WebApp/{HealthChecks => Extensions}/HealthCheckExtensions.cs (98%) create mode 100644 templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs diff --git a/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs b/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs index e02b515b..2bb3bdbb 100644 --- a/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs +++ b/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs @@ -6,4 +6,5 @@ public sealed class ServiceConfigurations public string BaseAddress { get; set; } public object Authentication { get; set; } public object Headers { get; set; } + public int LimitPerMinute { get; set; } } \ No newline at end of file diff --git a/templates/Bff/src/MockApi/HealthChecks/HealthCheckExtensions.cs b/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs similarity index 96% rename from templates/Bff/src/MockApi/HealthChecks/HealthCheckExtensions.cs rename to templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs index 3ef70958..77f5680f 100644 --- a/templates/Bff/src/MockApi/HealthChecks/HealthCheckExtensions.cs +++ b/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; -namespace MockApi.HealthChecks; +namespace MockApi.Extensions; internal static class HealthCheckExtensions { diff --git a/templates/Bff/src/MockApi/Program.cs b/templates/Bff/src/MockApi/Program.cs index 107400c8..d2cf014f 100644 --- a/templates/Bff/src/MockApi/Program.cs +++ b/templates/Bff/src/MockApi/Program.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using MockApi.Endpoints; +using MockApi.Extensions; using MockApi.GrpcServices; -using MockApi.HealthChecks; var builder = WebApplication.CreateBuilder(args); diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 8136ae5c..2fe1cabe 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -12,7 +12,9 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) { var serviceKey = ServicesKeys.Orders.ToString(); - var ordersGroup = app.MapGroup(serviceKey).WithTags(serviceKey); + var ordersGroup = app.MapGroup(serviceKey) + .WithTags(serviceKey) + .RequireRateLimiting(serviceKey); ordersGroup.MapGet("/{id}", async ( diff --git a/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs b/templates/Bff/src/WebApp/Extensions/HealthCheckExtensions.cs similarity index 98% rename from templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs rename to templates/Bff/src/WebApp/Extensions/HealthCheckExtensions.cs index 7523b2bb..bf20367c 100644 --- a/templates/Bff/src/WebApp/HealthChecks/HealthCheckExtensions.cs +++ b/templates/Bff/src/WebApp/Extensions/HealthCheckExtensions.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; -namespace WebApp.HealthChecks; +namespace WebApp.Extensions; internal static class HealthCheckExtensions { diff --git a/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs b/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs new file mode 100644 index 00000000..37108e3d --- /dev/null +++ b/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs @@ -0,0 +1,44 @@ +using Infrastructure.Http; +using Microsoft.AspNetCore.RateLimiting; + +namespace WebApp.Extensions; + +internal static class RateLimitExtensions +{ + extension(IServiceCollection services) + { + internal IServiceCollection AddRateLimiting(IConfiguration configuration) + { + var serviceKeys = Enum.GetValues(); + foreach (var serviceKey in serviceKeys) + { + var serviceConfig = configuration.GetSection("Http") + .GetChildren() + .FirstOrDefault(x => x["Name"] == serviceKey.ToString()); + + if (serviceConfig != null && int.TryParse(serviceConfig["LimitPerMinute"], out int limitPerMinute)) + { + services.AddRateLimiter(options => + { + options.AddFixedWindowLimiter(serviceKey.ToString(), limiterOptions => + { + limiterOptions.PermitLimit = limitPerMinute; + limiterOptions.Window = TimeSpan.FromMinutes(1); + }); + + options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; + + options.OnRejected = async (context, cancellationToken) => + { + context.HttpContext.Response.Headers.RetryAfter = "60 seconds"; + await Task.CompletedTask; + }; + }); + } + } + + return services; + } + } +} + \ No newline at end of file diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs index d183e707..35b96590 100644 --- a/templates/Bff/src/WebApp/Program.cs +++ b/templates/Bff/src/WebApp/Program.cs @@ -5,8 +5,8 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Scalar.AspNetCore; using WebApp.Endpoints; +using WebApp.Extensions; using WebApp.GrpcServices; -using WebApp.HealthChecks; using WebApp.Middlewares; namespace WebApp; @@ -23,6 +23,7 @@ private static async Task Main(string[] args) builder.Services.AddGrpc(); builder.Services.AddOpenApi(); builder.Services.AddCustomHealthChecks(builder.Configuration); + builder.Services.AddRateLimiting(builder.Configuration); builder.Services.AddResponseCompression(); builder.Services.Configure(options => { @@ -46,6 +47,8 @@ private static async Task Main(string[] args) app.UseHttpsRedirection(); + app.UseRateLimiter(); + app.MapEndpoints() .MapGrpcServices() .UseCustomHealthChecks() diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index 984777fd..994a31f7 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -11,7 +11,8 @@ "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" - } + }, + "LimitPerMinute": 5 }, { "Name": "Payments", @@ -19,7 +20,8 @@ "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" - } + }, + "LimitPerMinute": 5 } ] } \ No newline at end of file diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index c73b50a3..9ab8417d 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -11,7 +11,8 @@ "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" - } + }, + "LimitPerMinute": 100 }, { "Name": "Payments", @@ -19,7 +20,8 @@ "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" - } + }, + "LimitPerMinute": 100 } ] } \ No newline at end of file From 956b58112075f7d4fb91acee96b3adfa839eed36 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 21 Jan 2026 07:50:26 -0300 Subject: [PATCH 022/161] 53 feat: add rate limiting configuration and enable in settings --- .../Bff/src/WebApp/Extensions/RateLimitExtensions.cs | 3 +++ templates/Bff/src/WebApp/Properties/launchSettings.json | 2 +- templates/Bff/src/WebApp/appsettings.Development.json | 8 +++----- templates/Bff/src/WebApp/appsettings.json | 8 +++----- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs b/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs index 37108e3d..f6bcc98d 100644 --- a/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs +++ b/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs @@ -9,6 +9,9 @@ internal static class RateLimitExtensions { internal IServiceCollection AddRateLimiting(IConfiguration configuration) { + if (!configuration.GetValue("RATE_LIMITING_ENABLED")) + return services; + var serviceKeys = Enum.GetValues(); foreach (var serviceKey in serviceKeys) { diff --git a/templates/Bff/src/WebApp/Properties/launchSettings.json b/templates/Bff/src/WebApp/Properties/launchSettings.json index 4772c572..c4535c50 100644 --- a/templates/Bff/src/WebApp/Properties/launchSettings.json +++ b/templates/Bff/src/WebApp/Properties/launchSettings.json @@ -4,11 +4,11 @@ "Development": { "commandName": "Project", "dotnetRunMessages": true, - "sqlDebugging": true, "launchBrowser": false, "applicationUrl": "https://*:7176;http://*:5011", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", + "RATE_LIMITING_ENABLED": "true", "LOGGING__LOGLEVEL__DEFAULT": "Debug", "LOGGING__LOGLEVEL__MICROSOFT": "Information", "LOGGING__LOGLEVEL__MICROSOFT_HOSTING_LIFETIME": "Information", diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index 994a31f7..4834525a 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -1,8 +1,6 @@ { "ConnectionStrings": { - "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", - "Redis": "localhost:6379", - "RabbitMq": "amqp://guest:guest@localhost:5672/" + "Redis": "localhost:6379" }, "Http": [ { @@ -12,7 +10,7 @@ "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 5 + "LimitPerMinute": 500 }, { "Name": "Payments", @@ -21,7 +19,7 @@ "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 5 + "LimitPerMinute": 500 } ] } \ No newline at end of file diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index 9ab8417d..278e7dee 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -1,8 +1,6 @@ { "ConnectionStrings": { - "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", - "Redis": "redis:6379", - "RabbitMq": "amqp://guest:guest@rabbitmq:5672/" + "Redis": "redis:6379" }, "Http": [ { @@ -12,7 +10,7 @@ "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 100 + "LimitPerMinute": 500 }, { "Name": "Payments", @@ -21,7 +19,7 @@ "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 100 + "LimitPerMinute": 500 } ] } \ No newline at end of file From 3df8bdbff06be084c6025e5e4b93f4ab283f738b Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 21 Jan 2026 07:54:04 -0300 Subject: [PATCH 023/161] 53 feat: add custom health checks and configuration extensions --- .../{HealthChecks => Extensions}/HealthCheckExtensions.cs | 2 +- templates/Full/src/WebApp/Program.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename templates/Full/src/WebApp/{HealthChecks => Extensions}/HealthCheckExtensions.cs (98%) diff --git a/templates/Full/src/WebApp/HealthChecks/HealthCheckExtensions.cs b/templates/Full/src/WebApp/Extensions/HealthCheckExtensions.cs similarity index 98% rename from templates/Full/src/WebApp/HealthChecks/HealthCheckExtensions.cs rename to templates/Full/src/WebApp/Extensions/HealthCheckExtensions.cs index f31395ff..e9ec35d3 100644 --- a/templates/Full/src/WebApp/HealthChecks/HealthCheckExtensions.cs +++ b/templates/Full/src/WebApp/Extensions/HealthCheckExtensions.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Diagnostics.HealthChecks; using RabbitMQ.Client; -namespace WebApp.HealthChecks; +namespace WebApp.Extensions; internal static class HealthCheckExtensions { diff --git a/templates/Full/src/WebApp/Program.cs b/templates/Full/src/WebApp/Program.cs index cf3cc085..4b6b5685 100644 --- a/templates/Full/src/WebApp/Program.cs +++ b/templates/Full/src/WebApp/Program.cs @@ -6,8 +6,8 @@ using Microsoft.AspNetCore.Http.Json; using Microsoft.AspNetCore.Server.Kestrel.Core; using WebApp.Endpoints; +using WebApp.Extensions; using WebApp.GrpcServices; -using WebApp.HealthChecks; using WebApp.Middlewares; namespace WebApp; From 307b1132689d5aed1b9c5700082a4bef0a942890 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 21 Jan 2026 08:03:12 -0300 Subject: [PATCH 024/161] feat(53): add grpc dependencies and update related services --- templates/Bff/src/Contracts/Contracts.csproj | 3 + .../Bff/src/Contracts/packages.lock.json | 188 +++++++++++++++++- .../Bff/src/Infrastructure/packages.lock.json | 74 ++++++- .../GrpcServices/GrpcServiceExtensions.cs | 2 +- .../src/MockApi/GrpcServices/OrderService.cs | 73 ++----- templates/Bff/src/MockApi/MockApi.csproj | 4 - templates/Bff/src/MockApi/packages.lock.json | 27 +-- templates/Bff/src/WebApp/WebApp.csproj | 1 - templates/Bff/src/WebApp/packages.lock.json | 27 +-- .../tests/IntegrationTests/packages.lock.json | 5 +- 10 files changed, 312 insertions(+), 92 deletions(-) diff --git a/templates/Bff/src/Contracts/Contracts.csproj b/templates/Bff/src/Contracts/Contracts.csproj index 7daf16e2..be735abb 100644 --- a/templates/Bff/src/Contracts/Contracts.csproj +++ b/templates/Bff/src/Contracts/Contracts.csproj @@ -2,6 +2,9 @@ true + + + diff --git a/templates/Bff/src/Contracts/packages.lock.json b/templates/Bff/src/Contracts/packages.lock.json index 6afd6786..f7f2344e 100644 --- a/templates/Bff/src/Contracts/packages.lock.json +++ b/templates/Bff/src/Contracts/packages.lock.json @@ -1,6 +1,192 @@ { "version": 2, "dependencies": { - "net10.0": {} + "net10.0": { + "Grpc.AspNetCore": { + "type": "Direct", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } + }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.31.1", + "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" + }, + "Grpc.AspNetCore.Server": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "diSC/ZeNdSdxHdYSOpYwuSBBDYpuNVtJQFJfiBB0WrYOQ4lVMmdxuUZJcViahQyo8pCvS3Mueo5lqFxwwMF/iw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.AspNetCore.Server.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "y5KGO1GO0N2L/hCCMR05mmoK8j+v8rKvZ+9nothAxKx2Tf2CwV8f4TM5K0GkKfDsp4vrc4lm90MU6E+DeN7YIw==", + "dependencies": { + "Grpc.AspNetCore.Server": "2.76.0", + "Grpc.Net.ClientFactory": "2.76.0" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "cSxC2tdnFdXXuBgIn1pjc4YBx7LXTCp4M0qn+SMBS35VWZY+cEQYLWTBDDhdBH1HzU7BV+ncVZlniGQHMpRJKQ==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "K1oldmqw2+Gn69nGRzZLhqSiUZwelX1GrBu/cUl9wNf1C0uB61vFS6JcxUUv9P8VoUJhFsmV44JA6lI2EUt4xw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "XI+kO69L9AV8B9N0UQOmH911r6MOEp9huHiavEsY56DJYuzJ9KAxNGy37dpV6CLbgCaN2uKmpOsZ9Pao6bmpVQ==", + "dependencies": { + "Grpc.Net.Client": "2.76.0", + "Microsoft.Extensions.Http": "8.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "bZpiMVYgvpB44/wBh1RotrkqC7bg2FOasLri2GhR3hMKyzsiTxCoDE49YjPrJeFc4RW0wS8u+EInI09sjxVFRA==", + "dependencies": { + "Grpc.Core.Api": "2.76.0" + } + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3PZp/YSkIXrF7QK7PfC1bkyRYwqOHpWFad8Qx+4wkuumAeXo1NHaxpS9LboNA9OvNSAu+QOVlXbMyoY+pHSqcw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Http": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + } + } } } \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/packages.lock.json b/templates/Bff/src/Infrastructure/packages.lock.json index 3664a4ea..11a45ea5 100644 --- a/templates/Bff/src/Infrastructure/packages.lock.json +++ b/templates/Bff/src/Infrastructure/packages.lock.json @@ -107,6 +107,64 @@ "StackExchange.Redis": "[2.6.122, 3.0.0)" } }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.31.1", + "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" + }, + "Grpc.AspNetCore.Server": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "diSC/ZeNdSdxHdYSOpYwuSBBDYpuNVtJQFJfiBB0WrYOQ4lVMmdxuUZJcViahQyo8pCvS3Mueo5lqFxwwMF/iw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0" + } + }, + "Grpc.AspNetCore.Server.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "y5KGO1GO0N2L/hCCMR05mmoK8j+v8rKvZ+9nothAxKx2Tf2CwV8f4TM5K0GkKfDsp4vrc4lm90MU6E+DeN7YIw==", + "dependencies": { + "Grpc.AspNetCore.Server": "2.76.0", + "Grpc.Net.ClientFactory": "2.76.0" + } + }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "cSxC2tdnFdXXuBgIn1pjc4YBx7LXTCp4M0qn+SMBS35VWZY+cEQYLWTBDDhdBH1HzU7BV+ncVZlniGQHMpRJKQ==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "K1oldmqw2+Gn69nGRzZLhqSiUZwelX1GrBu/cUl9wNf1C0uB61vFS6JcxUUv9P8VoUJhFsmV44JA6lI2EUt4xw==", + "dependencies": { + "Grpc.Net.Common": "2.76.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Grpc.Net.ClientFactory": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "XI+kO69L9AV8B9N0UQOmH911r6MOEp9huHiavEsY56DJYuzJ9KAxNGy37dpV6CLbgCaN2uKmpOsZ9Pao6bmpVQ==", + "dependencies": { + "Grpc.Net.Client": "2.76.0", + "Microsoft.Extensions.Http": "8.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "bZpiMVYgvpB44/wBh1RotrkqC7bg2FOasLri2GhR3hMKyzsiTxCoDE49YjPrJeFc4RW0wS8u+EInI09sjxVFRA==", + "dependencies": { + "Grpc.Core.Api": "2.76.0" + } + }, + "Grpc.Tools": { + "type": "Transitive", + "resolved": "2.76.0", + "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" + }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", "resolved": "10.0.1", @@ -311,7 +369,21 @@ } }, "contracts": { - "type": "Project" + "type": "Project", + "dependencies": { + "Grpc.AspNetCore": "[2.76.0, )" + } + }, + "Grpc.AspNetCore": { + "type": "CentralTransitive", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", diff --git a/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs b/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs index e45b669c..30b57448 100644 --- a/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs +++ b/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs @@ -4,7 +4,7 @@ internal static class GrpcServiceExtensions { public static WebApplication MapGrpcServices(this WebApplication app) { - // app.MapGrpcService(); + app.MapGrpcService(); return app; } diff --git a/templates/Bff/src/MockApi/GrpcServices/OrderService.cs b/templates/Bff/src/MockApi/GrpcServices/OrderService.cs index 366400fa..28ed9a33 100644 --- a/templates/Bff/src/MockApi/GrpcServices/OrderService.cs +++ b/templates/Bff/src/MockApi/GrpcServices/OrderService.cs @@ -1,59 +1,14 @@ -// using System.Globalization; -// using Application.Common.Requests; -// using Application.Common.Services; -// using Application.Common.UseCases; -// using Grpc.Core; -// using GrpcOrder; -// using static GrpcOrder.OrderService; -// using GetOrderRequest = Application.Orders.GetOrderRequest; -// using OrderDto = Application.Orders.OrderDto; - -// namespace MockApi.GrpcServices; - -// public class OrderService( -// IBaseInOutUseCase> useCase, -// IHybridCacheService cache -// ) : OrderServiceBase -// { -// private readonly IBaseInOutUseCase> _useCase = useCase; -// private readonly IHybridCacheService _cache = cache; - -// public override async Task Get( -// GrpcOrder.GetOrderRequest request, -// ServerCallContext context -// ) -// { -// var response = await _cache.GetOrCreateAsync( -// $"{nameof(OrderService)}-{request.Id}", -// async cancellationToken => -// { -// var correlationId = Guid.TryParse(request.CorrelationId, out var guid) ? guid : Guid.Empty; -// return await _useCase.HandleAsync(new(correlationId, request.Id), cancellationToken); -// }, -// context.CancellationToken -// ); - -// if (!response.Success || response.Data == null) -// return new() { Success = false, Message = response.Message }; - -// OrderReply orderReply = new() -// { -// Success = true, -// Message = string.Empty, -// Data = new() -// { -// Id = response.Data.Id, -// Total = double.TryParse(response.Data.Total.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var total) ? total : 0.0 -// } -// }; - -// orderReply.Data.Items?.AddRange(response.Data.Items?.Select(i => new GrpcOrder.ItemDto -// { -// Id = i.Id, -// Name = i.Name, -// Value = double.TryParse(i.Value.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : 0.0 -// })); - -// return orderReply; -// } -// } +using AutoFixture; +using Grpc.Core; +using GrpcOrder; +using static GrpcOrder.OrderService; + +namespace MockApi.GrpcServices; + +public class OrderService : OrderServiceBase +{ + public override async Task Get( + GetOrderRequest request, + ServerCallContext context + ) => new Fixture().Create(); +} diff --git a/templates/Bff/src/MockApi/MockApi.csproj b/templates/Bff/src/MockApi/MockApi.csproj index 321e875c..ded60fc5 100644 --- a/templates/Bff/src/MockApi/MockApi.csproj +++ b/templates/Bff/src/MockApi/MockApi.csproj @@ -1,12 +1,8 @@  - - - - diff --git a/templates/Bff/src/MockApi/packages.lock.json b/templates/Bff/src/MockApi/packages.lock.json index 4a86fc4e..6885a8ae 100644 --- a/templates/Bff/src/MockApi/packages.lock.json +++ b/templates/Bff/src/MockApi/packages.lock.json @@ -20,17 +20,6 @@ "Fare": "[2.1.1, 3.0.0)" } }, - "Grpc.AspNetCore": { - "type": "Direct", - "requested": "[2.76.0, )", - "resolved": "2.76.0", - "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", - "dependencies": { - "Google.Protobuf": "3.31.1", - "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", - "Grpc.Tools": "2.76.0" - } - }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", @@ -114,7 +103,21 @@ } }, "contracts": { - "type": "Project" + "type": "Project", + "dependencies": { + "Grpc.AspNetCore": "[2.76.0, )" + } + }, + "Grpc.AspNetCore": { + "type": "CentralTransitive", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } } } } diff --git a/templates/Bff/src/WebApp/WebApp.csproj b/templates/Bff/src/WebApp/WebApp.csproj index f5df78ec..f4aeb591 100644 --- a/templates/Bff/src/WebApp/WebApp.csproj +++ b/templates/Bff/src/WebApp/WebApp.csproj @@ -3,7 +3,6 @@ - diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json index 4487b49f..9aa1724d 100644 --- a/templates/Bff/src/WebApp/packages.lock.json +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -26,17 +26,6 @@ "resolved": "9.0.0", "contentHash": "XYdNlA437KeF8p9qOpZFyNqAN+c0FXt/JjTvzH/Qans0q0O3pPE8KPnn39ucQQjR/Roum1vLTP3kXiUs8VHyuA==" }, - "Grpc.AspNetCore": { - "type": "Direct", - "requested": "[2.76.0, )", - "resolved": "2.76.0", - "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", - "dependencies": { - "Google.Protobuf": "3.31.1", - "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", - "Grpc.Tools": "2.76.0" - } - }, "Microsoft.AspNetCore.OpenApi": { "type": "Direct", "requested": "[10.0.1, )", @@ -166,7 +155,10 @@ } }, "contracts": { - "type": "Project" + "type": "Project", + "dependencies": { + "Grpc.AspNetCore": "[2.76.0, )" + } }, "infrastructure": { "type": "Project", @@ -184,6 +176,17 @@ "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )" } }, + "Grpc.AspNetCore": { + "type": "CentralTransitive", + "requested": "[2.76.0, )", + "resolved": "2.76.0", + "contentHash": "LyXMmpN2Ba0TE35SOLSKbGqIYtJuhc1UgiaGfoW1X8KJERV70QI5KGW+ckEY7MrXoFWN/uWo4B70siVhbDmCgQ==", + "dependencies": { + "Google.Protobuf": "3.31.1", + "Grpc.AspNetCore.Server.ClientFactory": "2.76.0", + "Grpc.Tools": "2.76.0" + } + }, "Microsoft.Extensions.Caching.Hybrid": { "type": "CentralTransitive", "requested": "[10.1.0, )", diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json index ae5301f8..310dd11a 100644 --- a/templates/Bff/tests/IntegrationTests/packages.lock.json +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -546,7 +546,10 @@ } }, "contracts": { - "type": "Project" + "type": "Project", + "dependencies": { + "Grpc.AspNetCore": "[2.76.0, )" + } }, "infrastructure": { "type": "Project", From 7626b461cc7bd4f567a26a83e4dc12bac5e0a872 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 21 Jan 2026 16:54:39 -0300 Subject: [PATCH 025/161] 53 feat: implement payment creation gRPC service and request model --- .../Payments/CreatePaymentRequest.cs | 19 ++++++ .../Bff/src/Contracts/Protos/payment.proto | 33 +++++++++++ .../Infrastructure/Grpc/BaseGrpcService.cs | 9 +++ .../Infrastructure/Grpc/PaymentsService.cs | 28 +++++++++ .../GrpcServices/GrpcServiceExtensions.cs | 11 ---- .../src/WebApp/GrpcServices/OrderService.cs | 59 ------------------- 6 files changed, 89 insertions(+), 70 deletions(-) create mode 100644 templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs create mode 100644 templates/Bff/src/Contracts/Protos/payment.proto create mode 100644 templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs create mode 100644 templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs delete mode 100644 templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs delete mode 100644 templates/Bff/src/WebApp/GrpcServices/OrderService.cs diff --git a/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs b/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs new file mode 100644 index 00000000..6940da50 --- /dev/null +++ b/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs @@ -0,0 +1,19 @@ +using Contracts.Common; + +namespace Contracts.Payments; + +/// +/// Request to create a payment request +/// /// +/// Correlation identifier for tracking the request +/// Unique identifier for the order +/// Amount to be paid +/// Currency of the payment +/// Method of payment +public sealed record CreatePaymentRequest( + Guid CorrelationId, + int OrderId, + double Amount, + string Currency, + string PaymentMethod +) : BaseRequest(CorrelationId); \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Protos/payment.proto b/templates/Bff/src/Contracts/Protos/payment.proto new file mode 100644 index 00000000..19330c3e --- /dev/null +++ b/templates/Bff/src/Contracts/Protos/payment.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +option csharp_namespace = "GrpcPayment"; + +package Protos; + +// Service for managing payments +service PaymentService { + // Creates a payment + rpc Create (CreatePaymentRequest) returns (PaymentReply); +} + +// Request message for creating a payment +message CreatePaymentRequest { + // Unique identifier for the order + int32 order_id = 1; + // Amount to be paid + double amount = 2; + // Correlation ID for tracking requests + string correlation_id = 3; + // Currency of the payment + string currency = 4; + // Method of payment + string payment_method = 5; +} + +// Reply message for payment processing +message PaymentReply { + // Indicates if the payment was successful + bool success = 1; + // Message providing additional information + string message = 2; +} \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs new file mode 100644 index 00000000..ed5b2ee6 --- /dev/null +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -0,0 +1,9 @@ +using Grpc.Net.Client; + +namespace Infrastructure.Grpc; + +public abstract class BaseGrpcService(string baseAddress) +{ + protected GrpcChannel Channel { get; } = GrpcChannel.ForAddress(baseAddress); +} + diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs new file mode 100644 index 00000000..2a5ed627 --- /dev/null +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -0,0 +1,28 @@ +using Contracts.Common; +using Contracts.Payments; +using static GrpcPayment.PaymentService; + +namespace Infrastructure.Grpc; + +public sealed class PaymentsService : BaseGrpcService +{ + private readonly PaymentServiceClient _Client; + + public PaymentsService(string baseAddress) : base(baseAddress) => _Client = new(Channel); + + public async Task CreateAsync(CreatePaymentRequest request) + { + var grpcRequest = new GrpcPayment.CreatePaymentRequest + { + CorrelationId = request.CorrelationId.ToString(), + OrderId = request.OrderId, + Amount = request.Amount, + Currency = request.Currency, + PaymentMethod = request.PaymentMethod + }; + + var result = await _Client.CreateAsync(grpcRequest); + + return new(result.Success, result.Message); + } +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs b/templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs deleted file mode 100644 index 92bc81f1..00000000 --- a/templates/Bff/src/WebApp/GrpcServices/GrpcServiceExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace WebApp.GrpcServices; - -internal static class GrpcServiceExtensions -{ - public static WebApplication MapGrpcServices(this WebApplication app) - { - // app.MapGrpcService(); - - return app; - } -} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/GrpcServices/OrderService.cs b/templates/Bff/src/WebApp/GrpcServices/OrderService.cs deleted file mode 100644 index f374b991..00000000 --- a/templates/Bff/src/WebApp/GrpcServices/OrderService.cs +++ /dev/null @@ -1,59 +0,0 @@ -// using System.Globalization; -// using Application.Common.Requests; -// using Application.Common.Services; -// using Application.Common.UseCases; -// using Grpc.Core; -// using GrpcOrder; -// using static GrpcOrder.OrderService; -// using GetOrderRequest = Application.Orders.GetOrderRequest; -// using OrderDto = Application.Orders.OrderDto; - -// namespace WebApp.GrpcServices; - -// public class OrderService( -// IBaseInOutUseCase> useCase, -// IHybridCacheService cache -// ) : OrderServiceBase -// { -// private readonly IBaseInOutUseCase> _useCase = useCase; -// private readonly IHybridCacheService _cache = cache; - -// public override async Task Get( -// GrpcOrder.GetOrderRequest request, -// ServerCallContext context -// ) -// { -// var response = await _cache.GetOrCreateAsync( -// $"{nameof(OrderService)}-{request.Id}", -// async cancellationToken => -// { -// var correlationId = Guid.TryParse(request.CorrelationId, out var guid) ? guid : Guid.Empty; -// return await _useCase.HandleAsync(new(correlationId, request.Id), cancellationToken); -// }, -// context.CancellationToken -// ); - -// if (!response.Success || response.Data == null) -// return new() { Success = false, Message = response.Message }; - -// OrderReply orderReply = new() -// { -// Success = true, -// Message = string.Empty, -// Data = new() -// { -// Id = response.Data.Id, -// Total = double.TryParse(response.Data.Total.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var total) ? total : 0.0 -// } -// }; - -// orderReply.Data.Items?.AddRange(response.Data.Items?.Select(i => new GrpcOrder.ItemDto -// { -// Id = i.Id, -// Name = i.Name, -// Value = double.TryParse(i.Value.ToString(CultureInfo.InvariantCulture), NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : 0.0 -// })); - -// return orderReply; -// } -// } From 5a958afdfa76d42626b96d218b33f15044b532ad Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 21 Jan 2026 17:25:24 -0300 Subject: [PATCH 026/161] 53 feat: add payment creation endpoint and refactor request handling --- .../Payments/CreatePaymentRequest.cs | 16 +++++++- .../Infrastructure/Grpc/PaymentsService.cs | 12 +----- .../WebApp/Endpoints/EndpointExtensions.cs | 5 ++- .../src/WebApp/Endpoints/OrderEndpoints.cs | 1 - .../src/WebApp/Endpoints/PaymentEndpoints.cs | 37 +++++++++++++++++++ templates/Bff/src/WebApp/Program.cs | 2 - 6 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs diff --git a/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs b/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs index 6940da50..37ed9604 100644 --- a/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs +++ b/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs @@ -16,4 +16,18 @@ public sealed record CreatePaymentRequest( double Amount, string Currency, string PaymentMethod -) : BaseRequest(CorrelationId); \ No newline at end of file +) : BaseRequest(CorrelationId) +{ + /// + /// Implicit conversion to gRPC CreatePaymentRequest + /// + /// + public static implicit operator GrpcPayment.CreatePaymentRequest(CreatePaymentRequest request) => new() + { + CorrelationId = request.CorrelationId.ToString(), + OrderId = request.OrderId, + Amount = request.Amount, + Currency = request.Currency, + PaymentMethod = request.PaymentMethod + }; +} \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index 2a5ed627..2d1a8e52 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -7,21 +7,11 @@ namespace Infrastructure.Grpc; public sealed class PaymentsService : BaseGrpcService { private readonly PaymentServiceClient _Client; - public PaymentsService(string baseAddress) : base(baseAddress) => _Client = new(Channel); public async Task CreateAsync(CreatePaymentRequest request) { - var grpcRequest = new GrpcPayment.CreatePaymentRequest - { - CorrelationId = request.CorrelationId.ToString(), - OrderId = request.OrderId, - Amount = request.Amount, - Currency = request.Currency, - PaymentMethod = request.PaymentMethod - }; - - var result = await _Client.CreateAsync(grpcRequest); + var result = await _Client.CreateAsync(request); return new(result.Success, result.Message); } diff --git a/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs b/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs index c14e93de..64c8b12a 100644 --- a/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs +++ b/templates/Bff/src/WebApp/Endpoints/EndpointExtensions.cs @@ -4,7 +4,10 @@ internal static class EndpointExtensions { public static WebApplication MapEndpoints(this WebApplication app) { - app.MapOrderEndpoints(); + app + .MapOrderEndpoints() + .MapPaymentEndpoints(); + return app; } } \ No newline at end of file diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 2fe1cabe..e7186d54 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -17,7 +17,6 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) .RequireRateLimiting(serviceKey); ordersGroup.MapGet("/{id}", async ( - [FromKeyedServices(ServicesKeys.Orders)] BaseHttpService httpService, [FromRoute] int id, [FromServices] HybridCacheService cache, diff --git a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs new file mode 100644 index 00000000..bd7f99cd --- /dev/null +++ b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs @@ -0,0 +1,37 @@ +using Contracts.Common; +using Contracts.Payments; +using Infrastructure.Grpc; +using Infrastructure.Http; +using Microsoft.AspNetCore.Mvc; + +namespace WebApp.Endpoints; + +public static partial class PaymentEndpoints +{ + private const string BasePath = "api/payments"; + + public static IEndpointRouteBuilder MapPaymentEndpoints(this IEndpointRouteBuilder endpoints) + { + var serviceKey = ServicesKeys.Payments.ToString(); + var group = endpoints.MapGroup(BasePath) + .WithTags(serviceKey) + .RequireRateLimiting(serviceKey); + + group.MapPost("/", async ( + [FromServices] PaymentsService paymentsService, + [FromBody] CreatePaymentRequest request + ) => + { + var result = await paymentsService.CreateAsync(request); + + return result.Success ? Results.Ok(result) : Results.BadRequest(result); + }) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status500InternalServerError) + .WithDescription("Creates a new payment") + .WithName("CreatePayment"); + + return endpoints; + } +} \ No newline at end of file diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs index 35b96590..a881223d 100644 --- a/templates/Bff/src/WebApp/Program.cs +++ b/templates/Bff/src/WebApp/Program.cs @@ -6,7 +6,6 @@ using Scalar.AspNetCore; using WebApp.Endpoints; using WebApp.Extensions; -using WebApp.GrpcServices; using WebApp.Middlewares; namespace WebApp; @@ -50,7 +49,6 @@ private static async Task Main(string[] args) app.UseRateLimiter(); app.MapEndpoints() - .MapGrpcServices() .UseCustomHealthChecks() .UseResponseCompression() .UseMiddleware(); From 76ce1af8438b3955ad1003da23b1bb898261a8b9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 21 Jan 2026 20:28:58 -0300 Subject: [PATCH 027/161] 53 feat: remove unused order service and update payment service --- .../Payments/CreatePaymentRequest.cs | 33 ------------ .../Bff/src/Contracts/Protos/order.proto | 53 ------------------- .../Bff/src/Contracts/Protos/payment.proto | 2 +- .../Infrastructure/Grpc/BaseGrpcService.cs | 51 +++++++++++++++++- .../Infrastructure/Grpc/PaymentsService.cs | 12 ++--- .../GrpcServices/GrpcServiceExtensions.cs | 2 +- .../src/MockApi/GrpcServices/OrderService.cs | 14 ----- .../MockApi/GrpcServices/PaymentService.cs | 14 +++++ 8 files changed, 68 insertions(+), 113 deletions(-) delete mode 100644 templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs delete mode 100644 templates/Bff/src/Contracts/Protos/order.proto delete mode 100644 templates/Bff/src/MockApi/GrpcServices/OrderService.cs create mode 100644 templates/Bff/src/MockApi/GrpcServices/PaymentService.cs diff --git a/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs b/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs deleted file mode 100644 index 37ed9604..00000000 --- a/templates/Bff/src/Contracts/Payments/CreatePaymentRequest.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Contracts.Common; - -namespace Contracts.Payments; - -/// -/// Request to create a payment request -/// /// -/// Correlation identifier for tracking the request -/// Unique identifier for the order -/// Amount to be paid -/// Currency of the payment -/// Method of payment -public sealed record CreatePaymentRequest( - Guid CorrelationId, - int OrderId, - double Amount, - string Currency, - string PaymentMethod -) : BaseRequest(CorrelationId) -{ - /// - /// Implicit conversion to gRPC CreatePaymentRequest - /// - /// - public static implicit operator GrpcPayment.CreatePaymentRequest(CreatePaymentRequest request) => new() - { - CorrelationId = request.CorrelationId.ToString(), - OrderId = request.OrderId, - Amount = request.Amount, - Currency = request.Currency, - PaymentMethod = request.PaymentMethod - }; -} \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Protos/order.proto b/templates/Bff/src/Contracts/Protos/order.proto deleted file mode 100644 index 9ac9e17a..00000000 --- a/templates/Bff/src/Contracts/Protos/order.proto +++ /dev/null @@ -1,53 +0,0 @@ -syntax = "proto3"; - -option csharp_namespace = "GrpcOrder"; - -package Protos; - -// Service for managing orders -service OrderService { - // Retrieves an order by its ID - rpc Get (GetOrderRequest) returns (OrderReply); -} - -// Request message for getting an order -message GetOrderRequest { - // Unique identifier for the order - int32 id = 1; - // Correlation ID for tracking requests - string correlation_id = 2; -} - -// Data transfer object for an item -message ItemDto { - // Unique identifier for the item - int32 id = 1; - // Name of the item - string name = 2; - // Description of the item - string description = 3; - // Price of the item - double value = 4; -} - -// Data transfer object for an order -message OrderDto { - // Unique identifier for the order - int32 id = 1; - // Description of the order - string description = 2; - // Total amount for the order - double total = 3; - // List of items in the order - repeated ItemDto items = 4; -} - -// Reply message for order retrieval -message OrderReply { - // Indicates if the operation was successful - bool success = 1; - // Message providing additional information - string message = 2; - // The order data - OrderDto data = 3; -} \ No newline at end of file diff --git a/templates/Bff/src/Contracts/Protos/payment.proto b/templates/Bff/src/Contracts/Protos/payment.proto index 19330c3e..17d64541 100644 --- a/templates/Bff/src/Contracts/Protos/payment.proto +++ b/templates/Bff/src/Contracts/Protos/payment.proto @@ -6,7 +6,7 @@ package Protos; // Service for managing payments service PaymentService { - // Creates a payment + // Creates a payment request for an order rpc Create (CreatePaymentRequest) returns (PaymentReply); } diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index ed5b2ee6..228085b6 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -1,9 +1,56 @@ +using System.Diagnostics; +using System.Linq.Expressions; +using Grpc.Core; using Grpc.Net.Client; +using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; -public abstract class BaseGrpcService(string baseAddress) +public abstract class BaseGrpcService where TService : class { - protected GrpcChannel Channel { get; } = GrpcChannel.ForAddress(baseAddress); + protected GrpcChannel Channel { get; } + protected ILoggerFactory _loggerFactory; + protected readonly Stopwatch _stopwatch = new(); + protected readonly string _className = typeof(TService).Name; + + public BaseGrpcService(string baseAddress, ILogger logger) + { + Channel = GrpcChannel.ForAddress(baseAddress); + _loggerFactory = logger; + } + + public async Task HandleAsync( + Task> action + ) where TRequest : class where TResponse : class + { + _stopwatch.Start(); + try + { + _logger.LogInformation("[{ClassName}] | [HandleAsync] | Sending gRPC request", _className); + + var response = await HandleInternalAsync(); + + _logger.LogInformation( + "[{ClassName}] | [HandleAsync] | gRPC request completed in {ElapsedMilliseconds} ms", + _className, + _stopwatch.ElapsedMilliseconds + ); + + return response; + } + catch (RpcException rpcException) + { + _logger.LogError( + rpcException, + "[{ClassName}] | [HandleAsync] | gRPC request failed with status {Status} in {ElapsedMilliseconds} ms", + _className, + rpcException.Status, + _stopwatch.ElapsedMilliseconds + ); + throw; + } + } + + public virtual Task HandleInternalAsync() where TRequest : class where TResponse : class; } diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index 2d1a8e52..99c820e3 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -1,18 +1,12 @@ -using Contracts.Common; -using Contracts.Payments; +using GrpcPayment; using static GrpcPayment.PaymentService; namespace Infrastructure.Grpc; -public sealed class PaymentsService : BaseGrpcService +public sealed class PaymentsService : BaseGrpcService { private readonly PaymentServiceClient _Client; public PaymentsService(string baseAddress) : base(baseAddress) => _Client = new(Channel); - public async Task CreateAsync(CreatePaymentRequest request) - { - var result = await _Client.CreateAsync(request); - - return new(result.Success, result.Message); - } + public async Task CreateAsync(CreatePaymentRequest request) => await _Client.CreateAsync(request); } \ No newline at end of file diff --git a/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs b/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs index 30b57448..a4c0d044 100644 --- a/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs +++ b/templates/Bff/src/MockApi/GrpcServices/GrpcServiceExtensions.cs @@ -4,7 +4,7 @@ internal static class GrpcServiceExtensions { public static WebApplication MapGrpcServices(this WebApplication app) { - app.MapGrpcService(); + app.MapGrpcService(); return app; } diff --git a/templates/Bff/src/MockApi/GrpcServices/OrderService.cs b/templates/Bff/src/MockApi/GrpcServices/OrderService.cs deleted file mode 100644 index 28ed9a33..00000000 --- a/templates/Bff/src/MockApi/GrpcServices/OrderService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AutoFixture; -using Grpc.Core; -using GrpcOrder; -using static GrpcOrder.OrderService; - -namespace MockApi.GrpcServices; - -public class OrderService : OrderServiceBase -{ - public override async Task Get( - GetOrderRequest request, - ServerCallContext context - ) => new Fixture().Create(); -} diff --git a/templates/Bff/src/MockApi/GrpcServices/PaymentService.cs b/templates/Bff/src/MockApi/GrpcServices/PaymentService.cs new file mode 100644 index 00000000..ec6df9f0 --- /dev/null +++ b/templates/Bff/src/MockApi/GrpcServices/PaymentService.cs @@ -0,0 +1,14 @@ +using AutoFixture; +using Grpc.Core; +using GrpcPayment; +using static GrpcPayment.PaymentService; + +namespace MockApi.GrpcServices; + +public class PaymentService : PaymentServiceBase +{ + public override async Task Create( + CreatePaymentRequest request, + ServerCallContext context + ) => new Fixture().Create(); +} From a73b56c1280076d1d779d10e0938995346f5b809 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 21 Jan 2026 20:30:07 -0300 Subject: [PATCH 028/161] CMGO-53 feat: update payment creation logic in gRPC service --- .../Bff/src/MockApi/GrpcServices/PaymentService.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/Bff/src/MockApi/GrpcServices/PaymentService.cs b/templates/Bff/src/MockApi/GrpcServices/PaymentService.cs index ec6df9f0..bfec4737 100644 --- a/templates/Bff/src/MockApi/GrpcServices/PaymentService.cs +++ b/templates/Bff/src/MockApi/GrpcServices/PaymentService.cs @@ -1,4 +1,3 @@ -using AutoFixture; using Grpc.Core; using GrpcPayment; using static GrpcPayment.PaymentService; @@ -7,8 +6,9 @@ namespace MockApi.GrpcServices; public class PaymentService : PaymentServiceBase { - public override async Task Create( - CreatePaymentRequest request, - ServerCallContext context - ) => new Fixture().Create(); + public override async Task Create(CreatePaymentRequest request, ServerCallContext context) => new() + { + Success = true, + Message = "Payment created successfully" + }; } From 4aa88be7aea0f4dcb235358e610fbdc64ed2bcdd Mon Sep 17 00:00:00 2001 From: "Giovanni B. Previatti" Date: Mon, 26 Jan 2026 07:22:52 -0300 Subject: [PATCH 029/161] feat: refactor BaseGrpcService and PaymentsService for improved request handling --- .../Infrastructure/Grpc/BaseGrpcService.cs | 41 ++++++++++--------- .../Infrastructure/Grpc/PaymentsService.cs | 31 ++++++++++---- .../src/WebApp/Endpoints/PaymentEndpoints.cs | 6 +-- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index 228085b6..bae6470a 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -1,56 +1,57 @@ using System.Diagnostics; -using System.Linq.Expressions; using Grpc.Core; using Grpc.Net.Client; using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; -public abstract class BaseGrpcService where TService : class +public abstract class BaseGrpcService { protected GrpcChannel Channel { get; } - protected ILoggerFactory _loggerFactory; - protected readonly Stopwatch _stopwatch = new(); - protected readonly string _className = typeof(TService).Name; + protected readonly ILogger logger; + protected readonly Stopwatch stopwatch = new(); + protected readonly string className; - public BaseGrpcService(string baseAddress, ILogger logger) + public BaseGrpcService(string baseAddress, ILogger logger) { + var classType = GetType(); + className = classType.Name; Channel = GrpcChannel.ForAddress(baseAddress); - _loggerFactory = logger; + this.logger = logger; } - public async Task HandleAsync( - Task> action - ) where TRequest : class where TResponse : class + public async Task HandleAsync(TRequest request) + where TRequest : class + where TResponse : class { - _stopwatch.Start(); + stopwatch.Start(); try { - _logger.LogInformation("[{ClassName}] | [HandleAsync] | Sending gRPC request", _className); + logger.LogInformation("[{ClassName}] | [HandleAsync] | Sending gRPC request", className); - var response = await HandleInternalAsync(); + var response = await HandleInternalAsync(request); - _logger.LogInformation( + logger.LogInformation( "[{ClassName}] | [HandleAsync] | gRPC request completed in {ElapsedMilliseconds} ms", - _className, - _stopwatch.ElapsedMilliseconds + className, + stopwatch.ElapsedMilliseconds ); return response; } catch (RpcException rpcException) { - _logger.LogError( + logger.LogError( rpcException, "[{ClassName}] | [HandleAsync] | gRPC request failed with status {Status} in {ElapsedMilliseconds} ms", - _className, + className, rpcException.Status, - _stopwatch.ElapsedMilliseconds + stopwatch.ElapsedMilliseconds ); throw; } } - public virtual Task HandleInternalAsync() where TRequest : class where TResponse : class; + public abstract Task HandleInternalAsync(TRequest request) where TRequest : class where TResponse : class; } diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index 99c820e3..faa6f1f3 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -1,12 +1,29 @@ -using GrpcPayment; -using static GrpcPayment.PaymentService; +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; -public sealed class PaymentsService : BaseGrpcService +public sealed class PaymentsService : BaseGrpcService { - private readonly PaymentServiceClient _Client; - public PaymentsService(string baseAddress) : base(baseAddress) => _Client = new(Channel); + private readonly GrpcPayment.PaymentService.PaymentServiceClient _Client; - public async Task CreateAsync(CreatePaymentRequest request) => await _Client.CreateAsync(request); -} \ No newline at end of file + public PaymentsService(string baseAddress, ILogger logger) + : base(baseAddress, logger) + { + _Client = new GrpcPayment.PaymentService.PaymentServiceClient(Channel); + } + + public override async Task HandleInternalAsync(TRequest request) + where TRequest : class + where TResponse : class + { + if (request is not GrpcPayment.CreatePaymentRequest grpcRequest) + throw new InvalidOperationException($"Unsupported request type: {request?.GetType().FullName}"); + + var asyncCall = _Client.CreateAsync(grpcRequest); + var grpcResponse = await asyncCall.ResponseAsync; + + return (TResponse)(object)grpcResponse!; + } +} diff --git a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs index bd7f99cd..f86fa452 100644 --- a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs @@ -1,5 +1,5 @@ using Contracts.Common; -using Contracts.Payments; +using GrpcPayment; using Infrastructure.Grpc; using Infrastructure.Http; using Microsoft.AspNetCore.Mvc; @@ -22,7 +22,7 @@ public static IEndpointRouteBuilder MapPaymentEndpoints(this IEndpointRouteBuild [FromBody] CreatePaymentRequest request ) => { - var result = await paymentsService.CreateAsync(request); + var result = await paymentsService.HandleAsync(request); return result.Success ? Results.Ok(result) : Results.BadRequest(result); }) @@ -34,4 +34,4 @@ [FromBody] CreatePaymentRequest request return endpoints; } -} \ No newline at end of file +} From f8124a3924770e73afdbc0445714aa8f61b7cc04 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 28 Jan 2026 07:24:18 -0300 Subject: [PATCH 030/161] 53 feat: enhance BaseGrpcService with logging and refactor PaymentsService --- templates/Bff/Directory.Build.props | 2 + .../Infrastructure/Grpc/BaseGrpcService.cs | 64 +++++++++++-------- .../Infrastructure/Grpc/PaymentsService.cs | 21 +----- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/templates/Bff/Directory.Build.props b/templates/Bff/Directory.Build.props index a31e77ed..60f92d98 100644 --- a/templates/Bff/Directory.Build.props +++ b/templates/Bff/Directory.Build.props @@ -4,6 +4,8 @@ enable enable true + latest + Recommended true false diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index bae6470a..88dea67e 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -5,53 +5,65 @@ namespace Infrastructure.Grpc; -public abstract class BaseGrpcService +public partial class BaseGrpcService { protected GrpcChannel Channel { get; } - protected readonly ILogger logger; - protected readonly Stopwatch stopwatch = new(); - protected readonly string className; + public ILogger Logger { get; } + public Stopwatch Stopwatch { get; } = new(); + public string ClassName { get; } public BaseGrpcService(string baseAddress, ILogger logger) { var classType = GetType(); - className = classType.Name; + ClassName = classType.Name; Channel = GrpcChannel.ForAddress(baseAddress); - this.logger = logger; + Logger = logger; } - public async Task HandleAsync(TRequest request) + protected async Task ExecuteHandlerAsync( + TRequest request, + Func>> handler + ) where TRequest : class where TResponse : class { - stopwatch.Start(); + Stopwatch.Restart(); try { - logger.LogInformation("[{ClassName}] | [HandleAsync] | Sending gRPC request", className); + StartingRequest(Logger, ClassName); - var response = await HandleInternalAsync(request); + var response = await handler(request); - logger.LogInformation( - "[{ClassName}] | [HandleAsync] | gRPC request completed in {ElapsedMilliseconds} ms", - className, - stopwatch.ElapsedMilliseconds - ); - - return response; + RequestCompleted(Logger, ClassName, Stopwatch.ElapsedMilliseconds); + return await response.ResponseAsync; } - catch (RpcException rpcException) + catch (Exception ex) { - logger.LogError( - rpcException, - "[{ClassName}] | [HandleAsync] | gRPC request failed with status {Status} in {ElapsedMilliseconds} ms", - className, - rpcException.Status, - stopwatch.ElapsedMilliseconds - ); + RequestFailed(Logger, ClassName, Stopwatch.ElapsedMilliseconds, ex); + throw; } } - public abstract Task HandleInternalAsync(TRequest request) where TRequest : class where TResponse : class; + [LoggerMessage( + EventId = 1, + Level = LogLevel.Information, + Message = "[{ClassName}] | [ExecuteHandlerAsync] | Starting request" + )] + public static partial void StartingRequest(ILogger logger, string className); + + [LoggerMessage( + EventId = 2, + Level = LogLevel.Information, + Message = "[{ClassName}] | [ExecuteHandlerAsync] | Completed in {ElapsedMilliseconds} ms" + )] + public static partial void RequestCompleted(ILogger logger, string className, long elapsedMilliseconds); + + [LoggerMessage( + EventId = 3, + Level = LogLevel.Error, + Message = "[{ClassName}] | [ExecuteHandlerAsync] | Failed in {ElapsedMilliseconds} ms" + )] + public static partial void RequestFailed(ILogger logger, string className, long elapsedMilliseconds, Exception exception); } diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index faa6f1f3..ec6a2384 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -1,5 +1,3 @@ -using System; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; @@ -9,21 +7,8 @@ public sealed class PaymentsService : BaseGrpcService private readonly GrpcPayment.PaymentService.PaymentServiceClient _Client; public PaymentsService(string baseAddress, ILogger logger) - : base(baseAddress, logger) - { - _Client = new GrpcPayment.PaymentService.PaymentServiceClient(Channel); - } + : base(baseAddress, logger) => _Client = new GrpcPayment.PaymentService.PaymentServiceClient(Channel); - public override async Task HandleInternalAsync(TRequest request) - where TRequest : class - where TResponse : class - { - if (request is not GrpcPayment.CreatePaymentRequest grpcRequest) - throw new InvalidOperationException($"Unsupported request type: {request?.GetType().FullName}"); - - var asyncCall = _Client.CreateAsync(grpcRequest); - var grpcResponse = await asyncCall.ResponseAsync; - - return (TResponse)(object)grpcResponse!; - } + public async Task CreatePaymentAsync(GrpcPayment.CreatePaymentRequest request) => + await ExecuteHandlerAsync(request, _Client.CreateAsync); } From fd48bbf2058a85b6062f01857110c37cea2a0bc0 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 28 Jan 2026 07:24:59 -0300 Subject: [PATCH 031/161] 53 feat: add analysis level and mode to Directory.Build.props --- templates/Full/Directory.Build.props | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/Full/Directory.Build.props b/templates/Full/Directory.Build.props index a31e77ed..60f92d98 100644 --- a/templates/Full/Directory.Build.props +++ b/templates/Full/Directory.Build.props @@ -4,6 +4,8 @@ enable enable true + latest + Recommended true false From af2666af12b59fe9a0b2f97d569bb94d163e2f51 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 28 Jan 2026 07:32:05 -0300 Subject: [PATCH 032/161] 53 feat: update logging in services and adjust package versions --- templates/Bff/Directory.Build.props | 3 -- templates/Bff/Directory.Packages.props | 1 + .../Cache/HybridCacheService.cs | 52 ++++++++++++++++--- .../Infrastructure/Grpc/BaseGrpcService.cs | 3 -- .../Infrastructure/Grpc/PaymentsService.cs | 4 +- .../Infrastructure/Http/BaseHttpService.cs | 39 +++++++++----- templates/Full/Directory.Build.props | 3 -- templates/Full/Directory.Packages.props | 1 + 8 files changed, 75 insertions(+), 31 deletions(-) diff --git a/templates/Bff/Directory.Build.props b/templates/Bff/Directory.Build.props index 60f92d98..5053742d 100644 --- a/templates/Bff/Directory.Build.props +++ b/templates/Bff/Directory.Build.props @@ -9,7 +9,4 @@ true false - - - \ No newline at end of file diff --git a/templates/Bff/Directory.Packages.props b/templates/Bff/Directory.Packages.props index 9e19c223..1bc13279 100644 --- a/templates/Bff/Directory.Packages.props +++ b/templates/Bff/Directory.Packages.props @@ -5,6 +5,7 @@ $(NoWarn);NU1507 + diff --git a/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs b/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs index 6c265b0a..af013a1d 100644 --- a/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs +++ b/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs @@ -4,7 +4,7 @@ namespace Infrastructure.Cache; -public sealed class HybridCacheService(HybridCache cache, ILogger logger) +public sealed partial class HybridCacheService(HybridCache cache, ILogger logger) { private readonly HybridCache _cache = cache; private readonly ILogger _logger = logger; @@ -15,30 +15,68 @@ public async ValueTask GetOrCreateAsync( CancellationToken cancellationToken ) { - _logger.LogDebug("[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Retrieving cache entry", key); + RetrievingCacheEntry(_logger, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - _logger.LogDebug("[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Cache entry retrieved", key); + CacheEntryRetrieved(_logger, key); return result; } public async ValueTask CreateAsync(string key, TResult value, CancellationToken cancellationToken) { - _logger.LogDebug("[HybridCacheService] | [CreateAsync] | [{Key}] | Creating cache entry", key); + CreatingCacheEntry(_logger, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - _logger.LogDebug("[HybridCacheService] | [CreateAsync] | [{Key}] | Cache entry created", key); + CacheEntryCreated(_logger, key); } public async ValueTask DeleteAsync(string key, CancellationToken cancellationToken) { - _logger.LogDebug("[HybridCacheService] | [DeleteAsync] | [{Key}] | Deleting cache entry", key); + DeletingCacheEntry(_logger, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - _logger.LogDebug("[HybridCacheService] | [DeleteAsync] | [{Key}] | Cache entry deleted", key); + CacheEntryDeleted(_logger, key); } + + [LoggerMessage( + EventId = 1, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Retrieving cache entry" + )] + private static partial void RetrievingCacheEntry(ILogger logger, string key); + + [LoggerMessage( + EventId = 2, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Cache entry retrieved" + )] + private static partial void CacheEntryRetrieved(ILogger logger, string key); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Creating cache entry" + )] + private static partial void CreatingCacheEntry(ILogger logger, string key); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Cache entry created" + )] + private static partial void CacheEntryCreated(ILogger logger, string key); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Deleting cache entry" + )] + private static partial void DeletingCacheEntry(ILogger logger, string key); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Cache entry deleted" + )] + private static partial void CacheEntryDeleted(ILogger logger, string key); } diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index 88dea67e..c51e993b 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -46,21 +46,18 @@ Func>> handler } [LoggerMessage( - EventId = 1, Level = LogLevel.Information, Message = "[{ClassName}] | [ExecuteHandlerAsync] | Starting request" )] public static partial void StartingRequest(ILogger logger, string className); [LoggerMessage( - EventId = 2, Level = LogLevel.Information, Message = "[{ClassName}] | [ExecuteHandlerAsync] | Completed in {ElapsedMilliseconds} ms" )] public static partial void RequestCompleted(ILogger logger, string className, long elapsedMilliseconds); [LoggerMessage( - EventId = 3, Level = LogLevel.Error, Message = "[{ClassName}] | [ExecuteHandlerAsync] | Failed in {ElapsedMilliseconds} ms" )] diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index ec6a2384..66c25e50 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -9,6 +9,6 @@ public sealed class PaymentsService : BaseGrpcService public PaymentsService(string baseAddress, ILogger logger) : base(baseAddress, logger) => _Client = new GrpcPayment.PaymentService.PaymentServiceClient(Channel); - public async Task CreatePaymentAsync(GrpcPayment.CreatePaymentRequest request) => - await ExecuteHandlerAsync(request, _Client.CreateAsync); + // public async Task CreatePaymentAsync(GrpcPayment.CreatePaymentRequest request) => + // await ExecuteHandlerAsync(request, _Client.CreateAsync); } diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 38eaba76..fd70161e 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -1,10 +1,11 @@ using System.Diagnostics; +using System.Net; using System.Text.Json; using Microsoft.Extensions.Logging; namespace Infrastructure.Http; -public class BaseHttpService(HttpClient httpClient, ILogger logger) +public partial class BaseHttpService(HttpClient httpClient, ILogger logger) { protected readonly HttpClient _httpClient = httpClient; protected readonly ILogger _logger = logger; @@ -21,7 +22,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log ) where TRequest : class where TResponse : class { _stopwatch.Start(); - _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); + SendingRequest(_logger, method, requestUri); HttpRequestMessage requestMessage = new(method, requestUri); @@ -40,10 +41,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log if (!response.IsSuccessStatusCode) { - _logger.LogWarning( - "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms", - method, requestUri, response.ReasonPhrase, response.StatusCode, _stopwatch.ElapsedMilliseconds - ); + RequestFailed(_logger, method, requestUri, response.ReasonPhrase, response.StatusCode, _stopwatch.ElapsedMilliseconds); return null; } @@ -51,7 +49,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); - _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); + RequestCompleted(_logger, method, requestUri, _stopwatch.ElapsedMilliseconds); return result; } @@ -64,7 +62,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log ) where TResponse : class { _stopwatch.Start(); - _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request", method, requestUri); + SendingRequest(_logger, method, requestUri); var requestMessage = new HttpRequestMessage(method, requestUri); @@ -75,10 +73,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log if (!response.IsSuccessStatusCode) { - _logger.LogWarning( - "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms", - method, requestUri, response.ReasonPhrase, response.StatusCode, _stopwatch.ElapsedMilliseconds - ); + RequestFailed(_logger, method, requestUri, response.ReasonPhrase, response.StatusCode, _stopwatch.ElapsedMilliseconds); return null; } @@ -86,8 +81,26 @@ public class BaseHttpService(HttpClient httpClient, ILogger log var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); - _logger.LogDebug("[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms", method, requestUri, _stopwatch.ElapsedMilliseconds); + RequestCompleted(_logger, method, requestUri, _stopwatch.ElapsedMilliseconds); return result; } + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request" + )] + private static partial void SendingRequest(ILogger logger, HttpMethod method, string requestUri); + + [LoggerMessage( + Level = LogLevel.Warning, + Message = "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms" + )] + private static partial void RequestFailed(ILogger logger, HttpMethod method, string requestUri, string? message, HttpStatusCode statusCode, long elapsedMilliseconds); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms" + )] + private static partial void RequestCompleted(ILogger logger, HttpMethod method, string requestUri, long elapsedMilliseconds); } diff --git a/templates/Full/Directory.Build.props b/templates/Full/Directory.Build.props index 60f92d98..5053742d 100644 --- a/templates/Full/Directory.Build.props +++ b/templates/Full/Directory.Build.props @@ -9,7 +9,4 @@ true false - - - \ No newline at end of file diff --git a/templates/Full/Directory.Packages.props b/templates/Full/Directory.Packages.props index b9b9e9d1..d60a8ed1 100644 --- a/templates/Full/Directory.Packages.props +++ b/templates/Full/Directory.Packages.props @@ -5,6 +5,7 @@ $(NoWarn);NU1507 + From 291552c3a0df7ab33b7156fc5e2edac6c8f203c8 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 28 Jan 2026 07:33:07 -0300 Subject: [PATCH 033/161] 53 feat: use current culture for grpc exporter protocol check --- .../src/Infrastructure/InfrastructureDependencyInjection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 320058ab..803b46eb 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -11,6 +11,7 @@ using Infrastructure.Http; using Polly; using Polly.Extensions.Http; +using System.Globalization; namespace Infrastructure; @@ -34,7 +35,7 @@ public WebApplicationBuilder AddInfrastructure() internal WebApplicationBuilder AddOpenTelemetry() { var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower() == "grpc" + var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower(CultureInfo.CurrentCulture) == "grpc" ? OtlpExportProtocol.Grpc : OtlpExportProtocol.HttpProtobuf; var exporterMetricsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"); From 52c410507c3fce58b238bb55f665f25481d47a68 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 28 Jan 2026 07:42:17 -0300 Subject: [PATCH 034/161] 53 refactor: update BaseHttpService and InfrastructureDependencyInjection for improved error handling --- .../Infrastructure/Http/BaseHttpService.cs | 38 +++++++++---------- .../InfrastructureDependencyInjection.cs | 10 +++-- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index fd70161e..15ee752c 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -7,27 +7,27 @@ namespace Infrastructure.Http; public partial class BaseHttpService(HttpClient httpClient, ILogger logger) { - protected readonly HttpClient _httpClient = httpClient; - protected readonly ILogger _logger = logger; - protected readonly JsonSerializerOptions _jsonSerializerOptions = new(JsonSerializerDefaults.Web); - protected readonly Stopwatch _stopwatch = new(); + public HttpClient HttpClient { get; } = httpClient; + public ILogger Logger { get; } = logger; + public JsonSerializerOptions JsonSerializerOptions { get; } = new(JsonSerializerDefaults.Web); + public Stopwatch Stopwatch { get; } = new(); public async Task SendAsync( string requestUri, HttpMethod method, - CancellationToken cancellationToken, TRequest request, Dictionary? headers = null, - string contentType = "application/json" + string contentType = "application/json", + CancellationToken cancellationToken = default ) where TRequest : class where TResponse : class { - _stopwatch.Start(); - SendingRequest(_logger, method, requestUri); + Stopwatch.Start(); + SendingRequest(Logger, method, requestUri); HttpRequestMessage requestMessage = new(method, requestUri); using MemoryStream memoryStream = new(); - await JsonSerializer.SerializeAsync(memoryStream, request, _jsonSerializerOptions, cancellationToken); + await JsonSerializer.SerializeAsync(memoryStream, request, JsonSerializerOptions, cancellationToken); memoryStream.Seek(0, SeekOrigin.Begin); using var requestContent = new StreamContent(memoryStream); @@ -37,19 +37,19 @@ public partial class BaseHttpService(HttpClient httpClient, ILogger(content, _jsonSerializerOptions, cancellationToken); + var result = await JsonSerializer.DeserializeAsync(content, JsonSerializerOptions, cancellationToken); - RequestCompleted(_logger, method, requestUri, _stopwatch.ElapsedMilliseconds); + RequestCompleted(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); return result; } @@ -61,27 +61,27 @@ public partial class BaseHttpService(HttpClient httpClient, ILogger? headers = null ) where TResponse : class { - _stopwatch.Start(); - SendingRequest(_logger, method, requestUri); + Stopwatch.Start(); + SendingRequest(Logger, method, requestUri); var requestMessage = new HttpRequestMessage(method, requestUri); if (headers != null) foreach (var header in headers) requestMessage.Headers.Add(header.Key, header.Value); - using var response = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + using var response = await HttpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); if (!response.IsSuccessStatusCode) { - RequestFailed(_logger, method, requestUri, response.ReasonPhrase, response.StatusCode, _stopwatch.ElapsedMilliseconds); + RequestFailed(Logger, method, requestUri, response.ReasonPhrase, response.StatusCode, Stopwatch.ElapsedMilliseconds); return null; } var content = await response.Content.ReadAsStreamAsync(cancellationToken); - var result = await JsonSerializer.DeserializeAsync(content, _jsonSerializerOptions, cancellationToken); + var result = await JsonSerializer.DeserializeAsync(content, JsonSerializerOptions, cancellationToken); - RequestCompleted(_logger, method, requestUri, _stopwatch.ElapsedMilliseconds); + RequestCompleted(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); return result; } diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 803b46eb..457222e1 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -15,7 +15,9 @@ namespace Infrastructure; +#pragma warning disable CA1708 // Identifiers should differ by more than case public static class InfrastructureDependencyInjection +#pragma warning restore CA1708 // Identifiers should differ by more than case { extension(WebApplicationBuilder builder) { @@ -109,7 +111,7 @@ internal IServiceCollection AddCache(IConfiguration configuration) services .AddStackExchangeRedisCache(options => { - options.Configuration = configuration.GetConnectionString("Redis") ?? throw new NullReferenceException("Redis connection string is not configured."); + options.Configuration = configuration.GetConnectionString("Redis") ?? throw new ArgumentNullException(configuration.GetConnectionString("Redis"),"Redis connection string is not configured."); options.Configuration += ",abortConnect=false,connectTimeout=5000,syncTimeout=5000"; }) .AddHybridCache(options => @@ -129,7 +131,7 @@ internal IServiceCollection AddCache(IConfiguration configuration) internal IServiceCollection AddHttp(IConfiguration configuration) { var httpConfigurations = configuration.GetSection("Http").Get>() - ?? throw new NullReferenceException("Http services configuration is not configured."); + ?? throw new ArgumentNullException(configuration.GetSection("Http").Path, "Http services configuration is not configured."); var serviceKeys = Enum.GetValues(); @@ -139,12 +141,12 @@ internal IServiceCollection AddHttp(IConfiguration configuration) var serviceConfiguration = httpConfigurations.FirstOrDefault(x => string.Equals(x.Name, serviceName, StringComparison.OrdinalIgnoreCase)) - ?? throw new NullReferenceException($"{serviceName} service configuration is not configured."); + ?? throw new ArgumentNullException($"{serviceName} service configuration is not configured."); services.AddHttpClient(serviceName, client => { client.BaseAddress = new Uri(serviceConfiguration.BaseAddress) - ?? throw new NullReferenceException($"{serviceName} service address is not configured."); + ?? throw new ArgumentNullException($"{serviceName} service address is not configured."); if (serviceConfiguration.Headers is Dictionary headers && headers.Count > 0) foreach (var header in headers) From e69f797380330b5fcd4efdd7df37d8023bcb698c Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 28 Jan 2026 08:10:41 -0300 Subject: [PATCH 035/161] 53 refactor: rename logging methods in BaseHttpService and BaseGrpcService --- .../src/Infrastructure/Grpc/BaseGrpcService.cs | 12 ++++++------ .../src/Infrastructure/Http/BaseHttpService.cs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index c51e993b..eb60b2d3 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -30,16 +30,16 @@ Func>> handler Stopwatch.Restart(); try { - StartingRequest(Logger, ClassName); + StartingRequestLog(Logger, ClassName); var response = await handler(request); - RequestCompleted(Logger, ClassName, Stopwatch.ElapsedMilliseconds); + RequestCompletedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds); return await response.ResponseAsync; } catch (Exception ex) { - RequestFailed(Logger, ClassName, Stopwatch.ElapsedMilliseconds, ex); + RequestFailedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds, ex); throw; } @@ -49,18 +49,18 @@ Func>> handler Level = LogLevel.Information, Message = "[{ClassName}] | [ExecuteHandlerAsync] | Starting request" )] - public static partial void StartingRequest(ILogger logger, string className); + public static partial void StartingRequestLog(ILogger logger, string className); [LoggerMessage( Level = LogLevel.Information, Message = "[{ClassName}] | [ExecuteHandlerAsync] | Completed in {ElapsedMilliseconds} ms" )] - public static partial void RequestCompleted(ILogger logger, string className, long elapsedMilliseconds); + public static partial void RequestCompletedLog(ILogger logger, string className, long elapsedMilliseconds); [LoggerMessage( Level = LogLevel.Error, Message = "[{ClassName}] | [ExecuteHandlerAsync] | Failed in {ElapsedMilliseconds} ms" )] - public static partial void RequestFailed(ILogger logger, string className, long elapsedMilliseconds, Exception exception); + public static partial void RequestFailedLog(ILogger logger, string className, long elapsedMilliseconds, Exception exception); } diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 15ee752c..2ea06c7e 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -22,7 +22,7 @@ public partial class BaseHttpService(HttpClient httpClient, ILogger(content, JsonSerializerOptions, cancellationToken); - RequestCompleted(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); + RequestCompletedLog(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); return result; } @@ -62,7 +62,7 @@ public partial class BaseHttpService(HttpClient httpClient, ILogger(content, JsonSerializerOptions, cancellationToken); - RequestCompleted(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); + RequestCompletedLog(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); return result; } @@ -90,17 +90,17 @@ public partial class BaseHttpService(HttpClient httpClient, ILogger Date: Thu, 29 Jan 2026 07:20:40 -0300 Subject: [PATCH 036/161] 53 feat: improve error handling and refactor payment creation logic --- .../src/Infrastructure/Grpc/BaseGrpcService.cs | 15 ++++++++++----- .../src/Infrastructure/Grpc/PaymentsService.cs | 5 +++-- .../Bff/src/WebApp/Endpoints/OrderEndpoints.cs | 8 ++++++-- .../Bff/src/WebApp/Endpoints/PaymentEndpoints.cs | 3 ++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index eb60b2d3..d0a28576 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -20,23 +20,28 @@ public BaseGrpcService(string baseAddress, ILogger logger) Logger = logger; } - protected async Task ExecuteHandlerAsync( - TRequest request, - Func>> handler - ) + protected async Task ExecuteHandlerAsync(Func> handler) where TRequest : class where TResponse : class { Stopwatch.Restart(); + try { StartingRequestLog(Logger, ClassName); - var response = await handler(request); + var response = handler.Invoke(); RequestCompletedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds); + return await response.ResponseAsync; } + catch (RpcException rpcEx) + { + RequestFailedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds, rpcEx); + + throw; + } catch (Exception ex) { RequestFailedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds, ex); diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index 66c25e50..b35ee4e2 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -1,3 +1,4 @@ +using GrpcPayment; using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; @@ -9,6 +10,6 @@ public sealed class PaymentsService : BaseGrpcService public PaymentsService(string baseAddress, ILogger logger) : base(baseAddress, logger) => _Client = new GrpcPayment.PaymentService.PaymentServiceClient(Channel); - // public async Task CreatePaymentAsync(GrpcPayment.CreatePaymentRequest request) => - // await ExecuteHandlerAsync(request, _Client.CreateAsync); + public async Task CreatePaymentAsync(CreatePaymentRequest request) => + await ExecuteHandlerAsync(() => _Client.CreateAsync(request)); } diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index e7186d54..2ec9c3c8 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -3,6 +3,7 @@ using Infrastructure.Cache; using Contracts.Orders; using Contracts.Common; +using System.Globalization; namespace WebApp.Endpoints; @@ -55,12 +56,15 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) CancellationToken cancellationToken ) => { - var response = await httpService.SendAsync>("orders", HttpMethod.Post, cancellationToken, request); + var response = await httpService.SendAsync>( + "orders", HttpMethod.Post, request, + cancellationToken: cancellationToken + ); if (response == null) return Results.BadRequest(); - var id = response.Data?.Id.ToString() ?? "unknown"; + var id = response.Data?.Id.ToString(CultureInfo.InvariantCulture) ?? "unknown"; return Results.Created($"/orders/{id}", response); }) diff --git a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs index f86fa452..65cda82f 100644 --- a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs @@ -13,6 +13,7 @@ public static partial class PaymentEndpoints public static IEndpointRouteBuilder MapPaymentEndpoints(this IEndpointRouteBuilder endpoints) { var serviceKey = ServicesKeys.Payments.ToString(); + var group = endpoints.MapGroup(BasePath) .WithTags(serviceKey) .RequireRateLimiting(serviceKey); @@ -22,7 +23,7 @@ public static IEndpointRouteBuilder MapPaymentEndpoints(this IEndpointRouteBuild [FromBody] CreatePaymentRequest request ) => { - var result = await paymentsService.HandleAsync(request); + var result = await paymentsService.CreatePaymentAsync(request); return result.Success ? Results.Ok(result) : Results.BadRequest(result); }) From ac376db7a9e4b5a44f0db2d7fcae2d708563844f Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 29 Jan 2026 07:38:56 -0300 Subject: [PATCH 037/161] 53 refactor: update payment service client and improve health check --- .../Bff/src/Infrastructure/Grpc/PaymentsService.cs | 4 ++-- .../InfrastructureDependencyInjection.cs | 13 +++++++++++++ .../src/MockApi/Extensions/HealthCheckExtensions.cs | 2 +- .../Bff/src/WebApp/Endpoints/PaymentEndpoints.cs | 4 +--- .../Middlewares/ExceptionHandlingMiddleware.cs | 12 +++++++++--- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index b35ee4e2..9d7260e8 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -5,10 +5,10 @@ namespace Infrastructure.Grpc; public sealed class PaymentsService : BaseGrpcService { - private readonly GrpcPayment.PaymentService.PaymentServiceClient _Client; + private readonly PaymentService.PaymentServiceClient _Client; public PaymentsService(string baseAddress, ILogger logger) - : base(baseAddress, logger) => _Client = new GrpcPayment.PaymentService.PaymentServiceClient(Channel); + : base(baseAddress, logger) => _Client = new PaymentService.PaymentServiceClient(Channel); public async Task CreatePaymentAsync(CreatePaymentRequest request) => await ExecuteHandlerAsync(() => _Client.CreateAsync(request)); diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 457222e1..de69cf69 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -12,6 +12,7 @@ using Polly; using Polly.Extensions.Http; using System.Globalization; +using Infrastructure.Grpc; namespace Infrastructure; @@ -168,6 +169,18 @@ internal IServiceCollection AddHttp(IConfiguration configuration) return services; } + internal IServiceCollection AddGrpc(this IServiceCollection services) + { + services.AddKeyedScoped(ServicesKeys.Payments, (serviceProvider, _) => + { + var logger = serviceProvider.GetRequiredService>(); + + return new PaymentsService(logger); + }); + + return services; + } + internal static IAsyncPolicy GetRetryPolicy() => HttpPolicyExtensions .HandleTransientHttpError() .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); diff --git a/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs b/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs index 77f5680f..fc9ecd97 100644 --- a/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs +++ b/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs @@ -19,7 +19,7 @@ public static IApplicationBuilder UseCustomHealthChecks( this IApplicationBuilder app ) { - app.UseHealthChecks("/health", new HealthCheckOptions() + app.UseHealthChecks("/health", new HealthCheckOptions { Predicate = _ => true, ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse diff --git a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs index 65cda82f..cf35c367 100644 --- a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs @@ -8,13 +8,11 @@ namespace WebApp.Endpoints; public static partial class PaymentEndpoints { - private const string BasePath = "api/payments"; - public static IEndpointRouteBuilder MapPaymentEndpoints(this IEndpointRouteBuilder endpoints) { var serviceKey = ServicesKeys.Payments.ToString(); - var group = endpoints.MapGroup(BasePath) + var group = endpoints.MapGroup(serviceKey) .WithTags(serviceKey) .RequireRateLimiting(serviceKey); diff --git a/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs index 361fdfb9..12f5d2f5 100644 --- a/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs +++ b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -2,7 +2,7 @@ namespace WebApp.Middlewares; -internal sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger) +internal sealed partial class ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger) { private readonly RequestDelegate _next = next; private readonly ILogger _logger = logger; @@ -22,11 +22,17 @@ public async Task InvokeAsync(HttpContext context) private async Task HandleExceptionAsync(HttpContext context, Exception exception) { - _logger.LogError(exception, "[{ClassName}] | [{Method}] | {Message}", _className, nameof(HandleExceptionAsync), exception.Message); - context.Response.ContentType = "application/json"; context.Response.StatusCode = (int) HttpStatusCode.BadRequest; + + RequestFailedLog(_logger, _className, nameof(HandleExceptionAsync), exception.Message); } + + [LoggerMessage( + Level = LogLevel.Error, + Message = "[{ClassName}] | [{Method}] | Request failed | Message: {Message}" + )] + public static partial void RequestFailedLog(ILogger logger, string className, string method, string message); } public record ExceptionResponse(HttpStatusCode StatusCode, string Description); From 687caccf900a5bc5e95c54f4185e067765edc661 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 29 Jan 2026 07:42:23 -0300 Subject: [PATCH 038/161] 53 refactor: simplify ExecuteHandlerAsync method signature --- templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs | 4 +--- templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs | 2 +- .../src/Infrastructure/InfrastructureDependencyInjection.cs | 5 +++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index d0a28576..f5f9237a 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -20,9 +20,7 @@ public BaseGrpcService(string baseAddress, ILogger logger) Logger = logger; } - protected async Task ExecuteHandlerAsync(Func> handler) - where TRequest : class - where TResponse : class + protected async Task ExecuteHandlerAsync(Func> handler) where TResponse : class { Stopwatch.Restart(); diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index 9d7260e8..092e7d11 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -11,5 +11,5 @@ public PaymentsService(string baseAddress, ILogger logger) : base(baseAddress, logger) => _Client = new PaymentService.PaymentServiceClient(Channel); public async Task CreatePaymentAsync(CreatePaymentRequest request) => - await ExecuteHandlerAsync(() => _Client.CreateAsync(request)); + await ExecuteHandlerAsync(() => _Client.CreateAsync(request)); } diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index de69cf69..1ef6da5e 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -28,7 +28,8 @@ public WebApplicationBuilder AddInfrastructure() builder.Services .AddCache(configuration) - .AddHttp(configuration); + .AddHttp(configuration) + .AddGrpc(configuration); builder.AddOpenTelemetry(); @@ -169,7 +170,7 @@ internal IServiceCollection AddHttp(IConfiguration configuration) return services; } - internal IServiceCollection AddGrpc(this IServiceCollection services) + internal IServiceCollection AddGrpc(IConfiguration configuration) { services.AddKeyedScoped(ServicesKeys.Payments, (serviceProvider, _) => { From 56851c52105a9150a0a53a5aa223f165c73aa436 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 29 Jan 2026 07:52:13 -0300 Subject: [PATCH 039/161] 53 refactor: update BaseGrpcService and PaymentsService for gRPC client --- .../Infrastructure/Grpc/BaseGrpcService.cs | 33 ++++++++++++++++--- .../Infrastructure/Grpc/PaymentsService.cs | 10 ++---- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index f5f9237a..8531cc75 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -1,25 +1,48 @@ using System.Diagnostics; using Grpc.Core; -using Grpc.Net.Client; +using Grpc.Net.ClientFactory; using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; -public partial class BaseGrpcService +public partial class BaseGrpcService where TGrpcService : ClientBase { - protected GrpcChannel Channel { get; } + /// + /// gRPC client instance + /// + protected TGrpcService Client { get; } + /// + /// Logger instance for logging + /// public ILogger Logger { get; } + /// + /// Stopwatch instance for measuring request duration + /// public Stopwatch Stopwatch { get; } = new(); + /// + /// Class name of the derived gRPC service + /// public string ClassName { get; } - public BaseGrpcService(string baseAddress, ILogger logger) + /// + /// Base gRPC service constructor + /// + /// logger instance for logging + /// gRPC client factory for creating gRPC clients + public BaseGrpcService(ILogger logger, GrpcClientFactory grpcClientFactory) { var classType = GetType(); ClassName = classType.Name; - Channel = GrpcChannel.ForAddress(baseAddress); Logger = logger; + Client = grpcClientFactory.CreateClient(ClassName); } + /// + /// Executes a gRPC handler with logging and error handling + /// + /// TResponse type returned by the gRPC handler + /// handler function representing the gRPC call + /// Task representing the asynchronous operation, containing the gRPC response protected async Task ExecuteHandlerAsync(Func> handler) where TResponse : class { Stopwatch.Restart(); diff --git a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs index 092e7d11..ef1e01f2 100644 --- a/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/PaymentsService.cs @@ -1,15 +1,11 @@ +using Grpc.Net.ClientFactory; using GrpcPayment; using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; -public sealed class PaymentsService : BaseGrpcService +public sealed class PaymentsService(ILogger logger, GrpcClientFactory grpcClientFactory) : BaseGrpcService(logger, grpcClientFactory) { - private readonly PaymentService.PaymentServiceClient _Client; - - public PaymentsService(string baseAddress, ILogger logger) - : base(baseAddress, logger) => _Client = new PaymentService.PaymentServiceClient(Channel); - public async Task CreatePaymentAsync(CreatePaymentRequest request) => - await ExecuteHandlerAsync(() => _Client.CreateAsync(request)); + await ExecuteHandlerAsync(() => Client.CreateAsync(request)); } From 74a076583644cfd2076cbc9d3c2505d3044a5095 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 30 Jan 2026 07:30:01 -0300 Subject: [PATCH 040/161] 53 feat: add logging methods for request handling in services --- .../Bff/src/Infrastructure/Common/Logs.cs | 126 ++++++++++++++++++ .../Infrastructure/Grpc/BaseGrpcService.cs | 35 ++--- .../Infrastructure/Http/BaseHttpService.cs | 44 ++---- .../ExceptionHandlingMiddleware.cs | 11 +- 4 files changed, 154 insertions(+), 62 deletions(-) create mode 100644 templates/Bff/src/Infrastructure/Common/Logs.cs diff --git a/templates/Bff/src/Infrastructure/Common/Logs.cs b/templates/Bff/src/Infrastructure/Common/Logs.cs new file mode 100644 index 00000000..9031b073 --- /dev/null +++ b/templates/Bff/src/Infrastructure/Common/Logs.cs @@ -0,0 +1,126 @@ +using System.Net; +using Microsoft.Extensions.Logging; + +namespace Infrastructure.Common; + +public static partial class Logs +{ + /// + /// Logs the sending of a request + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + [LoggerMessage( + EventId = 1, + Level = LogLevel.Information, + Message = "[{ClassName}] | [{Method}] | Sending request" + )] + public static partial void SendingRequest(ILogger logger, string className, string method); + + /// + /// Logs the sending of a request + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + /// HTTP method of the request + /// URI of the request + [LoggerMessage( + EventId = 2, + Level = LogLevel.Information, + Message = "[{ClassName}] | [{Method}] | [{HttpMethod}] | [{RequestUri}] | Sending request" + )] + public static partial void SendingRequest(ILogger logger, string className, string method, HttpMethod httpMethod, string requestUri); + + /// + /// Logs a completed request with elapsed time + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + [LoggerMessage( + EventId = 3, + Level = LogLevel.Information, + Message = "[{ClassName}] | [{Method}] | Request completed" + )] + public static partial void RequestCompleted(ILogger logger, string className, string method); + + + /// + /// Logs a completed request with elapsed time + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + /// elapsed time in milliseconds + [LoggerMessage( + EventId = 4, + Level = LogLevel.Information, + Message = "[{ClassName}] | [{Method}] | Request completed in {ElapsedMilliseconds} ms" + )] + public static partial void RequestCompletedWithElapsed(ILogger logger, string className, string method, long elapsedMilliseconds); + + /// + /// Logs a completed request with elapsed time + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + /// HTTP method of the request + /// URI of the request + /// elapsed time in milliseconds + [LoggerMessage( + EventId = 5, + Level = LogLevel.Information, + Message = "[{ClassName}] | [{Method}] | [{HttpMethod}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms" + )] + public static partial void RequestCompletedWithElapsed(ILogger logger, string className, string method, HttpMethod httpMethod, string requestUri, long elapsedMilliseconds); + + /// + /// Logs a failed request + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + /// message associated with the request + [LoggerMessage( + EventId = 6, + Level = LogLevel.Error, + Message = "[{ClassName}] | [{Method}] | Request failed | Message: {Message}" + )] + public static partial void RequestFailed(ILogger logger, string className, string method, string message); + + /// + /// Logs a failed request with elapsed time + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + /// elapsed time in milliseconds + /// exception that occurred + [LoggerMessage( + EventId = 7, + Level = LogLevel.Error, + Message = "[{ClassName}] | [{Method}] | Failed in {ElapsedMilliseconds} ms" + )] + public static partial void RequestFailedWithElapsed(ILogger logger, string className, string method, long elapsedMilliseconds, Exception exception); + + /// + /// Logs a failed request with elapsed time + /// + /// logger instance for logging + /// class name of the calling service + /// method name of the calling service + /// HTTP method of the request + /// URI of the request + /// message associated with the request + /// HTTP status code of the response + /// elapsed time in milliseconds + [LoggerMessage( + EventId = 8, + Level = LogLevel.Error, + Message = "[{ClassName}] | [{Method}] | [{HttpMethod}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms" + )] + public static partial void RequestFailedWithElapsed(ILogger logger, string className, string method, HttpMethod httpMethod, string requestUri, string? message, HttpStatusCode statusCode, long elapsedMilliseconds); +} \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index 8531cc75..883baa53 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -1,6 +1,8 @@ using System.Diagnostics; +using System.Runtime.CompilerServices; using Grpc.Core; using Grpc.Net.ClientFactory; +using Infrastructure.Common; using Microsoft.Extensions.Logging; namespace Infrastructure.Grpc; @@ -42,51 +44,36 @@ public BaseGrpcService(ILogger logger, GrpcClientFactory grpcClientFactory) /// /// TResponse type returned by the gRPC handler /// handler function representing the gRPC call + /// name of the calling method (automatically provided) /// Task representing the asynchronous operation, containing the gRPC response - protected async Task ExecuteHandlerAsync(Func> handler) where TResponse : class + protected async Task ExecuteHandlerAsync( + Func> handler, + [CallerMemberName] string? methodName = null + ) where TResponse : class { Stopwatch.Restart(); try { - StartingRequestLog(Logger, ClassName); + Logs.SendingRequest(Logger, ClassName, methodName!); var response = handler.Invoke(); - RequestCompletedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds); - + Logs.RequestCompletedWithElapsed(Logger, ClassName, methodName!, Stopwatch.ElapsedMilliseconds); return await response.ResponseAsync; } catch (RpcException rpcEx) { - RequestFailedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds, rpcEx); + Logs.RequestFailedWithElapsed(Logger, ClassName, methodName!, Stopwatch.ElapsedMilliseconds, rpcEx); throw; } catch (Exception ex) { - RequestFailedLog(Logger, ClassName, Stopwatch.ElapsedMilliseconds, ex); + Logs.RequestFailedWithElapsed(Logger, ClassName, methodName!, Stopwatch.ElapsedMilliseconds, ex); throw; } } - - [LoggerMessage( - Level = LogLevel.Information, - Message = "[{ClassName}] | [ExecuteHandlerAsync] | Starting request" - )] - public static partial void StartingRequestLog(ILogger logger, string className); - - [LoggerMessage( - Level = LogLevel.Information, - Message = "[{ClassName}] | [ExecuteHandlerAsync] | Completed in {ElapsedMilliseconds} ms" - )] - public static partial void RequestCompletedLog(ILogger logger, string className, long elapsedMilliseconds); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "[{ClassName}] | [ExecuteHandlerAsync] | Failed in {ElapsedMilliseconds} ms" - )] - public static partial void RequestFailedLog(ILogger logger, string className, long elapsedMilliseconds, Exception exception); } diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 2ea06c7e..5a24cfa4 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -1,20 +1,22 @@ using System.Diagnostics; -using System.Net; using System.Text.Json; +using Infrastructure.Common; using Microsoft.Extensions.Logging; namespace Infrastructure.Http; -public partial class BaseHttpService(HttpClient httpClient, ILogger logger) +public class BaseHttpService(HttpClient httpClient, ILogger logger) { public HttpClient HttpClient { get; } = httpClient; public ILogger Logger { get; } = logger; public JsonSerializerOptions JsonSerializerOptions { get; } = new(JsonSerializerDefaults.Web); public Stopwatch Stopwatch { get; } = new(); + private readonly string _className = nameof(BaseHttpService); + private readonly string _method = nameof(SendAsync); public async Task SendAsync( string requestUri, - HttpMethod method, + HttpMethod httpMethod, TRequest request, Dictionary? headers = null, string contentType = "application/json", @@ -22,9 +24,9 @@ public partial class BaseHttpService(HttpClient httpClient, ILogger(content, JsonSerializerOptions, cancellationToken); - RequestCompletedLog(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); + Logs.RequestCompletedWithElapsed(Logger, _className, _method, httpMethod, requestUri, Stopwatch.ElapsedMilliseconds); return result; } public async Task SendAsync( string requestUri, - HttpMethod method, + HttpMethod httpMethod, CancellationToken cancellationToken, Dictionary? headers = null ) where TResponse : class { Stopwatch.Start(); - SendingRequestLog(Logger, method, requestUri); + Logs.SendingRequest(Logger, _className, _method, httpMethod, requestUri); - var requestMessage = new HttpRequestMessage(method, requestUri); + var requestMessage = new HttpRequestMessage(httpMethod, requestUri); if (headers != null) foreach (var header in headers) requestMessage.Headers.Add(header.Key, header.Value); @@ -73,7 +75,7 @@ public partial class BaseHttpService(HttpClient httpClient, ILogger(content, JsonSerializerOptions, cancellationToken); - RequestCompletedLog(Logger, method, requestUri, Stopwatch.ElapsedMilliseconds); + Logs.RequestCompletedWithElapsed(Logger, _className, _method, httpMethod, requestUri, Stopwatch.ElapsedMilliseconds); return result; } - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Sending request" - )] - private static partial void SendingRequestLog(ILogger logger, HttpMethod method, string requestUri); - - [LoggerMessage( - Level = LogLevel.Warning, - Message = "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms" - )] - private static partial void RequestFailedLog(ILogger logger, HttpMethod method, string requestUri, string? message, HttpStatusCode statusCode, long elapsedMilliseconds); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "[BaseHttpService] | [SendAsync] | [{Method}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms" - )] - private static partial void RequestCompletedLog(ILogger logger, HttpMethod method, string requestUri, long elapsedMilliseconds); } diff --git a/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs index 12f5d2f5..523ea071 100644 --- a/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs +++ b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -1,8 +1,9 @@ using System.Net; +using Infrastructure.Common; namespace WebApp.Middlewares; -internal sealed partial class ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger) +internal sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger) { private readonly RequestDelegate _next = next; private readonly ILogger _logger = logger; @@ -25,14 +26,8 @@ private async Task HandleExceptionAsync(HttpContext context, Exception exception context.Response.ContentType = "application/json"; context.Response.StatusCode = (int) HttpStatusCode.BadRequest; - RequestFailedLog(_logger, _className, nameof(HandleExceptionAsync), exception.Message); + Logs.RequestFailed(_logger, _className, nameof(HandleExceptionAsync), exception.Message); } - - [LoggerMessage( - Level = LogLevel.Error, - Message = "[{ClassName}] | [{Method}] | Request failed | Message: {Message}" - )] - public static partial void RequestFailedLog(ILogger logger, string className, string method, string message); } public record ExceptionResponse(HttpStatusCode StatusCode, string Description); From 218f06b9f1f390034aaaa22d5205d00b44ff5e7f Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 30 Jan 2026 07:48:50 -0300 Subject: [PATCH 041/161] 53 feat: add service configuration and update service keys --- .../ServiceConfiguration.cs} | 4 +- .../src/Infrastructure/Common/ServicesKey.cs | 7 ++++ .../src/Infrastructure/Http/ServicesKeys.cs | 7 ---- .../InfrastructureDependencyInjection.cs | 40 ++++++++++++------- .../src/WebApp/Endpoints/OrderEndpoints.cs | 7 ++-- .../src/WebApp/Endpoints/PaymentEndpoints.cs | 4 +- .../Extensions/HealthCheckExtensions.cs | 4 +- .../WebApp/Extensions/RateLimitExtensions.cs | 12 ++---- .../src/WebApp/appsettings.Development.json | 6 +-- templates/Bff/src/WebApp/appsettings.json | 2 +- 10 files changed, 49 insertions(+), 44 deletions(-) rename templates/Bff/src/Infrastructure/{Http/ServiceConfigurations.cs => Common/ServiceConfiguration.cs} (74%) create mode 100644 templates/Bff/src/Infrastructure/Common/ServicesKey.cs delete mode 100644 templates/Bff/src/Infrastructure/Http/ServicesKeys.cs diff --git a/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs b/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs similarity index 74% rename from templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs rename to templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs index 2bb3bdbb..5f68093f 100644 --- a/templates/Bff/src/Infrastructure/Http/ServiceConfigurations.cs +++ b/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs @@ -1,6 +1,6 @@ -namespace Infrastructure.Http; +namespace Infrastructure.Common; -public sealed class ServiceConfigurations +public sealed class ServiceConfiguration { public string Name { get; set; } public string BaseAddress { get; set; } diff --git a/templates/Bff/src/Infrastructure/Common/ServicesKey.cs b/templates/Bff/src/Infrastructure/Common/ServicesKey.cs new file mode 100644 index 00000000..d9e05955 --- /dev/null +++ b/templates/Bff/src/Infrastructure/Common/ServicesKey.cs @@ -0,0 +1,7 @@ +namespace Infrastructure.Common; + +public enum ServicesKey +{ + Orders, + Payments +} diff --git a/templates/Bff/src/Infrastructure/Http/ServicesKeys.cs b/templates/Bff/src/Infrastructure/Http/ServicesKeys.cs deleted file mode 100644 index 75cf3f9d..00000000 --- a/templates/Bff/src/Infrastructure/Http/ServicesKeys.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Infrastructure.Http; - -public enum ServicesKeys -{ - Orders, - Payments -} diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 1ef6da5e..17a1aa5b 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -13,6 +13,8 @@ using Polly.Extensions.Http; using System.Globalization; using Infrastructure.Grpc; +using Infrastructure.Common; +using GrpcPayment; namespace Infrastructure; @@ -26,10 +28,13 @@ public WebApplicationBuilder AddInfrastructure() { var configuration = builder.Configuration; + var serviceConfiguration = configuration.GetSection("Services").Get>() + ?? throw new ArgumentNullException(configuration.GetSection("Services").Path, "Services configuration is not configured."); + builder.Services .AddCache(configuration) - .AddHttp(configuration) - .AddGrpc(configuration); + .AddHttp(serviceConfiguration) + .AddGrpc(serviceConfiguration); builder.AddOpenTelemetry(); @@ -130,27 +135,25 @@ internal IServiceCollection AddCache(IConfiguration configuration) return services; } - internal IServiceCollection AddHttp(IConfiguration configuration) + internal IServiceCollection AddHttp(List serviceConfiguration) { - var httpConfigurations = configuration.GetSection("Http").Get>() - ?? throw new ArgumentNullException(configuration.GetSection("Http").Path, "Http services configuration is not configured."); - var serviceKeys = Enum.GetValues(); + var serviceKeys = Enum.GetValues(); foreach (var serviceKey in serviceKeys) { var serviceName = serviceKey.ToString(); - var serviceConfiguration = httpConfigurations.FirstOrDefault(x => + var serviceConfig = serviceConfiguration.FirstOrDefault(x => string.Equals(x.Name, serviceName, StringComparison.OrdinalIgnoreCase)) ?? throw new ArgumentNullException($"{serviceName} service configuration is not configured."); services.AddHttpClient(serviceName, client => { - client.BaseAddress = new Uri(serviceConfiguration.BaseAddress) + client.BaseAddress = new Uri(serviceConfig.BaseAddress) ?? throw new ArgumentNullException($"{serviceName} service address is not configured."); - if (serviceConfiguration.Headers is Dictionary headers && headers.Count > 0) + if (serviceConfig.Headers is Dictionary headers && headers.Count > 0) foreach (var header in headers) client.DefaultRequestHeaders.Add(header.Key, header.Value); }) @@ -170,14 +173,21 @@ internal IServiceCollection AddHttp(IConfiguration configuration) return services; } - internal IServiceCollection AddGrpc(IConfiguration configuration) + internal IServiceCollection AddGrpc(List serviceConfiguration) { - services.AddKeyedScoped(ServicesKeys.Payments, (serviceProvider, _) => - { - var logger = serviceProvider.GetRequiredService>(); + services + .AddGrpcClient(o => + { + var paymentsConfiguration = serviceConfiguration.FirstOrDefault(x => + string.Equals(x.Name, ServicesKey.Payments.ToString(), StringComparison.OrdinalIgnoreCase)) + ?? throw new ArgumentNullException($"{ServicesKey.Payments} gRPC service configuration is not configured."); - return new PaymentsService(logger); - }); + o.Address = new Uri(paymentsConfiguration.BaseAddress); + }) + .SetHandlerLifetime(TimeSpan.FromMinutes(5)) + .AddPolicyHandler(GetRetryPolicy()); + + services.AddScoped(); return services; } diff --git a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs index 2ec9c3c8..36a59baf 100644 --- a/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/OrderEndpoints.cs @@ -4,6 +4,7 @@ using Contracts.Orders; using Contracts.Common; using System.Globalization; +using Infrastructure.Common; namespace WebApp.Endpoints; @@ -11,14 +12,14 @@ internal static class OrderEndpoints { public static WebApplication MapOrderEndpoints(this WebApplication app) { - var serviceKey = ServicesKeys.Orders.ToString(); + var serviceKey = ServicesKey.Orders.ToString(); var ordersGroup = app.MapGroup(serviceKey) .WithTags(serviceKey) .RequireRateLimiting(serviceKey); ordersGroup.MapGet("/{id}", async ( - [FromKeyedServices(ServicesKeys.Orders)] BaseHttpService httpService, + [FromKeyedServices(ServicesKey.Orders)] BaseHttpService httpService, [FromRoute] int id, [FromServices] HybridCacheService cache, CancellationToken cancellationToken, @@ -52,7 +53,7 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) ordersGroup.MapPost("/", async ( [FromBody] CreateOrderRequest request, - [FromKeyedServices(ServicesKeys.Orders)] BaseHttpService httpService, + [FromKeyedServices(ServicesKey.Orders)] BaseHttpService httpService, CancellationToken cancellationToken ) => { diff --git a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs index cf35c367..4c43276f 100644 --- a/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs +++ b/templates/Bff/src/WebApp/Endpoints/PaymentEndpoints.cs @@ -1,7 +1,7 @@ using Contracts.Common; using GrpcPayment; +using Infrastructure.Common; using Infrastructure.Grpc; -using Infrastructure.Http; using Microsoft.AspNetCore.Mvc; namespace WebApp.Endpoints; @@ -10,7 +10,7 @@ public static partial class PaymentEndpoints { public static IEndpointRouteBuilder MapPaymentEndpoints(this IEndpointRouteBuilder endpoints) { - var serviceKey = ServicesKeys.Payments.ToString(); + var serviceKey = ServicesKey.Payments.ToString(); var group = endpoints.MapGroup(serviceKey) .WithTags(serviceKey) diff --git a/templates/Bff/src/WebApp/Extensions/HealthCheckExtensions.cs b/templates/Bff/src/WebApp/Extensions/HealthCheckExtensions.cs index bf20367c..b8c36ea2 100644 --- a/templates/Bff/src/WebApp/Extensions/HealthCheckExtensions.cs +++ b/templates/Bff/src/WebApp/Extensions/HealthCheckExtensions.cs @@ -1,5 +1,5 @@ using HealthChecks.UI.Client; -using Infrastructure.Http; +using Infrastructure.Common; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; @@ -21,7 +21,7 @@ IConfiguration configuration tags: ["services"] ); - var serviceKeys = Enum.GetValues(); + var serviceKeys = Enum.GetValues(); foreach (var serviceKey in serviceKeys) { var baseAddress = configuration.GetSection("Http") diff --git a/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs b/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs index f6bcc98d..5eb1d745 100644 --- a/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs +++ b/templates/Bff/src/WebApp/Extensions/RateLimitExtensions.cs @@ -1,4 +1,4 @@ -using Infrastructure.Http; +using Infrastructure.Common; using Microsoft.AspNetCore.RateLimiting; namespace WebApp.Extensions; @@ -12,10 +12,10 @@ internal IServiceCollection AddRateLimiting(IConfiguration configuration) if (!configuration.GetValue("RATE_LIMITING_ENABLED")) return services; - var serviceKeys = Enum.GetValues(); + var serviceKeys = Enum.GetValues(); foreach (var serviceKey in serviceKeys) { - var serviceConfig = configuration.GetSection("Http") + var serviceConfig = configuration.GetSection("Services") .GetChildren() .FirstOrDefault(x => x["Name"] == serviceKey.ToString()); @@ -30,12 +30,6 @@ internal IServiceCollection AddRateLimiting(IConfiguration configuration) }); options.RejectionStatusCode = StatusCodes.Status429TooManyRequests; - - options.OnRejected = async (context, cancellationToken) => - { - context.HttpContext.Response.Headers.RetryAfter = "60 seconds"; - await Task.CompletedTask; - }; }); } } diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index 4834525a..bb370e45 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -2,10 +2,10 @@ "ConnectionStrings": { "Redis": "localhost:6379" }, - "Http": [ + "Services": [ { "Name": "Orders", - "BaseAddress": "http://localhost:5012", + "BaseAddress": "http://mock-api:5012", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" @@ -14,7 +14,7 @@ }, { "Name": "Payments", - "BaseAddress": "http://localhost:5012", + "BaseAddress": "http://mock-api:5012", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index 278e7dee..88fb589e 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -2,7 +2,7 @@ "ConnectionStrings": { "Redis": "redis:6379" }, - "Http": [ + "Services": [ { "Name": "Orders", "BaseAddress": "http://mock-api:5012", From 8730f1596f7b795999f0f991834831a0f68cb712 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 30 Jan 2026 08:16:15 -0300 Subject: [PATCH 042/161] 53 refactor: update field accessors and improve naming conventions --- .../IntegrationTests/Fixtures/BaseFixture.cs | 2 +- .../IntegrationTests/WebApp/Http/ApiHelper.cs | 22 +++++++------- .../WebApp/Http/BaseHttpFixture.cs | 6 ++-- .../IntegrationTests/WebApp/Http/OrderTest.cs | 30 +++++++++---------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs b/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs index 608a3ab6..2e63ec18 100644 --- a/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs +++ b/templates/Bff/tests/IntegrationTests/Fixtures/BaseFixture.cs @@ -2,5 +2,5 @@ public class BaseFixture { - public CancellationToken cancellationToken = CancellationToken.None; + public CancellationToken CancellationToken { get; } = CancellationToken.None; } diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/ApiHelper.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/ApiHelper.cs index 367fe5f2..eb2be9ac 100644 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/ApiHelper.cs +++ b/templates/Bff/tests/IntegrationTests/WebApp/Http/ApiHelper.cs @@ -5,7 +5,7 @@ namespace IntegrationTests.WebApp.Http; public sealed class ApiHelper(HttpClient httpClient) { - public HttpClient httpClient = httpClient; + readonly HttpClient _httpClient = httpClient; private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { @@ -16,28 +16,28 @@ public void AddHeaders(Dictionary headers) { foreach (var header in headers) { - if (httpClient.DefaultRequestHeaders.Contains(header.Key)) + if (_httpClient.DefaultRequestHeaders.Contains(header.Key)) { - httpClient.DefaultRequestHeaders.Remove(header.Key); + _httpClient.DefaultRequestHeaders.Remove(header.Key); } - httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); + _httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); } } public async Task GetAsync(string resourceUrl) => - await httpClient.GetAsync(resourceUrl); + await _httpClient.GetAsync(resourceUrl); public async Task PostAsync(string resourceUrl, dynamic dataClass) => - await httpClient.PostAsync(resourceUrl, SerializeRequest(dataClass)); + await _httpClient.PostAsync(resourceUrl, SerializeRequest(dataClass)); public async Task PutAsync(string resourceUrl, dynamic data) => - await httpClient.PutAsync(resourceUrl, SerializeRequest(data)); + await _httpClient.PutAsync(resourceUrl, SerializeRequest(data)); public async Task DeleteAsync(string resourceUrl) => - await httpClient.DeleteAsync(resourceUrl); + await _httpClient.DeleteAsync(resourceUrl); - public StringContent SerializeRequest(dynamic data) + public static StringContent SerializeRequest(dynamic data) { var json = JsonSerializer.Serialize(data); return new StringContent(json, Encoding.UTF8, "application/json"); @@ -53,8 +53,8 @@ public StringContent SerializeRequest(dynamic data) return JsonSerializer.Deserialize(content, _jsonSerializerOptions); } - public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(httpClient.BaseAddress!, new GrpcChannelOptions + public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(_httpClient.BaseAddress!, new GrpcChannelOptions { - HttpClient = httpClient + HttpClient = _httpClient }); } diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/BaseHttpFixture.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/BaseHttpFixture.cs index cb793f25..c251bb7d 100644 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/BaseHttpFixture.cs +++ b/templates/Bff/tests/IntegrationTests/WebApp/Http/BaseHttpFixture.cs @@ -6,9 +6,9 @@ namespace IntegrationTests.WebApp.Http; public class BaseHttpFixture : BaseFixture { - public ApiHelper apiHelper; - public string resourceUrl = string.Empty; + public ApiHelper ApiHelper { get; set; } = null!; + public string ResourceUrl { get; set; } = string.Empty; public void SetApiHelper(CustomWebApplicationFactory customWebApplicationFactory) => - apiHelper = new(customWebApplicationFactory.CreateClient()); + ApiHelper = new(customWebApplicationFactory.CreateClient()); } \ No newline at end of file diff --git a/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs b/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs index c1894497..d8c77673 100644 --- a/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs +++ b/templates/Bff/tests/IntegrationTests/WebApp/Http/OrderTest.cs @@ -14,23 +14,23 @@ public OrderTest(CustomWebApplicationFactory customWebApplicationFactor { _fixture = fixture; _fixture.SetApiHelper(customWebApplicationFactory); - _fixture.resourceUrl = "orders/{0}"; + _fixture.ResourceUrl = "orders/{0}"; } - [Fact(DisplayName = nameof(Given_A_Get_By_Id_Valid_Request_Then_Pass))] - public async Task Given_A_Get_By_Id_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAGetByIdValidRequestThenPass))] + public async Task GivenAGetByIdValidRequestThenPass() { // Arrange var id = 1; - var url = string.Format(_fixture.resourceUrl, id); - _fixture.apiHelper.AddHeaders(new Dictionary + var url = string.Format(System.Globalization.CultureInfo.InvariantCulture, _fixture.ResourceUrl, id); + _fixture.ApiHelper.AddHeaders(new Dictionary { { "CorrelationId", Guid.NewGuid().ToString() }, { "CacheEnabled", "false" } }); // Act - var result = await _fixture.apiHelper.GetAsync(url); + var result = await _fixture.ApiHelper.GetAsync(url); var response = await ApiHelper.DeSerializeResponse>(result); var data = response?.Data; @@ -42,20 +42,20 @@ public async Task Given_A_Get_By_Id_Valid_Request_Then_Pass() Assert.NotEmpty(data.Items); } - [Fact(DisplayName = nameof(Given_A_Get_By_Id_Invalid_Request_Then_Fails))] - public async Task Given_A_Get_By_Id_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAGetByIdInvalidRequestThenFails))] + public async Task GivenAGetByIdInvalidRequestThenFails() { // Arrange var id = 9999999; - var url = string.Format(_fixture.resourceUrl, id); - _fixture.apiHelper.AddHeaders(new Dictionary + var url = string.Format(System.Globalization.CultureInfo.InvariantCulture, _fixture.ResourceUrl, id); + _fixture.ApiHelper.AddHeaders(new Dictionary { { "CorrelationId", Guid.NewGuid().ToString() }, { "CacheEnabled", "false" } }); // Act - var result = await _fixture.apiHelper.GetAsync(url); + var result = await _fixture.ApiHelper.GetAsync(url); // Assert Assert.NotNull(result); @@ -63,11 +63,11 @@ public async Task Given_A_Get_By_Id_Invalid_Request_Then_Fails() Assert.False(result.IsSuccessStatusCode); } - [Fact(DisplayName = nameof(Given_A_Valid_Create_Request_Then_Pass))] - public async Task Given_A_Valid_Create_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidCreateRequestThenPass))] + public async Task GivenAValidCreateRequestThenPass() { // Arrange - var url = string.Format(_fixture.resourceUrl, string.Empty); + var url = string.Format(System.Globalization.CultureInfo.InvariantCulture, _fixture.ResourceUrl, string.Empty); CreateOrderRequest request = new(Guid.NewGuid(), "Test Order", [ new("Item 1", "Description 1", 500.0m), @@ -75,7 +75,7 @@ public async Task Given_A_Valid_Create_Request_Then_Pass() ]); // Act - var result = await _fixture.apiHelper.PostAsync(url, request); + var result = await _fixture.ApiHelper.PostAsync(url, request); var response = await ApiHelper.DeSerializeResponse>(result); var data = response?.Data; From 2fef6a93500965cfa71efdf200eeb04df30bc226 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 30 Jan 2026 08:28:03 -0300 Subject: [PATCH 043/161] 53 feat: add gRPC service registration and update base addresses --- .../src/Infrastructure/InfrastructureDependencyInjection.cs | 3 ++- templates/Bff/src/WebApp/Program.cs | 1 - templates/Bff/src/WebApp/appsettings.Development.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 17a1aa5b..d9339002 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -175,8 +175,9 @@ internal IServiceCollection AddHttp(List serviceConfigurat internal IServiceCollection AddGrpc(List serviceConfiguration) { + services.AddGrpc(); services - .AddGrpcClient(o => + .AddGrpcClient(nameof(PaymentsService), o => { var paymentsConfiguration = serviceConfiguration.FirstOrDefault(x => string.Equals(x.Name, ServicesKey.Payments.ToString(), StringComparison.OrdinalIgnoreCase)) diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs index a881223d..1b8991fe 100644 --- a/templates/Bff/src/WebApp/Program.cs +++ b/templates/Bff/src/WebApp/Program.cs @@ -19,7 +19,6 @@ private static async Task Main(string[] args) var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); - builder.Services.AddGrpc(); builder.Services.AddOpenApi(); builder.Services.AddCustomHealthChecks(builder.Configuration); builder.Services.AddRateLimiting(builder.Configuration); diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index bb370e45..fc9cda9d 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -5,7 +5,7 @@ "Services": [ { "Name": "Orders", - "BaseAddress": "http://mock-api:5012", + "BaseAddress": "http://localhost:5012", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" @@ -14,7 +14,7 @@ }, { "Name": "Payments", - "BaseAddress": "http://mock-api:5012", + "BaseAddress": "http://localhost:5012", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" From a752b855c84df4b9d6d11d489341512aca01ac07 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 30 Jan 2026 08:30:24 -0300 Subject: [PATCH 044/161] 53 feat: update base addresses for Orders and Payments services --- templates/Bff/src/WebApp/appsettings.Development.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index fc9cda9d..b41eb709 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -5,7 +5,7 @@ "Services": [ { "Name": "Orders", - "BaseAddress": "http://localhost:5012", + "BaseAddress": "https://localhost:7177", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" @@ -14,7 +14,7 @@ }, { "Name": "Payments", - "BaseAddress": "http://localhost:5012", + "BaseAddress": "https://localhost:7177", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" From e30b795b6a5d3a09dc33475deb322e3b2a8b8e82 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 3 Feb 2026 07:19:23 -0300 Subject: [PATCH 045/161] 53 feat: add versioning to service configurations in appsettings --- .../src/Infrastructure/Common/ServiceConfiguration.cs | 1 + .../InfrastructureDependencyInjection.cs | 2 ++ templates/Bff/src/WebApp/appsettings.Development.json | 10 ++++++---- templates/Bff/src/WebApp/appsettings.json | 6 ++++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs b/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs index 5f68093f..3ed77b67 100644 --- a/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs +++ b/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs @@ -7,4 +7,5 @@ public sealed class ServiceConfiguration public object Authentication { get; set; } public object Headers { get; set; } public int LimitPerMinute { get; set; } + public int Version { get; set; } } \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index d9339002..8ee314bb 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -153,6 +153,8 @@ internal IServiceCollection AddHttp(List serviceConfigurat client.BaseAddress = new Uri(serviceConfig.BaseAddress) ?? throw new ArgumentNullException($"{serviceName} service address is not configured."); + client.DefaultRequestVersion = new(serviceConfig.Version, 0); + if (serviceConfig.Headers is Dictionary headers && headers.Count > 0) foreach (var header in headers) client.DefaultRequestHeaders.Add(header.Key, header.Value); diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index b41eb709..a79f6ee8 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -5,21 +5,23 @@ "Services": [ { "Name": "Orders", - "BaseAddress": "https://localhost:7177", + "BaseAddress": "http://localhost:5012", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 500 + "LimitPerMinute": 500, + "Version": 2 }, { "Name": "Payments", - "BaseAddress": "https://localhost:7177", + "BaseAddress": "http://localhost:5012", "Headers": { "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 500 + "LimitPerMinute": 500, + "Version": 2 } ] } \ No newline at end of file diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index 88fb589e..08036583 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -10,7 +10,8 @@ "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 500 + "LimitPerMinute": 500, + "Version": 2 }, { "Name": "Payments", @@ -19,7 +20,8 @@ "Accept": "application/json", "Accept-Encoding": "gzip, deflate, br" }, - "LimitPerMinute": 500 + "LimitPerMinute": 500, + "Version": 2 } ] } \ No newline at end of file From 64e2a1655418af7ee73822d30b2d4547955e19c7 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 3 Feb 2026 07:19:35 -0300 Subject: [PATCH 046/161] 53 feat: add http2 support in Kestrel configuration --- templates/Bff/docker-compose.yml | 2 ++ templates/Bff/src/MockApi/Program.cs | 8 +------- templates/Bff/src/MockApi/Properties/launchSettings.json | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index 69607e62..65ffb526 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -23,6 +23,8 @@ services: start_period: 15s networks: - hexagonal_solution_template_network + environment: + - KESTREL__ENDPOINTS__HTTP__PROTOCOLS=http2 redis: image: redis:8 diff --git a/templates/Bff/src/MockApi/Program.cs b/templates/Bff/src/MockApi/Program.cs index d2cf014f..88b40346 100644 --- a/templates/Bff/src/MockApi/Program.cs +++ b/templates/Bff/src/MockApi/Program.cs @@ -1,5 +1,4 @@ -using Microsoft.AspNetCore.Server.Kestrel.Core; -using MockApi.Endpoints; +using MockApi.Endpoints; using MockApi.Extensions; using MockApi.GrpcServices; @@ -9,11 +8,6 @@ builder.Services.AddResponseCompression(); builder.Services.AddCustomHealthChecks(); -builder.WebHost.ConfigureKestrel(options => - options.ConfigureEndpointDefaults(listenOptions => - listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3 -)); - var app = builder.Build(); app.UseHttpsRedirection(); diff --git a/templates/Bff/src/MockApi/Properties/launchSettings.json b/templates/Bff/src/MockApi/Properties/launchSettings.json index 6c998bc4..80ee8ef4 100644 --- a/templates/Bff/src/MockApi/Properties/launchSettings.json +++ b/templates/Bff/src/MockApi/Properties/launchSettings.json @@ -8,7 +8,7 @@ "applicationUrl": "https://*:7177;http://*:5012", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - + "KESTREL__ENDPOINTS__HTTP__PROTOCOLS": "http2" } } } From ff9631a39347af2a780b900580feb842d1716805 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 3 Feb 2026 08:04:05 -0300 Subject: [PATCH 047/161] 53 feat: rename version to protocolVersion and update usages --- .../Common/ServiceConfiguration.cs | 2 +- .../Infrastructure/Http/BaseHttpService.cs | 23 ++++++++++++++++--- .../InfrastructureDependencyInjection.cs | 4 ++-- .../src/WebApp/appsettings.Development.json | 4 ++-- templates/Bff/src/WebApp/appsettings.json | 4 ++-- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs b/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs index 3ed77b67..7ccc93a0 100644 --- a/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs +++ b/templates/Bff/src/Infrastructure/Common/ServiceConfiguration.cs @@ -7,5 +7,5 @@ public sealed class ServiceConfiguration public object Authentication { get; set; } public object Headers { get; set; } public int LimitPerMinute { get; set; } - public int Version { get; set; } + public int ProtocolVersion { get; set; } } \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 5a24cfa4..f9fa852a 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -1,19 +1,28 @@ using System.Diagnostics; +using System.Net; using System.Text.Json; using Infrastructure.Common; using Microsoft.Extensions.Logging; namespace Infrastructure.Http; -public class BaseHttpService(HttpClient httpClient, ILogger logger) +public class BaseHttpService(HttpClient httpClient, ILogger logger, int httpProtocolVersion = 2) { public HttpClient HttpClient { get; } = httpClient; public ILogger Logger { get; } = logger; + public int HttpProtocolVersion { get; } = httpProtocolVersion; public JsonSerializerOptions JsonSerializerOptions { get; } = new(JsonSerializerDefaults.Web); public Stopwatch Stopwatch { get; } = new(); private readonly string _className = nameof(BaseHttpService); private readonly string _method = nameof(SendAsync); + private static Version GetHttpVersion(int httpVersion) => httpVersion switch + { + 1 => HttpVersion.Version11, + 3 => HttpVersion.Version30, + 2 or _ => HttpVersion.Version20 + }; + public async Task SendAsync( string requestUri, HttpMethod httpMethod, @@ -26,7 +35,11 @@ public class BaseHttpService(HttpClient httpClient, ILogger log Stopwatch.Start(); Logs.SendingRequest(Logger, _className, _method, httpMethod, requestUri); - HttpRequestMessage requestMessage = new(httpMethod, requestUri); + HttpRequestMessage requestMessage = new(httpMethod, requestUri) + { + Version = GetHttpVersion(HttpProtocolVersion), + VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher + }; using MemoryStream memoryStream = new(); await JsonSerializer.SerializeAsync(memoryStream, request, JsonSerializerOptions, cancellationToken); @@ -66,7 +79,11 @@ public class BaseHttpService(HttpClient httpClient, ILogger log Stopwatch.Start(); Logs.SendingRequest(Logger, _className, _method, httpMethod, requestUri); - var requestMessage = new HttpRequestMessage(httpMethod, requestUri); + var requestMessage = new HttpRequestMessage(httpMethod, requestUri) + { + Version = GetHttpVersion(HttpProtocolVersion), + VersionPolicy = HttpVersionPolicy.RequestVersionOrHigher + }; if (headers != null) foreach (var header in headers) requestMessage.Headers.Add(header.Key, header.Value); diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 8ee314bb..686e42c5 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -153,7 +153,7 @@ internal IServiceCollection AddHttp(List serviceConfigurat client.BaseAddress = new Uri(serviceConfig.BaseAddress) ?? throw new ArgumentNullException($"{serviceName} service address is not configured."); - client.DefaultRequestVersion = new(serviceConfig.Version, 0); + client.DefaultRequestVersion = new(serviceConfig.ProtocolVersion, 0); if (serviceConfig.Headers is Dictionary headers && headers.Count > 0) foreach (var header in headers) @@ -168,7 +168,7 @@ internal IServiceCollection AddHttp(List serviceConfigurat var logger = serviceProvider.GetRequiredService>(); var client = httpClientFactory.CreateClient(serviceName); - return new(client, logger); + return new(client, logger, serviceConfig.ProtocolVersion); }); } diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index a79f6ee8..9a50acb2 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -11,7 +11,7 @@ "Accept-Encoding": "gzip, deflate, br" }, "LimitPerMinute": 500, - "Version": 2 + "ProtocolVersion": 2 }, { "Name": "Payments", @@ -21,7 +21,7 @@ "Accept-Encoding": "gzip, deflate, br" }, "LimitPerMinute": 500, - "Version": 2 + "ProtocolVersion": 2 } ] } \ No newline at end of file diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index 08036583..cfc0c760 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -11,7 +11,7 @@ "Accept-Encoding": "gzip, deflate, br" }, "LimitPerMinute": 500, - "Version": 2 + "ProtocolVersion": 2 }, { "Name": "Payments", @@ -21,7 +21,7 @@ "Accept-Encoding": "gzip, deflate, br" }, "LimitPerMinute": 500, - "Version": 2 + "ProtocolVersion": 2 } ] } \ No newline at end of file From d486a46604883f1afc7e457f48a22e73ab871127 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 3 Feb 2026 08:04:29 -0300 Subject: [PATCH 048/161] 53 feat: configure response compression and remove http2 env var --- templates/Bff/docker-compose.yml | 2 -- templates/Bff/src/MockApi/Program.cs | 17 +++++++++++++++-- .../src/MockApi/Properties/launchSettings.json | 3 +-- templates/Bff/src/WebApp/Program.cs | 10 +++++++++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index 65ffb526..69607e62 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -23,8 +23,6 @@ services: start_period: 15s networks: - hexagonal_solution_template_network - environment: - - KESTREL__ENDPOINTS__HTTP__PROTOCOLS=http2 redis: image: redis:8 diff --git a/templates/Bff/src/MockApi/Program.cs b/templates/Bff/src/MockApi/Program.cs index 88b40346..21da2232 100644 --- a/templates/Bff/src/MockApi/Program.cs +++ b/templates/Bff/src/MockApi/Program.cs @@ -1,13 +1,26 @@ -using MockApi.Endpoints; +using Microsoft.AspNetCore.ResponseCompression; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using MockApi.Endpoints; using MockApi.Extensions; using MockApi.GrpcServices; var builder = WebApplication.CreateBuilder(args); builder.Services.AddGrpc(); -builder.Services.AddResponseCompression(); +builder.Services.AddResponseCompression( + options => + { + options.EnableForHttps = true; + options.MimeTypes = ResponseCompressionDefaults.MimeTypes; + options.Providers.Add(); + } +); builder.Services.AddCustomHealthChecks(); +builder.WebHost.ConfigureKestrel(options => options + .ConfigureEndpointDefaults(listenOptions => listenOptions.Protocols = HttpProtocols.Http2 +)); + var app = builder.Build(); app.UseHttpsRedirection(); diff --git a/templates/Bff/src/MockApi/Properties/launchSettings.json b/templates/Bff/src/MockApi/Properties/launchSettings.json index 80ee8ef4..a4821660 100644 --- a/templates/Bff/src/MockApi/Properties/launchSettings.json +++ b/templates/Bff/src/MockApi/Properties/launchSettings.json @@ -7,8 +7,7 @@ "launchBrowser": false, "applicationUrl": "https://*:7177;http://*:5012", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "KESTREL__ENDPOINTS__HTTP__PROTOCOLS": "http2" + "ASPNETCORE_ENVIRONMENT": "Development" } } } diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs index 1b8991fe..783fce29 100644 --- a/templates/Bff/src/WebApp/Program.cs +++ b/templates/Bff/src/WebApp/Program.cs @@ -2,6 +2,7 @@ using System.Text.Json.Serialization; using Infrastructure; using Microsoft.AspNetCore.Http.Json; +using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.Server.Kestrel.Core; using Scalar.AspNetCore; using WebApp.Endpoints; @@ -22,7 +23,14 @@ private static async Task Main(string[] args) builder.Services.AddOpenApi(); builder.Services.AddCustomHealthChecks(builder.Configuration); builder.Services.AddRateLimiting(builder.Configuration); - builder.Services.AddResponseCompression(); + builder.Services.AddResponseCompression( + options => + { + options.EnableForHttps = true; + options.MimeTypes = ResponseCompressionDefaults.MimeTypes; + options.Providers.Add(); + } + ); builder.Services.Configure(options => { options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; From 6c427248c7083d995722bce66b8158359d669243 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Feb 2026 13:09:06 -0300 Subject: [PATCH 049/161] feat: add timezoneId and fix naming test violation --- .../Common/Requests/BaseRequest.cs | 5 +- .../Full/src/Domain/Common/DomainEntity.cs | 25 ++- .../Orders/CreateOrderUseCaseTests.cs | 8 +- .../Orders/GetOrderUseCaseTests.cs | 16 +- .../Architecture/ApplicationTests.cs | 12 +- .../UnitTests/Architecture/DomainTests.cs | 16 +- .../UnitTests/Domain/DomainEntityTests.cs | 197 ++++++++++++++++++ .../UnitTests/Domain/NotificationTests.cs | 8 +- .../Full/tests/UnitTests/Domain/OrderTests.cs | 8 +- 9 files changed, 254 insertions(+), 41 deletions(-) create mode 100644 templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs diff --git a/templates/Full/src/Application/Common/Requests/BaseRequest.cs b/templates/Full/src/Application/Common/Requests/BaseRequest.cs index 6ccbb7a0..81c44ed5 100644 --- a/templates/Full/src/Application/Common/Requests/BaseRequest.cs +++ b/templates/Full/src/Application/Common/Requests/BaseRequest.cs @@ -1,16 +1,17 @@ using FluentValidation; namespace Application.Common.Requests; -public record BaseRequest(Guid CorrelationId); +public record BaseRequest(Guid CorrelationId, int TimezoneId = 0); public record BasePaginatedRequest( Guid CorrelationId, + int TimezoneId = 0, int Page = 1, int PageSize = 10, string? SortBy = null, bool SortDescending = false, Dictionary? SearchByValues = null -) : BaseRequest(CorrelationId); +) : BaseRequest(CorrelationId, TimezoneId); public sealed class BasePaginatedRequestValidator : AbstractValidator { diff --git a/templates/Full/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs index fdead773..cbfda8cd 100644 --- a/templates/Full/src/Domain/Common/DomainEntity.cs +++ b/templates/Full/src/Domain/Common/DomainEntity.cs @@ -3,12 +3,13 @@ public abstract class DomainEntity { protected DomainEntity() {} - protected DomainEntity(DateTime currentDate, string? user = null) + protected DomainEntity(DateTime currentDate, string? user = null, string timezoneId = "") { - CreatedAt = currentDate; + CreatedAt = currentDate == default ? DateTime.UtcNow : currentDate; CreatedBy = user ?? "System"; - UpdatedAt = currentDate; - UpdatedBy = user ?? "System"; + UpdatedAt = CreatedAt; + UpdatedBy = CreatedBy; + SetTimezoneId(timezoneId); } public int Id { get; init; } @@ -16,10 +17,24 @@ protected DomainEntity(DateTime currentDate, string? user = null) public string? CreatedBy { get; init; } public DateTime UpdatedAt { get; private set; } public string? UpdatedBy { get; private set; } + public string TimezoneId { get; private set; } + + private void SetTimezoneId(string timezoneId) + { + if (string.IsNullOrWhiteSpace(timezoneId)) + { + TimezoneId = TimeZoneInfo.Utc.Id; + return; + } + + TimeZoneInfo.FindSystemTimeZoneById(timezoneId); + TimezoneId = timezoneId; + } - public virtual void Update(string? user = null) + public virtual void Update(string? user = null, string timezoneId = "") { UpdatedAt = DateTime.UtcNow; + SetTimezoneId(timezoneId); UpdatedBy = user ?? "System"; } } diff --git a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs index 4dd34cb1..59dae347 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs @@ -140,8 +140,8 @@ public async Task Given_A_Invalid_Request_Then_Fails() _fixture.VerifyProduce(0); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails_When_There_Is_No_Items))] - public async Task Given_A_Invalid_Request_Then_Fails_When_There_Is_No_Items() + [Fact(DisplayName = nameof(GivenAInvalidRequestThenFailsWhenThereIsNoItems))] + public async Task GivenAInvalidRequestThenFailsWhenThereIsNoItems() { // Arrange var request = CreateOrderUseCaseFixture.SetInvalidRequestWithNoItems(); @@ -167,8 +167,8 @@ public async Task Given_A_Invalid_Request_Then_Fails_When_There_Is_No_Items() _fixture.VerifyProduce(); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Fails_When_Repository_Returns_Zero))] - public async Task Given_A_Valid_Request_Then_Fails_When_Repository_Returns_Zero() + [Fact(DisplayName = nameof(GivenAValidRequestThenFailsWhenRepositoryReturnsZero))] + public async Task GivenAValidRequestThenFailsWhenRepositoryReturnsZero() { // Arrange var request = _fixture.SetValidRequest(); diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index 998ec930..67aee300 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -27,8 +27,8 @@ public GetOrderUseCaseTest(GetOrderUseCaseFixture fixture) _fixture.ClearInvocations(); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange var request = _fixture.SetValidRequest(); @@ -55,8 +55,8 @@ public async Task Given_A_Valid_Request_Then_Pass() _fixture.VerifyFinishUseCaseLog(); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Without_Items_Then_Pass))] - public async Task Given_A_Valid_Request_Without_Items_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestWithoutItemsThenPass))] + public async Task GivenAValidRequestWithoutItemsThenPass() { // Arrange var request = _fixture.SetValidRequest(); @@ -88,8 +88,8 @@ public async Task Given_A_Valid_Request_Without_Items_Then_Pass() _fixture.VerifyFinishUseCaseLog(); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] + public async Task GivenAInvalidRequestThenFails() { // Arrange var request = _fixture.SetValidRequest(); @@ -111,8 +111,8 @@ public async Task Given_A_Invalid_Request_Then_Fails() _fixture.VerifyFinishUseCaseLog(0); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_When_Order_Not_Found_Then_Fails))] - public async Task Given_A_Valid_Request_When_Order_Not_Found_Then_Fails() + [Fact(DisplayName = nameof(GivenAValidRequestWhenOrderNotFoundThenFails))] + public async Task GivenAValidRequestWhenOrderNotFoundThenFails() { // Arrange var request = _fixture.SetValidRequest(); diff --git a/templates/Full/tests/UnitTests/Architecture/ApplicationTests.cs b/templates/Full/tests/UnitTests/Architecture/ApplicationTests.cs index 4fd71868..4bd3ac0a 100644 --- a/templates/Full/tests/UnitTests/Architecture/ApplicationTests.cs +++ b/templates/Full/tests/UnitTests/Architecture/ApplicationTests.cs @@ -9,13 +9,13 @@ public sealed class ApplicationTests { private static readonly Assembly _applicationAssembly = typeof(BaseResponse).Assembly; - [Theory(DisplayName = nameof(Application_Do_Not_Have_Classes_With_Not_Allowed_Names))] + [Theory(DisplayName = nameof(ApplicationDoNotHaveClassesWithNotAllowedNames))] [InlineData("Entity")] [InlineData("ValueObject")] [InlineData("Vo")] [InlineData("Service")] [InlineData("Controller")] - public void Application_Do_Not_Have_Classes_With_Not_Allowed_Names(string notAllowedClassName) + public void ApplicationDoNotHaveClassesWithNotAllowedNames(string notAllowedClassName) { // Arrange, Act var result = Types @@ -30,8 +30,8 @@ public void Application_Do_Not_Have_Classes_With_Not_Allowed_Names(string notAll Assert.True(result.IsSuccessful); } - [Fact(DisplayName = nameof(Application_Do_Not_Have_Infrastructure_Dependency))] - public void Application_Do_Not_Have_Infrastructure_Dependency() + [Fact(DisplayName = nameof(ApplicationDoNotHaveInfrastructureDependency))] + public void ApplicationDoNotHaveInfrastructureDependency() { // Arrange, Act var result = Types @@ -44,8 +44,8 @@ public void Application_Do_Not_Have_Infrastructure_Dependency() Assert.True(result.IsSuccessful); } - [Fact(DisplayName = nameof(Application_Should_Has_Valid_Scopes))] - public void Application_Should_Has_Valid_Scopes() + [Fact(DisplayName = nameof(ApplicationShouldHasValidScopes))] + public void ApplicationShouldHasValidScopes() { // Arrange ServiceCollection serviceCollection = new(); diff --git a/templates/Full/tests/UnitTests/Architecture/DomainTests.cs b/templates/Full/tests/UnitTests/Architecture/DomainTests.cs index 2b747788..b3438e99 100644 --- a/templates/Full/tests/UnitTests/Architecture/DomainTests.cs +++ b/templates/Full/tests/UnitTests/Architecture/DomainTests.cs @@ -9,7 +9,7 @@ public sealed class DomainTests { private static readonly Assembly _domainAssembly = typeof(Result).Assembly; - [Theory(DisplayName = nameof(Domain_Do_Not_Have_Classes_With_Not_Allowed_Names))] + [Theory(DisplayName = nameof(DomainDoNotHaveClassesWithNotAllowedNames))] [InlineData("Dto")] [InlineData("Dtos")] [InlineData("UseCase")] @@ -21,7 +21,7 @@ public sealed class DomainTests [InlineData("Controller")] [InlineData("Repository")] [InlineData("Query")] - public void Domain_Do_Not_Have_Classes_With_Not_Allowed_Names(string notAllowedClassName) + public void DomainDoNotHaveClassesWithNotAllowedNames(string notAllowedClassName) { // Arrange, Act var result = Types @@ -36,8 +36,8 @@ public void Domain_Do_Not_Have_Classes_With_Not_Allowed_Names(string notAllowedC Assert.True(result.IsSuccessful); } - [Fact(DisplayName = nameof(Domain_Do_Not_Have_Application_Dependency))] - public void Domain_Do_Not_Have_Application_Dependency() + [Fact(DisplayName = nameof(DomainDoNotHaveApplicationDependency))] + public void DomainDoNotHaveApplicationDependency() { // Arrange, Act var result = Types @@ -50,8 +50,8 @@ public void Domain_Do_Not_Have_Application_Dependency() Assert.True(result.IsSuccessful); } - [Fact(DisplayName = nameof(Domain_Do_Not_Have_Infrastructure_Dependency))] - public void Domain_Do_Not_Have_Infrastructure_Dependency() + [Fact(DisplayName = nameof(DomainDoNotHaveInfrastructureDependency))] + public void DomainDoNotHaveInfrastructureDependency() { // Arrange, Act var result = Types @@ -64,8 +64,8 @@ public void Domain_Do_Not_Have_Infrastructure_Dependency() Assert.True(result.IsSuccessful); } - [Fact(DisplayName = nameof(Domain_Should_Has_Valid_Scopes))] - public void Domain_Should_Has_Valid_Scopes() + [Fact(DisplayName = nameof(DomainShouldHasValidScopes))] + public void DomainShouldHasValidScopes() { // Arrange ServiceCollection serviceCollection = new(); diff --git a/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs b/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs new file mode 100644 index 00000000..60b09471 --- /dev/null +++ b/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs @@ -0,0 +1,197 @@ +using Domain.Common; + +namespace UnitTests.Domain; + +public sealed class DomainEntityTests +{ + private sealed class TestDomainEntity : DomainEntity + { + public TestDomainEntity() : base() { } + public TestDomainEntity(DateTime currentDate, string? user = null, string timezoneId = "") + : base(currentDate, user, timezoneId) { } + } + + [Fact(DisplayName = nameof(ConstructorWithValidParametersShouldCreateEntityWithProvidedValues))] + public void ConstructorWithValidParametersShouldCreateEntityWithProvidedValues() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var user = "TestUser"; + var timezoneId = "America/New_York"; + + var entity = new TestDomainEntity(currentDate, user, timezoneId); + + Assert.Equal(currentDate, entity.CreatedAt); + Assert.Equal(user, entity.CreatedBy); + Assert.Equal(currentDate, entity.UpdatedAt); + Assert.Equal(user, entity.UpdatedBy); + Assert.Equal(timezoneId, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(ConstructorWithDefaultDateTimeShouldUseUtcNow))] + public void ConstructorWithDefaultDateTimeShouldUseUtcNow() + { + var beforeCreation = DateTime.UtcNow; + + var entity = new TestDomainEntity(default); + + var afterCreation = DateTime.UtcNow; + Assert.True(entity.CreatedAt >= beforeCreation && entity.CreatedAt <= afterCreation); + Assert.Equal(entity.CreatedAt, entity.UpdatedAt); + } + + [Fact(DisplayName = nameof(ConstructorWithNullUserShouldDefaultToSystem))] + public void ConstructorWithNullUserShouldDefaultToSystem() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + + var entity = new TestDomainEntity(currentDate, null); + + Assert.Equal("System", entity.CreatedBy); + Assert.Equal("System", entity.UpdatedBy); + } + + [Fact(DisplayName = nameof(ConstructorWithValidTimezoneIdShouldSetTimezoneId))] + public void ConstructorWithValidTimezoneIdShouldSetTimezoneId() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var timezoneId = "America/Sao_Paulo"; + + var entity = new TestDomainEntity(currentDate, "TestUser", timezoneId); + + Assert.Equal(timezoneId, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(ConstructorWithInvalidTimezoneIdShouldDefaultToUtc))] + public void ConstructorWithInvalidTimezoneIdShouldDefaultToUtc() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var invalidTimezoneId = "Invalid/Timezone"; + + var entity = new TestDomainEntity(currentDate, "TestUser", invalidTimezoneId); + + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(ConstructorWithNullTimezoneIdShouldDefaultToUtc))] + public void ConstructorWithNullTimezoneIdShouldDefaultToUtc() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + string? nullTimezone = null; + + var entity = new TestDomainEntity(currentDate, "TestUser", nullTimezone!); + + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(ConstructorWithEmptyTimezoneIdShouldDefaultToUtc))] + public void ConstructorWithEmptyTimezoneIdShouldDefaultToUtc() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + + var entity = new TestDomainEntity(currentDate, "TestUser", string.Empty); + + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(ConstructorWithWhitespaceTimezoneIdShouldDefaultToUtc))] + public void ConstructorWithWhitespaceTimezoneIdShouldDefaultToUtc() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + + var entity = new TestDomainEntity(currentDate, "TestUser", " "); + + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(UpdateWithValidParametersShouldUpdateEntityProperties))] + public void UpdateWithValidParametersShouldUpdateEntityProperties() + { + var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var entity = new TestDomainEntity(initialDate, "InitialUser", "America/New_York"); + var beforeUpdate = DateTime.UtcNow; + + entity.Update("UpdatedUser", "Europe/London"); + + var afterUpdate = DateTime.UtcNow; + Assert.True(entity.UpdatedAt >= beforeUpdate && entity.UpdatedAt <= afterUpdate); + Assert.Equal("UpdatedUser", entity.UpdatedBy); + Assert.Equal("Europe/London", entity.TimezoneId); + Assert.Equal(initialDate, entity.CreatedAt); + Assert.Equal("InitialUser", entity.CreatedBy); + } + + [Fact(DisplayName = nameof(UpdateWithNullUserShouldDefaultToSystem))] + public void UpdateWithNullUserShouldDefaultToSystem() + { + var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var entity = new TestDomainEntity(initialDate, "InitialUser"); + + entity.Update(null); + + Assert.Equal("System", entity.UpdatedBy); + } + + [Fact(DisplayName = nameof(UpdateWithInvalidTimezoneIdShouldDefaultToUtc))] + public void UpdateWithInvalidTimezoneIdShouldDefaultToUtc() + { + var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var entity = new TestDomainEntity(initialDate, "TestUser", "America/New_York"); + + entity.Update("UpdatedUser", "Invalid/Timezone"); + + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Equal("UpdatedUser", entity.UpdatedBy); + } + + [Fact(DisplayName = nameof(UpdateWithEmptyTimezoneIdShouldDefaultToUtc))] + public void UpdateWithEmptyTimezoneIdShouldDefaultToUtc() + { + var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var entity = new TestDomainEntity(initialDate, "TestUser", "America/New_York"); + + entity.Update("UpdatedUser", string.Empty); + + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(UpdateWithoutParametersShouldUseDefaultValues))] + public void UpdateWithoutParametersShouldUseDefaultValues() + { + var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var entity = new TestDomainEntity(initialDate, "InitialUser", "America/New_York"); + var beforeUpdate = DateTime.UtcNow; + + entity.Update(); + + var afterUpdate = DateTime.UtcNow; + Assert.True(entity.UpdatedAt >= beforeUpdate && entity.UpdatedAt <= afterUpdate); + Assert.Equal("System", entity.UpdatedBy); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(ConstructorWithUtcTimezoneIdShouldSetUtcTimezone))] + public void ConstructorWithUtcTimezoneIdShouldSetUtcTimezone() + { + var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + + var entity = new TestDomainEntity(currentDate, "TestUser", TimeZoneInfo.Utc.Id); + + Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + } + + [Fact(DisplayName = nameof(UpdatePreservesCreatedAtAndCreatedByWhenUpdating))] + public void UpdatePreservesCreatedAtAndCreatedByWhenUpdating() + { + var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); + var initialUser = "InitialUser"; + var entity = new TestDomainEntity(initialDate, initialUser, "America/New_York"); + + entity.Update("UpdatedUser", "Europe/London"); + entity.Update("AnotherUser", "Asia/Tokyo"); + + Assert.Equal(initialDate, entity.CreatedAt); + Assert.Equal(initialUser, entity.CreatedBy); + Assert.Equal("AnotherUser", entity.UpdatedBy); + Assert.Equal("Asia/Tokyo", entity.TimezoneId); + } +} diff --git a/templates/Full/tests/UnitTests/Domain/NotificationTests.cs b/templates/Full/tests/UnitTests/Domain/NotificationTests.cs index a577361e..3800a9fa 100644 --- a/templates/Full/tests/UnitTests/Domain/NotificationTests.cs +++ b/templates/Full/tests/UnitTests/Domain/NotificationTests.cs @@ -4,8 +4,8 @@ namespace UnitTests.Domain; public sealed class NotificationTests { - [Fact(DisplayName = nameof(Given_A_New_Notification_When_Properties_Are_Provided_Then_Should_Create_Notification_With_Success))] - public void Given_A_New_Notification_When_Properties_Are_Provided_Then_Should_Create_Notification_With_Success() + [Fact(DisplayName = nameof(GivenANewNotificationWhenPropertiesAreProvidedThenShouldCreateNotificationWithSuccess))] + public void GivenANewNotificationWhenPropertiesAreProvidedThenShouldCreateNotificationWithSuccess() { /// Arrange var notificationType = "TestNotification"; @@ -25,8 +25,8 @@ public void Given_A_New_Notification_When_Properties_Are_Provided_Then_Should_Cr Assert.Contains("\"Test\":\"Message\"", notification.Message); } - [Fact(DisplayName = nameof(Given_A_New_Notification_When_Message_Is_Null_Then_Should_Create_Notification_With_Empty_Message))] - public void Given_A_New_Notification_When_Message_Is_Null_Then_Should_Create_Notification_With_Empty_Message() + [Fact(DisplayName = nameof(GivenANewNotificationWhenMessageIsNullThenShouldCreateNotificationWithEmptyMessage))] + public void GivenANewNotificationWhenMessageIsNullThenShouldCreateNotificationWithEmptyMessage() { /// Arrange var notificationType = "TestNotification"; diff --git a/templates/Full/tests/UnitTests/Domain/OrderTests.cs b/templates/Full/tests/UnitTests/Domain/OrderTests.cs index 4c5ea250..c2cd4b8e 100644 --- a/templates/Full/tests/UnitTests/Domain/OrderTests.cs +++ b/templates/Full/tests/UnitTests/Domain/OrderTests.cs @@ -4,8 +4,8 @@ namespace UnitTests.Domain; public sealed class OrderTests { - [Fact(DisplayName = nameof(Given_A_New_Order_When_Items_Are_Provided_Then_Should_Set_Total_With_Success))] - public void Given_A_New_Order_When_Items_Are_Provided_Then_Should_Set_Total_With_Success() + [Fact(DisplayName = nameof(GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess))] + public void GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess() { /// Arrange var items = new List() @@ -30,8 +30,8 @@ public void Given_A_New_Order_When_Items_Are_Provided_Then_Should_Set_Total_With Assert.Equal(items.Sum(i => i.Value), order.Total); } - [Fact(DisplayName = nameof(Given_A_New_Order_When_Items_Is_Empty_Then_Should_Return_Failure))] - public void Given_A_New_Order_When_Items_Is_Empty_Then_Should_Return_Failure() + [Fact(DisplayName = nameof(GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure))] + public void GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure() { /// Arrange Order order = new("Amazing Computer", Array.Empty()); From 8a004cda2d771208c6dd1f348dba179818bb8b6b Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Feb 2026 13:12:34 -0300 Subject: [PATCH 050/161] style(53): remove underscores from test method names for consistency --- .../Data/BaseRepositoryTest.cs | 18 +++++++++--------- .../WebApp/Grpc/Orders/GetOrderGrpcTest.cs | 8 ++++---- .../WebApp/Http/Orders/CreateOrderTest.cs | 8 ++++---- .../WebApp/Http/Orders/GetAllOrdersTest.cs | 16 ++++++++-------- .../WebApp/Http/Orders/GetOrderTest.cs | 8 ++++---- .../Notifications/CreateNotificationTest.cs | 8 ++++---- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs b/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs index dbe32e54..c3db7498 100644 --- a/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs +++ b/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs @@ -16,7 +16,7 @@ public BaseRepositoryTest(CustomWebApplicationFactory factory, BaseData } [Fact] - public async Task Given_A_Id_Then_Return_Order_With_Success() + public async Task GivenAIdThenReturnOrderWithSuccess() { // Arrange var id = 1; @@ -37,7 +37,7 @@ public async Task Given_A_Id_Then_Return_Order_With_Success() } [Fact] - public async Task Given_A_Id_Then_Return_OrderDto_With_Success() + public async Task GivenAIdThenReturnOrderDtoWithSuccess() { // Arrange var id = 1; @@ -68,7 +68,7 @@ public async Task Given_A_Id_Then_Return_OrderDto_With_Success() } [Fact] - public async Task Given_A_Order_And_Notification_Should_Execute_In_Parallel_With_Success() + public async Task GivenAOrderAndNotificationShouldExecuteInParallelWithSuccess() { // Arrange var id = 1; @@ -102,7 +102,7 @@ public async Task Given_A_Order_And_Notification_Should_Execute_In_Parallel_With } [Fact] - public async Task Given_A_Valid_Request_Then_Return_All_Orders_Paginated_With_Success() + public async Task GivenAValidRequestThenReturnAllOrdersPaginatedWithSuccess() { // Arrange var pageNumber = 1; @@ -123,7 +123,7 @@ public async Task Given_A_Valid_Request_Then_Return_All_Orders_Paginated_With_Su } [Fact] - public async Task Given_A_Valid_Request_Then_Return_All_OrdersDtos_Paginated_With_Success() + public async Task GivenAValidRequestThenReturnAllOrdersDtosPaginatedWithSuccess() { // Arrange var pageNumber = 1; @@ -156,7 +156,7 @@ public async Task Given_A_Valid_Request_Then_Return_All_OrdersDtos_Paginated_Wit } [Fact] - public async Task Given_A_Valid_Request_Then_Return_No_Orders_Paginated() + public async Task GivenAValidRequestThenReturnNoOrdersPaginated() { // Arrange var pageNumber = 50; @@ -177,7 +177,7 @@ public async Task Given_A_Valid_Request_Then_Return_No_Orders_Paginated() } [Fact] - public async Task Given_A_Valid_Request_Then_Return_Filtered_Orders_Paginated() + public async Task GivenAValidRequestThenReturnFilteredOrdersPaginated() { // Arrange var pageNumber = 1; @@ -204,7 +204,7 @@ public async Task Given_A_Valid_Request_Then_Return_Filtered_Orders_Paginated() } [Fact] - public async Task Given_A_Valid_Request_Then_Return_No_Filtered_Orders_Paginated() + public async Task GivenAValidRequestThenReturnNoFilteredOrdersPaginated() { // Arrange var pageNumber = 1; @@ -231,7 +231,7 @@ public async Task Given_A_Valid_Request_Then_Return_No_Filtered_Orders_Paginated [Theory] [InlineData(true)] [InlineData(false)] - public async Task Given_A_Valid_Request_Then_Return_Sorted_Orders_Paginated(bool sortDescending) + public async Task GivenAValidRequestThenReturnSortedOrdersPaginated(bool sortDescending) { // Arrange var pageNumber = 1; diff --git a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs index f4ea7772..eb1a89d9 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs @@ -24,8 +24,8 @@ public GetOrderGrpcTest(CustomWebApplicationFactory customWebApplicatio _service = new(_grpcChannel); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange GetOrderRequest request = new() @@ -48,8 +48,8 @@ public async Task Given_A_Valid_Request_Then_Pass() Assert.Equal(1000.0, response.Data.Total); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] + public async Task GivenAInvalidRequestThenFails() { // Arrange GetOrderRequest request = new() diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs index cf9af870..a4bf0208 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs @@ -29,8 +29,8 @@ public CreateOrderTest(CustomWebApplicationFactory customWebApplication _fixture.resourceUrl = "orders"; } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange var request = _fixture.SetValidRequest(); @@ -46,8 +46,8 @@ public async Task Given_A_Valid_Request_Then_Pass() Assert.NotNull(response.Data); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] + public async Task GivenAInvalidRequestThenFails() { // Arrange var request = _fixture.SetInvalidRequest(); diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs index 42d98d43..b562f8db 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs @@ -30,8 +30,8 @@ public GetAllOrdersTest(CustomWebApplicationFactory customWebApplicatio _fixture.resourceUrl = "orders/paginated"; } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange var request = _fixture.SetValidRequest(); @@ -49,8 +49,8 @@ public async Task Given_A_Valid_Request_Then_Pass() Assert.True(response.TotalRecords >= 0); } - [Fact(DisplayName = nameof(Given_An_Invalid_Page_Request_Then_Fails))] - public async Task Given_An_Invalid_Page_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAnInvalidPageRequestThenFails))] + public async Task GivenAnInvalidPageRequestThenFails() { // Arrange var request = _fixture.SetInvalidPageRequest(); @@ -66,8 +66,8 @@ public async Task Given_An_Invalid_Page_Request_Then_Fails() Assert.Contains("Page must be greater than 0", response.Message); } - [Fact(DisplayName = nameof(Given_An_Invalid_PageSize_Request_Then_Fails))] - public async Task Given_An_Invalid_PageSize_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAnInvalidPageSizeRequestThenFails))] + public async Task GivenAnInvalidPageSizeRequestThenFails() { // Arrange var request = _fixture.SetInvalidPageSizeRequest(); @@ -83,8 +83,8 @@ public async Task Given_An_Invalid_PageSize_Request_Then_Fails() Assert.Contains("PageSize must be greater than 0", response.Message); } - [Fact(DisplayName = nameof(Given_An_Valid_Request_When_Pass_Search_By_Values_Filter_Then_Pass))] - public async Task Given_An_Valid_Request_When_Pass_Search_By_Values_Filter_Then_Pass() + [Fact(DisplayName = nameof(GivenAnValidRequestWhenPassSearchByValuesFilterThenPass))] + public async Task GivenAnValidRequestWhenPassSearchByValuesFilterThenPass() { // Arrange var request = new BasePaginatedRequest( diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs index cde446ae..6bdc93f7 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs @@ -18,8 +18,8 @@ public GetOrderTest(CustomWebApplicationFactory customWebApplicationFac _fixture.resourceUrl = "orders/{0}"; } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange var id = 1; @@ -44,8 +44,8 @@ public async Task Given_A_Valid_Request_Then_Pass() Assert.NotEmpty(data.Items); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] + public async Task GivenAInvalidRequestThenFails() { // Arrange var id = 9999999; diff --git a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs index e50c394b..415d1539 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs @@ -30,8 +30,8 @@ public CreateNotificationTest(CustomWebApplicationFactory factory, Crea _fixture.SetServices(factory); } - [Fact(DisplayName = nameof(Given_A_Valid_Message_Then_Pass))] - public async Task Given_A_Valid_Message_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidMessageThenPass))] + public async Task GivenAValidMessageThenPass() { // Arrange var message = _fixture.SetValidMessage(); @@ -50,8 +50,8 @@ public async Task Given_A_Valid_Message_Then_Pass() Assert.Equal(message.NotificationType, notification.NotificationType); } - [Fact(DisplayName = nameof(Given_A_Duplicate_Message_Then_Should_Not_Create_Duplicated_Message))] - public async Task Given_A_Duplicate_Message_Then_Should_Not_Create_Duplicated_Message() + [Fact(DisplayName = nameof(GivenADuplicateMessageThenShouldNotCreateDuplicatedMessage))] + public async Task GivenADuplicateMessageThenShouldNotCreateDuplicatedMessage() { // Arrange var message = _fixture.SetValidMessage(); From 7ca41425b95ea4b342b87c98ddec50f1bdecb311 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Feb 2026 19:01:18 -0300 Subject: [PATCH 051/161] 53 refactor: simplify DomainEntity constructors and related tests --- .../Full/src/Domain/Common/DomainEntity.cs | 4 +- .../src/Domain/Notifications/Notification.cs | 2 +- templates/Full/src/Domain/Orders/Item.cs | 2 +- templates/Full/src/Domain/Orders/Order.cs | 2 +- .../WebApp/Http/Orders/GetAllOrdersTest.cs | 4 +- .../UnitTests/Domain/DomainEntityTests.cs | 54 ++++++------------- 6 files changed, 24 insertions(+), 44 deletions(-) diff --git a/templates/Full/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs index cbfda8cd..7f6ba573 100644 --- a/templates/Full/src/Domain/Common/DomainEntity.cs +++ b/templates/Full/src/Domain/Common/DomainEntity.cs @@ -3,9 +3,9 @@ public abstract class DomainEntity { protected DomainEntity() {} - protected DomainEntity(DateTime currentDate, string? user = null, string timezoneId = "") + protected DomainEntity(string? user = null, string timezoneId = "") { - CreatedAt = currentDate == default ? DateTime.UtcNow : currentDate; + CreatedAt = DateTime.UtcNow; CreatedBy = user ?? "System"; UpdatedAt = CreatedAt; UpdatedBy = CreatedBy; diff --git a/templates/Full/src/Domain/Notifications/Notification.cs b/templates/Full/src/Domain/Notifications/Notification.cs index a7a3f766..14edb2b9 100644 --- a/templates/Full/src/Domain/Notifications/Notification.cs +++ b/templates/Full/src/Domain/Notifications/Notification.cs @@ -12,7 +12,7 @@ public Notification( string notificationStatus, string? createdBy = null, object? message = null - ) : base(DateTime.UtcNow, createdBy) + ) : base(createdBy) { NotificationType = notificationType; NotificationStatus = notificationStatus; diff --git a/templates/Full/src/Domain/Orders/Item.cs b/templates/Full/src/Domain/Orders/Item.cs index 9b58c181..d259d47a 100644 --- a/templates/Full/src/Domain/Orders/Item.cs +++ b/templates/Full/src/Domain/Orders/Item.cs @@ -5,7 +5,7 @@ public sealed class Item : DomainEntity { public Item() { } - public Item(string name, string description, decimal value) : base(DateTime.UtcNow) + public Item(string name, string description, decimal value) : base() { Name = name; Description = description; diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index 1a9a7c09..6350e223 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -6,7 +6,7 @@ public sealed class Order : DomainEntity { public Order() { } - public Order(string description, ICollection items) : base(DateTime.UtcNow) + public Order(string description, ICollection items) : base() { Description = description; Items = items; diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs index b562f8db..f276802c 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs @@ -9,7 +9,7 @@ namespace IntegrationTests.WebApp.Http.Orders; public class GetAllOrdersTestFixture : BaseHttpFixture { - public BasePaginatedRequest SetValidRequest() => + public static BasePaginatedRequest SetValidRequest() => new(Guid.NewGuid(), 1, 10); public BasePaginatedRequest SetInvalidPageRequest() => @@ -34,7 +34,7 @@ public GetAllOrdersTest(CustomWebApplicationFactory customWebApplicatio public async Task GivenAValidRequestThenPass() { // Arrange - var request = _fixture.SetValidRequest(); + var request = GetAllOrdersTestFixture.SetValidRequest(); // Act var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); diff --git a/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs b/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs index 60b09471..ef5d5163 100644 --- a/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs +++ b/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs @@ -7,22 +7,19 @@ public sealed class DomainEntityTests private sealed class TestDomainEntity : DomainEntity { public TestDomainEntity() : base() { } - public TestDomainEntity(DateTime currentDate, string? user = null, string timezoneId = "") - : base(currentDate, user, timezoneId) { } + public TestDomainEntity(string? user = null, string timezoneId = "") + : base(user, timezoneId) { } } [Fact(DisplayName = nameof(ConstructorWithValidParametersShouldCreateEntityWithProvidedValues))] public void ConstructorWithValidParametersShouldCreateEntityWithProvidedValues() { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); var user = "TestUser"; var timezoneId = "America/New_York"; - var entity = new TestDomainEntity(currentDate, user, timezoneId); + var entity = new TestDomainEntity(user, timezoneId); - Assert.Equal(currentDate, entity.CreatedAt); Assert.Equal(user, entity.CreatedBy); - Assert.Equal(currentDate, entity.UpdatedAt); Assert.Equal(user, entity.UpdatedBy); Assert.Equal(timezoneId, entity.TimezoneId); } @@ -32,7 +29,7 @@ public void ConstructorWithDefaultDateTimeShouldUseUtcNow() { var beforeCreation = DateTime.UtcNow; - var entity = new TestDomainEntity(default); + var entity = new TestDomainEntity(); var afterCreation = DateTime.UtcNow; Assert.True(entity.CreatedAt >= beforeCreation && entity.CreatedAt <= afterCreation); @@ -44,7 +41,7 @@ public void ConstructorWithNullUserShouldDefaultToSystem() { var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - var entity = new TestDomainEntity(currentDate, null); + var entity = new TestDomainEntity(null); Assert.Equal("System", entity.CreatedBy); Assert.Equal("System", entity.UpdatedBy); @@ -53,10 +50,9 @@ public void ConstructorWithNullUserShouldDefaultToSystem() [Fact(DisplayName = nameof(ConstructorWithValidTimezoneIdShouldSetTimezoneId))] public void ConstructorWithValidTimezoneIdShouldSetTimezoneId() { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); var timezoneId = "America/Sao_Paulo"; - var entity = new TestDomainEntity(currentDate, "TestUser", timezoneId); + var entity = new TestDomainEntity("TestUser", timezoneId); Assert.Equal(timezoneId, entity.TimezoneId); } @@ -64,10 +60,9 @@ public void ConstructorWithValidTimezoneIdShouldSetTimezoneId() [Fact(DisplayName = nameof(ConstructorWithInvalidTimezoneIdShouldDefaultToUtc))] public void ConstructorWithInvalidTimezoneIdShouldDefaultToUtc() { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); var invalidTimezoneId = "Invalid/Timezone"; - var entity = new TestDomainEntity(currentDate, "TestUser", invalidTimezoneId); + var entity = new TestDomainEntity("TestUser", invalidTimezoneId); Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); } @@ -75,10 +70,9 @@ public void ConstructorWithInvalidTimezoneIdShouldDefaultToUtc() [Fact(DisplayName = nameof(ConstructorWithNullTimezoneIdShouldDefaultToUtc))] public void ConstructorWithNullTimezoneIdShouldDefaultToUtc() { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); string? nullTimezone = null; - var entity = new TestDomainEntity(currentDate, "TestUser", nullTimezone!); + var entity = new TestDomainEntity("TestUser", nullTimezone!); Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); } @@ -86,9 +80,7 @@ public void ConstructorWithNullTimezoneIdShouldDefaultToUtc() [Fact(DisplayName = nameof(ConstructorWithEmptyTimezoneIdShouldDefaultToUtc))] public void ConstructorWithEmptyTimezoneIdShouldDefaultToUtc() { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - - var entity = new TestDomainEntity(currentDate, "TestUser", string.Empty); + var entity = new TestDomainEntity("TestUser", string.Empty); Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); } @@ -96,9 +88,7 @@ public void ConstructorWithEmptyTimezoneIdShouldDefaultToUtc() [Fact(DisplayName = nameof(ConstructorWithWhitespaceTimezoneIdShouldDefaultToUtc))] public void ConstructorWithWhitespaceTimezoneIdShouldDefaultToUtc() { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - - var entity = new TestDomainEntity(currentDate, "TestUser", " "); + var entity = new TestDomainEntity("TestUser", " "); Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); } @@ -106,8 +96,7 @@ public void ConstructorWithWhitespaceTimezoneIdShouldDefaultToUtc() [Fact(DisplayName = nameof(UpdateWithValidParametersShouldUpdateEntityProperties))] public void UpdateWithValidParametersShouldUpdateEntityProperties() { - var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - var entity = new TestDomainEntity(initialDate, "InitialUser", "America/New_York"); + var entity = new TestDomainEntity("InitialUser", "America/New_York"); var beforeUpdate = DateTime.UtcNow; entity.Update("UpdatedUser", "Europe/London"); @@ -116,15 +105,13 @@ public void UpdateWithValidParametersShouldUpdateEntityProperties() Assert.True(entity.UpdatedAt >= beforeUpdate && entity.UpdatedAt <= afterUpdate); Assert.Equal("UpdatedUser", entity.UpdatedBy); Assert.Equal("Europe/London", entity.TimezoneId); - Assert.Equal(initialDate, entity.CreatedAt); Assert.Equal("InitialUser", entity.CreatedBy); } [Fact(DisplayName = nameof(UpdateWithNullUserShouldDefaultToSystem))] public void UpdateWithNullUserShouldDefaultToSystem() { - var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - var entity = new TestDomainEntity(initialDate, "InitialUser"); + var entity = new TestDomainEntity("InitialUser"); entity.Update(null); @@ -134,8 +121,7 @@ public void UpdateWithNullUserShouldDefaultToSystem() [Fact(DisplayName = nameof(UpdateWithInvalidTimezoneIdShouldDefaultToUtc))] public void UpdateWithInvalidTimezoneIdShouldDefaultToUtc() { - var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - var entity = new TestDomainEntity(initialDate, "TestUser", "America/New_York"); + var entity = new TestDomainEntity("TestUser", "America/New_York"); entity.Update("UpdatedUser", "Invalid/Timezone"); @@ -146,8 +132,7 @@ public void UpdateWithInvalidTimezoneIdShouldDefaultToUtc() [Fact(DisplayName = nameof(UpdateWithEmptyTimezoneIdShouldDefaultToUtc))] public void UpdateWithEmptyTimezoneIdShouldDefaultToUtc() { - var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - var entity = new TestDomainEntity(initialDate, "TestUser", "America/New_York"); + var entity = new TestDomainEntity("TestUser", "America/New_York"); entity.Update("UpdatedUser", string.Empty); @@ -157,8 +142,7 @@ public void UpdateWithEmptyTimezoneIdShouldDefaultToUtc() [Fact(DisplayName = nameof(UpdateWithoutParametersShouldUseDefaultValues))] public void UpdateWithoutParametersShouldUseDefaultValues() { - var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - var entity = new TestDomainEntity(initialDate, "InitialUser", "America/New_York"); + var entity = new TestDomainEntity("InitialUser", "America/New_York"); var beforeUpdate = DateTime.UtcNow; entity.Update(); @@ -172,9 +156,7 @@ public void UpdateWithoutParametersShouldUseDefaultValues() [Fact(DisplayName = nameof(ConstructorWithUtcTimezoneIdShouldSetUtcTimezone))] public void ConstructorWithUtcTimezoneIdShouldSetUtcTimezone() { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - - var entity = new TestDomainEntity(currentDate, "TestUser", TimeZoneInfo.Utc.Id); + var entity = new TestDomainEntity("TestUser", TimeZoneInfo.Utc.Id); Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); } @@ -182,14 +164,12 @@ public void ConstructorWithUtcTimezoneIdShouldSetUtcTimezone() [Fact(DisplayName = nameof(UpdatePreservesCreatedAtAndCreatedByWhenUpdating))] public void UpdatePreservesCreatedAtAndCreatedByWhenUpdating() { - var initialDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); var initialUser = "InitialUser"; - var entity = new TestDomainEntity(initialDate, initialUser, "America/New_York"); + var entity = new TestDomainEntity(initialUser, "America/New_York"); entity.Update("UpdatedUser", "Europe/London"); entity.Update("AnotherUser", "Asia/Tokyo"); - Assert.Equal(initialDate, entity.CreatedAt); Assert.Equal(initialUser, entity.CreatedBy); Assert.Equal("AnotherUser", entity.UpdatedBy); Assert.Equal("Asia/Tokyo", entity.TimezoneId); From 849700259a8c405f2165c1b52549eb51f0054172 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Feb 2026 19:17:36 -0300 Subject: [PATCH 052/161] feat(53): add logging helper methods for use case execution --- .../src/Application/Common/Helpers/Logs.cs | 273 ++++++++++++++++++ .../Common/UseCases/BaseInOutUseCase.cs | 16 +- .../Common/UseCases/BaseInUseCase.cs | 16 +- .../Common/UseCases/BaseOutUseCase.cs | 8 +- .../CreateNotificationUseCase.cs | 8 +- .../GetAllNotificationsUseCase.cs | 8 +- .../Notifications/GetNotificationUseCase.cs | 8 +- .../Application/Orders/CreateOrderUseCase.cs | 5 +- .../Application/Orders/GetAllOrdersUseCase.cs | 8 +- .../src/Application/Orders/GetOrderUseCase.cs | 6 +- .../Messaging/Consumers/BaseConsumer.cs | 41 +-- .../Messaging/Producers/ProducerService.cs | 21 +- .../ExceptionHandlingMiddleware.cs | 3 +- .../Common/BaseApplicationFixture.cs | 74 +++-- 14 files changed, 369 insertions(+), 126 deletions(-) create mode 100644 templates/Full/src/Application/Common/Helpers/Logs.cs diff --git a/templates/Full/src/Application/Common/Helpers/Logs.cs b/templates/Full/src/Application/Common/Helpers/Logs.cs new file mode 100644 index 00000000..baa2eefc --- /dev/null +++ b/templates/Full/src/Application/Common/Helpers/Logs.cs @@ -0,0 +1,273 @@ +using Microsoft.Extensions.Logging; + +namespace Application.Common.Helpers; + +public partial class Logs +{ + // Use Case Execution Logs + + /// + /// Logs the start of the execution of a use case, including the class name, method name, and correlation ID. + /// + /// The logger instance to use for logging. + /// The name of the class where the use case is executed. + /// The name of the method where the use case is executed. + /// The correlation ID for tracking the request. + [LoggerMessage( + EventId = 1, + Level = LogLevel.Information, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Start to execute use case" + )] + public static partial void StartToExecuteUseCase(ILogger logger, string className, string methodName, Guid correlationId); + + /// + /// Logs the completion of the execution of a use case, including the class name, method name, correlation ID, and elapsed time in milliseconds. + /// + /// The logger instance to use for logging. + /// The name of the class where the use case is executed. + /// The name of the method where the use case is executed. + /// The correlation ID for tracking the request. + /// The elapsed time in milliseconds for the execution of the use case. + [LoggerMessage( + EventId = 2, + Level = LogLevel.Information, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Elapsed time: {ElapsedMilliseconds} ms | Finished executing use case" + )] + public static partial void FinishedExecutingUseCase(ILogger logger, string className, string methodName, Guid correlationId, long elapsedMilliseconds); + + // Validation Logs + + /// + /// Logs validation errors encountered during request validation. + /// + /// The logger instance to use for logging. + /// The name of the class where the validation occurred. + /// The name of the method where the validation occurred. + /// The correlation ID for tracking the request. + /// The validation errors. + [LoggerMessage( + EventId = 3, + Level = LogLevel.Error, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Validation errors: {Errors}" + )] + public static partial void ValidationErrors(ILogger logger, string className, string methodName, Guid correlationId, string errors); + + /// + /// Logs when an entity is not found. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The name of the method. + /// The correlation ID for tracking the request. + /// The name of the entity that was not found. + [LoggerMessage( + EventId = 4, + Level = LogLevel.Warning, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {EntityName} not found." + )] + public static partial void NotFound(ILogger logger, string className, string methodName, Guid correlationId, string entityName); + + /// + /// Logs a generic operation failure with a custom message. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The name of the method. + /// The correlation ID for tracking the request. + /// The failure message. + [LoggerMessage( + EventId = 5, + Level = LogLevel.Warning, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Operation failed: {Message}" + )] + public static partial void OperationFailed(ILogger logger, string className, string methodName, Guid correlationId, string message); + + /// + /// Logs when starting to publish a message. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + /// The type of message being published. + [LoggerMessage( + EventId = 6, + Level = LogLevel.Information, + Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Publishing message: {MessageType}" + )] + public static partial void PublishingMessage(ILogger logger, string className, Guid correlationId, string messageType); + + /// + /// Logs when a message has been published successfully. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + /// The type of message published. + /// The elapsed time in milliseconds. + [LoggerMessage( + EventId = 7, + Level = LogLevel.Information, + Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Message published: {MessageType} | Elapsed time: {ElapsedMilliseconds} ms" + )] + public static partial void MessagePublished(ILogger logger, string className, Guid correlationId, string messageType, long elapsedMilliseconds); + + /// + /// Logs when starting to publish a batch of messages. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation IDs for tracking the requests. + /// The type of messages being published. + [LoggerMessage( + EventId = 8, + Level = LogLevel.Information, + Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationIds} | Publishing batch of messages: {MessageType}" + )] + public static partial void PublishingBatchMessages(ILogger logger, string className, object correlationIds, string messageType); + + /// + /// Logs when a batch of messages has been published successfully. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation IDs for tracking the requests. + /// The type of messages published. + /// The elapsed time in milliseconds. + [LoggerMessage( + EventId = 9, + Level = LogLevel.Information, + Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationIds} | Batch of messages published: {MessageType} | Elapsed time: {ElapsedMilliseconds} ms" + )] + public static partial void BatchMessagesPublished(ILogger logger, string className, object correlationIds, string messageType, long elapsedMilliseconds); + + /// + /// Logs when a message is received by a consumer. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + /// The type of message received. + [LoggerMessage( + EventId = 10, + Level = LogLevel.Information, + Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Received message: {MessageType}" + )] + public static partial void ReceivedMessage(ILogger logger, string className, Guid correlationId, string messageType); + + /// + /// Logs when a duplicate message is detected. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + [LoggerMessage( + EventId = 11, + Level = LogLevel.Warning, + Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Duplicate message detected. Skipping processing." + )] + public static partial void DuplicateMessageDetected(ILogger logger, string className, Guid correlationId); + + /// + /// Logs when starting to process a message. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + [LoggerMessage( + EventId = 12, + Level = LogLevel.Information, + Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Start processing message." + )] + public static partial void StartProcessingMessage(ILogger logger, string className, Guid correlationId); + + /// + /// Logs when a message has been processed successfully. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + /// The elapsed time in milliseconds. + [LoggerMessage( + EventId = 13, + Level = LogLevel.Information, + Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Processed message in {ElapsedMilliseconds} ms" + )] + public static partial void ProcessedMessage(ILogger logger, string className, Guid correlationId, long elapsedMilliseconds); + + /// + /// Logs when an error occurs while processing a message. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + /// The error message. + /// The stack trace. + [LoggerMessage( + EventId = 14, + Level = LogLevel.Error, + Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Error processing message: {ErrorMessage} | StackTrace: {StackTrace}" + )] + public static partial void ErrorProcessingMessage(ILogger logger, string className, Guid? correlationId, string errorMessage, string? stackTrace); + + /// + /// Logs when a null message is received. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The type of message expected. + [LoggerMessage( + EventId = 15, + Level = LogLevel.Warning, + Message = "[{ClassName}] | [HandleMessageAsync] | Received null message of type {MessageType}" + )] + public static partial void ReceivedNullMessage(ILogger logger, string className, string messageType); + + /// + /// Logs when an error occurs while deserializing a message. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + /// The application ID. + /// The cluster ID. + /// The error message. + /// The stack trace. + [LoggerMessage( + EventId = 16, + Level = LogLevel.Error, + Message = "[{ClassName}] | [HandleRabbitMqAsync] | [{CorrelationId}] | AppId: {AppId} | ClusterId: {ClusterId} | Error deserializing message: {ErrorMessage} | StackTrace: {StackTrace}" + )] + public static partial void ErrorDeserializingMessage(ILogger logger, string className, string? correlationId, string? appId, string? clusterId, string errorMessage, string? stackTrace); + + /// + /// Logs when an unexpected error occurs. + /// + /// The logger instance to use for logging. + /// The name of the class. + /// The correlation ID for tracking the request. + /// The application ID. + /// The cluster ID. + /// The error message. + /// The stack trace. + [LoggerMessage( + EventId = 17, + Level = LogLevel.Error, + Message = "[{ClassName}] | [HandleRabbitMqAsync] | [{CorrelationId}] | AppId: {AppId} | ClusterId: {ClusterId} | Unexpected error: {ErrorMessage} | StackTrace: {StackTrace}" + )] + public static partial void UnexpectedError(ILogger logger, string className, string? correlationId, string? appId, string? clusterId, string errorMessage, string? stackTrace); + + /// + /// Logs when an exception is handled by the exception handling middleware. + /// + /// The logger instance to use for logging. + /// The exception that occurred. + /// The name of the class. + /// The name of the method. + /// The exception message. + [LoggerMessage( + EventId = 18, + Level = LogLevel.Error, + Message = "[{ClassName}] | [{MethodName}] | {Message}" + )] + public static partial void ExceptionHandlerError(ILogger logger, Exception exception, string className, string methodName, string message); +} \ No newline at end of file diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 4b2edb9b..26823038 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -1,5 +1,6 @@ using System.Diagnostics.Metrics; using Application.Common.Constants; +using Application.Common.Helpers; using Application.Common.Repositories; using Application.Common.Requests; using Application.Common.Services; @@ -49,10 +50,7 @@ CancellationToken cancellationToken { stopWatch.Restart(); - logger.LogInformation( - DefaultApplicationMessages.StartToExecuteUseCase, - ClassName, HandleMethodName, request.CorrelationId - ); + Logs.StartToExecuteUseCase(logger, ClassName, HandleMethodName, request.CorrelationId); TResponseData response; if (_validator != null) @@ -61,10 +59,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { string errors = string.Join(", ", validationResult.Errors); - logger.LogError( - DefaultApplicationMessages.ValidationErrors, - ClassName, HandleMethodName, request.CorrelationId, errors - ); + Logs.ValidationErrors(logger, ClassName, HandleMethodName, request.CorrelationId, errors); response = Activator.CreateInstance(); response = response with @@ -79,10 +74,7 @@ CancellationToken cancellationToken response = await HandleInternalAsync(request, cancellationToken); - logger.LogInformation( - DefaultApplicationMessages.FinishedExecutingUseCase, - ClassName, HandleMethodName, request.CorrelationId, stopWatch.ElapsedMilliseconds - ); + Logs.FinishedExecutingUseCase(logger, ClassName, HandleMethodName, request.CorrelationId, stopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); _useCaseExecutionElapsedTime.Record(stopWatch.ElapsedMilliseconds); diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 5d971497..bbc7dd11 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -6,6 +6,7 @@ using Application.Common.Constants; using System.Diagnostics.Metrics; using Application.Common.Repositories; +using Application.Common.Helpers; namespace Application.Common.UseCases; @@ -45,10 +46,7 @@ CancellationToken cancellationToken { stopWatch.Restart(); - logger.LogInformation( - DefaultApplicationMessages.StartToExecuteUseCase, - ClassName, HandleMethodName, request.CorrelationId - ); + Logs.StartToExecuteUseCase(logger, ClassName, HandleMethodName, request.CorrelationId); if (_validator != null) { @@ -56,10 +54,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { var errors = string.Join(", ", validationResult.Errors); - logger.LogError( - DefaultApplicationMessages.ValidationErrors, - ClassName, HandleMethodName, request.CorrelationId, errors - ); + Logs.ValidationErrors(logger, ClassName, HandleMethodName, request.CorrelationId, errors); return; } @@ -67,10 +62,7 @@ CancellationToken cancellationToken await HandleInternalAsync(request, cancellationToken); - logger.LogInformation( - DefaultApplicationMessages.FinishedExecutingUseCase, - ClassName, HandleMethodName, request.CorrelationId, stopWatch.ElapsedMilliseconds - ); + Logs.FinishedExecutingUseCase(logger, ClassName, HandleMethodName, request.CorrelationId, stopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); _useCaseExecutionElapsedTime.Record(stopWatch.ElapsedMilliseconds); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 2832e492..1b5017bf 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -4,6 +4,7 @@ using Application.Common.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Application.Common.Helpers; namespace Application.Common.UseCases; @@ -36,14 +37,11 @@ public async Task HandleAsync(CancellationToken cancellationToken { stopWatch.Restart(); var correlationId = Guid.NewGuid(); - logger.LogInformation(DefaultApplicationMessages.StartToExecuteUseCase, ClassName, HandleMethodName, correlationId); + Logs.StartToExecuteUseCase(logger, ClassName, HandleMethodName, correlationId); var response = await HandleInternalAsync(cancellationToken); - logger.LogInformation( - DefaultApplicationMessages.FinishedExecutingUseCase, - ClassName, HandleMethodName, correlationId, stopWatch.ElapsedMilliseconds - ); + Logs.FinishedExecutingUseCase(logger, ClassName, HandleMethodName, correlationId, stopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); _useCaseExecutionElapsedTime.Record(stopWatch.ElapsedMilliseconds); diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 21d6d8cb..5f083660 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -1,6 +1,7 @@ using Application.Common.Constants; using Application.Common.Requests; using Application.Common.UseCases; +using Application.Common.Helpers; using Domain.Notifications; using FluentValidation; using Microsoft.Extensions.Logging; @@ -42,12 +43,7 @@ CancellationToken cancellationToken if (addResult == 0) { - logger.LogWarning( - "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Failed to create notification.", - ClassName, - HandleMethodName, - request.CorrelationId - ); + Logs.OperationFailed(logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); } } } diff --git a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs index e6e45013..cc25377a 100644 --- a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs @@ -1,6 +1,7 @@ using Application.Common.Constants; using Application.Common.Requests; using Application.Common.UseCases; +using Application.Common.Helpers; using Domain.Notifications; using Microsoft.Extensions.Logging; @@ -33,12 +34,7 @@ CancellationToken cancellationToken if (notifications is null || !notifications.Any()) { - logger.LogWarning( - "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | No notifications found.", - ClassName, - HandleMethodName, - request.CorrelationId - ); + Logs.NoNotificationsFound(logger, ClassName, HandleMethodName, request.CorrelationId); return new(false, 0, 0, [], "No notifications found."); } diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index 35b8f353..065a9113 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -3,6 +3,7 @@ using Application.Common.Repositories; using Application.Common.Requests; using Application.Common.UseCases; +using Application.Common.Helpers; using Domain.Notifications; using FluentValidation; using Microsoft.Extensions.DependencyInjection; @@ -44,12 +45,7 @@ CancellationToken cancellationToken if (notification is null) { - logger.LogWarning( - "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Notification not found.", - ClassName, - HandleMethodName, - request.CorrelationId - ); + Logs.NotificationNotFound(logger, ClassName, HandleMethodName, request.CorrelationId); return new(false, null, "Notification not found."); } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index e2dfb7e7..369ed35a 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -2,6 +2,7 @@ using Application.Common.Messages; using Application.Common.Requests; using Application.Common.UseCases; +using Application.Common.Helpers; using Domain.Orders; using FluentValidation; using Microsoft.Extensions.Logging; @@ -51,7 +52,7 @@ CancellationToken cancellationToken var createResult = newOrder.SetTotal(); if (createResult.IsFailure) { - logger.LogWarning("[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {Message}", ClassName, HandleMethodName, correlationId, createResult.Message); + Logs.OperationFailed(logger, ClassName, HandleMethodName, correlationId, createResult.Message); response = new(false, null, createResult.Message); @@ -63,7 +64,7 @@ CancellationToken cancellationToken var addResult = await _repository.AddAsync(newOrder, correlationId, cancellationToken); if (addResult == 0) { - logger.LogWarning("[{ClassName}] | [{MethodName}] | [{CorrelationId}] | " + "Failed to create order.", ClassName, HandleMethodName, correlationId); + Logs.OperationFailed(logger, ClassName, HandleMethodName, correlationId, "Failed to create order."); response = new(false, null, "Failed to create order."); diff --git a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs index ee5f2e78..c0f331a1 100644 --- a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs +++ b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs @@ -1,6 +1,7 @@ using Application.Common.Constants; using Application.Common.Requests; using Application.Common.UseCases; +using Application.Common.Helpers; using Domain.Orders; using Microsoft.Extensions.Logging; @@ -31,12 +32,7 @@ CancellationToken cancellationToken if (orders is null || !orders.Any()) { - logger.LogWarning( - "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | No orders found.", - ClassName, - HandleMethodName, - request.CorrelationId - ); + Logs.NoOrdersFound(logger, ClassName, HandleMethodName, request.CorrelationId); return new(false, 0, 0, [], "No orders found."); } diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index 7dd8c980..3e8e7f19 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -1,5 +1,6 @@ using Application.Common.Requests; using Application.Common.UseCases; +using Application.Common.Helpers; using Domain.Orders; using FluentValidation; using Microsoft.Extensions.Logging; @@ -42,10 +43,7 @@ CancellationToken cancellationToken if (order is null) { - logger.LogWarning( - "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Order not found.", - ClassName, HandleMethodName, request.CorrelationId - ); + Logs.OrderNotFound(logger, ClassName, HandleMethodName, request.CorrelationId); return new(false, null, "Order not found."); } diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index e5471074..58453c17 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -2,6 +2,7 @@ using System.Text.Json; using Application.Common.Messages; using Application.Common.Services; +using Application.Common.Helpers; using Infrastructure.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -52,10 +53,7 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi var hybridCacheService = serviceProvider.GetRequiredService(); - logger.LogInformation( - "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Received message: {MessageType}", - _className, message.CorrelationId, typeof(TMessage).Name - ); + Logs.ReceivedMessage(logger, _className, message.CorrelationId, typeof(TMessage).Name); var isExecutedKey = _className + "-" + message.CorrelationId; var isExecuted = await hybridCacheService.GetOrCreateAsync( @@ -66,33 +64,21 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi if (isExecuted) { - logger.LogWarning( - "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Duplicate message detected. Skipping processing.", - _className, message.CorrelationId - ); + Logs.DuplicateMessageDetected(logger, _className, message.CorrelationId); return; } - logger.LogInformation( - "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Start processing message.", - _className, message.CorrelationId - ); + Logs.StartProcessingMessage(logger, _className, message.CorrelationId); await HandleUseCaseAsync(serviceProvider, message, cancellationToken); await hybridCacheService.CreateAsync(isExecutedKey, true, cancellationToken); - logger.LogInformation( - "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Processed message in {ElapsedMilliseconds} ms", - _className, message.CorrelationId, _stopwatch.ElapsedMilliseconds - ); + Logs.ProcessedMessage(logger, _className, message.CorrelationId, _stopwatch.ElapsedMilliseconds); } catch (Exception ex) { - logger.LogError( - "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Error processing message: {ErrorMessage} | StackTrace: {StackTrace}", - _className, message?.CorrelationId, ex.Message, ex.StackTrace - ); + Logs.ErrorProcessingMessage(logger, _className, message?.CorrelationId, ex.Message, ex.StackTrace); _ = producerService.HandleAsync(message!, CancellationToken.None, _queueName + "_deadLetter"); @@ -140,28 +126,19 @@ await channel.QueueDeclareAsync( if (message == null || message.GetType() != typeof(TMessage)) { - logger.LogWarning( - "[{ClassName}] | [HandleMessageAsync] | Received null message of type {MessageType}", - _className, typeof(TMessage).Name - ); + Logs.ReceivedNullMessage(logger, _className, typeof(TMessage).Name); return; } } catch (JsonException ex) { - logger.LogError( - "[{ClassName}] | [HandleRabbitMqAsync] | [{CorrelationId}] | AppId: {AppId} | ClusterId: {ClusterId} | Error deserializing message: {ErrorMessage} | StackTrace: {StackTrace}", - _className, basicProperties.CorrelationId, basicProperties.AppId, basicProperties.ClusterId, ex.Message, ex.StackTrace - ); + Logs.ErrorDeserializingMessage(logger, _className, basicProperties.CorrelationId, basicProperties.AppId, basicProperties.ClusterId, ex.Message, ex.StackTrace); throw; } catch (Exception ex) { - logger.LogError( - "[{ClassName}] | [HandleRabbitMqAsync] | [{CorrelationId}] | AppId: {AppId} | ClusterId: {ClusterId} | Unexpected error: {ErrorMessage} | StackTrace: {StackTrace}", - _className, basicProperties.CorrelationId, basicProperties.AppId, basicProperties.ClusterId, ex.Message, ex.StackTrace - ); + Logs.UnexpectedError(logger, _className, basicProperties.CorrelationId, basicProperties.AppId, basicProperties.ClusterId, ex.Message, ex.StackTrace); throw; } diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 1170fa3e..70add50a 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -2,6 +2,7 @@ using System.Text.Json; using Application.Common.Messages; using Application.Common.Services; +using Application.Common.Helpers; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using RabbitMQ.Client; @@ -43,10 +44,7 @@ public async Task HandleAsync( using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - _logger.LogInformation( - "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Publishing message: {MessageType}", - _className, message.CorrelationId, typeof(TMessage).Name - ); + Logs.PublishingMessage(_logger, _className, message.CorrelationId, typeof(TMessage).Name); await channel.BasicPublishAsync( exchange: exchange, @@ -55,10 +53,7 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - _logger.LogInformation( - "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Message published: {MessageType} | Elapsed time: {ElapsedMilliseconds} ms", - _className, message.CorrelationId, typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds - ); + Logs.MessagePublished(_logger, _className, message.CorrelationId, typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds); } public async Task HandleAsync( @@ -75,10 +70,7 @@ public async Task HandleAsync( using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - _logger.LogInformation( - "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Publishing batch of messages: {MessageType}", - _className, messages.Select(m => m.CorrelationId), typeof(TMessage).Name - ); + Logs.PublishingBatchMessages(_logger, _className, messages.Select(m => m.CorrelationId), typeof(TMessage).Name); foreach (var message in messages) await channel.BasicPublishAsync( @@ -88,9 +80,6 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - _logger.LogInformation( - "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Batch of messages published: {MessageType} | Elapsed time: {ElapsedMilliseconds} ms", - _className, messages.Select(m => m.CorrelationId), typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds - ); + Logs.BatchMessagesPublished(_logger, _className, messages.Select(m => m.CorrelationId), typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds); } } diff --git a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs index 99d1fb61..33b81f22 100644 --- a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs +++ b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -1,5 +1,6 @@ using System.Net; using Application.Common.Requests; +using Application.Common.Helpers; namespace WebApp.Middlewares; @@ -23,7 +24,7 @@ public async Task InvokeAsync(HttpContext context) private async Task HandleExceptionAsync(HttpContext context, Exception exception) { - _logger.LogError(exception, "[{ClassName}] | [{Method}] | {Message}", _className, nameof(HandleExceptionAsync), exception.Message); + Logs.ExceptionHandlerError(_logger, exception, _className, nameof(HandleExceptionAsync), exception.Message); BaseResponse response = new(false, exception.Message); diff --git a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs index c54b40e9..b8a28b4d 100644 --- a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs +++ b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs @@ -24,6 +24,9 @@ public class BaseApplicationFixture : BaseFixture public BaseApplicationFixture() { + // Setup logger to enable logging by default + mockLogger.Setup(l => l.IsEnabled(It.IsAny())).Returns(true); + MockServiceProviderServices(); } @@ -97,24 +100,59 @@ public void SetInvalidGetOrCreateAsync() => mockCache.Setup(c => c.GetO It.IsAny() )); - public void VerifyStartUseCaseLog(int times = 1) => VerifyLogInformation("Start to execute use case", times); - public void VerifyFinishUseCaseLog(int times = 1) => VerifyLogInformation("Finished executing use case", times); - public void VerifyFinishUseCaseWithCacheLog(int times = 1) => VerifyLogInformation("Finished executing use case with cache key", times); - - public void VerifyLogInformation(string message, int times = 1) => mockLogger.VerifyLog( - l => l.LogInformation($"*{message}*"), - Times.Exactly(times) - ); - - public void VerifyLogWarning(string message, int times = 1) => mockLogger.VerifyLog( - l => l.LogWarning($"*{message}*"), - Times.Exactly(times) - ); - - public void VerifyLogError(string message, int times = 1) => mockLogger.VerifyLog( - l => l.LogError($"*{message}*"), - Times.Exactly(times) - ); + public void VerifyStartUseCaseLog(int times = 1) => mockLogger.Verify( + x => x.Log( + LogLevel.Information, + It.Is(e => e.Id == 1), + It.Is((v, t) =>v.ToString()!.Contains("Start to execute use case")), + It.IsAny(), + It.IsAny>()), + Times.Exactly(times)); + + public void VerifyFinishUseCaseLog(int times = 1) => mockLogger.Verify( + x => x.Log( + LogLevel.Information, + It.Is(e => e.Id == 2), + It.Is((v, t) => v.ToString()!.Contains("Finished executing use case")), + It.IsAny(), + It.IsAny>()), + Times.Exactly(times)); + + public void VerifyFinishUseCaseWithCacheLog(int times = 1) => mockLogger.Verify( + x => x.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Finished executing use case with cache key")), + It.IsAny(), + It.IsAny>()), + Times.Exactly(times)); + + public void VerifyLogInformation(string message, int times = 1) => mockLogger.Verify( + x => x.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains(message)), + It.IsAny(), + It.IsAny>()), + Times.Exactly(times)); + + public void VerifyLogWarning(string message, int times = 1) => mockLogger.Verify( + x => x.Log( + LogLevel.Warning, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains(message)), + It.IsAny(), + It.IsAny>()), + Times.Exactly(times)); + + public void VerifyLogError(string message, int times = 1) => mockLogger.Verify( + x => x.Log( + LogLevel.Error, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains(message)), + It.IsAny(), + It.IsAny>()), + Times.Exactly(times)); public void VerifyCache(int times) => mockCache.Verify( c => c.GetOrCreateAsync( From 75950233acb70143a7ff513f491211cc5c29b251 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Feb 2026 19:52:06 -0300 Subject: [PATCH 053/161] 53 refactor: update fields to properties in use case classes --- .../ApplicationDependencyInjection.cs | 2 +- .../Common/UseCases/BaseInOutUseCase.cs | 22 +++++++++---------- .../Common/UseCases/BaseInUseCase.cs | 22 +++++++++---------- .../Common/UseCases/BaseOutUseCase.cs | 16 +++++++------- .../Common/UseCases/BaseUseCase.cs | 12 +++++----- .../CreateNotificationUseCase.cs | 4 ++-- .../GetAllNotificationsUseCase.cs | 4 ++-- .../Notifications/GetNotificationUseCase.cs | 4 ++-- .../Application/Orders/CreateOrderUseCase.cs | 8 +++---- .../Application/Orders/GetAllOrdersUseCase.cs | 4 ++-- .../src/Application/Orders/GetOrderUseCase.cs | 4 ++-- .../InfrastructureCacheDependencyInjection.cs | 2 +- .../Cache/Services/HybridCacheService.cs | 13 ++++++----- .../InfrastructureDataDependencyInjection.cs | 8 +++---- 14 files changed, 63 insertions(+), 62 deletions(-) diff --git a/templates/Full/src/Application/ApplicationDependencyInjection.cs b/templates/Full/src/Application/ApplicationDependencyInjection.cs index be60f15c..9a13eb2a 100644 --- a/templates/Full/src/Application/ApplicationDependencyInjection.cs +++ b/templates/Full/src/Application/ApplicationDependencyInjection.cs @@ -22,7 +22,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services private static void RegisterUseCases(IServiceCollection services, Assembly applicationAssembly) { var useCaseTypes = applicationAssembly.GetTypes() - .Where(t => t.IsClass && !t.IsAbstract && t.Name.EndsWith("UseCase")) + .Where(t => t.IsClass && !t.IsAbstract && t.Name.EndsWith("UseCase", StringComparison.Ordinal)) .ToList(); foreach (var useCaseType in useCaseTypes) diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 26823038..eaea11f9 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -21,9 +21,9 @@ public abstract class BaseInOutUseCase : BaseUseCase, I where TRequest : BaseRequest where TResponseData : BaseResponse { - protected readonly IHybridCacheService _cache; - protected readonly IProduceService _produceService; - protected readonly IBaseRepository _repository; + protected IHybridCacheService Cache { get; } + protected IProduceService ProduceService { get; } + protected IBaseRepository Repository { get; } private readonly IValidator _validator; private readonly Histogram _useCaseExecuted; private readonly Gauge _useCaseExecutionElapsedTime; @@ -31,9 +31,9 @@ public abstract class BaseInOutUseCase : BaseUseCase, I protected BaseInOutUseCase(IServiceProvider serviceProvider) : base(serviceProvider) { - _cache = serviceProvider.GetRequiredService(); - _produceService = serviceProvider.GetRequiredService(); - _repository = serviceProvider.GetRequiredService(); + Cache = serviceProvider.GetRequiredService(); + ProduceService = serviceProvider.GetRequiredService(); + Repository = serviceProvider.GetRequiredService(); _validator = serviceProvider.GetRequiredService>(); _useCaseExecuted = DefaultConfigurations.Meter @@ -48,9 +48,9 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - stopWatch.Restart(); + StopWatch.Restart(); - Logs.StartToExecuteUseCase(logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.StartToExecuteUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId); TResponseData response; if (_validator != null) @@ -59,7 +59,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { string errors = string.Join(", ", validationResult.Errors); - Logs.ValidationErrors(logger, ClassName, HandleMethodName, request.CorrelationId, errors); + Logs.ValidationErrors(Logger, ClassName, HandleMethodName, request.CorrelationId, errors); response = Activator.CreateInstance(); response = response with @@ -74,10 +74,10 @@ CancellationToken cancellationToken response = await HandleInternalAsync(request, cancellationToken); - Logs.FinishedExecutingUseCase(logger, ClassName, HandleMethodName, request.CorrelationId, stopWatch.ElapsedMilliseconds); + Logs.FinishedExecutingUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); - _useCaseExecutionElapsedTime.Record(stopWatch.ElapsedMilliseconds); + _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index bbc7dd11..89e0ee39 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -17,9 +17,9 @@ public interface IBaseInUseCase where TRequest : BaseRequest public abstract class BaseInUseCase : BaseUseCase, IBaseInUseCase where TRequest : BaseRequest { - protected readonly IHybridCacheService _cache; - protected readonly IProduceService _produceService; - protected readonly IBaseRepository _repository; + protected IHybridCacheService Cache { get; } + protected IProduceService ProduceService { get; } + protected IBaseRepository Repository { get; } private readonly IValidator _validator; private readonly Histogram _useCaseExecuted; private readonly Gauge _useCaseExecutionElapsedTime; @@ -27,9 +27,9 @@ public abstract class BaseInUseCase : BaseUseCase, IBaseInUseCase(); - _produceService = serviceProvider.GetRequiredService(); - _repository = serviceProvider.GetRequiredService(); + Cache = serviceProvider.GetRequiredService(); + ProduceService = serviceProvider.GetRequiredService(); + Repository = serviceProvider.GetRequiredService(); _validator = serviceProvider.GetRequiredService>(); _useCaseExecuted = DefaultConfigurations.Meter @@ -44,9 +44,9 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - stopWatch.Restart(); + StopWatch.Restart(); - Logs.StartToExecuteUseCase(logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.StartToExecuteUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId); if (_validator != null) { @@ -54,7 +54,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { var errors = string.Join(", ", validationResult.Errors); - Logs.ValidationErrors(logger, ClassName, HandleMethodName, request.CorrelationId, errors); + Logs.ValidationErrors(Logger, ClassName, HandleMethodName, request.CorrelationId, errors); return; } @@ -62,10 +62,10 @@ CancellationToken cancellationToken await HandleInternalAsync(request, cancellationToken); - Logs.FinishedExecutingUseCase(logger, ClassName, HandleMethodName, request.CorrelationId, stopWatch.ElapsedMilliseconds); + Logs.FinishedExecutingUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); - _useCaseExecutionElapsedTime.Record(stopWatch.ElapsedMilliseconds); + _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); } public abstract Task HandleInternalAsync(TRequest request, CancellationToken cancellationToken); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 1b5017bf..6d012760 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -15,16 +15,16 @@ public interface IBaseOutUseCase where TResponseData : BaseRespon public abstract class BaseOutUseCase : BaseUseCase, IBaseOutUseCase where TResponseData : BaseResponse { - protected readonly IHybridCacheService _cache; - protected readonly IProduceService _produceService; + protected IHybridCacheService Cache { get; } + protected IProduceService ProduceService { get; } private readonly Histogram _useCaseExecuted; private readonly Gauge _useCaseExecutionElapsedTime; protected const string HandleMethodName = nameof(HandleAsync); protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvider) { - _cache = serviceProvider.GetRequiredService(); - _produceService = serviceProvider.GetRequiredService(); + Cache = serviceProvider.GetRequiredService(); + ProduceService = serviceProvider.GetRequiredService(); _useCaseExecuted = DefaultConfigurations.Meter .CreateHistogram($"{ClassName}.Executed", "total", "Number of times the use case was executed"); @@ -35,16 +35,16 @@ protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvide public async Task HandleAsync(CancellationToken cancellationToken) { - stopWatch.Restart(); + StopWatch.Restart(); var correlationId = Guid.NewGuid(); - Logs.StartToExecuteUseCase(logger, ClassName, HandleMethodName, correlationId); + Logs.StartToExecuteUseCase(Logger, ClassName, HandleMethodName, correlationId); var response = await HandleInternalAsync(cancellationToken); - Logs.FinishedExecutingUseCase(logger, ClassName, HandleMethodName, correlationId, stopWatch.ElapsedMilliseconds); + Logs.FinishedExecutingUseCase(Logger, ClassName, HandleMethodName, correlationId, StopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); - _useCaseExecutionElapsedTime.Record(stopWatch.ElapsedMilliseconds); + _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index cb836050..5be8384d 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -6,18 +6,18 @@ namespace Application.Common.UseCases; public abstract class BaseUseCase { - protected readonly IServiceProvider serviceProvider; - protected readonly ILogger logger; - protected readonly Stopwatch stopWatch = new(); - protected string ClassName; + protected IServiceProvider ServiceProvider { get; } + protected ILogger Logger { get; } + protected Stopwatch StopWatch { get; } = new(); + protected string ClassName { get; set; } protected BaseUseCase(IServiceProvider serviceProvider) { var classType = GetType(); ClassName = classType.Name; - this.serviceProvider = serviceProvider; + ServiceProvider = serviceProvider; - logger = serviceProvider.GetRequiredService().CreateLogger(classType); + Logger = serviceProvider.GetRequiredService().CreateLogger(classType); } } \ No newline at end of file diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 5f083660..74cfc645 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -39,11 +39,11 @@ CancellationToken cancellationToken request.Message ); - var addResult = await _repository.AddAsync(notification, request.CorrelationId, cancellationToken); + var addResult = await Repository.AddAsync(notification, request.CorrelationId, cancellationToken); if (addResult == 0) { - Logs.OperationFailed(logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); + Logs.OperationFailed(Logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); } } } diff --git a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs index cc25377a..d8753931 100644 --- a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs @@ -15,7 +15,7 @@ public override async Task> HandleInterna CancellationToken cancellationToken ) { - var (notifications, totalRecords) = await _repository.GetAllPaginatedAsync( + var (notifications, totalRecords) = await Repository.GetAllPaginatedAsync( request.CorrelationId, request.Page, request.PageSize, @@ -34,7 +34,7 @@ CancellationToken cancellationToken if (notifications is null || !notifications.Any()) { - Logs.NoNotificationsFound(logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(Notification)); return new(false, 0, 0, [], "No notifications found."); } diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index 065a9113..443f6161 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -30,7 +30,7 @@ public override async Task> HandleInternalAsync( CancellationToken cancellationToken ) { - var notification = await _repository.GetByIdAsNoTrackingAsync( + var notification = await Repository.GetByIdAsNoTrackingAsync( request.Id, request.CorrelationId, n => new() @@ -45,7 +45,7 @@ CancellationToken cancellationToken if (notification is null) { - Logs.NotificationNotFound(logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(notification)); return new(false, null, "Notification not found."); } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 369ed35a..d6a9174d 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -52,7 +52,7 @@ CancellationToken cancellationToken var createResult = newOrder.SetTotal(); if (createResult.IsFailure) { - Logs.OperationFailed(logger, ClassName, HandleMethodName, correlationId, createResult.Message); + Logs.OperationFailed(Logger, ClassName, HandleMethodName, correlationId, createResult.Message); response = new(false, null, createResult.Message); @@ -61,10 +61,10 @@ CancellationToken cancellationToken return response; } - var addResult = await _repository.AddAsync(newOrder, correlationId, cancellationToken); + var addResult = await Repository.AddAsync(newOrder, correlationId, cancellationToken); if (addResult == 0) { - Logs.OperationFailed(logger, ClassName, HandleMethodName, correlationId, "Failed to create order."); + Logs.OperationFailed(Logger, ClassName, HandleMethodName, correlationId, "Failed to create order."); response = new(false, null, "Failed to create order."); @@ -91,7 +91,7 @@ CancellationToken cancellationToken return response; } - private void CreateNotification(Guid correlationId, string notificationStatus, object message) => _ = _produceService.HandleAsync( + private void CreateNotification(Guid correlationId, string notificationStatus, object message) => _ = ProduceService.HandleAsync( new CreateNotificationMessage( correlationId, NotificationType.OrderCreated, diff --git a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs index c0f331a1..e2f277a4 100644 --- a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs +++ b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs @@ -15,7 +15,7 @@ public override async Task> HandleInternalAsync( CancellationToken cancellationToken ) { - var (orders, totalRecords) = await _repository.GetAllPaginatedAsync( + var (orders, totalRecords) = await Repository.GetAllPaginatedAsync( request.CorrelationId, request.Page, request.PageSize, @@ -32,7 +32,7 @@ CancellationToken cancellationToken if (orders is null || !orders.Any()) { - Logs.NoOrdersFound(logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(orders)); return new(false, 0, 0, [], "No orders found."); } diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index 3e8e7f19..28a8af05 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -24,7 +24,7 @@ public override async Task> HandleInternalAsync( CancellationToken cancellationToken ) { - var order = await _repository.GetByIdAsNoTrackingAsync( + var order = await Repository.GetByIdAsNoTrackingAsync( request.Id, request.CorrelationId, o => new OrderDto() @@ -43,7 +43,7 @@ CancellationToken cancellationToken if (order is null) { - Logs.OrderNotFound(logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(order)); return new(false, null, "Order not found."); } diff --git a/templates/Full/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs b/templates/Full/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs index ce4802ad..b32f1a66 100644 --- a/templates/Full/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/Cache/InfrastructureCacheDependencyInjection.cs @@ -14,7 +14,7 @@ public IServiceCollection AddCache(IConfiguration configuration) services .AddStackExchangeRedisCache(options => { - options.Configuration = configuration.GetConnectionString("Redis") ?? throw new NullReferenceException("Redis connection string is not configured."); + options.Configuration = configuration.GetConnectionString("Redis") ?? throw new InvalidOperationException("Redis connection string is not configured."); options.Configuration += ",abortConnect=false,connectTimeout=5000,syncTimeout=5000"; }) .AddHybridCache(options => diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 5a8d451f..1a2d14d1 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -1,5 +1,6 @@ using Application.Common.Constants; using Application.Common.Services; +using Infrastructure.Common; using Microsoft.Extensions.Caching.Hybrid; using Microsoft.Extensions.Logging; @@ -16,30 +17,30 @@ public async ValueTask GetOrCreateAsync( CancellationToken cancellationToken ) { - _logger.LogDebug("[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Retrieving cache entry", key); + Logs.RetrievingCacheEntry(_logger, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - _logger.LogDebug("[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Cache entry retrieved", key); + Logs.CacheEntryRetrieved(_logger, key); return result; } public async ValueTask CreateAsync(string key, TResult value, CancellationToken cancellationToken) { - _logger.LogDebug("[HybridCacheService] | [CreateAsync] | [{Key}] | Creating cache entry", key); + Logs.CreatingCacheEntry(_logger, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - _logger.LogDebug("[HybridCacheService] | [CreateAsync] | [{Key}] | Cache entry created", key); + Logs.CacheEntryCreated(_logger, key); } public async ValueTask DeleteAsync(string key, CancellationToken cancellationToken) { - _logger.LogDebug("[HybridCacheService] | [DeleteAsync] | [{Key}] | Deleting cache entry", key); + Logs.DeletingCacheEntry(_logger, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - _logger.LogDebug("[HybridCacheService] | [DeleteAsync] | [{Key}] | Cache entry deleted", key); + Logs.CacheEntryDeleted(_logger, key); } } diff --git a/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs b/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs index 775c688e..3d16e29a 100644 --- a/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs @@ -11,15 +11,15 @@ internal static class InfrastructureDataDependencyInjection { public IServiceCollection AddData(IConfiguration configuration) { - bool.TryParse( + var enableSensitiveDataLogging = bool.TryParse( Environment.GetEnvironmentVariable("ENABLE_SENSITIVE_DATA_LOGGING"), - out var enableSensitiveDataLogging - ); + out var parsedValue + ) && parsedValue; services.AddPooledDbContextFactory(options => { options.UseSqlServer( - configuration.GetConnectionString("OrderDb") ?? throw new NullReferenceException("OrderDb connection string is not configured.") + configuration.GetConnectionString("OrderDb") ?? throw new InvalidOperationException("OrderDb connection string is not configured.") ); options.EnableSensitiveDataLogging(enableSensitiveDataLogging); }); From 250b37a810075d1d53cf88ccde8f9e47c10e865c Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 14 Feb 2026 08:30:00 -0300 Subject: [PATCH 054/161] 53 refactor: replace logger calls with logging helper methods --- .../Common/BaseBackgroundService.cs | 5 +--- .../Data/Common/BaseRepository.cs | 19 ++++---------- .../Messaging/Consumers/BaseConsumer.cs | 2 +- ...ructureOpenTelemetryDependencyInjection.cs | 2 +- .../tests/CommonTests/Fixtures/BaseFixture.cs | 8 +++--- .../Common/CustomWebApplicationFactory.cs | 9 ++++--- .../IntegrationTests/Data/BaseDataFixture.cs | 4 +-- .../WebApp/Grpc/Common/ApiGrpcHelper.cs | 6 ++--- .../WebApp/Grpc/Orders/GetOrderGrpcTest.cs | 10 +++---- .../WebApp/Http/Common/ApiHelper.cs | 25 +++++++----------- .../WebApp/Http/Common/BaseHttpFixture.cs | 6 ++--- .../WebApp/Http/Orders/CreateOrderTest.cs | 14 +++++----- .../WebApp/Http/Orders/GetAllOrdersTest.cs | 26 +++++++++---------- 13 files changed, 61 insertions(+), 75 deletions(-) diff --git a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs index fa375965..b6245288 100644 --- a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs +++ b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs @@ -28,10 +28,7 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken) } catch (Exception ex) { - logger.LogError( - "[BaseBackgroundService] | [ExecuteAsync] | Unexpected error in background service. | Message: {ErrorMessage} | StackTrace: {StackTrace}", - ex.Message, ex.StackTrace - ); + Logs.UnexpectedErrorInBackgroundService(logger, ex.Message, ex.StackTrace); throw; } diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 024d491f..0f8cd47b 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using Application.Common.Repositories; using Domain.Common; +using Infrastructure.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -13,7 +14,6 @@ public class BaseRepository( IDbContextFactory dbContextFactory ) : IBaseRepository { - protected readonly ILogger logger = logger; private readonly Stopwatch _stopwatch = new(); private readonly IDbContextFactory _dbContextFactory = dbContextFactory; private readonly MyDbContext _dbContext = dbContextFactory.CreateDbContext(); @@ -28,11 +28,7 @@ private async Task HandleBaseQueryAsync( { _stopwatch.Restart(); - logger.LogDebug( - "[BaseRepository] | [{Method}] | CorrelationId: {CorrelationId} | Starting database operation.", - methodName, - correlationId - ); + Logs.StartingDatabaseOperation(logger, methodName, correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) @@ -40,12 +36,7 @@ private async Task HandleBaseQueryAsync( var result = await query.Invoke(dbSet); - logger.LogDebug( - "[BaseRepository] | [{Method}] | CorrelationId: {CorrelationId} | Query executed in {ElapsedMilliseconds} ms.", - methodName, - correlationId, - _stopwatch.ElapsedMilliseconds - ); + Logs.QueryExecuted(logger, methodName, correlationId, _stopwatch.ElapsedMilliseconds); return result; } @@ -246,7 +237,7 @@ params Expression>[]? includes if (searchByValues != null && searchByValues.Count != 0) foreach (var searchByValue in searchByValues) query = query.Where(e => - EF.Property(e, searchByValue.Key).Contains(searchByValue.Value.ToLowerInvariant()) + EF.Property(e, searchByValue.Key).Contains(searchByValue.Value.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase) ); var items = await query @@ -287,7 +278,7 @@ params Expression>[]? includes if (searchByValues != null && searchByValues.Count != 0) foreach (var searchByValue in searchByValues) query = query.Where(e => - EF.Property(e, searchByValue.Key).Contains(searchByValue.Value.ToLowerInvariant()) + EF.Property(e, searchByValue.Key).Contains(searchByValue.Value.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase) ); var items = await query diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index 58453c17..2f767148 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -2,13 +2,13 @@ using System.Text.Json; using Application.Common.Messages; using Application.Common.Services; -using Application.Common.Helpers; using Infrastructure.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using RabbitMQ.Client; using RabbitMQ.Client.Events; +using Logs = Application.Common.Helpers.Logs; namespace Infrastructure.Messaging.Consumers; diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 9adf6e8c..6a2dacc3 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -17,7 +17,7 @@ internal static class InfrastructureOpenTelemetryDependencyInjection public WebApplicationBuilder AddOpenTelemetry() { var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower() == "grpc" + var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "grpc" ? OtlpExportProtocol.Grpc : OtlpExportProtocol.HttpProtobuf; var exporterMetricsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"); diff --git a/templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs b/templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs index 0b8da73e..214514c8 100644 --- a/templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs +++ b/templates/Full/tests/CommonTests/Fixtures/BaseFixture.cs @@ -3,12 +3,14 @@ namespace CommonTests.Fixtures; public class BaseFixture { - public Fixture autoFixture = new(); + public Fixture AutoFixture { get; } - public CancellationToken cancellationToken = CancellationToken.None; + public CancellationToken CancellationToken { get; } public BaseFixture() { - autoFixture.Behaviors.Add(new OmitOnRecursionBehavior()); + AutoFixture = new Fixture(); + AutoFixture.Behaviors.Add(new OmitOnRecursionBehavior()); + CancellationToken = CancellationToken.None; } } diff --git a/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs b/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs index bf0f7415..ca8398d1 100644 --- a/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs +++ b/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs @@ -14,9 +14,9 @@ public sealed class WebApplicationFactoryCollectionDefinition : IClassFixture : WebApplicationFactory, IDisposable where TProgram : class { - protected string? _connectionString = "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;"; + protected string? ConnectionString { get; } = "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;"; - public MyDbContext? MyDbContext; + public MyDbContext? MyDbContext { get; set; } public CustomWebApplicationFactory() => SetDbContext(); @@ -34,14 +34,14 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) services.Remove(dbConnectionDescriptor!); - services.AddDbContextFactory((options) => options.UseSqlServer(_connectionString)); + services.AddDbContextFactory((options) => options.UseSqlServer(ConnectionString)); }); } public void SetDbContext() { var contextOptions = new DbContextOptionsBuilder() - .UseSqlServer(_connectionString) + .UseSqlServer(ConnectionString) .Options; MyDbContext = new(contextOptions); @@ -53,6 +53,7 @@ public void SetDbContext() public new void Dispose() { MyDbContext!.Dispose(); + GC.SuppressFinalize(this); // base.Dispose(); } } diff --git a/templates/Full/tests/IntegrationTests/Data/BaseDataFixture.cs b/templates/Full/tests/IntegrationTests/Data/BaseDataFixture.cs index f5be68bb..f186ec92 100644 --- a/templates/Full/tests/IntegrationTests/Data/BaseDataFixture.cs +++ b/templates/Full/tests/IntegrationTests/Data/BaseDataFixture.cs @@ -8,11 +8,11 @@ namespace IntegrationTests.Data; public class BaseDataFixture : BaseFixture { - public required IBaseRepository repository; + public required IBaseRepository Repository { get; set; } public void SetRepository(CustomWebApplicationFactory factory) { var scope = factory.Services.CreateAsyncScope(); - repository = scope.ServiceProvider.GetRequiredService(); + Repository = scope.ServiceProvider.GetRequiredService(); } } diff --git a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs index b9c4f292..a97d6255 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Common/ApiGrpcHelper.cs @@ -3,10 +3,10 @@ namespace IntegrationTests.WebApp.Grpc.Common; public sealed class ApiGrpcHelper(HttpClient httpClient) { - public HttpClient httpClient = httpClient; + public HttpClient HttpClient { get; } = httpClient; - public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(httpClient.BaseAddress!, new GrpcChannelOptions + public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(HttpClient.BaseAddress!, new GrpcChannelOptions { - HttpClient = httpClient + HttpClient = HttpClient }); } diff --git a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs index eb1a89d9..363e4920 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs @@ -10,17 +10,17 @@ namespace IntegrationTests.WebApp.Grpc.Orders; [Collection("WebApplicationFactoryCollectionDefinition")] public class GetOrderGrpcTest : BaseFixture { - public CustomWebApplicationFactory customWebApplicationFactory; + public CustomWebApplicationFactory CustomWebApplicationFactory { get; } - public ApiGrpcHelper apiGrpcHelper; + public ApiGrpcHelper ApiGrpcHelper { get; } private readonly GrpcChannel _grpcChannel; private readonly OrderService.OrderServiceClient _service; public GetOrderGrpcTest(CustomWebApplicationFactory customWebApplicationFactory) { - this.customWebApplicationFactory = customWebApplicationFactory; - apiGrpcHelper = new(this.customWebApplicationFactory.CreateClient()); - _grpcChannel = apiGrpcHelper.AsGrpcClientChannel(); + CustomWebApplicationFactory = customWebApplicationFactory; + ApiGrpcHelper = new(CustomWebApplicationFactory.CreateClient()); + _grpcChannel = ApiGrpcHelper.AsGrpcClientChannel(); _service = new(_grpcChannel); } diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs index d2962430..7bca09d0 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/ApiHelper.cs @@ -5,7 +5,7 @@ namespace IntegrationTests.WebApp.Http.Common; public sealed class ApiHelper(HttpClient httpClient) { - public HttpClient httpClient = httpClient; + public HttpClient HttpClient { get; } = httpClient; private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { @@ -16,42 +16,37 @@ public void AddHeaders(Dictionary headers) { foreach (var header in headers) { - if (httpClient.DefaultRequestHeaders.Contains(header.Key)) + if (HttpClient.DefaultRequestHeaders.Contains(header.Key)) { - httpClient.DefaultRequestHeaders.Remove(header.Key); + HttpClient.DefaultRequestHeaders.Remove(header.Key); } - httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); + HttpClient.DefaultRequestHeaders.Add(header.Key, header.Value); } } public async Task GetAsync(string resourceUrl) => - await httpClient.GetAsync(resourceUrl); + await HttpClient.GetAsync(resourceUrl); public async Task PostAsync(string resourceUrl, dynamic dataClass) => - await httpClient.PostAsync(resourceUrl, SerializeRequest(dataClass)); + await HttpClient.PostAsync(resourceUrl, SerializeRequest(dataClass)); public async Task PutAsync(string resourceUrl, dynamic data) => - await httpClient.PutAsync(resourceUrl, SerializeRequest(data)); + await HttpClient.PutAsync(resourceUrl, SerializeRequest(data)); public async Task DeleteAsync(string resourceUrl) => - await httpClient.DeleteAsync(resourceUrl); + await HttpClient.DeleteAsync(resourceUrl); - public StringContent SerializeRequest(dynamic data) + public static StringContent SerializeRequest(dynamic data) { var json = JsonSerializer.Serialize(data); return new StringContent(json, Encoding.UTF8, "application/json"); } - public async Task DeSerializeResponse(HttpResponseMessage response) + public static async Task DeSerializeResponse(HttpResponseMessage response) { var content = await response.Content.ReadAsStreamAsync(); return JsonSerializer.Deserialize(content, _jsonSerializerOptions); } - - public GrpcChannel AsGrpcClientChannel() => GrpcChannel.ForAddress(httpClient.BaseAddress!, new GrpcChannelOptions - { - HttpClient = httpClient - }); } diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs index 64636bb8..ae8798d8 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Common/BaseHttpFixture.cs @@ -6,9 +6,9 @@ namespace IntegrationTests.WebApp.Http.Common; public class BaseHttpFixture : BaseFixture { - public ApiHelper apiHelper; - public string resourceUrl = string.Empty; + public ApiHelper ApiHelper { get; set; } = null!; + public string ResourceUrl { get; set; } = string.Empty; public void SetApiHelper(CustomWebApplicationFactory customWebApplicationFactory) => - apiHelper = new(customWebApplicationFactory.CreateClient()); + ApiHelper = new(customWebApplicationFactory.CreateClient()); } \ No newline at end of file diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs index a4bf0208..89beff88 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs @@ -9,9 +9,9 @@ namespace IntegrationTests.WebApp.Http.Orders; public class CreateOrderTestFixture : BaseHttpFixture { - public CreateOrderRequest SetValidRequest() => autoFixture.Create(); + public CreateOrderRequest SetValidRequest() => AutoFixture.Create(); - public CreateOrderRequest SetInvalidRequest() => autoFixture + public CreateOrderRequest SetInvalidRequest() => AutoFixture .Build() .With(r => r.Description, string.Empty) .Create(); @@ -26,7 +26,7 @@ public CreateOrderTest(CustomWebApplicationFactory customWebApplication { _fixture = fixture; _fixture.SetApiHelper(customWebApplicationFactory); - _fixture.resourceUrl = "orders"; + _fixture.ResourceUrl = "orders"; } [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] @@ -36,8 +36,8 @@ public async Task GivenAValidRequestThenPass() var request = _fixture.SetValidRequest(); // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.PostAsync(_fixture.ResourceUrl, request); + var response = await ApiHelper.DeSerializeResponse>(result); // Assert Assert.NotNull(result); @@ -53,8 +53,8 @@ public async Task GivenAInvalidRequestThenFails() var request = _fixture.SetInvalidRequest(); // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.PostAsync(_fixture.ResourceUrl, request); + var response = await ApiHelper.DeSerializeResponse>(result); // Assert Assert.NotNull(response); Assert.NotNull(result); diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs index f276802c..91eae446 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetAllOrdersTest.cs @@ -12,10 +12,10 @@ public class GetAllOrdersTestFixture : BaseHttpFixture public static BasePaginatedRequest SetValidRequest() => new(Guid.NewGuid(), 1, 10); - public BasePaginatedRequest SetInvalidPageRequest() => + public static BasePaginatedRequest SetInvalidPageRequest() => new(Guid.NewGuid(), 0, 10); - public BasePaginatedRequest SetInvalidPageSizeRequest() => + public static BasePaginatedRequest SetInvalidPageSizeRequest() => new(Guid.NewGuid(), 1, 0); } @@ -27,7 +27,7 @@ public GetAllOrdersTest(CustomWebApplicationFactory customWebApplicatio { _fixture = fixture; _fixture.SetApiHelper(customWebApplicationFactory); - _fixture.resourceUrl = "orders/paginated"; + _fixture.ResourceUrl = "orders/paginated"; } [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] @@ -37,8 +37,8 @@ public async Task GivenAValidRequestThenPass() var request = GetAllOrdersTestFixture.SetValidRequest(); // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.PostAsync(_fixture.ResourceUrl, request); + var response = await ApiHelper.DeSerializeResponse>(result); // Assert Assert.NotNull(result); @@ -53,11 +53,11 @@ public async Task GivenAValidRequestThenPass() public async Task GivenAnInvalidPageRequestThenFails() { // Arrange - var request = _fixture.SetInvalidPageRequest(); + var request = GetAllOrdersTestFixture.SetInvalidPageRequest(); // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.PostAsync(_fixture.ResourceUrl, request); + var response = await ApiHelper.DeSerializeResponse>(result); // Assert Assert.NotNull(result); @@ -70,11 +70,11 @@ public async Task GivenAnInvalidPageRequestThenFails() public async Task GivenAnInvalidPageSizeRequestThenFails() { // Arrange - var request = _fixture.SetInvalidPageSizeRequest(); + var request = GetAllOrdersTestFixture.SetInvalidPageSizeRequest(); // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.PostAsync(_fixture.ResourceUrl, request); + var response = await ApiHelper.DeSerializeResponse>(result); // Assert Assert.NotNull(result); @@ -93,8 +93,8 @@ public async Task GivenAnValidRequestWhenPassSearchByValuesFilterThenPass() ); // Act - var result = await _fixture.apiHelper.PostAsync(_fixture.resourceUrl, request); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.PostAsync(_fixture.ResourceUrl, request); + var response = await ApiHelper.DeSerializeResponse>(result); // Assert Assert.NotNull(result); From b828456e5231af4cd227585b1419b677a4c9b219 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 14 Feb 2026 08:32:07 -0300 Subject: [PATCH 055/161] 53 refactor: standardize property names and improve casing --- .../Data/BaseRepositoryTest.cs | 40 +++++++++---------- .../WebApp/Http/Orders/GetOrderTest.cs | 21 +++++----- .../Notifications/CreateNotificationTest.cs | 10 ++--- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs b/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs index c3db7498..62f0452a 100644 --- a/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs +++ b/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs @@ -22,10 +22,10 @@ public async Task GivenAIdThenReturnOrderWithSuccess() var id = 1; // Act - var result = await _fixture!.repository!.GetByIdAsNoTrackingAsync( + var result = await _fixture!.Repository!.GetByIdAsNoTrackingAsync( id, Guid.NewGuid(), - _fixture.cancellationToken, + _fixture.CancellationToken, includes: o => o.Items ); @@ -43,7 +43,7 @@ public async Task GivenAIdThenReturnOrderDtoWithSuccess() var id = 1; // Act - var result = await _fixture!.repository!.GetByIdAsNoTrackingAsync( + var result = await _fixture!.Repository!.GetByIdAsNoTrackingAsync( id, Guid.NewGuid(), selector: o => new OrderDto() @@ -57,7 +57,7 @@ public async Task GivenAIdThenReturnOrderDtoWithSuccess() Value = i.Value }).ToArray() }, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -74,16 +74,16 @@ public async Task GivenAOrderAndNotificationShouldExecuteInParallelWithSuccess() var id = 1; // Act - var orderTask = _fixture!.repository!.GetByIdAsNoTrackingAsync( + var orderTask = _fixture!.Repository!.GetByIdAsNoTrackingAsync( id, Guid.NewGuid(), - _fixture.cancellationToken + _fixture.CancellationToken ); - var notificationTask = _fixture!.repository!.GetByIdAsNoTrackingAsync( + var notificationTask = _fixture!.Repository!.GetByIdAsNoTrackingAsync( id, Guid.NewGuid(), - _fixture.cancellationToken, + _fixture.CancellationToken, newContext: true ); @@ -109,11 +109,11 @@ public async Task GivenAValidRequestThenReturnAllOrdersPaginatedWithSuccess() var pageSize = 5; // Act - var (result, totalRecords) = await _fixture!.repository!.GetAllPaginatedAsync( + var (result, totalRecords) = await _fixture!.Repository!.GetAllPaginatedAsync( Guid.NewGuid(), pageNumber, pageSize, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -130,7 +130,7 @@ public async Task GivenAValidRequestThenReturnAllOrdersDtosPaginatedWithSuccess( var pageSize = 5; // Act - var (result, totalRecords) = await _fixture!.repository!.GetAllPaginatedAsync( + var (result, totalRecords) = await _fixture!.Repository!.GetAllPaginatedAsync( Guid.NewGuid(), pageNumber, pageSize, @@ -144,7 +144,7 @@ public async Task GivenAValidRequestThenReturnAllOrdersDtosPaginatedWithSuccess( Value = i.Value }).ToArray() }, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -163,11 +163,11 @@ public async Task GivenAValidRequestThenReturnNoOrdersPaginated() var pageSize = 5; // Act - var (result, totalRecords) = await _fixture!.repository!.GetAllPaginatedAsync( + var (result, totalRecords) = await _fixture!.Repository!.GetAllPaginatedAsync( Guid.NewGuid(), pageNumber, pageSize, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -188,11 +188,11 @@ public async Task GivenAValidRequestThenReturnFilteredOrdersPaginated() }; // Act - var (result, totalRecords) = await _fixture!.repository!.GetAllPaginatedAsync( + var (result, totalRecords) = await _fixture!.Repository!.GetAllPaginatedAsync( Guid.NewGuid(), pageNumber, pageSize, - _fixture.cancellationToken, + _fixture.CancellationToken, searchByValues: searchByValues ); @@ -214,11 +214,11 @@ public async Task GivenAValidRequestThenReturnNoFilteredOrdersPaginated() }; // Act - var (result, totalRecords) = await _fixture!.repository!.GetAllPaginatedAsync( + var (result, totalRecords) = await _fixture!.Repository!.GetAllPaginatedAsync( Guid.NewGuid(), pageNumber, pageSize, - _fixture.cancellationToken, + _fixture.CancellationToken, searchByValues: searchByValues ); @@ -239,11 +239,11 @@ public async Task GivenAValidRequestThenReturnSortedOrdersPaginated(bool sortDes var sortBy = "Description"; // Act - var (result, totalRecords) = await _fixture!.repository!.GetAllPaginatedAsync( + var (result, totalRecords) = await _fixture!.Repository!.GetAllPaginatedAsync( Guid.NewGuid(), pageNumber, pageSize, - _fixture.cancellationToken, + _fixture.CancellationToken, sortBy: sortBy, sortDescending: sortDescending ); diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs index 6bdc93f7..58931b36 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/GetOrderTest.cs @@ -1,4 +1,5 @@ -using System.Net; +using System.Globalization; +using System.Net; using Application.Common.Requests; using Application.Orders; using IntegrationTests.Common; @@ -15,7 +16,7 @@ public GetOrderTest(CustomWebApplicationFactory customWebApplicationFac { _fixture = fixture; _fixture.SetApiHelper(customWebApplicationFactory); - _fixture.resourceUrl = "orders/{0}"; + _fixture.ResourceUrl = "orders/{0}"; } [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] @@ -23,15 +24,15 @@ public async Task GivenAValidRequestThenPass() { // Arrange var id = 1; - var url = string.Format(_fixture.resourceUrl, id); - _fixture.apiHelper.AddHeaders(new Dictionary + var url = string.Format(CultureInfo.InvariantCulture, _fixture.ResourceUrl, id); + _fixture.ApiHelper.AddHeaders(new Dictionary { { "CorrelationId", Guid.NewGuid().ToString() } }); // Act - var result = await _fixture.apiHelper.GetAsync(url); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.GetAsync(url); + var response = await ApiHelper.DeSerializeResponse>(result); var data = response?.Data; // Assert @@ -49,15 +50,15 @@ public async Task GivenAInvalidRequestThenFails() { // Arrange var id = 9999999; - var url = string.Format(_fixture.resourceUrl, id); - _fixture.apiHelper.AddHeaders(new Dictionary + var url = string.Format(CultureInfo.InvariantCulture, _fixture.ResourceUrl, id); + _fixture.ApiHelper.AddHeaders(new Dictionary { { "CorrelationId", Guid.NewGuid().ToString() } }); // Act - var result = await _fixture.apiHelper.GetAsync(url); - var response = await _fixture.apiHelper.DeSerializeResponse>(result); + var result = await _fixture.ApiHelper.GetAsync(url); + var response = await ApiHelper.DeSerializeResponse>(result); // Assert Assert.NotNull(result); diff --git a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs index 415d1539..8dc21ee8 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs @@ -16,7 +16,7 @@ public class CreateNotificationTestFixture : BaseMessagingFixture SetServices(scope); } - public CreateNotificationMessage SetValidMessage() => autoFixture.Build().Create(); + public CreateNotificationMessage SetValidMessage() => AutoFixture.Build().Create(); } [Collection("WebApplicationFactoryCollectionDefinition")] @@ -39,10 +39,10 @@ public async Task GivenAValidMessageThenPass() // Act await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); - var notification = await _fixture.repository.FirstOrDefaultAsNoTrackingAsync( + var notification = await _fixture.Repository.FirstOrDefaultAsNoTrackingAsync( Guid.NewGuid(), n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -60,10 +60,10 @@ public async Task GivenADuplicateMessageThenShouldNotCreateDuplicatedMessage() await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); - var notifications = await _fixture.repository.GetByWhereAsNoTrackingAsync( + var notifications = await _fixture.Repository.GetByWhereAsNoTrackingAsync( Guid.NewGuid(), n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus, - cancellationToken: _fixture.cancellationToken + cancellationToken: _fixture.CancellationToken ); // Assert From 0ae693ced596c784c61b41b98507521d643949b6 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 14 Feb 2026 08:33:35 -0300 Subject: [PATCH 056/161] 53 refactor: standardize property casing in BaseMessagingFixture --- .../Full/src/Infrastructure/Common/Logs.cs | 118 ++++++++++++++++++ .../Messaging/Common/BaseMessagingFixture.cs | 16 +-- 2 files changed, 126 insertions(+), 8 deletions(-) create mode 100644 templates/Full/src/Infrastructure/Common/Logs.cs diff --git a/templates/Full/src/Infrastructure/Common/Logs.cs b/templates/Full/src/Infrastructure/Common/Logs.cs new file mode 100644 index 00000000..575ba5ac --- /dev/null +++ b/templates/Full/src/Infrastructure/Common/Logs.cs @@ -0,0 +1,118 @@ +using Microsoft.Extensions.Logging; + +namespace Infrastructure.Common; + +internal static partial class Logs +{ + /// + /// Logs an unexpected error in a background service. + /// + /// The logger instance to use for logging. + /// The error message. + /// The stack trace of the error. + [LoggerMessage( + EventId = 200, + Level = LogLevel.Error, + Message = "[BaseBackgroundService] | [ExecuteAsync] | Unexpected error in background service. | Message: {ErrorMessage} | StackTrace: {StackTrace}" + )] + public static partial void UnexpectedErrorInBackgroundService(ILogger logger, string errorMessage, string? stackTrace); + + /// + /// Logs the start of a database operation. + /// + /// The logger instance to use for logging. + /// The method name. + /// The correlation ID for tracking the request. + [LoggerMessage( + EventId = 201, + Level = LogLevel.Debug, + Message = "[BaseRepository] | [{Method}] | CorrelationId: {CorrelationId} | Starting database operation." + )] + public static partial void StartingDatabaseOperation(ILogger logger, string method, Guid correlationId); + + /// + /// Logs the completion of a database query. + /// + /// The logger instance to use for logging. + /// The method name. + /// The correlation ID for tracking the request. + /// The elapsed time in milliseconds. + [LoggerMessage( + EventId = 202, + Level = LogLevel.Debug, + Message = "[BaseRepository] | [{Method}] | CorrelationId: {CorrelationId} | Query executed in {ElapsedMilliseconds} ms." + )] + public static partial void QueryExecuted(ILogger logger, string method, Guid correlationId, long elapsedMilliseconds); + + /// + /// Logs the start of retrieving a cache entry. + /// + /// The logger instance to use for logging. + /// The cache key. + [LoggerMessage( + EventId = 203, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Retrieving cache entry" + )] + public static partial void RetrievingCacheEntry(ILogger logger, string key); + + /// + /// Logs the completion of retrieving a cache entry. + /// + /// The logger instance to use for logging. + /// The cache key. + [LoggerMessage( + EventId = 204, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Cache entry retrieved" + )] + public static partial void CacheEntryRetrieved(ILogger logger, string key); + + /// + /// Logs the start of creating a cache entry. + /// + /// The logger instance to use for logging. + /// The cache key. + [LoggerMessage( + EventId = 205, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Creating cache entry" + )] + public static partial void CreatingCacheEntry(ILogger logger, string key); + + /// + /// Logs the completion of creating a cache entry. + /// + /// The logger instance to use for logging. + /// The cache key. + [LoggerMessage( + EventId = 206, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Cache entry created" + )] + public static partial void CacheEntryCreated(ILogger logger, string key); + + /// + /// Logs the start of deleting a cache entry. + /// + /// The logger instance to use for logging. + /// The cache key. + [LoggerMessage( + EventId = 207, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Deleting cache entry" + )] + public static partial void DeletingCacheEntry(ILogger logger, string key); + + /// + /// Logs the completion of deleting a cache entry. + /// + /// The logger instance to use for logging. + /// The cache key. + [LoggerMessage( + EventId = 208, + Level = LogLevel.Debug, + Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Cache entry deleted" + )] + public static partial void CacheEntryDeleted(ILogger logger, string key); +} diff --git a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs index 98e0a23c..b426e1ce 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Common/BaseMessagingFixture.cs @@ -10,20 +10,20 @@ namespace IntegrationTests.WebApp.Messaging.Common; public class BaseMessagingFixture : BaseFixture { - public IProduceService produceService; - public IBaseRepository repository; + public IProduceService ProduceService { get; set; } = null!; + public IBaseRepository Repository { get; set; } = null!; public void SetServices(AsyncServiceScope scope) { - produceService = scope.ServiceProvider.GetRequiredService(); - repository = scope.ServiceProvider.GetRequiredService(); + ProduceService = scope.ServiceProvider.GetRequiredService(); + Repository = scope.ServiceProvider.GetRequiredService(); } public void SetServices(CustomWebApplicationFactory factory) { var scope = factory.Services.CreateAsyncScope(); - produceService = scope.ServiceProvider.GetRequiredService(); - repository = scope.ServiceProvider.GetRequiredService(); + ProduceService = scope.ServiceProvider.GetRequiredService(); + Repository = scope.ServiceProvider.GetRequiredService(); } public async Task HandleProducerAsync( @@ -32,8 +32,8 @@ public async Task HandleProducerAsync( int delay = 1500 ) where TMessage : BaseMessage { - await produceService.HandleAsync(message, cancellationToken, queueName); + await ProduceService.HandleAsync(message, CancellationToken, queueName); - await Task.Delay(delay, cancellationToken); + await Task.Delay(delay, CancellationToken); } } From 58228c5437a793d52df3402cdf4d476fdcea8e02 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 18 Feb 2026 07:37:07 -0300 Subject: [PATCH 057/161] 53 feat: update domain entity constructors to include timezone handling --- .../Full/src/Domain/Common/DomainEntity.cs | 26 +++----- .../src/Domain/Notifications/Notification.cs | 5 +- templates/Full/src/Domain/Orders/Item.cs | 8 ++- templates/Full/src/Domain/Orders/Order.cs | 11 +++- .../UnitTests/Domain/DomainEntityTests.cs | 62 +++++++------------ .../UnitTests/Domain/NotificationTests.cs | 7 ++- .../Full/tests/UnitTests/Domain/OrderTests.cs | 34 +++++++++- 7 files changed, 87 insertions(+), 66 deletions(-) diff --git a/templates/Full/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs index 7f6ba573..4c3f55e2 100644 --- a/templates/Full/src/Domain/Common/DomainEntity.cs +++ b/templates/Full/src/Domain/Common/DomainEntity.cs @@ -3,38 +3,28 @@ public abstract class DomainEntity { protected DomainEntity() {} - protected DomainEntity(string? user = null, string timezoneId = "") + protected DomainEntity(string user, string? timezoneId = null) { CreatedAt = DateTime.UtcNow; - CreatedBy = user ?? "System"; + CreatedBy = user; + CreatedByTimezoneId = TimeZoneInfo.FindSystemTimeZoneById(string.IsNullOrWhiteSpace(timezoneId) ? TimeZoneInfo.Utc.Id : timezoneId).Id; UpdatedAt = CreatedAt; UpdatedBy = CreatedBy; - SetTimezoneId(timezoneId); + UpdatedByTimezoneId = CreatedByTimezoneId; } public int Id { get; init; } public DateTime CreatedAt { get; init; } public string? CreatedBy { get; init; } + public string CreatedByTimezoneId { get; init; } public DateTime UpdatedAt { get; private set; } public string? UpdatedBy { get; private set; } - public string TimezoneId { get; private set; } + public string? UpdatedByTimezoneId { get; private set; } - private void SetTimezoneId(string timezoneId) - { - if (string.IsNullOrWhiteSpace(timezoneId)) - { - TimezoneId = TimeZoneInfo.Utc.Id; - return; - } - - TimeZoneInfo.FindSystemTimeZoneById(timezoneId); - TimezoneId = timezoneId; - } - - public virtual void Update(string? user = null, string timezoneId = "") + public virtual void Update(string? user = null, string? timezoneId = null) { UpdatedAt = DateTime.UtcNow; - SetTimezoneId(timezoneId); UpdatedBy = user ?? "System"; + UpdatedByTimezoneId = TimeZoneInfo.FindSystemTimeZoneById(string.IsNullOrWhiteSpace(timezoneId) ? TimeZoneInfo.Utc.Id : timezoneId).Id; } } diff --git a/templates/Full/src/Domain/Notifications/Notification.cs b/templates/Full/src/Domain/Notifications/Notification.cs index 14edb2b9..ffbecbcf 100644 --- a/templates/Full/src/Domain/Notifications/Notification.cs +++ b/templates/Full/src/Domain/Notifications/Notification.cs @@ -10,9 +10,10 @@ public Notification() {} public Notification( string notificationType, string notificationStatus, + object? message = null, string? createdBy = null, - object? message = null - ) : base(createdBy) + string? timezoneId = null + ) : base(createdBy ?? "System", timezoneId) { NotificationType = notificationType; NotificationStatus = notificationStatus; diff --git a/templates/Full/src/Domain/Orders/Item.cs b/templates/Full/src/Domain/Orders/Item.cs index d259d47a..f9d20406 100644 --- a/templates/Full/src/Domain/Orders/Item.cs +++ b/templates/Full/src/Domain/Orders/Item.cs @@ -5,7 +5,13 @@ public sealed class Item : DomainEntity { public Item() { } - public Item(string name, string description, decimal value) : base() + public Item( + string name, + string description, + decimal value, + string? createdBy = null, + string? timezoneId = null + ) : base(createdBy ?? "System", timezoneId) { Name = name; Description = description; diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index 6350e223..c1dea298 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -6,7 +6,12 @@ public sealed class Order : DomainEntity { public Order() { } - public Order(string description, ICollection items) : base() + public Order( + string description, + ICollection items, + string? createdBy = null, + string? timezoneId = null + ) : base(createdBy ?? "System", timezoneId) { Description = description; Items = items; @@ -16,13 +21,13 @@ public Order(string description, ICollection items) : base() public decimal Total { get; private set; } public ICollection Items { get; private set; } - public Result SetTotal() + public Result SetTotal(string user = "System", string? timezoneId = null) { if (Items == null || Items.Count == 0) return Result.Fail("Order must have at least one item."); Total = Items.Sum(item => item.Value); - Update(); + Update(user, timezoneId); return Result.Ok(); } diff --git a/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs b/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs index ef5d5163..301ed049 100644 --- a/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs +++ b/templates/Full/tests/UnitTests/Domain/DomainEntityTests.cs @@ -7,7 +7,7 @@ public sealed class DomainEntityTests private sealed class TestDomainEntity : DomainEntity { public TestDomainEntity() : base() { } - public TestDomainEntity(string? user = null, string timezoneId = "") + public TestDomainEntity(string user, string timezoneId = "") : base(user, timezoneId) { } } @@ -20,31 +20,9 @@ public void ConstructorWithValidParametersShouldCreateEntityWithProvidedValues() var entity = new TestDomainEntity(user, timezoneId); Assert.Equal(user, entity.CreatedBy); + Assert.Equal(timezoneId, entity.CreatedByTimezoneId); Assert.Equal(user, entity.UpdatedBy); - Assert.Equal(timezoneId, entity.TimezoneId); - } - - [Fact(DisplayName = nameof(ConstructorWithDefaultDateTimeShouldUseUtcNow))] - public void ConstructorWithDefaultDateTimeShouldUseUtcNow() - { - var beforeCreation = DateTime.UtcNow; - - var entity = new TestDomainEntity(); - - var afterCreation = DateTime.UtcNow; - Assert.True(entity.CreatedAt >= beforeCreation && entity.CreatedAt <= afterCreation); - Assert.Equal(entity.CreatedAt, entity.UpdatedAt); - } - - [Fact(DisplayName = nameof(ConstructorWithNullUserShouldDefaultToSystem))] - public void ConstructorWithNullUserShouldDefaultToSystem() - { - var currentDate = new DateTime(2026, 2, 13, 10, 30, 0, DateTimeKind.Utc); - - var entity = new TestDomainEntity(null); - - Assert.Equal("System", entity.CreatedBy); - Assert.Equal("System", entity.UpdatedBy); + Assert.Equal(timezoneId, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(ConstructorWithValidTimezoneIdShouldSetTimezoneId))] @@ -54,7 +32,8 @@ public void ConstructorWithValidTimezoneIdShouldSetTimezoneId() var entity = new TestDomainEntity("TestUser", timezoneId); - Assert.Equal(timezoneId, entity.TimezoneId); + Assert.Equal(timezoneId, entity.CreatedByTimezoneId); + Assert.Equal(timezoneId, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(ConstructorWithInvalidTimezoneIdShouldDefaultToUtc))] @@ -62,9 +41,9 @@ public void ConstructorWithInvalidTimezoneIdShouldDefaultToUtc() { var invalidTimezoneId = "Invalid/Timezone"; - var entity = new TestDomainEntity("TestUser", invalidTimezoneId); + var result = Assert.Throws(() => new TestDomainEntity("TestUser", invalidTimezoneId)); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Contains("not found on the local computer.", result.Message); } [Fact(DisplayName = nameof(ConstructorWithNullTimezoneIdShouldDefaultToUtc))] @@ -74,7 +53,8 @@ public void ConstructorWithNullTimezoneIdShouldDefaultToUtc() var entity = new TestDomainEntity("TestUser", nullTimezone!); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.CreatedByTimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(ConstructorWithEmptyTimezoneIdShouldDefaultToUtc))] @@ -82,7 +62,8 @@ public void ConstructorWithEmptyTimezoneIdShouldDefaultToUtc() { var entity = new TestDomainEntity("TestUser", string.Empty); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.CreatedByTimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(ConstructorWithWhitespaceTimezoneIdShouldDefaultToUtc))] @@ -90,7 +71,8 @@ public void ConstructorWithWhitespaceTimezoneIdShouldDefaultToUtc() { var entity = new TestDomainEntity("TestUser", " "); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.CreatedByTimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(UpdateWithValidParametersShouldUpdateEntityProperties))] @@ -104,7 +86,7 @@ public void UpdateWithValidParametersShouldUpdateEntityProperties() var afterUpdate = DateTime.UtcNow; Assert.True(entity.UpdatedAt >= beforeUpdate && entity.UpdatedAt <= afterUpdate); Assert.Equal("UpdatedUser", entity.UpdatedBy); - Assert.Equal("Europe/London", entity.TimezoneId); + Assert.Equal("Europe/London", entity.UpdatedByTimezoneId); Assert.Equal("InitialUser", entity.CreatedBy); } @@ -123,10 +105,9 @@ public void UpdateWithInvalidTimezoneIdShouldDefaultToUtc() { var entity = new TestDomainEntity("TestUser", "America/New_York"); - entity.Update("UpdatedUser", "Invalid/Timezone"); + var result = Assert.Throws(() => entity.Update("UpdatedUser", "Invalid/Timezone")); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); - Assert.Equal("UpdatedUser", entity.UpdatedBy); + Assert.Contains("not found on the local computer.", result.Message); } [Fact(DisplayName = nameof(UpdateWithEmptyTimezoneIdShouldDefaultToUtc))] @@ -136,7 +117,8 @@ public void UpdateWithEmptyTimezoneIdShouldDefaultToUtc() entity.Update("UpdatedUser", string.Empty); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Equal("America/New_York", entity.CreatedByTimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(UpdateWithoutParametersShouldUseDefaultValues))] @@ -150,7 +132,7 @@ public void UpdateWithoutParametersShouldUseDefaultValues() var afterUpdate = DateTime.UtcNow; Assert.True(entity.UpdatedAt >= beforeUpdate && entity.UpdatedAt <= afterUpdate); Assert.Equal("System", entity.UpdatedBy); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(ConstructorWithUtcTimezoneIdShouldSetUtcTimezone))] @@ -158,7 +140,8 @@ public void ConstructorWithUtcTimezoneIdShouldSetUtcTimezone() { var entity = new TestDomainEntity("TestUser", TimeZoneInfo.Utc.Id); - Assert.Equal(TimeZoneInfo.Utc.Id, entity.TimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.CreatedByTimezoneId); + Assert.Equal(TimeZoneInfo.Utc.Id, entity.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(UpdatePreservesCreatedAtAndCreatedByWhenUpdating))] @@ -172,6 +155,7 @@ public void UpdatePreservesCreatedAtAndCreatedByWhenUpdating() Assert.Equal(initialUser, entity.CreatedBy); Assert.Equal("AnotherUser", entity.UpdatedBy); - Assert.Equal("Asia/Tokyo", entity.TimezoneId); + Assert.Equal("America/New_York", entity.CreatedByTimezoneId); + Assert.Equal("Asia/Tokyo", entity.UpdatedByTimezoneId); } } diff --git a/templates/Full/tests/UnitTests/Domain/NotificationTests.cs b/templates/Full/tests/UnitTests/Domain/NotificationTests.cs index 3800a9fa..aa9453fd 100644 --- a/templates/Full/tests/UnitTests/Domain/NotificationTests.cs +++ b/templates/Full/tests/UnitTests/Domain/NotificationTests.cs @@ -11,16 +11,18 @@ public void GivenANewNotificationWhenPropertiesAreProvidedThenShouldCreateNotifi var notificationType = "TestNotification"; var notificationStatus = "Success"; var createdBy = "System"; + var timezoneId = "America/New_York"; var message = new { Test = "Message" }; /// Act - Notification notification = new(notificationType, notificationStatus, createdBy, message); + Notification notification = new(notificationType, notificationStatus, message, createdBy, timezoneId); // Assert Assert.NotNull(notification); Assert.Equal(notificationType, notification.NotificationType); Assert.Equal(notificationStatus, notification.NotificationStatus); Assert.Equal(createdBy, notification.CreatedBy); + Assert.Equal(timezoneId, notification.CreatedByTimezoneId); Assert.NotNull(notification.Message); Assert.Contains("\"Test\":\"Message\"", notification.Message); } @@ -32,10 +34,11 @@ public void GivenANewNotificationWhenMessageIsNullThenShouldCreateNotificationWi var notificationType = "TestNotification"; var notificationStatus = "Success"; var createdBy = "System"; + var timezoneId = "America/New_York"; object? message = null; /// Act - Notification notification = new(notificationType, notificationStatus, createdBy, message); + Notification notification = new(notificationType, notificationStatus, message, createdBy, timezoneId); // Assert Assert.NotNull(notification); diff --git a/templates/Full/tests/UnitTests/Domain/OrderTests.cs b/templates/Full/tests/UnitTests/Domain/OrderTests.cs index c2cd4b8e..f263b5e8 100644 --- a/templates/Full/tests/UnitTests/Domain/OrderTests.cs +++ b/templates/Full/tests/UnitTests/Domain/OrderTests.cs @@ -6,6 +6,34 @@ public sealed class OrderTests { [Fact(DisplayName = nameof(GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess))] public void GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess() + { + /// Arrange + var items = new List() + { + new("Computer", "Desktop", 900), + new("Mouse", "Razer", 100), + new("Headphone", "Logitech", 100), + }; + Order order = new("Amazing Computer", items, "John Doe", "America/New_York"); + var initialUpdatedAt = order.UpdatedAt; + + /// Act + var result = order.SetTotal(); + + // Assert + Assert.NotNull(order); + Assert.NotNull(result); + Assert.True(result.Success); + Assert.Empty(result.Message); + Assert.NotEqual(0, order.Total); + Assert.Equal("John Doe", order.CreatedBy); + Assert.Equal("America/New_York", order.CreatedByTimezoneId); + Assert.NotEqual(initialUpdatedAt, order.UpdatedAt); + Assert.Equal(items.Sum(i => i.Value), order.Total); + } + + [Fact(DisplayName = nameof(GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldSetTotalWithSuccess))] + public void GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldSetTotalWithSuccess() { /// Arrange var items = new List() @@ -28,6 +56,10 @@ public void GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess() Assert.NotEqual(0, order.Total); Assert.NotEqual(initialUpdatedAt, order.UpdatedAt); Assert.Equal(items.Sum(i => i.Value), order.Total); + Assert.Equal("System", order.CreatedBy); + Assert.Equal("UTC", order.CreatedByTimezoneId); + Assert.Equal("System", order.UpdatedBy); + Assert.Equal("UTC", order.UpdatedByTimezoneId); } [Fact(DisplayName = nameof(GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure))] @@ -37,7 +69,7 @@ public void GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure() Order order = new("Amazing Computer", Array.Empty()); /// Act - var result = order.SetTotal(); + var result = order.SetTotal("John Doe", "America/New_York"); // Assert Assert.NotNull(order); From 727af27bb3c01e15fbf85b21e1d1d9a8b18e85b7 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 18 Feb 2026 07:37:36 -0300 Subject: [PATCH 058/161] 53 feat: update BaseRequest to include user and timezone handling --- .../Application/Common/Requests/BaseRequest.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/templates/Full/src/Application/Common/Requests/BaseRequest.cs b/templates/Full/src/Application/Common/Requests/BaseRequest.cs index 81c44ed5..5cf1caa3 100644 --- a/templates/Full/src/Application/Common/Requests/BaseRequest.cs +++ b/templates/Full/src/Application/Common/Requests/BaseRequest.cs @@ -1,17 +1,18 @@ using FluentValidation; namespace Application.Common.Requests; -public record BaseRequest(Guid CorrelationId, int TimezoneId = 0); +public record BaseRequest(Guid CorrelationId, string User = "", string TimezoneId = ""); public record BasePaginatedRequest( Guid CorrelationId, - int TimezoneId = 0, int Page = 1, int PageSize = 10, string? SortBy = null, bool SortDescending = false, - Dictionary? SearchByValues = null -) : BaseRequest(CorrelationId, TimezoneId); + Dictionary? SearchByValues = null, + string User = "", + string TimezoneId = "" +) : BaseRequest(CorrelationId, User, TimezoneId); public sealed class BasePaginatedRequestValidator : AbstractValidator { @@ -23,8 +24,14 @@ public BasePaginatedRequestValidator() RuleFor(r => r.PageSize) .GreaterThan(0) + .WithMessage("PageSize must be greater than 0") .LessThanOrEqualTo(100) .WithMessage("PageSize must be less than or equal to 100"); + + RuleFor(r => r.TimezoneId) + .NotEmpty() + .When(r => !string.IsNullOrEmpty(r.User)) + .WithMessage("TimezoneId is required when User is provided"); } } From fe9ea46dce646c72d8133e371c2cf10c09c8c908 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 18 Feb 2026 07:37:52 -0300 Subject: [PATCH 059/161] 53 refactor: standardize property names and improve casing --- .../CreateNotificationUseCase.cs | 5 +- .../Application/Orders/CreateOrderUseCase.cs | 15 +++- .../Common/BaseApplicationFixture.cs | 80 +++++++++---------- .../CreateNotificationUseCaseTests.cs | 42 +++++----- .../GetAllNotificationsUseCaseTests.cs | 26 +++--- .../GetNotificationUseCaseTest.cs | 18 +++-- .../Orders/CreateOrderUseCaseTests.cs | 64 ++++++++------- .../Orders/GetAllOrdersUseCaseTest.cs | 36 +++++---- .../Orders/GetOrderUseCaseTests.cs | 26 +++--- 9 files changed, 166 insertions(+), 146 deletions(-) diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 74cfc645..93059143 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -1,10 +1,8 @@ -using Application.Common.Constants; using Application.Common.Requests; using Application.Common.UseCases; using Application.Common.Helpers; using Domain.Notifications; using FluentValidation; -using Microsoft.Extensions.Logging; namespace Application.Notifications; @@ -35,8 +33,9 @@ CancellationToken cancellationToken var notification = new Notification( request.NotificationType, request.NotificationStatus, + request.Message, request.CreatedBy, - request.Message + request.TimezoneId ); var addResult = await Repository.AddAsync(notification, request.CorrelationId, cancellationToken); diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index d6a9174d..055b3e0e 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -5,11 +5,16 @@ using Application.Common.Helpers; using Domain.Orders; using FluentValidation; -using Microsoft.Extensions.Logging; namespace Application.Orders; -public sealed record CreateOrderRequest(Guid CorrelationId, string Description, CreateOrderItemRequest[] Items) : BaseRequest(CorrelationId); +public sealed record CreateOrderRequest( + Guid CorrelationId, + string Description, + CreateOrderItemRequest[] Items, + string CreatedBy = "", + string TimezoneId = "" +) : BaseRequest(CorrelationId, CreatedBy, TimezoneId); public sealed record CreateOrderItemRequest(string Name, string Description, decimal Value); @@ -48,7 +53,11 @@ CancellationToken cancellationToken .Select(i => new Item(i.Name, i.Description, i.Value)) .ToList(); - var newOrder = new Order(request.Description, items); + var newOrder = new Order( + request.Description, items, + request.CreatedBy, request.TimezoneId + ); + var createResult = newOrder.SetTotal(); if (createResult.IsFailure) { diff --git a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs index b8a28b4d..38a72d88 100644 --- a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs +++ b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs @@ -13,57 +13,57 @@ public class BaseApplicationFixture : BaseFixture where TRequest : class where TUseCase : class { - public Mock mockServiceProvider = new(); - public Mock mockLogger = new(); - public Mock mockLoggerFactory = new(); - public Mock mockProduceService = new(); - public Mock mockRepository = new(); - public Mock> mockValidator = new(); - public Mock mockCache = new(); - public TUseCase useCase = default!; + public Mock MockServiceProvider { get; } = new(); + public Mock MockLogger { get; } = new(); + public Mock MockLoggerFactory { get; } = new(); + public Mock MockProduceService { get; } = new(); + public Mock MockRepository { get; } = new(); + public Mock> MockValidator { get; } = new(); + public Mock MockCache { get; } = new(); + public TUseCase UseCase { get; set; } = default!; public BaseApplicationFixture() { // Setup logger to enable logging by default - mockLogger.Setup(l => l.IsEnabled(It.IsAny())).Returns(true); + MockLogger.Setup(l => l.IsEnabled(It.IsAny())).Returns(true); MockServiceProviderServices(); } public void MockServiceProviderServices() { - mockServiceProvider + MockServiceProvider .Setup(r => r.GetService(typeof(ILoggerFactory))) - .Returns(mockLoggerFactory.Object); + .Returns(MockLoggerFactory.Object); - mockLoggerFactory + MockLoggerFactory .Setup(l => l.CreateLogger(It.IsAny())) - .Returns(mockLogger.Object); + .Returns(MockLogger.Object); - mockServiceProvider + MockServiceProvider .Setup(r => r.GetService(typeof(IValidator))) - .Returns(mockValidator.Object); + .Returns(MockValidator.Object); - mockServiceProvider + MockServiceProvider .Setup(r => r.GetService(typeof(IHybridCacheService))) - .Returns(mockCache.Object); + .Returns(MockCache.Object); - mockServiceProvider + MockServiceProvider .Setup(r => r.GetService(typeof(IProduceService))) - .Returns(mockProduceService.Object); + .Returns(MockProduceService.Object); - mockServiceProvider + MockServiceProvider .Setup(r => r.GetService(typeof(IBaseRepository))) - .Returns(mockRepository.Object); + .Returns(MockRepository.Object); } public void ClearInvocations() { - mockLogger.Reset(); - mockValidator.Reset(); - mockCache.Reset(); - mockProduceService.Reset(); - mockRepository.Reset(); + MockLogger.Reset(); + MockValidator.Reset(); + MockCache.Reset(); + MockProduceService.Reset(); + MockRepository.Reset(); } public BasePaginatedRequest SetValidBasePaginatedRequest() => new(Guid.NewGuid(), 1, 10); @@ -71,8 +71,8 @@ public void ClearInvocations() public void SetSuccessfulValidator(TRequest request) { var validationResult = new ValidationResult(); - mockValidator - .Setup(v => v.ValidateAsync(request, cancellationToken)) + MockValidator + .Setup(v => v.ValidateAsync(request, CancellationToken)) .ReturnsAsync(validationResult); } @@ -82,25 +82,25 @@ public void SetFailedValidator(TRequest request) { Errors = [new("Description", "Description is required")] }; - mockValidator - .Setup(v => v.ValidateAsync(request, cancellationToken)) + MockValidator + .Setup(v => v.ValidateAsync(request, CancellationToken)) .ReturnsAsync(validationResult); } - public void SetValidGetOrCreateAsync(TResult result) => mockCache + public void SetValidGetOrCreateAsync(TResult result) => MockCache .Setup(c => c.GetOrCreateAsync( It.IsAny(), It.IsAny>>(), It.IsAny() )).ReturnsAsync(result); - public void SetInvalidGetOrCreateAsync() => mockCache.Setup(c => c.GetOrCreateAsync( + public void SetInvalidGetOrCreateAsync() => MockCache.Setup(c => c.GetOrCreateAsync( It.IsAny(), It.IsAny>>(), It.IsAny() )); - public void VerifyStartUseCaseLog(int times = 1) => mockLogger.Verify( + public void VerifyStartUseCaseLog(int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Information, It.Is(e => e.Id == 1), @@ -109,7 +109,7 @@ public void VerifyStartUseCaseLog(int times = 1) => mockLogger.Verify( It.IsAny>()), Times.Exactly(times)); - public void VerifyFinishUseCaseLog(int times = 1) => mockLogger.Verify( + public void VerifyFinishUseCaseLog(int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Information, It.Is(e => e.Id == 2), @@ -118,7 +118,7 @@ public void VerifyFinishUseCaseLog(int times = 1) => mockLogger.Verify( It.IsAny>()), Times.Exactly(times)); - public void VerifyFinishUseCaseWithCacheLog(int times = 1) => mockLogger.Verify( + public void VerifyFinishUseCaseWithCacheLog(int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Information, It.IsAny(), @@ -127,7 +127,7 @@ public void VerifyFinishUseCaseWithCacheLog(int times = 1) => mockLogger.Verify( It.IsAny>()), Times.Exactly(times)); - public void VerifyLogInformation(string message, int times = 1) => mockLogger.Verify( + public void VerifyLogInformation(string message, int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Information, It.IsAny(), @@ -136,7 +136,7 @@ public void VerifyLogInformation(string message, int times = 1) => mockLogger.Ve It.IsAny>()), Times.Exactly(times)); - public void VerifyLogWarning(string message, int times = 1) => mockLogger.Verify( + public void VerifyLogWarning(string message, int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Warning, It.IsAny(), @@ -145,7 +145,7 @@ public void VerifyLogWarning(string message, int times = 1) => mockLogger.Verify It.IsAny>()), Times.Exactly(times)); - public void VerifyLogError(string message, int times = 1) => mockLogger.Verify( + public void VerifyLogError(string message, int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Error, It.IsAny(), @@ -154,7 +154,7 @@ public void VerifyLogError(string message, int times = 1) => mockLogger.Verify( It.IsAny>()), Times.Exactly(times)); - public void VerifyCache(int times) => mockCache.Verify( + public void VerifyCache(int times) => MockCache.Verify( c => c.GetOrCreateAsync( It.IsAny(), It.IsAny>>(), @@ -163,7 +163,7 @@ public void VerifyCache(int times) => mockCache.Verify( Times.Exactly(times) ); - public void VerifyProduce(int times = 1) where TMessage : BaseMessage => mockProduceService.Verify( + public void VerifyProduce(int times = 1) where TMessage : BaseMessage => MockProduceService.Verify( p => p.HandleAsync( It.IsAny(), It.IsAny(), diff --git a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs index 80b6c69f..e11ad46e 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs @@ -9,9 +9,9 @@ namespace UnitTests.Application.Notifications; public sealed class CreateNotificationRequestValidationFixture { - public IValidator validator = new CreateNotificationRequestValidator(); + public IValidator Validator { get; } = new CreateNotificationRequestValidator(); - public CreateNotificationRequest GetValidRequest() => + public static CreateNotificationRequest GetValidRequest() => new(Guid.NewGuid(), "TestNotification", "Success", "System", new { Test = "Message" }); } @@ -23,10 +23,10 @@ public sealed class CreateNotificationRequestValidationTests(CreateNotificationR public async Task GivenAValidRequestThenPass() { // Arrange - var request = _fixture.GetValidRequest(); + var request = CreateNotificationRequestValidationFixture.GetValidRequest(); // Act - var result = await _fixture.validator.TestValidateAsync(request); + var result = await _fixture.Validator.TestValidateAsync(request); // Assert result.ShouldNotHaveAnyValidationErrors(); @@ -36,14 +36,14 @@ public async Task GivenAValidRequestThenPass() public async Task GivenAnInvalidRequestThenFails() { // Arrange - var request = _fixture.GetValidRequest() with + var request = CreateNotificationRequestValidationFixture.GetValidRequest() with { CorrelationId = Guid.Empty, NotificationType = string.Empty }; // Act - var result = await _fixture.validator.TestValidateAsync(request); + var result = await _fixture.Validator.TestValidateAsync(request); // Assert result.ShouldHaveValidationErrorFor("CorrelationId"); @@ -55,14 +55,16 @@ public sealed class CreateNotificationUseCaseFixture : BaseApplicationFixture + public static CreateNotificationRequest SetValidRequest() => new(Guid.NewGuid(), "TestNotification", "Success", "System", new { Test = "Message" }); +#pragma warning disable CA1848 public void VerifyFailedToCreateNotificationLog(int times = 1) => - mockLogger.VerifyLog(l => l.LogWarning("*Failed to create notification.*"), Times.Exactly(times)); + MockLogger.VerifyLog(l => l.LogWarning("*Failed to create notification.*"), Times.Exactly(times)); +#pragma warning restore CA1848 } public sealed class CreateNotificationUseCaseTests : IClassFixture @@ -79,52 +81,52 @@ public CreateNotificationUseCaseTests(CreateNotificationUseCaseFixture fixture) public async Task GivenAValidRequestThenPass() { // Arrange - var request = _fixture.SetValidRequest(); + var request = CreateNotificationUseCaseFixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - _fixture.mockRepository.SetSuccessfulAddAsync(); + _fixture.MockRepository.SetSuccessfulAddAsync(); // Act - await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert _fixture.VerifyStartUseCaseLog(); _fixture.VerifyFinishUseCaseLog(); _fixture.VerifyFailedToCreateNotificationLog(0); - _fixture.mockRepository.VerifyAddAsync(1); + _fixture.MockRepository.VerifyAddAsync(1); } [Fact(DisplayName = nameof(GivenAnInvalidRequestThenFails))] public async Task GivenAnInvalidRequestThenFails() { // Arrange - var request = _fixture.SetValidRequest(); + var request = CreateNotificationUseCaseFixture.SetValidRequest(); _fixture.SetFailedValidator(request); // Act - await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert _fixture.VerifyStartUseCaseLog(); _fixture.VerifyFinishUseCaseLog(0); _fixture.VerifyFailedToCreateNotificationLog(0); - _fixture.mockRepository.VerifyAddAsync(0); + _fixture.MockRepository.VerifyAddAsync(0); } [Fact(DisplayName = nameof(GivenAValidRequestWhenRepositoryFailsThenFails))] public async Task GivenAValidRequestWhenRepositoryFailsThenFails() { // Arrange - var request = _fixture.SetValidRequest(); + var request = CreateNotificationUseCaseFixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - _fixture.mockRepository.SetFailedAddAsync(); + _fixture.MockRepository.SetFailedAddAsync(); // Act - await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert _fixture.VerifyStartUseCaseLog(); _fixture.VerifyFinishUseCaseLog(); _fixture.VerifyFailedToCreateNotificationLog(1); - _fixture.mockRepository.VerifyAddAsync(1); + _fixture.MockRepository.VerifyAddAsync(1); } } diff --git a/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs index 95f64652..a918dbfc 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs @@ -10,14 +10,16 @@ public sealed class GetAllNotificationsUseCaseFixture : BaseApplicationFixture + public static new BasePaginatedRequest SetValidBasePaginatedRequest() => new(Guid.NewGuid(), 1, 10); +#pragma warning disable CA1848 public void VerifyNoNotificationsFoundLog(int times = 1) => - mockLogger.VerifyLog(l => l.LogWarning("*No notifications found.*"), Times.Exactly(times)); + MockLogger.VerifyLog(l => l.LogWarning("*No notifications found.*"), Times.Exactly(times)); +#pragma warning restore CA1848 } public sealed class GetAllNotificationsUseCaseTests : IClassFixture @@ -35,14 +37,14 @@ public async Task GivenAValidRequestThenPass() { // Arrange var totalRecords = 5; - var request = _fixture.SetValidBasePaginatedRequest(); + var request = GetAllNotificationsUseCaseFixture.SetValidBasePaginatedRequest(); _fixture.SetSuccessfulValidator(request); - var expectedNotifications = _fixture.autoFixture.CreateMany(totalRecords); + var expectedNotifications = _fixture.AutoFixture.CreateMany(totalRecords); - _fixture.mockRepository.SetValidGetAllPaginatedAsyncNoIncludes(expectedNotifications, totalRecords); + _fixture.MockRepository.SetValidGetAllPaginatedAsyncNoIncludes(expectedNotifications, totalRecords); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.NotNull(result); @@ -62,11 +64,11 @@ public async Task GivenAValidRequestThenPass() public async Task GivenAnInvalidRequestThenFails() { // Arrange - var request = _fixture.SetValidBasePaginatedRequest(); + var request = GetAllNotificationsUseCaseFixture.SetValidBasePaginatedRequest(); _fixture.SetFailedValidator(request); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); @@ -82,12 +84,12 @@ public async Task GivenAnInvalidRequestThenFails() public async Task GivenAValidRequestWhenNoNotificationsFoundThenFails() { // Arrange - var request = _fixture.SetValidBasePaginatedRequest(); + var request = GetAllNotificationsUseCaseFixture.SetValidBasePaginatedRequest(); _fixture.SetSuccessfulValidator(request); - _fixture.mockRepository.SetInvalidGetAllPaginatedAsync(); + _fixture.MockRepository.SetInvalidGetAllPaginatedAsync(); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); diff --git a/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs index 8e642927..2be88e15 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs @@ -9,14 +9,16 @@ public sealed class GetNotificationUseCaseFixture : BaseApplicationFixture - new(Guid.NewGuid(), Math.Abs(autoFixture.Create()) + 1); + new(Guid.NewGuid(), Math.Abs(AutoFixture.Create()) + 1); +#pragma warning disable CA1848 public void VerifyNotificationNotFoundLog(int times = 1) => - mockLogger.VerifyLog(l => l.LogWarning("*Notification not found.*"), Times.Exactly(times)); + MockLogger.VerifyLog(l => l.LogWarning("*Notification not found.*"), Times.Exactly(times)); +#pragma warning restore CA1848 } public sealed class GetNotificationUseCaseTests : IClassFixture @@ -35,11 +37,11 @@ public async Task GivenAValidRequestThenPass() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - var expectedNotification = _fixture.autoFixture.Create(); - _fixture.mockRepository.SetupGetByIdAsNoTrackingAsync(expectedNotification); + var expectedNotification = _fixture.AutoFixture.Create(); + _fixture.MockRepository.SetupGetByIdAsNoTrackingAsync(expectedNotification); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.NotNull(result); @@ -63,7 +65,7 @@ public async Task GivenAnInvalidRequestThenFails() _fixture.SetFailedValidator(request); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); @@ -83,7 +85,7 @@ public async Task GivenAValidRequestWhenNotificationNotFoundThenFails() _fixture.SetSuccessfulValidator(request); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); diff --git a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs index 59dae347..819cfa21 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs @@ -10,9 +10,9 @@ namespace UnitTests.Application.Orders; public sealed class CreateOrderRequestValidationFixture { - public IValidator validator = new CreateOrderRequestValidator(); + public IValidator Validator { get; } = new CreateOrderRequestValidator(); - public CreateOrderRequest GetValidRequest() => new(Guid.NewGuid(), "new order", [ + public static CreateOrderRequest GetValidRequest() => new(Guid.NewGuid(), "new order", [ new("item1", "description1", 10.0m), new("item2", "description2", 20.0m) ]); @@ -22,31 +22,31 @@ public sealed class CreateOrderRequestValidationTests(CreateOrderRequestValidati { private readonly CreateOrderRequestValidationFixture _fixture = fixture; - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange - var request = _fixture.GetValidRequest(); + var request = CreateOrderRequestValidationFixture.GetValidRequest(); // Act - var result = await _fixture.validator.TestValidateAsync(request); + var result = await _fixture.Validator.TestValidateAsync(request); // Assert result.ShouldNotHaveAnyValidationErrors(); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAnInvalidRequestThenFails))] + public async Task GivenAnInvalidRequestThenFails() { // Arrange - var request = _fixture.GetValidRequest() with + var request = CreateOrderRequestValidationFixture.GetValidRequest() with { CorrelationId = Guid.Empty, Description = string.Empty, Items = [] }; // Act - var result = await _fixture.validator.TestValidateAsync(request); + var result = await _fixture.Validator.TestValidateAsync(request); // Assert result.ShouldHaveValidationErrorFor("CorrelationId"); @@ -59,12 +59,12 @@ public sealed class CreateOrderUseCaseFixture : BaseApplicationFixture(1); return new CreateOrderRequest(Guid.NewGuid(), "AwesomeComputer", [.. items]); @@ -73,11 +73,13 @@ public CreateOrderRequest SetValidRequest() public static CreateOrderRequest SetInvalidRequestWithNoItems() => new(Guid.NewGuid(), "AwesomeComputer", []); +#pragma warning disable CA1848 public void VerifyCreateOrderLogNoItemsError(int times = 1) => - mockLogger.VerifyLog(l => l.LogWarning("*Order must have at least one item.*"), Times.Exactly(times)); + MockLogger.VerifyLog(l => l.LogWarning("*Order must have at least one item.*"), Times.Exactly(times)); public void VerifyFailedToCreateOrderLog(int times = 1) => - mockLogger.VerifyLog(l => l.LogWarning("*Failed to create order.*"), Times.Exactly(times)); + MockLogger.VerifyLog(l => l.LogWarning("*Failed to create order.*"), Times.Exactly(times)); +#pragma warning restore CA1848 } public sealed class CreateOrderUseCaseTest : IClassFixture @@ -90,16 +92,16 @@ public CreateOrderUseCaseTest(CreateOrderUseCaseFixture fixture) _fixture.ClearInvocations(); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - _fixture.mockRepository.SetSuccessfulAddAsync(); + _fixture.MockRepository.SetSuccessfulAddAsync(); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.NotNull(result); @@ -110,21 +112,21 @@ public async Task Given_A_Valid_Request_Then_Pass() _fixture.VerifyFinishUseCaseLog(); _fixture.VerifyCreateOrderLogNoItemsError(0); _fixture.VerifyFailedToCreateOrderLog(0); - _fixture.mockRepository.VerifyAddAsync(1); + _fixture.MockRepository.VerifyAddAsync(1); _fixture.VerifyProduce(); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAnInvalidRequestThenFails))] + public async Task GivenAnInvalidRequestThenFails() { // Arrange var request = _fixture.SetValidRequest(); _fixture.SetFailedValidator(request); // Act - var result = await _fixture.useCase.HandleAsync( + var result = await _fixture.UseCase.HandleAsync( request, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -136,7 +138,7 @@ public async Task Given_A_Invalid_Request_Then_Fails() _fixture.VerifyFinishUseCaseLog(0); _fixture.VerifyCreateOrderLogNoItemsError(0); _fixture.VerifyFailedToCreateOrderLog(0); - _fixture.mockRepository.VerifyAddAsync(0); + _fixture.MockRepository.VerifyAddAsync(0); _fixture.VerifyProduce(0); } @@ -148,9 +150,9 @@ public async Task GivenAInvalidRequestThenFailsWhenThereIsNoItems() _fixture.SetSuccessfulValidator(request); // Act - var result = await _fixture.useCase.HandleAsync( + var result = await _fixture.UseCase.HandleAsync( request, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -162,7 +164,7 @@ public async Task GivenAInvalidRequestThenFailsWhenThereIsNoItems() _fixture.VerifyStartUseCaseLog(); _fixture.VerifyCreateOrderLogNoItemsError(1); _fixture.VerifyFailedToCreateOrderLog(0); - _fixture.mockRepository.VerifyAddAsync(0); + _fixture.MockRepository.VerifyAddAsync(0); _fixture.VerifyFinishUseCaseLog(); _fixture.VerifyProduce(); } @@ -173,12 +175,12 @@ public async Task GivenAValidRequestThenFailsWhenRepositoryReturnsZero() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - _fixture.mockRepository.SetFailedAddAsync(); + _fixture.MockRepository.SetFailedAddAsync(); // Act - var result = await _fixture.useCase.HandleAsync( + var result = await _fixture.UseCase.HandleAsync( request, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -188,7 +190,7 @@ public async Task GivenAValidRequestThenFailsWhenRepositoryReturnsZero() Assert.Equal("Failed to create order.", result.Message); _fixture.VerifyStartUseCaseLog(); - _fixture.mockRepository.VerifyAddAsync(1); + _fixture.MockRepository.VerifyAddAsync(1); _fixture.VerifyCreateOrderLogNoItemsError(0); _fixture.VerifyFailedToCreateOrderLog(1); _fixture.VerifyFinishUseCaseLog(); diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs index 6af4de04..cc4434f6 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs @@ -10,11 +10,13 @@ public sealed class GetAllOrdersUseCaseFixture : BaseApplicationFixture - mockLogger.VerifyLog(l => l.LogWarning("*No orders found.*"), Times.Exactly(times)); + MockLogger.VerifyLog(l => l.LogWarning("*No orders found.*"), Times.Exactly(times)); +#pragma warning restore CA1848 } public sealed class GetAllOrdersUseCaseTest : IClassFixture @@ -27,19 +29,19 @@ public GetAllOrdersUseCaseTest(GetAllOrdersUseCaseFixture fixture) _fixture.ClearInvocations(); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_Then_Pass))] - public async Task Given_A_Valid_Request_Then_Pass() + [Fact(DisplayName = nameof(GivenAValidRequestThenPass))] + public async Task GivenAValidRequestThenPass() { // Arrange var totalRecords = 5; var request = _fixture.SetValidBasePaginatedRequest(); _fixture.SetSuccessfulValidator(request); - var expectedOrders = _fixture.autoFixture.CreateMany(totalRecords); + var expectedOrders = _fixture.AutoFixture.CreateMany(totalRecords); - _fixture.mockRepository.SetValidGetAllPaginatedAsyncNoIncludes(expectedOrders, totalRecords); + _fixture.MockRepository.SetValidGetAllPaginatedAsyncNoIncludes(expectedOrders, totalRecords); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.NotNull(result); @@ -51,21 +53,21 @@ public async Task Given_A_Valid_Request_Then_Pass() Assert.Equal(totalRecords, result.TotalRecords); _fixture.VerifyStartUseCaseLog(); - _fixture.mockRepository.VerifyGetAllPaginatedNoIncludes(1); + _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(1); _fixture.VerifyNoOrdersFoundLog(0); _fixture.VerifyFinishUseCaseLog(); } - [Fact(DisplayName = nameof(Given_A_Valid_Request_When_No_Orders_Found_Then_Fails))] - public async Task Given_A_Valid_Request_When_No_Orders_Found_Then_Fails() + [Fact(DisplayName = nameof(GivenAValidRequestWhenNoOrdersFoundThenFails))] + public async Task GivenAValidRequestWhenNoOrdersFoundThenFails() { // Arrange var request = _fixture.SetValidBasePaginatedRequest(); _fixture.SetSuccessfulValidator(request); - _fixture.mockRepository.SetInvalidGetAllPaginatedAsync(); + _fixture.MockRepository.SetInvalidGetAllPaginatedAsync(); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); @@ -74,20 +76,20 @@ public async Task Given_A_Valid_Request_When_No_Orders_Found_Then_Fails() Assert.Equal("No orders found.", result.Message); _fixture.VerifyStartUseCaseLog(); - _fixture.mockRepository.VerifyGetAllPaginatedNoIncludes(1); + _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(1); _fixture.VerifyNoOrdersFoundLog(1); _fixture.VerifyFinishUseCaseLog(); } - [Fact(DisplayName = nameof(Given_A_Invalid_Request_Then_Fails))] - public async Task Given_A_Invalid_Request_Then_Fails() + [Fact(DisplayName = nameof(GivenAnInvalidRequestThenFails))] + public async Task GivenAnInvalidRequestThenFails() { // Arrange var request = _fixture.SetValidBasePaginatedRequest(); _fixture.SetFailedValidator(request); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); @@ -95,7 +97,7 @@ public async Task Given_A_Invalid_Request_Then_Fails() Assert.NotEmpty(result.Message); _fixture.VerifyStartUseCaseLog(); - _fixture.mockRepository.VerifyGetAllPaginatedNoIncludes(0); + _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(0); _fixture.VerifyNoOrdersFoundLog(0); _fixture.VerifyFinishUseCaseLog(0); } diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index 67aee300..f350c40f 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -9,12 +9,14 @@ public sealed class GetOrderUseCaseFixture : BaseApplicationFixture new(Guid.NewGuid(), autoFixture.Create()); + public GetOrderRequest SetValidRequest() => new(Guid.NewGuid(), AutoFixture.Create()); +#pragma warning disable CA1848 public void VerifyOrderNotFoundLog(int times = 1) => - mockLogger.VerifyLog(l => l.LogWarning("*Order not found.*"), Times.Exactly(times)); + MockLogger.VerifyLog(l => l.LogWarning("*Order not found.*"), Times.Exactly(times)); +#pragma warning restore CA1848 } public sealed class GetOrderUseCaseTest : IClassFixture @@ -33,11 +35,11 @@ public async Task GivenAValidRequestThenPass() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - var expectedOrder = _fixture.autoFixture.Create(); - _fixture.mockRepository.SetupGetByIdAsNoTrackingAsync(expectedOrder); + var expectedOrder = _fixture.AutoFixture.Create(); + _fixture.MockRepository.SetupGetByIdAsNoTrackingAsync(expectedOrder); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.NotNull(result); @@ -68,10 +70,10 @@ public async Task GivenAValidRequestWithoutItemsThenPass() Total = 1000m, Items = [] }; - _fixture.mockRepository.SetupGetByIdAsNoTrackingAsync(expectedOrder); + _fixture.MockRepository.SetupGetByIdAsNoTrackingAsync(expectedOrder); // Act - var result = await _fixture.useCase.HandleAsync(request, _fixture.cancellationToken); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.NotNull(result); @@ -96,9 +98,9 @@ public async Task GivenAInvalidRequestThenFails() _fixture.SetFailedValidator(request); // Act - var result = await _fixture.useCase.HandleAsync( + var result = await _fixture.UseCase.HandleAsync( request, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert @@ -119,9 +121,9 @@ public async Task GivenAValidRequestWhenOrderNotFoundThenFails() _fixture.SetSuccessfulValidator(request); // Act - var result = await _fixture.useCase.HandleAsync( + var result = await _fixture.UseCase.HandleAsync( request, - _fixture.cancellationToken + _fixture.CancellationToken ); // Assert From e714df387f9d1959c08b9c2a3b5866cdfab48bb7 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Feb 2026 07:43:42 -0300 Subject: [PATCH 060/161] 53 refactor: remove unused logger imports and update log verification --- .../Common/UseCases/BaseInOutUseCase.cs | 1 - .../src/Application/Orders/GetOrderUseCase.cs | 1 - .../Common/BaseApplicationFixture.cs | 12 ++++++++++-- .../Orders/GetAllOrdersUseCaseTest.cs | 17 ++++------------- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index eaea11f9..78ca7f37 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -6,7 +6,6 @@ using Application.Common.Services; using FluentValidation; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; namespace Application.Common.UseCases; diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index 28a8af05..549fa7e8 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -3,7 +3,6 @@ using Application.Common.Helpers; using Domain.Orders; using FluentValidation; -using Microsoft.Extensions.Logging; namespace Application.Orders; diff --git a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs index 38a72d88..988f0b30 100644 --- a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs +++ b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs @@ -24,7 +24,6 @@ public class BaseApplicationFixture : BaseFixture public BaseApplicationFixture() { - // Setup logger to enable logging by default MockLogger.Setup(l => l.IsEnabled(It.IsAny())).Returns(true); MockServiceProviderServices(); @@ -59,7 +58,7 @@ public void MockServiceProviderServices() public void ClearInvocations() { - MockLogger.Reset(); + MockLogger.Invocations.Clear(); MockValidator.Reset(); MockCache.Reset(); MockProduceService.Reset(); @@ -127,6 +126,15 @@ public void VerifyFinishUseCaseWithCacheLog(int times = 1) => MockLogger.Verify( It.IsAny>()), Times.Exactly(times)); + public void VerifyNotFoundLog(int times = 1) => MockLogger.Verify(l => l.Log( + LogLevel.Warning, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("not found.")), + null, + It.IsAny>()), + Times.Exactly(times) + ); + public void VerifyLogInformation(string message, int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Information, diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs index cc4434f6..711b4397 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs @@ -1,22 +1,13 @@ using Application.Common.Requests; using Application.Orders; using Domain.Orders; -using Microsoft.Extensions.Logging; using UnitTests.Application.Common; namespace UnitTests.Application.Orders; public sealed class GetAllOrdersUseCaseFixture : BaseApplicationFixture { - public GetAllOrdersUseCaseFixture() - { - UseCase = new(MockServiceProvider.Object); - } - -#pragma warning disable CA1848 - public void VerifyNoOrdersFoundLog(int times = 1) => - MockLogger.VerifyLog(l => l.LogWarning("*No orders found.*"), Times.Exactly(times)); -#pragma warning restore CA1848 + public GetAllOrdersUseCaseFixture() => UseCase = new(MockServiceProvider.Object); } public sealed class GetAllOrdersUseCaseTest : IClassFixture @@ -54,7 +45,7 @@ public async Task GivenAValidRequestThenPass() _fixture.VerifyStartUseCaseLog(); _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(1); - _fixture.VerifyNoOrdersFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(); } @@ -77,7 +68,7 @@ public async Task GivenAValidRequestWhenNoOrdersFoundThenFails() _fixture.VerifyStartUseCaseLog(); _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(1); - _fixture.VerifyNoOrdersFoundLog(1); + _fixture.VerifyNotFoundLog(1); _fixture.VerifyFinishUseCaseLog(); } @@ -98,7 +89,7 @@ public async Task GivenAnInvalidRequestThenFails() _fixture.VerifyStartUseCaseLog(); _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(0); - _fixture.VerifyNoOrdersFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(0); } } From 444041f98cc8e482c00bfb3033afce32c99d47db Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Feb 2026 07:44:09 -0300 Subject: [PATCH 061/161] 53 refactor: simplify fixture constructor and rename log verification --- .../Orders/GetOrderUseCaseTests.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index f350c40f..b6812d89 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -1,22 +1,13 @@ using Application.Orders; using Domain.Orders; -using Microsoft.Extensions.Logging; using UnitTests.Application.Common; namespace UnitTests.Application.Orders; public sealed class GetOrderUseCaseFixture : BaseApplicationFixture { - public GetOrderUseCaseFixture() - { - UseCase = new(MockServiceProvider.Object); - } + public GetOrderUseCaseFixture() => UseCase = new(MockServiceProvider.Object); public GetOrderRequest SetValidRequest() => new(Guid.NewGuid(), AutoFixture.Create()); - -#pragma warning disable CA1848 - public void VerifyOrderNotFoundLog(int times = 1) => - MockLogger.VerifyLog(l => l.LogWarning("*Order not found.*"), Times.Exactly(times)); -#pragma warning restore CA1848 } public sealed class GetOrderUseCaseTest : IClassFixture @@ -53,7 +44,7 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(expectedOrder.Items?.Count, result.Data.Items!.Count); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyOrderNotFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(); } @@ -86,7 +77,7 @@ public async Task GivenAValidRequestWithoutItemsThenPass() Assert.Equal(0, result.Data.Items?.Count); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyOrderNotFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(); } @@ -109,7 +100,7 @@ public async Task GivenAInvalidRequestThenFails() Assert.NotEmpty(result.Message); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyOrderNotFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(0); } @@ -133,7 +124,7 @@ public async Task GivenAValidRequestWhenOrderNotFoundThenFails() Assert.Equal("Order not found.", result.Message); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyOrderNotFoundLog(1); + _fixture.VerifyNotFoundLog(1); _fixture.VerifyFinishUseCaseLog(); } } From 7eb481e5a1472f85146342e4b52be5d1cfe729ab Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Feb 2026 07:50:23 -0300 Subject: [PATCH 062/161] 53 refactor: remove unused logger imports and improve logging calls --- templates/Full/.editorconfig | 6 ++++-- .../Full/src/Application/Common/UseCases/BaseInUseCase.cs | 1 - .../Full/src/Application/Common/UseCases/BaseOutUseCase.cs | 1 - .../Application/Notifications/GetAllNotificationsUseCase.cs | 2 -- .../src/Application/Notifications/GetNotificationUseCase.cs | 5 ----- .../Full/src/Application/Orders/GetAllOrdersUseCase.cs | 2 -- .../Infrastructure/Messaging/Producers/ProducerService.cs | 5 +++-- 7 files changed, 7 insertions(+), 15 deletions(-) diff --git a/templates/Full/.editorconfig b/templates/Full/.editorconfig index c4f02cf9..6dd59339 100644 --- a/templates/Full/.editorconfig +++ b/templates/Full/.editorconfig @@ -81,7 +81,6 @@ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case # Code style defaults csharp_using_directive_placement = outside_namespace:suggestion dotnet_sort_system_directives_first = true -csharp_prefer_braces = true:silent csharp_preserve_single_line_blocks = true:none csharp_preserve_single_line_statements = false:none csharp_prefer_static_local_function = true:suggestion @@ -162,7 +161,10 @@ dotnet_diagnostic.S2360.severity = none dotnet_diagnostic.CS8618.severity = none # CA1873: Avoid potentially expensive logging -dotnet_diagnostic.CA1873.severity = none +dotnet_diagnostic.CA1873.severity = error + +# IDE0005: Using directive is unnecessary +dotnet_diagnostic.IDE0005.severity = error # Xml project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 89e0ee39..329cd38f 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -1,7 +1,6 @@ using Application.Common.Requests; using FluentValidation; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Application.Common.Services; using Application.Common.Constants; using System.Diagnostics.Metrics; diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 6d012760..6d515610 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -3,7 +3,6 @@ using Application.Common.Requests; using Application.Common.Services; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Application.Common.Helpers; namespace Application.Common.UseCases; diff --git a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs index d8753931..ae29964e 100644 --- a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs @@ -1,9 +1,7 @@ -using Application.Common.Constants; using Application.Common.Requests; using Application.Common.UseCases; using Application.Common.Helpers; using Domain.Notifications; -using Microsoft.Extensions.Logging; namespace Application.Notifications; diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index 443f6161..70898234 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -1,13 +1,8 @@ -using System.Linq.Expressions; -using Application.Common.Constants; -using Application.Common.Repositories; using Application.Common.Requests; using Application.Common.UseCases; using Application.Common.Helpers; using Domain.Notifications; using FluentValidation; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; namespace Application.Notifications; diff --git a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs index e2f277a4..4e886010 100644 --- a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs +++ b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs @@ -1,9 +1,7 @@ -using Application.Common.Constants; using Application.Common.Requests; using Application.Common.UseCases; using Application.Common.Helpers; using Domain.Orders; -using Microsoft.Extensions.Logging; namespace Application.Orders; diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 70add50a..4cc1021b 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -70,7 +70,8 @@ public async Task HandleAsync( using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - Logs.PublishingBatchMessages(_logger, _className, messages.Select(m => m.CorrelationId), typeof(TMessage).Name); + var correlationIds = messages.Select(m => m.CorrelationId); + Logs.PublishingBatchMessages(_logger, _className, correlationIds, typeof(TMessage).Name); foreach (var message in messages) await channel.BasicPublishAsync( @@ -80,6 +81,6 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.BatchMessagesPublished(_logger, _className, messages.Select(m => m.CorrelationId), typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds); + Logs.BatchMessagesPublished(_logger, _className, correlationIds, typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds); } } From 6aff86cf7a6cfe0f47f264b3bb14cfa5cfe5cfef Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Feb 2026 08:03:08 -0300 Subject: [PATCH 063/161] 53 chore: change logging severity for CA1873 to none --- templates/Full/.editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Full/.editorconfig b/templates/Full/.editorconfig index 6dd59339..1a25dad5 100644 --- a/templates/Full/.editorconfig +++ b/templates/Full/.editorconfig @@ -161,7 +161,7 @@ dotnet_diagnostic.S2360.severity = none dotnet_diagnostic.CS8618.severity = none # CA1873: Avoid potentially expensive logging -dotnet_diagnostic.CA1873.severity = error +dotnet_diagnostic.CA1873.severity = none # IDE0005: Using directive is unnecessary dotnet_diagnostic.IDE0005.severity = error From 8c6809528b955ac661da539d9d5d4a10f60e9362 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Feb 2026 08:03:19 -0300 Subject: [PATCH 064/161] 53 refactor: replace log verification methods with new implementations --- .../Application/Common/BaseApplicationFixture.cs | 9 +++++++++ .../Notifications/CreateNotificationUseCaseTests.cs | 12 +++--------- .../Notifications/GetAllNotificationsUseCaseTests.cs | 12 +++--------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs index 988f0b30..cbd803b0 100644 --- a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs +++ b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs @@ -135,6 +135,15 @@ public void VerifyNotFoundLog(int times = 1) => MockLogger.Verify(l => l.Log( Times.Exactly(times) ); + public void VerifyOperationFailedLog(int times = 1) => MockLogger.Verify(l => l.Log( + LogLevel.Warning, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains("Operation failed")), + null, + It.IsAny>()), + Times.Exactly(times) + ); + public void VerifyLogInformation(string message, int times = 1) => MockLogger.Verify( x => x.Log( LogLevel.Information, diff --git a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs index e11ad46e..05532c41 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs @@ -2,7 +2,6 @@ using Domain.Notifications; using FluentValidation; using FluentValidation.TestHelper; -using Microsoft.Extensions.Logging; using UnitTests.Application.Common; namespace UnitTests.Application.Notifications; @@ -60,11 +59,6 @@ public CreateNotificationUseCaseFixture() public static CreateNotificationRequest SetValidRequest() => new(Guid.NewGuid(), "TestNotification", "Success", "System", new { Test = "Message" }); - -#pragma warning disable CA1848 - public void VerifyFailedToCreateNotificationLog(int times = 1) => - MockLogger.VerifyLog(l => l.LogWarning("*Failed to create notification.*"), Times.Exactly(times)); -#pragma warning restore CA1848 } public sealed class CreateNotificationUseCaseTests : IClassFixture @@ -91,7 +85,7 @@ public async Task GivenAValidRequestThenPass() // Assert _fixture.VerifyStartUseCaseLog(); _fixture.VerifyFinishUseCaseLog(); - _fixture.VerifyFailedToCreateNotificationLog(0); + _fixture.VerifyOperationFailedLog(0); _fixture.MockRepository.VerifyAddAsync(1); } @@ -108,7 +102,7 @@ public async Task GivenAnInvalidRequestThenFails() // Assert _fixture.VerifyStartUseCaseLog(); _fixture.VerifyFinishUseCaseLog(0); - _fixture.VerifyFailedToCreateNotificationLog(0); + _fixture.VerifyOperationFailedLog(0); _fixture.MockRepository.VerifyAddAsync(0); } @@ -126,7 +120,7 @@ public async Task GivenAValidRequestWhenRepositoryFailsThenFails() // Assert _fixture.VerifyStartUseCaseLog(); _fixture.VerifyFinishUseCaseLog(); - _fixture.VerifyFailedToCreateNotificationLog(1); + _fixture.VerifyOperationFailedLog(); _fixture.MockRepository.VerifyAddAsync(1); } } diff --git a/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs index a918dbfc..10317bd5 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs @@ -1,7 +1,6 @@ using Application.Common.Requests; using Application.Notifications; using Domain.Notifications; -using Microsoft.Extensions.Logging; using UnitTests.Application.Common; namespace UnitTests.Application.Notifications; @@ -15,11 +14,6 @@ public GetAllNotificationsUseCaseFixture() public static new BasePaginatedRequest SetValidBasePaginatedRequest() => new(Guid.NewGuid(), 1, 10); - -#pragma warning disable CA1848 - public void VerifyNoNotificationsFoundLog(int times = 1) => - MockLogger.VerifyLog(l => l.LogWarning("*No notifications found.*"), Times.Exactly(times)); -#pragma warning restore CA1848 } public sealed class GetAllNotificationsUseCaseTests : IClassFixture @@ -56,7 +50,7 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(totalRecords, result.TotalRecords); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNoNotificationsFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(); } @@ -76,7 +70,7 @@ public async Task GivenAnInvalidRequestThenFails() Assert.NotEmpty(result.Message); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNoNotificationsFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(0); } @@ -98,7 +92,7 @@ public async Task GivenAValidRequestWhenNoNotificationsFoundThenFails() Assert.Equal("No notifications found.", result.Message); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNoNotificationsFoundLog(1); + _fixture.VerifyNotFoundLog(1); _fixture.VerifyFinishUseCaseLog(); } } From e87298edacd0f807a539f887574c57c40f0b2055 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 22 Feb 2026 09:23:57 -0300 Subject: [PATCH 065/161] 53 feat: add Microsoft.EntityFrameworkCore package and update queries --- .../Full/src/Application/Application.csproj | 1 + .../Common/Repositories/IBaseRepository.cs | 59 +----- .../Notifications/GetNotificationUseCase.cs | 15 +- .../src/Application/Orders/GetOrderUseCase.cs | 24 +-- .../Full/src/Application/packages.lock.json | 42 ++++ .../Full/src/Infrastructure/Common/Logs.cs | 6 +- .../Data/Common/BaseRepository.cs | 181 ++---------------- .../src/Infrastructure/Infrastructure.csproj | 1 - .../src/Infrastructure/packages.lock.json | 25 +-- templates/Full/src/WebApp/packages.lock.json | 4 +- .../Data/BaseRepositoryTest.cs | 88 ++++----- .../Notifications/CreateNotificationTest.cs | 17 +- .../tests/IntegrationTests/packages.lock.json | 2 +- .../Full/tests/UnitTests/packages.lock.json | 43 +++++ 14 files changed, 189 insertions(+), 319 deletions(-) diff --git a/templates/Full/src/Application/Application.csproj b/templates/Full/src/Application/Application.csproj index 9162a973..a042ffcc 100644 --- a/templates/Full/src/Application/Application.csproj +++ b/templates/Full/src/Application/Application.csproj @@ -3,6 +3,7 @@ true + diff --git a/templates/Full/src/Application/Common/Repositories/IBaseRepository.cs b/templates/Full/src/Application/Common/Repositories/IBaseRepository.cs index 313ae5a3..4e042aa0 100644 --- a/templates/Full/src/Application/Common/Repositories/IBaseRepository.cs +++ b/templates/Full/src/Application/Common/Repositories/IBaseRepository.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using System.Runtime.CompilerServices; using Domain.Common; namespace Application.Common.Repositories; @@ -6,62 +7,10 @@ public interface IBaseRepository { Task AddAsync(TEntity entity, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; Task AddRangeAsync(TEntity[] entities, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; - Task AddOrUpdateIfNotExistsAsync(TEntity entity, Expression> predicate, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; Task UpdateAsync(TEntity entity, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; Task RemoveAsync(TEntity entity, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; Task RemoveRangeAsync(TEntity[] entities, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; - Task CheckExistsByWhereAsync(Expression> predicate, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; - Task CheckExistsByWhereAsNoTrackingAsync(Expression> predicate, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity; - - Task FirstOrDefaultAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity; - Task FirstOrDefaultAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - Expression> selector, - CancellationToken cancellationToken, - bool? newContext = null - ) where TEntity : DomainEntity; - Task GetByIdAsNoTrackingAsync( - int id, - Guid correlationId, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity; - Task GetByIdAsNoTrackingAsync( - int id, - Guid correlationId, - Expression> selector, - CancellationToken cancellationToken, - bool? newContext = null - ) where TEntity : DomainEntity; - Task> GetByWhereAsync( - Guid correlationId, - Expression> predicate, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity; - Task> GetByWhereAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity; - Task> GetByWhereAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - Expression> selector, - CancellationToken cancellationToken, - bool? newContext = null - ) where TEntity : DomainEntity; + IQueryable GetQueryable(Guid correlationId, bool? newContext = null, [CallerMemberName] string methodName = null!) where TEntity : DomainEntity; Task<(IEnumerable Items, int TotalRecords)> GetAllPaginatedAsync( Guid correlationId, int page, @@ -86,8 +35,4 @@ params Expression>[]? includes Expression> predicate = null!, bool? newContext = null ) where TEntity : DomainEntity; - - Task BeginTransactionAsync(CancellationToken cancellationToken); - Task CommitTransactionAsync(CancellationToken cancellationToken); - Task RollbackTransactionAsync(CancellationToken cancellationToken); } diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index 70898234..95f0d730 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -3,6 +3,7 @@ using Application.Common.Helpers; using Domain.Notifications; using FluentValidation; +using Microsoft.EntityFrameworkCore; namespace Application.Notifications; @@ -25,18 +26,16 @@ public override async Task> HandleInternalAsync( CancellationToken cancellationToken ) { - var notification = await Repository.GetByIdAsNoTrackingAsync( - request.Id, - request.CorrelationId, - n => new() + var notification = await Repository.GetQueryable(request.CorrelationId) + .Where(n => n.Id == request.Id) + .Select(n => new NotificationDto() { Id = n.Id, Message = n.Message, NotificationType = n.NotificationType, - NotificationStatus = n.NotificationStatus, - }, - cancellationToken - ); + NotificationStatus = n.NotificationStatus + }) + .FirstOrDefaultAsync(cancellationToken); if (notification is null) { diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index 549fa7e8..cc60de82 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -3,6 +3,7 @@ using Application.Common.Helpers; using Domain.Orders; using FluentValidation; +using Microsoft.EntityFrameworkCore; namespace Application.Orders; @@ -23,22 +24,13 @@ public override async Task> HandleInternalAsync( CancellationToken cancellationToken ) { - var order = await Repository.GetByIdAsNoTrackingAsync( - request.Id, - request.CorrelationId, - o => new OrderDto() - { - Id = o.Id, - Total = o.Total, - Items = o.Items.Select(i => new ItemDto - { - Id = i.Id, - Name = i.Name, - Value = i.Value - }).ToArray() - }, - cancellationToken - ); + var order = await Repository.GetQueryable(request.CorrelationId) + .Select(o => new OrderDto + { + Id = o.Id, + Description = o.Description, + Total = o.Total, + }).FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); if (order is null) { diff --git a/templates/Full/src/Application/packages.lock.json b/templates/Full/src/Application/packages.lock.json index 11025338..846649c4 100644 --- a/templates/Full/src/Application/packages.lock.json +++ b/templates/Full/src/Application/packages.lock.json @@ -18,6 +18,18 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" } }, + "Microsoft.EntityFrameworkCore": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, "Microsoft.Extensions.Logging": { "type": "Direct", "requested": "[10.0.1, )", @@ -29,6 +41,36 @@ "Microsoft.Extensions.Options": "10.0.1" } }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", "resolved": "10.0.1", diff --git a/templates/Full/src/Infrastructure/Common/Logs.cs b/templates/Full/src/Infrastructure/Common/Logs.cs index 575ba5ac..b513c6d5 100644 --- a/templates/Full/src/Infrastructure/Common/Logs.cs +++ b/templates/Full/src/Infrastructure/Common/Logs.cs @@ -26,7 +26,7 @@ internal static partial class Logs [LoggerMessage( EventId = 201, Level = LogLevel.Debug, - Message = "[BaseRepository] | [{Method}] | CorrelationId: {CorrelationId} | Starting database operation." + Message = "[BaseRepository] | [{Method}] | [{CorrelationId}] | Starting database operation." )] public static partial void StartingDatabaseOperation(ILogger logger, string method, Guid correlationId); @@ -40,9 +40,9 @@ internal static partial class Logs [LoggerMessage( EventId = 202, Level = LogLevel.Debug, - Message = "[BaseRepository] | [{Method}] | CorrelationId: {CorrelationId} | Query executed in {ElapsedMilliseconds} ms." + Message = "[BaseRepository] | [{Method}] | [{CorrelationId}] | Finished database operation executed in {ElapsedMilliseconds} ms." )] - public static partial void QueryExecuted(ILogger logger, string method, Guid correlationId, long elapsedMilliseconds); + public static partial void FinishedDatabaseOperation(ILogger logger, string method, Guid correlationId, long elapsedMilliseconds); /// /// Logs the start of retrieving a cache entry. diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 0f8cd47b..4c133b52 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -36,11 +36,31 @@ private async Task HandleBaseQueryAsync( var result = await query.Invoke(dbSet); - Logs.QueryExecuted(logger, methodName, correlationId, _stopwatch.ElapsedMilliseconds); + Logs.FinishedDatabaseOperation(logger, methodName, correlationId, _stopwatch.ElapsedMilliseconds); return result; } + public IQueryable GetQueryable( + Guid correlationId, + bool? newContext = null, + [CallerMemberName] + string methodName = null! + ) where TEntity : DomainEntity + { + _stopwatch.Restart(); + + Logs.StartingDatabaseOperation(logger, methodName, correlationId); + + var dbSet = _dbContext.Set(); + if (newContext.GetValueOrDefault()) + dbSet = _dbContextFactory.CreateDbContext().Set(); + + Logs.FinishedDatabaseOperation(logger, methodName, correlationId, _stopwatch.ElapsedMilliseconds); + + return dbSet; + } + public async Task AddAsync(TEntity entity, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity => await HandleBaseQueryAsync(async dbEntitySet => { @@ -57,17 +77,6 @@ await HandleBaseQueryAsync(async dbEntitySet => return await _dbContext.SaveChangesAsync(cancellationToken); }, correlationId, newContext); - public async Task AddOrUpdateIfNotExistsAsync(TEntity entity, Expression> predicate, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity => - await HandleBaseQueryAsync(async dbEntitySet => - { - if (!await dbEntitySet.AsNoTracking().AnyAsync(predicate)) - await dbEntitySet.AddAsync(entity, cancellationToken); - else - dbEntitySet.Update(entity); - - return await _dbContext.SaveChangesAsync(cancellationToken); - }, correlationId, newContext); - public async Task UpdateAsync(TEntity entity, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity => await HandleBaseQueryAsync(async dbEntitySet => { @@ -92,121 +101,6 @@ await HandleBaseQueryAsync(async dbEntitySet => return await _dbContext.SaveChangesAsync(cancellationToken); }, correlationId, newContext); - public async Task CheckExistsByWhereAsync(Expression> predicate, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity => - await HandleBaseQueryAsync(async dbEntitySet => - { - return await dbEntitySet.AnyAsync(predicate, cancellationToken); - }, correlationId, newContext); - - public async Task CheckExistsByWhereAsNoTrackingAsync(Expression> predicate, Guid correlationId, CancellationToken cancellationToken, bool? newContext = null) where TEntity : DomainEntity => - await HandleBaseQueryAsync(async dbEntitySet => - { - return await dbEntitySet.AsNoTracking().AnyAsync(predicate, cancellationToken); - }, correlationId, newContext); - public async Task GetByIdAsNoTrackingAsync( - int id, - Guid correlationId, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity => await HandleBaseQueryAsync(async dbEntitySet => - { - var query = dbEntitySet.AsNoTracking(); - - if (includes is not null) - query = SetIncludes(includes, query); - - return await query.FirstOrDefaultAsync(o => o.Id == id, cancellationToken) ?? default!; - }, correlationId, newContext); - - public async Task GetByIdAsNoTrackingAsync( - int id, - Guid correlationId, - Expression> selector, - CancellationToken cancellationToken, - bool? newContext = null - ) where TEntity : DomainEntity => await HandleBaseQueryAsync(async dbEntitySet => await dbEntitySet - .Where(o => o.Id == id) - .Select(selector) - .FirstOrDefaultAsync(cancellationToken) ?? default!, - correlationId, - newContext - ); - - public async Task> GetByWhereAsync( - Guid correlationId, - Expression> predicate, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity => await HandleBaseQueryAsync>(async dbEntitySet => - { - var query = dbEntitySet.AsQueryable(); - - if (includes is not null) - query = SetIncludes(includes, query); - - return await query.Where(predicate).ToListAsync(cancellationToken); - }, correlationId, newContext); - - public async Task> GetByWhereAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity => await HandleBaseQueryAsync>(async dbEntitySet => - { - var query = dbEntitySet.AsNoTracking(); - - if (includes is not null) - query = SetIncludes(includes, query); - - return await query.Where(predicate).ToListAsync(cancellationToken); - }, correlationId, newContext); - - public async Task> GetByWhereAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - Expression> selector, - CancellationToken cancellationToken, - bool? newContext = null - ) where TEntity : DomainEntity => await HandleBaseQueryAsync>(async dbEntitySet => - await dbEntitySet.Where(predicate).Select(selector).ToListAsync(cancellationToken), - correlationId, - newContext - ); - - public async Task FirstOrDefaultAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - CancellationToken cancellationToken, - bool? newContext = null, - params Expression>[]? includes - ) where TEntity : DomainEntity => await HandleBaseQueryAsync(async dbEntitySet => - { - var query = dbEntitySet.AsNoTracking(); - - if (includes is not null) - query = SetIncludes(includes, query); - - return await query.FirstOrDefaultAsync(predicate, cancellationToken) ?? default!; - }, correlationId, newContext); - - public async Task FirstOrDefaultAsNoTrackingAsync( - Guid correlationId, - Expression> predicate, - Expression> selector, - CancellationToken cancellationToken, - bool? newContext = null - ) where TEntity : DomainEntity => await HandleBaseQueryAsync(async dbEntitySet => await dbEntitySet - .Where(predicate) - .Select(selector) - .FirstOrDefaultAsync(cancellationToken) ?? default!, - correlationId, - newContext - ); - public async Task<(IEnumerable Items, int TotalRecords)> GetAllPaginatedAsync( Guid correlationId, int page, @@ -289,37 +183,4 @@ params Expression>[]? includes return (items, totalRecords); }, correlationId, newContext); - - public async Task BeginTransactionAsync(CancellationToken cancellationToken) - { - if (_dbContext.Database.CurrentTransaction == null) - await _dbContext.Database.BeginTransactionAsync(cancellationToken); - } - - public async Task CommitTransactionAsync(CancellationToken cancellationToken) - { - if (_dbContext.Database.CurrentTransaction != null) - { - _dbContext.SaveChanges(); - await _dbContext.Database.CurrentTransaction.CommitAsync(cancellationToken); - } - } - - public async Task RollbackTransactionAsync(CancellationToken cancellationToken) - { - if (_dbContext.Database.CurrentTransaction != null) - await _dbContext.Database.CurrentTransaction.RollbackAsync(cancellationToken); - } - - private static IQueryable SetIncludes( - Expression>[] includes, - IQueryable query - ) where TEntity : DomainEntity - { - if (includes != null && includes.Length > 0) - foreach (var include in includes) - query = query.Include(include); - - return query; - } } diff --git a/templates/Full/src/Infrastructure/Infrastructure.csproj b/templates/Full/src/Infrastructure/Infrastructure.csproj index 30f85b68..f760b0e3 100644 --- a/templates/Full/src/Infrastructure/Infrastructure.csproj +++ b/templates/Full/src/Infrastructure/Infrastructure.csproj @@ -3,7 +3,6 @@ - runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json index aa77f7ab..4b9aeb36 100644 --- a/templates/Full/src/Infrastructure/packages.lock.json +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -2,18 +2,6 @@ "version": 2, "dependencies": { "net10.0": { - "Microsoft.EntityFrameworkCore": { - "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" - } - }, "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", "requested": "[10.0.1, )", @@ -766,6 +754,7 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", + "Microsoft.EntityFrameworkCore": "[10.0.1, )", "Microsoft.Extensions.Logging": "[10.0.1, )" } }, @@ -791,6 +780,18 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" } }, + "Microsoft.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", "requested": "[10.0.1, )", diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json index 5e3ca9e0..4f82b597 100644 --- a/templates/Full/src/WebApp/packages.lock.json +++ b/templates/Full/src/WebApp/packages.lock.json @@ -326,7 +326,8 @@ "dependencies": { "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", - "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )" + "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", + "Microsoft.EntityFrameworkCore": "[10.0.1, )" } }, "domain": { @@ -336,7 +337,6 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.EntityFrameworkCore": "[10.0.1, )", "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.1, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", diff --git a/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs b/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs index 62f0452a..5a8e5ed9 100644 --- a/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs +++ b/templates/Full/tests/IntegrationTests/Data/BaseRepositoryTest.cs @@ -1,6 +1,7 @@ using Application.Orders; using Domain.Orders; using IntegrationTests.Common; +using Microsoft.EntityFrameworkCore; using WebApp; namespace IntegrationTests.Data; @@ -15,27 +16,6 @@ public BaseRepositoryTest(CustomWebApplicationFactory factory, BaseData _fixture.SetRepository(factory); } - [Fact] - public async Task GivenAIdThenReturnOrderWithSuccess() - { - // Arrange - var id = 1; - - // Act - var result = await _fixture!.Repository!.GetByIdAsNoTrackingAsync( - id, - Guid.NewGuid(), - _fixture.CancellationToken, - includes: o => o.Items - ); - - // Assert - Assert.NotNull(result); - Assert.Equal(id, result!.Id); - Assert.NotNull(result.Items); - Assert.NotEmpty(result.Items); - } - [Fact] public async Task GivenAIdThenReturnOrderDtoWithSuccess() { @@ -43,10 +23,9 @@ public async Task GivenAIdThenReturnOrderDtoWithSuccess() var id = 1; // Act - var result = await _fixture!.Repository!.GetByIdAsNoTrackingAsync( - id, - Guid.NewGuid(), - selector: o => new OrderDto() + var result = await _fixture!.Repository!.GetQueryable(Guid.NewGuid()) + .Where(o => o.Id == id) + .Select(o => new OrderDto() { Id = o.Id, Total = o.Total, @@ -56,9 +35,7 @@ public async Task GivenAIdThenReturnOrderDtoWithSuccess() Name = i.Name, Value = i.Value }).ToArray() - }, - _fixture.CancellationToken - ); + }).FirstOrDefaultAsync(_fixture.CancellationToken); // Assert Assert.NotNull(result); @@ -74,31 +51,44 @@ public async Task GivenAOrderAndNotificationShouldExecuteInParallelWithSuccess() var id = 1; // Act - var orderTask = _fixture!.Repository!.GetByIdAsNoTrackingAsync( - id, - Guid.NewGuid(), - _fixture.CancellationToken - ); + var orderTask1 = _fixture!.Repository!.GetQueryable(Guid.NewGuid()) + .Where(o => o.Id == id) + .Select(o => new OrderDto() + { + Id = o.Id, + Total = o.Total, + Items = o.Items.Select(i => new ItemDto + { + Id = i.Id, + Name = i.Name, + Value = i.Value + }).ToArray() + }).FirstOrDefaultAsync(_fixture.CancellationToken); - var notificationTask = _fixture!.Repository!.GetByIdAsNoTrackingAsync( - id, - Guid.NewGuid(), - _fixture.CancellationToken, - newContext: true - ); + var orderTask2 = _fixture!.Repository!.GetQueryable(Guid.NewGuid(), true) + .Where(n => n.Id == id) + .Select(o => new OrderDto() + { + Id = o.Id, + Total = o.Total, + Items = o.Items.Select(i => new ItemDto + { + Id = i.Id, + Name = i.Name, + Value = i.Value + }).ToArray() + }) + .FirstOrDefaultAsync(_fixture.CancellationToken); - await Task.WhenAll(orderTask, notificationTask); - var order = await orderTask; - var notification = await notificationTask; + await Task.WhenAll(orderTask1, orderTask2); + var order1 = await orderTask1; + var order2 = await orderTask2; // Assert - Assert.NotNull(order); - Assert.Equal(id, order!.Id); - - Assert.NotNull(notification); - Assert.Equal(id, notification!.Id); - Assert.NotNull(notification.NotificationType); - Assert.NotNull(notification.NotificationStatus); + Assert.NotNull(order1); + Assert.Equal(id, order1!.Id); + Assert.NotNull(order2); + Assert.Equal(id, order2!.Id); } [Fact] diff --git a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs index 8dc21ee8..404f9666 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs @@ -5,6 +5,7 @@ using IntegrationTests.WebApp.Messaging.Common; using Microsoft.Extensions.DependencyInjection; using WebApp; +using Microsoft.EntityFrameworkCore; namespace IntegrationTests.WebApp.Messaging.Notifications; @@ -39,11 +40,9 @@ public async Task GivenAValidMessageThenPass() // Act await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); - var notification = await _fixture.Repository.FirstOrDefaultAsNoTrackingAsync( - Guid.NewGuid(), - n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus, - _fixture.CancellationToken - ); + var notification = await _fixture.Repository.GetQueryable(Guid.NewGuid()) + .Where(n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus) + .FirstOrDefaultAsync(_fixture.CancellationToken); // Assert Assert.NotNull(notification); @@ -60,11 +59,9 @@ public async Task GivenADuplicateMessageThenShouldNotCreateDuplicatedMessage() await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); - var notifications = await _fixture.Repository.GetByWhereAsNoTrackingAsync( - Guid.NewGuid(), - n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus, - cancellationToken: _fixture.CancellationToken - ); + var notifications = await _fixture.Repository.GetQueryable(Guid.NewGuid()) + .Where(n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus) + .ToListAsync(_fixture.CancellationToken); // Assert Assert.Single(notifications); diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json index c64d0489..53e2fad7 100644 --- a/templates/Full/tests/IntegrationTests/packages.lock.json +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -742,6 +742,7 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", + "Microsoft.EntityFrameworkCore": "[10.0.1, )", "Microsoft.Extensions.Logging": "[10.0.1, )" } }, @@ -763,7 +764,6 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.EntityFrameworkCore": "[10.0.1, )", "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.1, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", diff --git a/templates/Full/tests/UnitTests/packages.lock.json b/templates/Full/tests/UnitTests/packages.lock.json index 5ce4b748..e930406c 100644 --- a/templates/Full/tests/UnitTests/packages.lock.json +++ b/templates/Full/tests/UnitTests/packages.lock.json @@ -84,6 +84,36 @@ "resolved": "18.0.1", "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", "resolved": "10.0.1", @@ -186,6 +216,7 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", + "Microsoft.EntityFrameworkCore": "[10.0.1, )", "Microsoft.Extensions.Logging": "[10.0.1, )" } }, @@ -228,6 +259,18 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" } }, + "Microsoft.EntityFrameworkCore": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", + "Microsoft.Extensions.Caching.Memory": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1" + } + }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", "requested": "[10.0.1, )", From 39f0c9255ac6645836e91249587095c9566cfb23 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 22 Feb 2026 09:29:52 -0300 Subject: [PATCH 066/161] 53 chore: update docker-compose service name to full version --- templates/Full/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 254211c6..c82c113d 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -1,4 +1,4 @@ -name: hexagonal-solution-template +name: hexagonal-solution-template-full services: sqlserver: From a6855c74d1796bb13d93c37a30a8b4dfd4396b87 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 22 Feb 2026 18:49:00 -0300 Subject: [PATCH 067/161] feat(53): add MockQueryable.Moq package and update dependencies --- templates/Full/Directory.Packages.props | 17 +- .../Full/src/Application/packages.lock.json | 86 ++-- templates/Full/src/Domain/packages.lock.json | 12 +- .../src/Infrastructure/packages.lock.json | 312 +++++-------- templates/Full/src/WebApp/packages.lock.json | 44 +- .../tests/IntegrationTests/packages.lock.json | 422 +++++++++--------- .../Full/tests/UnitTests/UnitTests.csproj | 1 + .../Full/tests/UnitTests/packages.lock.json | 134 ++++-- 8 files changed, 490 insertions(+), 538 deletions(-) diff --git a/templates/Full/Directory.Packages.props b/templates/Full/Directory.Packages.props index d60a8ed1..c070cd01 100644 --- a/templates/Full/Directory.Packages.props +++ b/templates/Full/Directory.Packages.props @@ -16,16 +16,17 @@ - - - - - + + + + + - - - + + + + diff --git a/templates/Full/src/Application/packages.lock.json b/templates/Full/src/Application/packages.lock.json index 846649c4..f30a00bd 100644 --- a/templates/Full/src/Application/packages.lock.json +++ b/templates/Full/src/Application/packages.lock.json @@ -20,97 +20,97 @@ }, "Microsoft.EntityFrameworkCore": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.Extensions.Logging": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + "resolved": "10.0.2", + "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + "resolved": "10.0.2", + "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "resolved": "10.0.2", + "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "resolved": "10.0.2", + "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.2", + "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.2", + "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } } } diff --git a/templates/Full/src/Domain/packages.lock.json b/templates/Full/src/Domain/packages.lock.json index e50bae1d..003574ab 100644 --- a/templates/Full/src/Domain/packages.lock.json +++ b/templates/Full/src/Domain/packages.lock.json @@ -4,17 +4,17 @@ "net10.0": { "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" } } } diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json index 4b9aeb36..91088649 100644 --- a/templates/Full/src/Infrastructure/packages.lock.json +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -4,37 +4,35 @@ "net10.0": { "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "0VcVx+hIo7j6bCjTlgPB1D4vHyLdkY3pVmlcsvRR8APEr0vRQ+Nj2Q3qYXTUeHgp8gdBxQFDVCfcAXknevD2KQ==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "tBwpyZ75SqcRjmZyhLYJVGKyfz1Z1dmsYtvJGZ2QLSI1tOHgMuLPTovunHA1JF9a0EWJs+WB6tVWmqo3DTL/MQ==", "dependencies": { "Humanizer.Core": "2.14.1", - "Microsoft.Build.Framework": "17.14.28", - "Microsoft.Build.Tasks.Core": "17.14.28", - "Microsoft.Build.Utilities.Core": "17.14.28", - "Microsoft.CodeAnalysis.CSharp": "4.14.0", - "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.14.0", - "Microsoft.CodeAnalysis.Workspaces.MSBuild": "4.14.0", - "Microsoft.EntityFrameworkCore.Relational": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyModel": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Build.Framework": "18.0.2", + "Microsoft.CodeAnalysis.CSharp": "5.0.0", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "5.0.0", + "Microsoft.CodeAnalysis.Workspaces.MSBuild": "5.0.0", + "Microsoft.EntityFrameworkCore.Relational": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.DependencyModel": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", "Mono.TextTemplating": "3.0.0", "Newtonsoft.Json": "13.0.3" } }, "Microsoft.EntityFrameworkCore.SqlServer": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "Uz/M/Y2UPWJ+wJV8JGb7gp5H6Q8oE+/Fz/YZ5osBNDIxrlT+/I7AraVKxErSlz/nZq5JVy+jL1znnIcgI0G/Hw==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "x4ZO21dxzUeA2wvH5sdNBVVHuH+9+3DF3VV2pdENyHEZz+u0AyMi1a/NHzytPhn6IXBhqA8n6y9FAsdUWp8jyA==", "dependencies": { "Microsoft.Data.SqlClient": "6.1.1", - "Microsoft.EntityFrameworkCore.Relational": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore.Relational": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.Extensions.Caching.Hybrid": { @@ -51,13 +49,13 @@ }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "WEx0VM6KVv4Bf6lwe4WQTd4EixIfw38ZU3u/7zMe+uC5fOyiANu8Os/qyiqv2iEsIJb296tbd2E2BTaWIha3Vg==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Caching.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", "StackExchange.Redis": "2.7.27" } }, @@ -185,52 +183,10 @@ "resolved": "9.0.4", "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" }, - "Microsoft.Build": { - "type": "Transitive", - "resolved": "17.7.2", - "contentHash": "AmWnumxsMiRycFfE3kq/XnFFTAoPpCWl3UuiKQWCa5Z0+hBKVoiydzS2iXJGd3x+jry+qaTR9GzoezjV9NFT5A==", - "dependencies": { - "Microsoft.Build.Framework": "17.7.2", - "Microsoft.NET.StringTools": "17.7.2", - "System.Configuration.ConfigurationManager": "7.0.0", - "System.Reflection.MetadataLoadContext": "7.0.0", - "System.Security.Permissions": "7.0.0" - } - }, "Microsoft.Build.Framework": { "type": "Transitive", - "resolved": "17.14.28", - "contentHash": "wRcyTzGV0LRAtFdrddtioh59Ky4/zbvyraP0cQkDzRSRkhgAQb0K88D/JNC6VHLIXanRi3mtV1jU0uQkBwmiVg==" - }, - "Microsoft.Build.Tasks.Core": { - "type": "Transitive", - "resolved": "17.14.28", - "contentHash": "jk3O0tXp9QWPXhLJ7Pl8wm/eGtGgA1++vwHGWEmnwMU6eP//ghtcCUpQh9CQMwEKGDnH0aJf285V1s8yiSlKfQ==", - "dependencies": { - "Microsoft.Build.Framework": "17.14.28", - "Microsoft.Build.Utilities.Core": "17.14.28", - "Microsoft.NET.StringTools": "17.14.28", - "System.CodeDom": "9.0.0", - "System.Configuration.ConfigurationManager": "9.0.0", - "System.Diagnostics.EventLog": "9.0.0", - "System.Formats.Nrbf": "9.0.0", - "System.Resources.Extensions": "9.0.0", - "System.Security.Cryptography.Pkcs": "9.0.0", - "System.Security.Cryptography.ProtectedData": "9.0.0", - "System.Security.Cryptography.Xml": "9.0.0" - } - }, - "Microsoft.Build.Utilities.Core": { - "type": "Transitive", - "resolved": "17.14.28", - "contentHash": "rhSdPo8QfLXXWM+rY0x0z1G4KK4ZhMoIbHROyDj8MUBFab9nvHR0NaMnjzOgXldhmD2zi2ir8d6xCatNzlhF5g==", - "dependencies": { - "Microsoft.Build.Framework": "17.14.28", - "Microsoft.NET.StringTools": "17.14.28", - "System.Configuration.ConfigurationManager": "9.0.0", - "System.Diagnostics.EventLog": "9.0.0", - "System.Security.Cryptography.ProtectedData": "9.0.0" - } + "resolved": "18.0.2", + "contentHash": "sOSb+0J4G/jCBW/YqmRuL0eOMXgfw1KQLdC9TkbvfA5xs7uNm+PBQXJCOzSJGXtZcZrtXozcwxPmUiRUbmd7FA==" }, "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", @@ -239,73 +195,62 @@ }, "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "resolved": "5.0.0", + "contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0" } }, "Microsoft.CodeAnalysis.CSharp": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "resolved": "5.0.0", + "contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==", "dependencies": { "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.Common": "[4.14.0]" + "Microsoft.CodeAnalysis.Common": "[5.0.0]" } }, "Microsoft.CodeAnalysis.CSharp.Workspaces": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "QkgCEM4qJo6gdtblXtNgHqtykS61fxW+820hx5JN6n9DD4mQtqNB+6fPeJ3GQWg6jkkGz6oG9yZq7H3Gf0zwYw==", + "resolved": "5.0.0", + "contentHash": "Al/Q8B+yO8odSqGVpSvrShMFDvlQdIBU//F3E6Rb0YdiLSALE9wh/pvozPNnfmh5HDnvU+mkmSjpz4hQO++jaA==", "dependencies": { "Humanizer.Core": "2.14.1", "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.CSharp": "[4.14.0]", - "Microsoft.CodeAnalysis.Common": "[4.14.0]", - "Microsoft.CodeAnalysis.Workspaces.Common": "[4.14.0]", + "Microsoft.CodeAnalysis.CSharp": "[5.0.0]", + "Microsoft.CodeAnalysis.Common": "[5.0.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[5.0.0]", "System.Composition": "9.0.0" } }, "Microsoft.CodeAnalysis.Workspaces.Common": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "wNVK9JrqjqDC/WgBUFV6henDfrW87NPfo98nzah/+M/G1D6sBOPtXwqce3UQNn+6AjTnmkHYN1WV9XmTlPemTw==", + "resolved": "5.0.0", + "contentHash": "ZbUmIvT6lqTNKiv06Jl5wf0MTMi1vQ1oH7ou4CLcs2C/no/L7EhP3T8y3XXvn9VbqMcJaJnEsNA1jwYUMgc5jg==", "dependencies": { "Humanizer.Core": "2.14.1", "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.Common": "[4.14.0]", + "Microsoft.CodeAnalysis.Common": "[5.0.0]", "System.Composition": "9.0.0" } }, "Microsoft.CodeAnalysis.Workspaces.MSBuild": { "type": "Transitive", - "resolved": "4.14.0", - "contentHash": "YU7Sguzm1Cuhi2U6S0DRKcVpqAdBd2QmatpyE0KqYMJogJ9E27KHOWGUzAOjsyjAM7sNaUk+a8VPz24knDseFw==", + "resolved": "5.0.0", + "contentHash": "/G+LVoAGMz6Ae8nm+PGLxSw+F5RjYx/J7irbTO5uKAPw1bxHyQJLc/YOnpDxt+EpPtYxvC9wvBsg/kETZp1F9Q==", "dependencies": { "Humanizer.Core": "2.14.1", - "Microsoft.Build": "17.7.2", - "Microsoft.Build.Framework": "17.7.2", - "Microsoft.Build.Tasks.Core": "17.7.2", - "Microsoft.Build.Utilities.Core": "17.7.2", + "Microsoft.Build.Framework": "17.11.31", "Microsoft.CodeAnalysis.Analyzers": "3.11.0", - "Microsoft.CodeAnalysis.Workspaces.Common": "[4.14.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[5.0.0]", "Microsoft.Extensions.DependencyInjection": "9.0.0", "Microsoft.Extensions.Logging": "9.0.0", "Microsoft.Extensions.Logging.Abstractions": "9.0.0", "Microsoft.Extensions.Options": "9.0.0", "Microsoft.Extensions.Primitives": "9.0.0", + "Microsoft.VisualStudio.SolutionPersistence": "1.0.52", "Newtonsoft.Json": "13.0.3", - "System.CodeDom": "7.0.0", - "System.Composition": "9.0.0", - "System.Configuration.ConfigurationManager": "9.0.0", - "System.Diagnostics.EventLog": "9.0.0", - "System.Resources.Extensions": "9.0.0", - "System.Security.Cryptography.Pkcs": "7.0.2", - "System.Security.Cryptography.ProtectedData": "9.0.0", - "System.Security.Cryptography.Xml": "7.0.1", - "System.Security.Permissions": "9.0.0", - "System.Windows.Extensions": "9.0.0" + "System.Composition": "9.0.0" } }, "Microsoft.Data.SqlClient": { @@ -332,43 +277,43 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + "resolved": "10.0.2", + "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + "resolved": "10.0.2", + "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "8/5kYGwKN6wtc89QqcPTOZDAJSMX8MzKCf5OmYjIfAHWTfsUEpGKYrdtfNk4X36rQ0BiU3n57Y4rbtnerzJN0Q==", + "resolved": "10.0.2", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "resolved": "10.0.2", + "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "resolved": "10.0.2", + "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration": { @@ -382,10 +327,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "resolved": "10.0.2", + "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -399,13 +344,13 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "IiWPd4j8JLNjSkyXl5hvJwX2ZENDVQVPDHYgZmYdw8+YkY2xp9iQt0vjdnAQZLpo/ipeW1xgOqfSBEnivKWPYQ==" + "resolved": "10.0.2", + "contentHash": "DHXzj/FKmUDmhzw7AHbMXbNNcgO8Cb9PDbYOUDICjFAdIALy2WbC6pm+dFKiAUNtcYMucZu2iMVw1ef/sdcZFw==" }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", @@ -438,10 +383,10 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging.Configuration": { @@ -461,11 +406,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.2", + "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -482,8 +427,8 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.2", + "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, "Microsoft.Identity.Client": { "type": "Transitive", @@ -548,16 +493,16 @@ "Microsoft.IdentityModel.Logging": "7.7.1" } }, - "Microsoft.NET.StringTools": { - "type": "Transitive", - "resolved": "17.14.28", - "contentHash": "DMIeWDlxe0Wz0DIhJZ2FMoGQAN2yrGZOi5jjFhRYHWR5ONd0CS6IpAHlRnA7uA/5BF+BADvgsETxW2XrPiFc1A==" - }, "Microsoft.SqlServer.Server": { "type": "Transitive", "resolved": "1.0.0", "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" }, + "Microsoft.VisualStudio.SolutionPersistence": { + "type": "Transitive", + "resolved": "1.0.52", + "contentHash": "oNv2JtYXhpdJrX63nibx1JT3uCESOBQ1LAk7Dtz/sr0+laW0KRM6eKp4CZ3MHDR2siIkKsY8MmUkeP5DKkQQ5w==" + }, "Mono.TextTemplating": { "type": "Transitive", "resolved": "3.0.0", @@ -615,8 +560,8 @@ }, "System.CodeDom": { "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "oTE5IfuMoET8yaZP/vdvy9xO47guAv/rOhe4DODuFBN3ySprcQOlXqO3j+e/H/YpKKR5sglrxRaZ2HYOhNJrqA==" + "resolved": "6.0.0", + "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA==" }, "System.Composition": { "type": "Transitive", @@ -680,11 +625,6 @@ "resolved": "9.0.4", "contentHash": "getRQEXD8idlpb1KW56XuxImMy0FKp2WJPDf3Qr0kI/QKxxJSftqfDFVo0DZ3HCJRLU73qHSruv5q2l5O47jQQ==" }, - "System.Formats.Nrbf": { - "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "F/6tNE+ckmdFeSQAyQo26bQOqfPFKEfZcuqnp4kBE6/7jP26diP+QTHCJJ6vpEfaY6bLy+hBLiIQUSxSmNwLkA==" - }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "7.7.1", @@ -699,19 +639,6 @@ "resolved": "8.0.1", "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" }, - "System.Reflection.MetadataLoadContext": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "z9PvtMJra5hK8n+g0wmPtaG7HQRZpTmIPRw5Z0LEemlcdQMHuTD5D7OAY/fZuuz1L9db++QOcDF0gJTLpbMtZQ==" - }, - "System.Resources.Extensions": { - "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "tvhuT1D2OwPROdL1kRWtaTJliQo0WdyhvwDpd8RM997G7m3Hya5nhbYhNTS75x6Vu+ypSOgL5qxDCn8IROtCxw==", - "dependencies": { - "System.Formats.Nrbf": "9.0.0" - } - }, "System.Security.Cryptography.Pkcs": { "type": "Transitive", "resolved": "9.0.4", @@ -722,46 +649,25 @@ "resolved": "9.0.4", "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" }, - "System.Security.Cryptography.Xml": { - "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "GQZn5wFd+pyOfwWaCbqxG7trQ5ox01oR8kYgWflgtux4HiUNihGEgG2TktRWyH+9bw7NoEju1D41H/upwQeFQw==", - "dependencies": { - "System.Security.Cryptography.Pkcs": "9.0.0" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "H2VFD4SFVxieywNxn9/epb63/IOcPPfA0WOtfkljzNfu7GCcHIBQNuwP6zGCEIi7Ci/oj8aLPUNK9sYImMFf4Q==", - "dependencies": { - "System.Windows.Extensions": "9.0.0" - } - }, "System.Threading.RateLimiting": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "9.0.0", - "contentHash": "U9msthvnH2Fsw7xwAvIhNHOdnIjOQTwOc8Vd0oGOsiRcGMGoBFlUD6qtYawRUoQdKH9ysxesZ9juFElt1Jw/7A==" - }, "application": { "type": "Project", "dependencies": { "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.1, )", - "Microsoft.Extensions.Logging": "[10.0.1, )" + "Microsoft.EntityFrameworkCore": "[10.0.2, )", + "Microsoft.Extensions.Logging": "[10.0.2, )" } }, "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" } }, "FluentValidation": { @@ -782,34 +688,34 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Newtonsoft.Json": { diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json index 4f82b597..0109119f 100644 --- a/templates/Full/src/WebApp/packages.lock.json +++ b/templates/Full/src/WebApp/packages.lock.json @@ -163,20 +163,20 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + "resolved": "10.0.2", + "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + "resolved": "10.0.2", + "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "8/5kYGwKN6wtc89QqcPTOZDAJSMX8MzKCf5OmYjIfAHWTfsUEpGKYrdtfNk4X36rQ0BiU3n57Y4rbtnerzJN0Q==", + "resolved": "10.0.2", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.1" + "Microsoft.EntityFrameworkCore": "10.0.2" } }, "Microsoft.Identity.Client": { @@ -327,7 +327,7 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.1, )" + "Microsoft.EntityFrameworkCore": "[10.0.2, )" } }, "domain": { @@ -337,9 +337,9 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.1, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", - "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", @@ -368,22 +368,22 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2" } }, "Microsoft.EntityFrameworkCore.SqlServer": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "Uz/M/Y2UPWJ+wJV8JGb7gp5H6Q8oE+/Fz/YZ5osBNDIxrlT+/I7AraVKxErSlz/nZq5JVy+jL1znnIcgI0G/Hw==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "x4ZO21dxzUeA2wvH5sdNBVVHuH+9+3DF3VV2pdENyHEZz+u0AyMi1a/NHzytPhn6IXBhqA8n6y9FAsdUWp8jyA==", "dependencies": { "Microsoft.Data.SqlClient": "6.1.1", - "Microsoft.EntityFrameworkCore.Relational": "10.0.1" + "Microsoft.EntityFrameworkCore.Relational": "10.0.2" } }, "Microsoft.Extensions.Caching.Hybrid": { @@ -394,9 +394,9 @@ }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "WEx0VM6KVv4Bf6lwe4WQTd4EixIfw38ZU3u/7zMe+uC5fOyiANu8Os/qyiqv2iEsIJb296tbd2E2BTaWIha3Vg==", "dependencies": { "StackExchange.Redis": "2.7.27" } diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json index 53e2fad7..2adc5196 100644 --- a/templates/Full/tests/IntegrationTests/packages.lock.json +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -10,20 +10,20 @@ }, "Microsoft.AspNetCore.Mvc.Testing": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "84aSUoB++qrL9mlkAT1ybV9KQ5bv7sbpx2B5uo9se0ryYjNPIeiuknVy7r0FOwRk8T58PYybhIBa7WOkdMgOZQ==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "wXZUGX1hphnKUKEoi7nBuMjT6UOWJmkw4n424O44WSS20QPFYI9D/UiTkgog6S1N6sKFevGF2JmlcSGvKJH2ng==", "dependencies": { - "Microsoft.AspNetCore.TestHost": "10.0.1", - "Microsoft.Extensions.DependencyModel": "10.0.1", - "Microsoft.Extensions.Hosting": "10.0.1" + "Microsoft.AspNetCore.TestHost": "10.0.2", + "Microsoft.Extensions.DependencyModel": "10.0.2", + "Microsoft.Extensions.Hosting": "10.0.2" } }, "Microsoft.AspNetCore.TestHost": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "Vvos4CyBam5dCsH3eD1c9MQI4ESWwzNSJsToFz4i6NmfPsaySzNSiv0QYRmSAAIBXb8GXxPmuy42TkIrw2xCzQ==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "LhgZh1EiJ8crslosaxgWUK4pA0xwwGsptYmmxFtZixNpTvkby4pjR1jVBt8vqGKkDeI91SLs051xowFg96+cgw==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", @@ -185,150 +185,150 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + "resolved": "10.0.2", + "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + "resolved": "10.0.2", + "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "8/5kYGwKN6wtc89QqcPTOZDAJSMX8MzKCf5OmYjIfAHWTfsUEpGKYrdtfNk4X36rQ0BiU3n57Y4rbtnerzJN0Q==", + "resolved": "10.0.2", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "resolved": "10.0.2", + "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "resolved": "10.0.2", + "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "resolved": "10.0.2", + "contentHash": "Lws+o4DFw6p5NquRoYA3d5QVvi49ugNw7TxbW4QGLsL8F1LCCyJqWFy0+RMQ/hzUuS9aKV5NJ/XGAF5N9/RQcQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "resolved": "10.0.2", + "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "resolved": "10.0.2", + "contentHash": "/SdW50prUuenglSy7MXU3eVQkOk4/J4fjc+GIhv4NkTmaZOQyTqpVAYi8nRjNtOKHzCy7g5cSlOSgkbT7clLwQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Configuration.CommandLine": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "s5cxcdtIig66YT3J+7iHflMuorznK8kXuwBBPHMp4KImx5ZGE3FRa1Nj9fI/xMwFV+KzUMjqZ2MhOedPH8LiBQ==", + "resolved": "10.0.2", + "contentHash": "KhqLwsao0b40VNgWPdg/UdaW4mxHhrthTngL0G8wSb2YkNSGTrs2RrBzkouiw1eTeQfhYcy0tpGy5ETCO+xjfg==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "csD8Eps3HQ3yc0x6NhgTV+aIFKSs3qVlVCtFnMHz/JOjnv7eEj/qSXKXiK9LzBzB1qSfAVqFnB5iaX2nUmagIQ==", + "resolved": "10.0.2", + "contentHash": "Y+wv67KiW7q0+PxAg3osjohrddgxl/7mMriRZPBcYUgOzOq/O2rTvAXbuy1vINIarT2YmENfePaDcu2KgvIDXw==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "N/6GiwiZFCBFZDk3vg1PhHW3zMqqu5WWpmeZAA9VTXv7Q8pr8NZR/EQsH0DjzqydDksJtY6EQBsu81d5okQOlA==", + "resolved": "10.0.2", + "contentHash": "6vStNVa/7hcT6VrYvVMGCWUl/QIKwNeQaSGnKw1E4RPpZbQbOjDsATCbrQUa0sFUs7LW8T9aZ2NBKttMz1+WuA==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Physical": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", + "Microsoft.Extensions.FileProviders.Physical": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "0zW3eYBJlRctmgqk5s0kFIi5o5y2g80mvGCD8bkYxREPQlKUnr0ndU/Sop+UDIhyWN0fIi4RW63vo7BKTi7ncA==", + "resolved": "10.0.2", + "contentHash": "ovjOVr+rNxOT249iezwihlPNMaIJdBC6PMGeMnzhkm5EoKJWFjp3mmvtndfHY6A88X4wulXlidMhmjX8v6V/aw==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "ULEJ0nkaW90JYJGkFujPcJtADXcJpXiSOLbokPcWJZ8iDbtDINifEYAUVqZVr81IDNTrRFul6O8RolOKOsgFPg==", + "resolved": "10.0.2", + "contentHash": "4Doiw8ILZz60D3WuoKRPoUjcNO6a5tP+D1T3L4SdVm/AC8VcyEYgUVbpzu+XPCzstM2oPefLvOGXMde8C8UAKg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Json": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Physical": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Json": "10.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", + "Microsoft.Extensions.FileProviders.Physical": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "IiWPd4j8JLNjSkyXl5hvJwX2ZENDVQVPDHYgZmYdw8+YkY2xp9iQt0vjdnAQZLpo/ipeW1xgOqfSBEnivKWPYQ==" + "resolved": "10.0.2", + "contentHash": "DHXzj/FKmUDmhzw7AHbMXbNNcgO8Cb9PDbYOUDICjFAdIALy2WbC6pm+dFKiAUNtcYMucZu2iMVw1ef/sdcZFw==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", + "resolved": "10.0.2", + "contentHash": "+2lv/hi6VGnaJt4877BFkkySiMiHrKCeX7K5ElIshIKpF65o63zeXMnUR4U6EQ/eM75Hx7T8s3RcqnjdcnVB1A==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.2" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", + "resolved": "10.0.2", + "contentHash": "GaiaeKUuLuUbRPkUokndDuzonhO6dk4lcfGflHsCeXiJ5JrZxcyks1KuG6eB9pON16x/+9uWfa4w9g3oP8AYvQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { @@ -349,66 +349,66 @@ }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", + "resolved": "10.0.2", + "contentHash": "+r/eJ+slW/EmwWmH3En4gzRg1k6+yTqexoHBrMuz5fxsIKJA8MDiSGepjw/ko3XyNqg+w3dxQe+huoVXs5XDJw==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "4bxzGXIzZnz0Bf7czQ72jGvpOqJsRW/44PS7YLFXTTnu6cNcPvmSREDvBoH0ZVP2hAbMfL4sUoCUn54k70jPWw==", + "resolved": "10.0.2", + "contentHash": "4+ypApaugtHIz5Q2Z3oC4+erDbOgy0HrMFYS3Nm3qmTXyqK7sU7LJWY9gci99Wcx6j7ivgk8kdCkgmvsA4t0Ow==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileSystemGlobbing": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "49dFvGJjLSwGn76eHnP1gBvCJkL8HRYpCrG0DCvsP6wRpEQRLN2Fq8rTxbP+6jS7jmYKCnSVO5C65v4mT3rzeA==" + "resolved": "10.0.2", + "contentHash": "XozoMaWcFIv1tv0LDF+YMeZYjiNiNIewpNdZ3TEoVGf8ROrp0hVoEdUyUBsI8oYGM5U3Z5hiNEv0j2Z5COnMgg==" }, "Microsoft.Extensions.Hosting": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "0jjfjQSOFZlHhwOoIQw0WyzxtkDMYdnPY3iFrOLasxYq/5J4FDt1HWT8TktBclOVjFY1FOOkoOc99X7AhbqSIw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.Configuration.CommandLine": "10.0.1", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", - "Microsoft.Extensions.Configuration.Json": "10.0.1", - "Microsoft.Extensions.Configuration.UserSecrets": "10.0.1", - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Diagnostics": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Physical": "10.0.1", - "Microsoft.Extensions.Hosting.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Configuration": "10.0.1", - "Microsoft.Extensions.Logging.Console": "10.0.1", - "Microsoft.Extensions.Logging.Debug": "10.0.1", - "Microsoft.Extensions.Logging.EventLog": "10.0.1", - "Microsoft.Extensions.Logging.EventSource": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "resolved": "10.0.2", + "contentHash": "qUU5XAayGD0wySbu9ntSLCjM3gudmDLzuWVBbnMKlIbdgqhr0dC1bfULpFnPCVSQsU5TaSblU1qShFyTRwSjrQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Binder": "10.0.2", + "Microsoft.Extensions.Configuration.CommandLine": "10.0.2", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.2", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.2", + "Microsoft.Extensions.Configuration.Json": "10.0.2", + "Microsoft.Extensions.Configuration.UserSecrets": "10.0.2", + "Microsoft.Extensions.DependencyInjection": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Diagnostics": "10.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", + "Microsoft.Extensions.FileProviders.Physical": "10.0.2", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Configuration": "10.0.2", + "Microsoft.Extensions.Logging.Console": "10.0.2", + "Microsoft.Extensions.Logging.Debug": "10.0.2", + "Microsoft.Extensions.Logging.EventLog": "10.0.2", + "Microsoft.Extensions.Logging.EventSource": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", + "resolved": "10.0.2", + "contentHash": "CeAAPVOtI/wtBcHOwq6Pw3VPdGi+pNaGHZj6vfXX/5zr8beO9SyL7IOCSQ70BauFTAFS0QF7f6zu2A6hC8D6nw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.2", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Http": { @@ -426,98 +426,98 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", + "resolved": "10.0.2", + "contentHash": "XVtNJfLZVTDmQS5RCUjIr7QEAgGhJ3yQ0L3PduN7rE4aijmqYl0pIF09ZSU8jgnxml91Mw59ze220g8S7anaOg==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Binder": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.2" } }, "Microsoft.Extensions.Logging.Console": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", + "resolved": "10.0.2", + "contentHash": "Z6gBfpHqJsz2hGH+eUQUQI+DSHsDNhTKt8toHAtDhYFRlxUN1FKPKzNmTgSrAz1gtDTOBjDU5lQZ50463Ehw7A==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Configuration": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Configuration": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Microsoft.Extensions.Logging.Debug": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "VqfTvbX9C6BA0VeIlpzPlljnNsXxiI5CdUHb9ksWERH94WQ6ft3oLGUAa4xKcDGu4xF+rIZ8wj7IOAd6/q7vGw==", + "resolved": "10.0.2", + "contentHash": "LohXg8Uc+cGLzPhUTgwOR9Z/lqMFpDT+NylPFwT6yXZfJAm/QE+Gv14HNir9tfposwGl5IuR+3yfLOuc6PNHVg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging.EventLog": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Zp9MM+jFCa7oktIug62V9eNygpkf+6kFVatF+UC/ODeUwIr5givYKy8fYSSI9sWdxqDqv63y1x0mm2VjOl8GOw==", + "resolved": "10.0.2", + "contentHash": "gRQ3bpTZRCr2Z3Qx4RNP6G23XnJMXmRCsIgWg542B/cp/Rz8Jv3nfINVNmeOCy4aGQ65VfHtoi6Rd8RzoG5a5g==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "System.Diagnostics.EventLog": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "System.Diagnostics.EventLog": "10.0.2" } }, "Microsoft.Extensions.Logging.EventSource": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "WnFvZP+Y+lfeNFKPK/+mBpaCC7EeBDlobrQOqnP7rrw/+vE7yu8Rjczum1xbC0F/8cAHafog84DMp9200akMNQ==", + "resolved": "10.0.2", + "contentHash": "PSn5a9GF1TmdlvsSnBsddcl1Qcrk/mfJbyXEV+RNJTV5GC/Tkm8DI344C/3LXy/YG1r+aIyjPB6/FlWoCmvEdA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.2", + "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "resolved": "10.0.2", + "contentHash": "8njGDg0OdDBM4Zox0ybuUOJZkQ8HcH49F+POZBlG+nsfzEyqOCHyHEkWeRVI62qsssiugUVEKqUttT1ZbV0aJQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Binder": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.2", + "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, "Microsoft.Identity.Client": { "type": "Transitive", @@ -672,8 +672,8 @@ }, "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "xfaHEHVDkMOOZR5S6ZGezD0+vekdH1Nx/9Ih8/rOqOGSOk1fxiN3u94bYkBW/wigj0Uw2Wt3vvRj9mtYdgwEjw==" + "resolved": "10.0.2", + "contentHash": "TJPpTLF5MFPobq09c9BQ5X8QuviQfsKvH0Jbm7MkGylGvfIdRqJQLZDPC5sMRFkk9aZhmgir1NJKekip2NxfaA==" }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", @@ -742,8 +742,8 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.1, )", - "Microsoft.Extensions.Logging": "[10.0.1, )" + "Microsoft.EntityFrameworkCore": "[10.0.2, )", + "Microsoft.Extensions.Logging": "[10.0.2, )" } }, "commontests": { @@ -757,16 +757,16 @@ "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" } }, "infrastructure": { "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.1, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", - "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", @@ -866,27 +866,27 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.EntityFrameworkCore.SqlServer": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "Uz/M/Y2UPWJ+wJV8JGb7gp5H6Q8oE+/Fz/YZ5osBNDIxrlT+/I7AraVKxErSlz/nZq5JVy+jL1znnIcgI0G/Hw==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "x4ZO21dxzUeA2wvH5sdNBVVHuH+9+3DF3VV2pdENyHEZz+u0AyMi1a/NHzytPhn6IXBhqA8n6y9FAsdUWp8jyA==", "dependencies": { "Microsoft.Data.SqlClient": "6.1.1", - "Microsoft.EntityFrameworkCore.Relational": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore.Relational": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.Extensions.Caching.Hybrid": { @@ -903,34 +903,34 @@ }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "WEx0VM6KVv4Bf6lwe4WQTd4EixIfw38ZU3u/7zMe+uC5fOyiANu8Os/qyiqv2iEsIJb296tbd2E2BTaWIha3Vg==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Caching.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", "StackExchange.Redis": "2.7.27" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Newtonsoft.Json": { diff --git a/templates/Full/tests/UnitTests/UnitTests.csproj b/templates/Full/tests/UnitTests/UnitTests.csproj index f7de22de..25373a55 100644 --- a/templates/Full/tests/UnitTests/UnitTests.csproj +++ b/templates/Full/tests/UnitTests/UnitTests.csproj @@ -9,6 +9,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/templates/Full/tests/UnitTests/packages.lock.json b/templates/Full/tests/UnitTests/packages.lock.json index e930406c..0cbf03b3 100644 --- a/templates/Full/tests/UnitTests/packages.lock.json +++ b/templates/Full/tests/UnitTests/packages.lock.json @@ -28,6 +28,16 @@ "Microsoft.TestPlatform.TestHost": "18.0.1" } }, + "MockQueryable.Moq": { + "type": "Direct", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "3qrQC5f4fYab1ttnwxfQ52bdc8bSRyRdKq6sqLJK/WnTw7+KY3DiyT7iIgUX054YiKIHeCxXvqR9OoH976NI6A==", + "dependencies": { + "MockQueryable.EntityFrameworkCore": "10.0.2", + "Moq": "4.20.72" + } + }, "Moq": { "type": "Direct", "requested": "[4.20.72, )", @@ -86,60 +96,79 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "sp6Uq8Oc3RVGtmxLS3ZgzVeFHrpLNymsMudoeqRmB9pRTWgvq2S903sF5OnaaZmh4Bz6kpq7FwofE+DOhKJYvg==" + "resolved": "10.0.2", + "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Ug2lxkiz1bpnxQF/xdswLl5EBA6sAG2ig5nMjmtpQZO0C88ZnvUkbpH2vQq+8ultIRmvp5Ec2jndLGRMPjW0Ew==" + "resolved": "10.0.2", + "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.2", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" + } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "resolved": "10.0.2", + "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "resolved": "10.0.2", + "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.2", + "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.2", + "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.2", + "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -160,6 +189,21 @@ "Newtonsoft.Json": "13.0.3" } }, + "MockQueryable.Core": { + "type": "Transitive", + "resolved": "10.0.2", + "contentHash": "cIxlylfWTobgbj43B8GJnjjzlOq7aJDCiUtOlDBSB9oBzlaElihmq3qO7o/LP5YxPz6HeMZEKY7uMwlJe4CR/g==" + }, + "MockQueryable.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "10.0.2", + "contentHash": "CuVbsrABnV9J91NbHWXu13d0jE4L1sn3dgwTOHLoy1JKnKHohSV0ssHBuGibmWEHBdAStlxORIImxY+qmRm0uQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.2", + "Microsoft.EntityFrameworkCore.Relational": "10.0.2", + "MockQueryable.Core": "10.0.2" + } + }, "Mono.Cecil": { "type": "Transitive", "resolved": "0.11.3", @@ -216,8 +260,8 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.1, )", - "Microsoft.Extensions.Logging": "[10.0.1, )" + "Microsoft.EntityFrameworkCore": "[10.0.2, )", + "Microsoft.Extensions.Logging": "[10.0.2, )" } }, "commontests": { @@ -231,7 +275,7 @@ "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.1, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" } }, "AutoFixture": { @@ -261,34 +305,34 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "QsXvZ8G7Dl7x09rlq0b2dye7QqfReMq8yGdl7Mffi3Ip+aTa+JUMixBZ4lhCs9Ygjz2e9tiUACstxI+ADkwaFg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.1", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Newtonsoft.Json": { From 08932a6113b35b87072f88c66850be63da11ff51 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 22 Feb 2026 18:49:37 -0300 Subject: [PATCH 068/161] feat(53): enhance order and notification use cases with queryables --- .../src/Application/Orders/GetOrderUseCase.cs | 7 +++ .../Common/BaseApplicationFixture.cs | 4 +- .../Common/RepositoryMockExtensions.cs | 47 +++---------------- .../GetNotificationUseCaseTest.cs | 22 ++++----- .../Orders/GetOrderUseCaseTests.cs | 42 ++++++++++------- 5 files changed, 52 insertions(+), 70 deletions(-) diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index cc60de82..e6926686 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -30,6 +30,13 @@ CancellationToken cancellationToken Id = o.Id, Description = o.Description, Total = o.Total, + Items = o.Items.Select(i => new ItemDto + { + Id = i.Id, + Name = i.Name, + Description = i.Description, + Value = i.Value + }).ToList() }).FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken); if (order is null) diff --git a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs index cbd803b0..1ebf45e9 100644 --- a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs +++ b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs @@ -40,8 +40,8 @@ public void MockServiceProviderServices() .Returns(MockLogger.Object); MockServiceProvider - .Setup(r => r.GetService(typeof(IValidator))) - .Returns(MockValidator.Object); + .Setup(r => r.GetService(typeof(IValidator))) + .Returns(MockValidator.Object); MockServiceProvider .Setup(r => r.GetService(typeof(IHybridCacheService))) diff --git a/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs b/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs index 6473224a..54dbe057 100644 --- a/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs +++ b/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs @@ -1,6 +1,7 @@ using System.Linq.Expressions; using Application.Common.Repositories; using Domain.Common; +using MockQueryable; namespace UnitTests.Application.Common; @@ -14,39 +15,8 @@ public static void SetFailedAddAsync(this Mock mockRep .Setup(d => d.AddAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(0); - public static void SetupGetByIdAsNoTrackingAsync(this Mock mockRepository, TEntity entity) where TEntity : DomainEntity => mockRepository - .Setup(r => r.GetByIdAsNoTrackingAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>[]>() - )).ReturnsAsync(entity); - - public static void SetupGetByIdAsNoTrackingAsync(this Mock mockRepository, TResult result) where TEntity : DomainEntity => mockRepository - .Setup(r => r.GetByIdAsNoTrackingAsync( - It.IsAny(), - It.IsAny(), - It.IsAny>>(), - It.IsAny() - )).ReturnsAsync(result); - - public static void SetupGetByIdAsNoTrackingAsyncNotFound(this Mock mockRepository) where TEntity : DomainEntity => mockRepository - .Setup(r => r.GetByIdAsNoTrackingAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>[]>() - )).ReturnsAsync((TEntity) null!); - - public static void SetupGetByIdAsNoTrackingAsyncNotFound(this Mock mockRepository) where TEntity : DomainEntity => mockRepository - .Setup(r => r.GetByIdAsNoTrackingAsync( - It.IsAny(), - It.IsAny(), - It.IsAny>>(), - It.IsAny() - )); + public static void SetupQueryable(this Mock mockRepository, ICollection entities) where TEntity : DomainEntity => mockRepository + .Setup(r => r.GetQueryable(It.IsAny(), It.IsAny(), It.IsAny())).Returns(entities.BuildMock()); public static void VerifyAddAsync(this Mock mockRepository, int times) where TEntity : DomainEntity => mockRepository.Verify( d => d.AddAsync(It.IsAny(), It.IsAny(), It.IsAny()), @@ -94,7 +64,7 @@ public static void SetInvalidGetAllPaginatedAsync(this Mock>>() )).ReturnsAsync(([], 0)); - public static void VerifyGetAllPaginatedNoIncludes(this Mock mockRepository, int times) where TEntity : DomainEntity where TResult : class => mockRepository + public static void VerifyGetAllPaginatedNoIncludes(this Mock mockRepository, int times = 1) where TEntity : DomainEntity where TResult : class => mockRepository .Verify(r => r.GetAllPaginatedAsync( It.IsAny(), It.IsAny(), @@ -107,12 +77,7 @@ public static void VerifyGetAllPaginatedNoIncludes(this Mock>>() ), Times.Exactly(times)); - public static void VerifyGetByIdAsync(this Mock mockRepository, int times) where TEntity : DomainEntity => mockRepository - .Verify(r => r.GetByIdAsNoTrackingAsync( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>[]>() + public static void VerifyQueryable(this Mock mockRepository, int times = 1) where TEntity : DomainEntity => mockRepository + .Verify(r => r.GetQueryable(It.IsAny(), It.IsAny(), It.IsAny() ), Times.Exactly(times)); } diff --git a/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs index 2be88e15..ab205af1 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs @@ -1,6 +1,5 @@ using Application.Notifications; using Domain.Notifications; -using Microsoft.Extensions.Logging; using UnitTests.Application.Common; namespace UnitTests.Application.Notifications; @@ -14,11 +13,6 @@ public GetNotificationUseCaseFixture() public GetNotificationRequest SetValidRequest() => new(Guid.NewGuid(), Math.Abs(AutoFixture.Create()) + 1); - -#pragma warning disable CA1848 - public void VerifyNotificationNotFoundLog(int times = 1) => - MockLogger.VerifyLog(l => l.LogWarning("*Notification not found.*"), Times.Exactly(times)); -#pragma warning restore CA1848 } public sealed class GetNotificationUseCaseTests : IClassFixture @@ -37,8 +31,10 @@ public async Task GivenAValidRequestThenPass() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - var expectedNotification = _fixture.AutoFixture.Create(); - _fixture.MockRepository.SetupGetByIdAsNoTrackingAsync(expectedNotification); + var expectedNotification = _fixture.AutoFixture.Build() + .With(n => n.Id, request.Id) + .Create(); + _fixture.MockRepository.SetupQueryable([expectedNotification]); // Act var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); @@ -52,8 +48,9 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(expectedNotification.NotificationType, result.Data.NotificationType); Assert.Equal(expectedNotification.NotificationStatus, result.Data.NotificationStatus); + _fixture.MockRepository.VerifyQueryable(); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotificationNotFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(); } @@ -72,8 +69,9 @@ public async Task GivenAnInvalidRequestThenFails() Assert.NotNull(result.Message); Assert.NotEmpty(result.Message); + _fixture.MockRepository.VerifyQueryable(0); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotificationNotFoundLog(0); + _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(0); } @@ -83,6 +81,7 @@ public async Task GivenAValidRequestWhenNotificationNotFoundThenFails() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); + _fixture.MockRepository.SetupQueryable([]); // Act var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); @@ -93,8 +92,9 @@ public async Task GivenAValidRequestWhenNotificationNotFoundThenFails() Assert.NotEmpty(result.Message); Assert.Equal("Notification not found.", result.Message); + _fixture.MockRepository.VerifyQueryable(); _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotificationNotFoundLog(1); + _fixture.VerifyNotFoundLog(1); _fixture.VerifyFinishUseCaseLog(); } } diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index b6812d89..1914f818 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -26,8 +26,18 @@ public async Task GivenAValidRequestThenPass() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - var expectedOrder = _fixture.AutoFixture.Create(); - _fixture.MockRepository.SetupGetByIdAsNoTrackingAsync(expectedOrder); + Order expectedOrder = new( + "Test Order", + [ + new("Item 1", "Description 1", 500m), + new("Item 2", "Description 2", 500m) + ] + ) + { + Id = request.Id + }; + expectedOrder.SetTotal(); + _fixture.MockRepository.SetupQueryable([expectedOrder]); // Act var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); @@ -43,6 +53,7 @@ public async Task GivenAValidRequestThenPass() Assert.NotNull(result.Data.Items); Assert.Equal(expectedOrder.Items?.Count, result.Data.Items!.Count); + _fixture.MockRepository.VerifyQueryable(); _fixture.VerifyStartUseCaseLog(); _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(); @@ -54,14 +65,15 @@ public async Task GivenAValidRequestWithoutItemsThenPass() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - OrderDto expectedOrder = new() + Order expectedOrder = new( + "Test Order", + [] + ) { - Id = 1, - Description = "Test Order", - Total = 1000m, - Items = [] + Id = request.Id }; - _fixture.MockRepository.SetupGetByIdAsNoTrackingAsync(expectedOrder); + + _fixture.MockRepository.SetupQueryable([expectedOrder]); // Act var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); @@ -76,6 +88,7 @@ public async Task GivenAValidRequestWithoutItemsThenPass() Assert.Equal(expectedOrder.Total, result.Data.Total); Assert.Equal(0, result.Data.Items?.Count); + _fixture.MockRepository.VerifyQueryable(); _fixture.VerifyStartUseCaseLog(); _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(); @@ -89,16 +102,14 @@ public async Task GivenAInvalidRequestThenFails() _fixture.SetFailedValidator(request); // Act - var result = await _fixture.UseCase.HandleAsync( - request, - _fixture.CancellationToken - ); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); Assert.NotNull(result.Message); Assert.NotEmpty(result.Message); + _fixture.MockRepository.VerifyQueryable(0); _fixture.VerifyStartUseCaseLog(); _fixture.VerifyNotFoundLog(0); _fixture.VerifyFinishUseCaseLog(0); @@ -110,12 +121,10 @@ public async Task GivenAValidRequestWhenOrderNotFoundThenFails() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); + _fixture.MockRepository.SetupQueryable([]); // Act - var result = await _fixture.UseCase.HandleAsync( - request, - _fixture.CancellationToken - ); + var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert Assert.False(result.Success); @@ -123,6 +132,7 @@ public async Task GivenAValidRequestWhenOrderNotFoundThenFails() Assert.NotEmpty(result.Message); Assert.Equal("Order not found.", result.Message); + _fixture.MockRepository.VerifyQueryable(); _fixture.VerifyStartUseCaseLog(); _fixture.VerifyNotFoundLog(1); _fixture.VerifyFinishUseCaseLog(); From 0040b88559877c34e7118b3c0099fbe7ddea3b54 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 22 Feb 2026 19:08:19 -0300 Subject: [PATCH 069/161] feat(53): add overload for SetupQueryable with correlationId and newContext --- .../Application/Common/RepositoryMockExtensions.cs | 3 +++ .../UnitTests/Application/Orders/GetOrderUseCaseTests.cs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs b/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs index 54dbe057..7c5baf11 100644 --- a/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs +++ b/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs @@ -18,6 +18,9 @@ public static void SetFailedAddAsync(this Mock mockRep public static void SetupQueryable(this Mock mockRepository, ICollection entities) where TEntity : DomainEntity => mockRepository .Setup(r => r.GetQueryable(It.IsAny(), It.IsAny(), It.IsAny())).Returns(entities.BuildMock()); + public static void SetupQueryable(this Mock mockRepository, Guid correlationId, bool? newContext, ICollection entities) where TEntity : DomainEntity => mockRepository + .Setup(r => r.GetQueryable(correlationId, newContext, It.IsAny())).Returns(entities.BuildMock()); + public static void VerifyAddAsync(this Mock mockRepository, int times) where TEntity : DomainEntity => mockRepository.Verify( d => d.AddAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(times) diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index 1914f818..9daaaf3b 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -37,7 +37,7 @@ public async Task GivenAValidRequestThenPass() Id = request.Id }; expectedOrder.SetTotal(); - _fixture.MockRepository.SetupQueryable([expectedOrder]); + _fixture.MockRepository.SetupQueryable(request.CorrelationId, null, [expectedOrder]); // Act var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); @@ -73,7 +73,7 @@ public async Task GivenAValidRequestWithoutItemsThenPass() Id = request.Id }; - _fixture.MockRepository.SetupQueryable([expectedOrder]); + _fixture.MockRepository.SetupQueryable(request.CorrelationId, null, [expectedOrder]); // Act var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); @@ -121,7 +121,7 @@ public async Task GivenAValidRequestWhenOrderNotFoundThenFails() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - _fixture.MockRepository.SetupQueryable([]); + _fixture.MockRepository.SetupQueryable(request.CorrelationId, null, []); // Act var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); From 2c239cdc3cf96776f75257440daaaa44ab27ccd0 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Mon, 23 Feb 2026 07:31:29 -0300 Subject: [PATCH 070/161] feat(53): improve logs --- .../src/Application/Common/Helpers/Logs.cs | 234 ++++++------------ .../Common/Services/IHybridCacheService.cs | 4 +- .../Common/UseCases/BaseInOutUseCase.cs | 4 +- .../Common/UseCases/BaseInUseCase.cs | 4 +- .../Common/UseCases/BaseOutUseCase.cs | 4 +- .../CreateNotificationUseCase.cs | 2 +- .../Application/Orders/CreateOrderUseCase.cs | 4 +- .../Cache/Services/HybridCacheService.cs | 29 ++- .../Common/BaseBackgroundService.cs | 4 +- .../Full/src/Infrastructure/Common/Logs.cs | 118 --------- .../Data/Common/BaseRepository.cs | 11 +- .../Messaging/Consumers/BaseConsumer.cs | 34 ++- .../Messaging/Producers/ProducerService.cs | 17 +- .../src/WebApp/Endpoints/OrderEndpoints.cs | 1 + .../src/WebApp/GrpcServices/OrderService.cs | 3 +- .../ExceptionHandlingMiddleware.cs | 2 +- .../Common/BaseApplicationFixture.cs | 3 + 17 files changed, 154 insertions(+), 324 deletions(-) delete mode 100644 templates/Full/src/Infrastructure/Common/Logs.cs diff --git a/templates/Full/src/Application/Common/Helpers/Logs.cs b/templates/Full/src/Application/Common/Helpers/Logs.cs index baa2eefc..b1d41490 100644 --- a/templates/Full/src/Application/Common/Helpers/Logs.cs +++ b/templates/Full/src/Application/Common/Helpers/Logs.cs @@ -4,68 +4,64 @@ namespace Application.Common.Helpers; public partial class Logs { - // Use Case Execution Logs - /// - /// Logs the start of the execution of a use case, including the class name, method name, and correlation ID. + /// Logs a generic debug message with a custom message. /// /// The logger instance to use for logging. - /// The name of the class where the use case is executed. - /// The name of the method where the use case is executed. + /// The name of the class. + /// The name of the method. /// The correlation ID for tracking the request. + /// The debug message. [LoggerMessage( EventId = 1, - Level = LogLevel.Information, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Start to execute use case" + Level = LogLevel.Debug, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void StartToExecuteUseCase(ILogger logger, string className, string methodName, Guid correlationId); + public static partial void Debug(ILogger logger, string className, string methodName, Guid correlationId, string message); /// - /// Logs the completion of the execution of a use case, including the class name, method name, correlation ID, and elapsed time in milliseconds. + /// Logs a generic information message with a custom message. /// /// The logger instance to use for logging. - /// The name of the class where the use case is executed. - /// The name of the method where the use case is executed. + /// The name of the class. + /// The name of the method. /// The correlation ID for tracking the request. - /// The elapsed time in milliseconds for the execution of the use case. + /// The information message. [LoggerMessage( EventId = 2, Level = LogLevel.Information, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Elapsed time: {ElapsedMilliseconds} ms | Finished executing use case" + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void FinishedExecutingUseCase(ILogger logger, string className, string methodName, Guid correlationId, long elapsedMilliseconds); - - // Validation Logs + public static partial void Information(ILogger logger, string className, string methodName, Guid correlationId, string message); /// - /// Logs validation errors encountered during request validation. + /// Logs a generic warning message with a custom message. /// /// The logger instance to use for logging. - /// The name of the class where the validation occurred. - /// The name of the method where the validation occurred. + /// The name of the class. + /// The name of the method. /// The correlation ID for tracking the request. - /// The validation errors. + /// The warning message. [LoggerMessage( EventId = 3, - Level = LogLevel.Error, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Validation errors: {Errors}" + Level = LogLevel.Warning, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void ValidationErrors(ILogger logger, string className, string methodName, Guid correlationId, string errors); + public static partial void Warning(ILogger logger, string className, string methodName, Guid correlationId, string message); /// - /// Logs when an entity is not found. + /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. /// The name of the class. /// The name of the method. - /// The correlation ID for tracking the request. - /// The name of the entity that was not found. + /// The failure message. [LoggerMessage( EventId = 4, - Level = LogLevel.Warning, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {EntityName} not found." + Level = LogLevel.Error, + Message = "[{ClassName}] | [{MethodName}] | Error: {Message}" )] - public static partial void NotFound(ILogger logger, string className, string methodName, Guid correlationId, string entityName); + public static partial void Error(ILogger logger, string className, string methodName, string message); /// /// Logs a generic operation failure with a custom message. @@ -77,197 +73,111 @@ public partial class Logs /// The failure message. [LoggerMessage( EventId = 5, - Level = LogLevel.Warning, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Operation failed: {Message}" + Level = LogLevel.Error, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Error: {Message}" )] - public static partial void OperationFailed(ILogger logger, string className, string methodName, Guid correlationId, string message); - + public static partial void Error(ILogger logger, string className, string methodName, Guid correlationId, string message); + /// - /// Logs when starting to publish a message. + /// Logs the start of the execution of an operation, including the class name, method name, and correlation ID. /// /// The logger instance to use for logging. - /// The name of the class. + /// The name of the class where the operation is executed. + /// The name of the method where the operation is executed. /// The correlation ID for tracking the request. - /// The type of message being published. [LoggerMessage( EventId = 6, Level = LogLevel.Information, - Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Publishing message: {MessageType}" + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Starting operation" )] - public static partial void PublishingMessage(ILogger logger, string className, Guid correlationId, string messageType); + public static partial void StartingOperation(ILogger logger, string className, string methodName, Guid correlationId); /// - /// Logs when a message has been published successfully. + /// Logs the completion of the execution of an operation, including the class name, method name, correlation ID, and elapsed time in milliseconds. /// /// The logger instance to use for logging. - /// The name of the class. + /// The name of the class where the operation is executed. + /// The name of the method where the operation is executed. /// The correlation ID for tracking the request. - /// The type of message published. - /// The elapsed time in milliseconds. + /// The elapsed time in milliseconds for the execution of the operation. [LoggerMessage( EventId = 7, Level = LogLevel.Information, - Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationId} | Message published: {MessageType} | Elapsed time: {ElapsedMilliseconds} ms" + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Elapsed time: {ElapsedMilliseconds} ms | Finished operation" )] - public static partial void MessagePublished(ILogger logger, string className, Guid correlationId, string messageType, long elapsedMilliseconds); + public static partial void FinishedOperation(ILogger logger, string className, string methodName, Guid correlationId, long elapsedMilliseconds); /// - /// Logs when starting to publish a batch of messages. + /// Logs validation errors encountered during request validation. /// /// The logger instance to use for logging. - /// The name of the class. - /// The correlation IDs for tracking the requests. - /// The type of messages being published. + /// The name of the class where the validation occurred. + /// The name of the method where the validation occurred. + /// The correlation ID for tracking the request. + /// The validation errors. [LoggerMessage( EventId = 8, - Level = LogLevel.Information, - Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationIds} | Publishing batch of messages: {MessageType}" + Level = LogLevel.Error, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Validation errors: {Errors}" )] - public static partial void PublishingBatchMessages(ILogger logger, string className, object correlationIds, string messageType); + public static partial void ValidationErrors(ILogger logger, string className, string methodName, Guid correlationId, string errors); /// - /// Logs when a batch of messages has been published successfully. + /// Logs when an entity is not found. /// /// The logger instance to use for logging. /// The name of the class. - /// The correlation IDs for tracking the requests. - /// The type of messages published. - /// The elapsed time in milliseconds. + /// The name of the method. + /// The correlation ID for tracking the request. + /// The name of the entity that was not found. [LoggerMessage( EventId = 9, - Level = LogLevel.Information, - Message = "[{ClassName}] | [HandleAsync] | CorrelationId: {CorrelationIds} | Batch of messages published: {MessageType} | Elapsed time: {ElapsedMilliseconds} ms" + Level = LogLevel.Warning, + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {EntityName} not found." )] - public static partial void BatchMessagesPublished(ILogger logger, string className, object correlationIds, string messageType, long elapsedMilliseconds); + public static partial void NotFound(ILogger logger, string className, string methodName, Guid correlationId, string entityName); /// - /// Logs when a message is received by a consumer. + /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. /// The name of the class. + /// The name of the method. /// The correlation ID for tracking the request. - /// The type of message received. + /// The failure message. [LoggerMessage( EventId = 10, - Level = LogLevel.Information, - Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Received message: {MessageType}" - )] - public static partial void ReceivedMessage(ILogger logger, string className, Guid correlationId, string messageType); - - /// - /// Logs when a duplicate message is detected. - /// - /// The logger instance to use for logging. - /// The name of the class. - /// The correlation ID for tracking the request. - [LoggerMessage( - EventId = 11, Level = LogLevel.Warning, - Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Duplicate message detected. Skipping processing." + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Failed operation: {Message}" )] - public static partial void DuplicateMessageDetected(ILogger logger, string className, Guid correlationId); + public static partial void FailedOperation(ILogger logger, string className, string methodName, Guid correlationId, string message); /// - /// Logs when starting to process a message. + /// Logs the start of an operation. /// /// The logger instance to use for logging. /// The name of the class. + /// The method name. /// The correlation ID for tracking the request. [LoggerMessage( - EventId = 12, - Level = LogLevel.Information, - Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Start processing message." + EventId = 11, + Level = LogLevel.Debug, + Message = "[{ClassName}] | [{Method}] | [{CorrelationId}] | Starting operation. | {Details}" )] - public static partial void StartProcessingMessage(ILogger logger, string className, Guid correlationId); + public static partial void DebugStartingOperation(ILogger logger, string className, string method, Guid correlationId, string details = ""); /// - /// Logs when a message has been processed successfully. + /// Logs the completion of an operation. /// /// The logger instance to use for logging. /// The name of the class. + /// The method name. /// The correlation ID for tracking the request. /// The elapsed time in milliseconds. [LoggerMessage( - EventId = 13, - Level = LogLevel.Information, - Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Processed message in {ElapsedMilliseconds} ms" - )] - public static partial void ProcessedMessage(ILogger logger, string className, Guid correlationId, long elapsedMilliseconds); - - /// - /// Logs when an error occurs while processing a message. - /// - /// The logger instance to use for logging. - /// The name of the class. - /// The correlation ID for tracking the request. - /// The error message. - /// The stack trace. - [LoggerMessage( - EventId = 14, - Level = LogLevel.Error, - Message = "[{ClassName}] | [HandleMessageAsync] | CorrelationId: {CorrelationId} | Error processing message: {ErrorMessage} | StackTrace: {StackTrace}" - )] - public static partial void ErrorProcessingMessage(ILogger logger, string className, Guid? correlationId, string errorMessage, string? stackTrace); - - /// - /// Logs when a null message is received. - /// - /// The logger instance to use for logging. - /// The name of the class. - /// The type of message expected. - [LoggerMessage( - EventId = 15, - Level = LogLevel.Warning, - Message = "[{ClassName}] | [HandleMessageAsync] | Received null message of type {MessageType}" - )] - public static partial void ReceivedNullMessage(ILogger logger, string className, string messageType); - - /// - /// Logs when an error occurs while deserializing a message. - /// - /// The logger instance to use for logging. - /// The name of the class. - /// The correlation ID for tracking the request. - /// The application ID. - /// The cluster ID. - /// The error message. - /// The stack trace. - [LoggerMessage( - EventId = 16, - Level = LogLevel.Error, - Message = "[{ClassName}] | [HandleRabbitMqAsync] | [{CorrelationId}] | AppId: {AppId} | ClusterId: {ClusterId} | Error deserializing message: {ErrorMessage} | StackTrace: {StackTrace}" - )] - public static partial void ErrorDeserializingMessage(ILogger logger, string className, string? correlationId, string? appId, string? clusterId, string errorMessage, string? stackTrace); - - /// - /// Logs when an unexpected error occurs. - /// - /// The logger instance to use for logging. - /// The name of the class. - /// The correlation ID for tracking the request. - /// The application ID. - /// The cluster ID. - /// The error message. - /// The stack trace. - [LoggerMessage( - EventId = 17, - Level = LogLevel.Error, - Message = "[{ClassName}] | [HandleRabbitMqAsync] | [{CorrelationId}] | AppId: {AppId} | ClusterId: {ClusterId} | Unexpected error: {ErrorMessage} | StackTrace: {StackTrace}" - )] - public static partial void UnexpectedError(ILogger logger, string className, string? correlationId, string? appId, string? clusterId, string errorMessage, string? stackTrace); - - /// - /// Logs when an exception is handled by the exception handling middleware. - /// - /// The logger instance to use for logging. - /// The exception that occurred. - /// The name of the class. - /// The name of the method. - /// The exception message. - [LoggerMessage( - EventId = 18, - Level = LogLevel.Error, - Message = "[{ClassName}] | [{MethodName}] | {Message}" + EventId = 12, + Level = LogLevel.Debug, + Message = "[{ClassName}] | [{Method}] | [{CorrelationId}] | Finished operation executed in {ElapsedMilliseconds} ms. | {Details}" )] - public static partial void ExceptionHandlerError(ILogger logger, Exception exception, string className, string methodName, string message); + public static partial void DebugFinishedOperation(ILogger logger, string className, string method, Guid correlationId, long elapsedMilliseconds, string details = ""); } \ No newline at end of file diff --git a/templates/Full/src/Application/Common/Services/IHybridCacheService.cs b/templates/Full/src/Application/Common/Services/IHybridCacheService.cs index d68be0c9..6c28823d 100644 --- a/templates/Full/src/Application/Common/Services/IHybridCacheService.cs +++ b/templates/Full/src/Application/Common/Services/IHybridCacheService.cs @@ -3,16 +3,18 @@ namespace Application.Common.Services; public interface IHybridCacheService { ValueTask GetOrCreateAsync( + Guid correlationId, string key, Func> factory, CancellationToken cancellationToken ); ValueTask CreateAsync( + Guid correlationId, string key, TResult value, CancellationToken cancellationToken ); - ValueTask DeleteAsync(string key, CancellationToken cancellationToken); + ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken); } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 78ca7f37..cbdc78b7 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -49,7 +49,7 @@ CancellationToken cancellationToken { StopWatch.Restart(); - Logs.StartToExecuteUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); TResponseData response; if (_validator != null) @@ -73,7 +73,7 @@ CancellationToken cancellationToken response = await HandleInternalAsync(request, cancellationToken); - Logs.FinishedExecutingUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 329cd38f..c1a54799 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -45,7 +45,7 @@ CancellationToken cancellationToken { StopWatch.Restart(); - Logs.StartToExecuteUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); if (_validator != null) { @@ -61,7 +61,7 @@ CancellationToken cancellationToken await HandleInternalAsync(request, cancellationToken); - Logs.FinishedExecutingUseCase(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 6d515610..8b0f15b5 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -36,11 +36,11 @@ public async Task HandleAsync(CancellationToken cancellationToken { StopWatch.Restart(); var correlationId = Guid.NewGuid(); - Logs.StartToExecuteUseCase(Logger, ClassName, HandleMethodName, correlationId); + Logs.StartingOperation(Logger, ClassName, HandleMethodName, correlationId); var response = await HandleInternalAsync(cancellationToken); - Logs.FinishedExecutingUseCase(Logger, ClassName, HandleMethodName, correlationId, StopWatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger, ClassName, HandleMethodName, correlationId, StopWatch.ElapsedMilliseconds); _useCaseExecuted.Record(1); _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 93059143..6655acad 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken if (addResult == 0) { - Logs.OperationFailed(Logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); + Logs.FailedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); } } } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 055b3e0e..66034f48 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -61,7 +61,7 @@ CancellationToken cancellationToken var createResult = newOrder.SetTotal(); if (createResult.IsFailure) { - Logs.OperationFailed(Logger, ClassName, HandleMethodName, correlationId, createResult.Message); + Logs.FailedOperation(Logger, ClassName, HandleMethodName, correlationId, createResult.Message); response = new(false, null, createResult.Message); @@ -73,7 +73,7 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(newOrder, correlationId, cancellationToken); if (addResult == 0) { - Logs.OperationFailed(Logger, ClassName, HandleMethodName, correlationId, "Failed to create order."); + Logs.FailedOperation(Logger, ClassName, HandleMethodName, correlationId, "Failed to create order."); response = new(false, null, "Failed to create order."); diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 1a2d14d1..c74865ee 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -1,6 +1,7 @@ +using System.Diagnostics; using Application.Common.Constants; +using Application.Common.Helpers; using Application.Common.Services; -using Infrastructure.Common; using Microsoft.Extensions.Caching.Hybrid; using Microsoft.Extensions.Logging; @@ -10,37 +11,45 @@ internal sealed class HybridCacheService(HybridCache cache, ILogger _logger = logger; + private readonly string _className = nameof(HybridCacheService); + private readonly Stopwatch _stopwatch = new(); public async ValueTask GetOrCreateAsync( + Guid correlationId, string key, Func> factory, CancellationToken cancellationToken ) { - Logs.RetrievingCacheEntry(_logger, key); + _stopwatch.Restart(); + Logs.DebugStartingOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); + var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - Logs.CacheEntryRetrieved(_logger, key); - + Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, _stopwatch.ElapsedMilliseconds, key); return result; } - public async ValueTask CreateAsync(string key, TResult value, CancellationToken cancellationToken) + public async ValueTask CreateAsync(Guid correlationId, string key, TResult value, CancellationToken cancellationToken) { - Logs.CreatingCacheEntry(_logger, key); + _stopwatch.Restart(); + + Logs.DebugStartingOperation(_logger, _className, nameof(CreateAsync), correlationId, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - Logs.CacheEntryCreated(_logger, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, _stopwatch.ElapsedMilliseconds, key); } - public async ValueTask DeleteAsync(string key, CancellationToken cancellationToken) + public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) { - Logs.DeletingCacheEntry(_logger, key); + _stopwatch.Restart(); + + Logs.DebugStartingOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - Logs.CacheEntryDeleted(_logger, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, _stopwatch.ElapsedMilliseconds, key); } } diff --git a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs index b6245288..69520130 100644 --- a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs +++ b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs @@ -1,4 +1,4 @@ -using System.Diagnostics; +using Application.Common.Helpers; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -28,7 +28,7 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken) } catch (Exception ex) { - Logs.UnexpectedErrorInBackgroundService(logger, ex.Message, ex.StackTrace); + Logs.Error(logger, nameof(BaseBackgroundService<>), nameof(ExecuteAsync), Guid.NewGuid(), ex.Message); throw; } diff --git a/templates/Full/src/Infrastructure/Common/Logs.cs b/templates/Full/src/Infrastructure/Common/Logs.cs deleted file mode 100644 index b513c6d5..00000000 --- a/templates/Full/src/Infrastructure/Common/Logs.cs +++ /dev/null @@ -1,118 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Infrastructure.Common; - -internal static partial class Logs -{ - /// - /// Logs an unexpected error in a background service. - /// - /// The logger instance to use for logging. - /// The error message. - /// The stack trace of the error. - [LoggerMessage( - EventId = 200, - Level = LogLevel.Error, - Message = "[BaseBackgroundService] | [ExecuteAsync] | Unexpected error in background service. | Message: {ErrorMessage} | StackTrace: {StackTrace}" - )] - public static partial void UnexpectedErrorInBackgroundService(ILogger logger, string errorMessage, string? stackTrace); - - /// - /// Logs the start of a database operation. - /// - /// The logger instance to use for logging. - /// The method name. - /// The correlation ID for tracking the request. - [LoggerMessage( - EventId = 201, - Level = LogLevel.Debug, - Message = "[BaseRepository] | [{Method}] | [{CorrelationId}] | Starting database operation." - )] - public static partial void StartingDatabaseOperation(ILogger logger, string method, Guid correlationId); - - /// - /// Logs the completion of a database query. - /// - /// The logger instance to use for logging. - /// The method name. - /// The correlation ID for tracking the request. - /// The elapsed time in milliseconds. - [LoggerMessage( - EventId = 202, - Level = LogLevel.Debug, - Message = "[BaseRepository] | [{Method}] | [{CorrelationId}] | Finished database operation executed in {ElapsedMilliseconds} ms." - )] - public static partial void FinishedDatabaseOperation(ILogger logger, string method, Guid correlationId, long elapsedMilliseconds); - - /// - /// Logs the start of retrieving a cache entry. - /// - /// The logger instance to use for logging. - /// The cache key. - [LoggerMessage( - EventId = 203, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Retrieving cache entry" - )] - public static partial void RetrievingCacheEntry(ILogger logger, string key); - - /// - /// Logs the completion of retrieving a cache entry. - /// - /// The logger instance to use for logging. - /// The cache key. - [LoggerMessage( - EventId = 204, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Cache entry retrieved" - )] - public static partial void CacheEntryRetrieved(ILogger logger, string key); - - /// - /// Logs the start of creating a cache entry. - /// - /// The logger instance to use for logging. - /// The cache key. - [LoggerMessage( - EventId = 205, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Creating cache entry" - )] - public static partial void CreatingCacheEntry(ILogger logger, string key); - - /// - /// Logs the completion of creating a cache entry. - /// - /// The logger instance to use for logging. - /// The cache key. - [LoggerMessage( - EventId = 206, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Cache entry created" - )] - public static partial void CacheEntryCreated(ILogger logger, string key); - - /// - /// Logs the start of deleting a cache entry. - /// - /// The logger instance to use for logging. - /// The cache key. - [LoggerMessage( - EventId = 207, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Deleting cache entry" - )] - public static partial void DeletingCacheEntry(ILogger logger, string key); - - /// - /// Logs the completion of deleting a cache entry. - /// - /// The logger instance to use for logging. - /// The cache key. - [LoggerMessage( - EventId = 208, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Cache entry deleted" - )] - public static partial void CacheEntryDeleted(ILogger logger, string key); -} diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 4c133b52..1e9aa135 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -1,9 +1,9 @@ using System.Diagnostics; using System.Linq.Expressions; using System.Runtime.CompilerServices; +using Application.Common.Helpers; using Application.Common.Repositories; using Domain.Common; -using Infrastructure.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -17,6 +17,7 @@ IDbContextFactory dbContextFactory private readonly Stopwatch _stopwatch = new(); private readonly IDbContextFactory _dbContextFactory = dbContextFactory; private readonly MyDbContext _dbContext = dbContextFactory.CreateDbContext(); + private readonly string _className = nameof(BaseRepository); private async Task HandleBaseQueryAsync( Func, Task> query, @@ -28,7 +29,7 @@ private async Task HandleBaseQueryAsync( { _stopwatch.Restart(); - Logs.StartingDatabaseOperation(logger, methodName, correlationId); + Logs.DebugStartingOperation(logger, _className, methodName, correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) @@ -36,7 +37,7 @@ private async Task HandleBaseQueryAsync( var result = await query.Invoke(dbSet); - Logs.FinishedDatabaseOperation(logger, methodName, correlationId, _stopwatch.ElapsedMilliseconds); + Logs.DebugFinishedOperation(logger, _className, methodName, correlationId, _stopwatch.ElapsedMilliseconds); return result; } @@ -50,13 +51,13 @@ public IQueryable GetQueryable( { _stopwatch.Restart(); - Logs.StartingDatabaseOperation(logger, methodName, correlationId); + Logs.DebugStartingOperation(logger, _className, methodName, correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) dbSet = _dbContextFactory.CreateDbContext().Set(); - Logs.FinishedDatabaseOperation(logger, methodName, correlationId, _stopwatch.ElapsedMilliseconds); + Logs.DebugFinishedOperation(logger, _className, methodName, correlationId, _stopwatch.ElapsedMilliseconds); return dbSet; } diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index 2f767148..ee1f7439 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -47,16 +47,19 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi async (message, cancellationToken) => { producerService = serviceProvider.GetRequiredService(); + var methodName = nameof(ExecuteInternalAsync); + try { _stopwatch.Restart(); var hybridCacheService = serviceProvider.GetRequiredService(); - Logs.ReceivedMessage(logger, _className, message.CorrelationId, typeof(TMessage).Name); + Logs.Debug(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " received. Checking if it has already been processed."); var isExecutedKey = _className + "-" + message.CorrelationId; var isExecuted = await hybridCacheService.GetOrCreateAsync( + message.CorrelationId, isExecutedKey, async (cancellationToken) => false, cancellationToken @@ -64,21 +67,21 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi if (isExecuted) { - Logs.DuplicateMessageDetected(logger, _className, message.CorrelationId); + Logs.Warning(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " has already been processed. Skipping."); return; } - Logs.StartProcessingMessage(logger, _className, message.CorrelationId); + Logs.DebugStartingOperation(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " processing started."); await HandleUseCaseAsync(serviceProvider, message, cancellationToken); - await hybridCacheService.CreateAsync(isExecutedKey, true, cancellationToken); + await hybridCacheService.CreateAsync(message.CorrelationId, isExecutedKey, true, cancellationToken); - Logs.ProcessedMessage(logger, _className, message.CorrelationId, _stopwatch.ElapsedMilliseconds); + Logs.DebugFinishedOperation(logger, _className, methodName, message.CorrelationId, _stopwatch.ElapsedMilliseconds, typeof(TMessage).Name + " processing finished."); } catch (Exception ex) { - Logs.ErrorProcessingMessage(logger, _className, message?.CorrelationId, ex.Message, ex.StackTrace); + Logs.Error(logger, _className, methodName, message.CorrelationId, ex.Message); _ = producerService.HandleAsync(message!, CancellationToken.None, _queueName + "_deadLetter"); @@ -95,6 +98,9 @@ CancellationToken cancellationToken { var connection = await _factory.CreateConnectionAsync(cancellationToken); var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); + var methodName = nameof(HandleRabbitMqAsync); + + Logs.Debug(logger, _className, methodName, Guid.NewGuid(), "Connected to RabbitMQ. Declaring queues."); await channel.QueueDeclareAsync( queue: _queueName, @@ -114,6 +120,8 @@ await channel.QueueDeclareAsync( AsyncEventingBasicConsumer consumer = new(channel); + Logs.Debug(logger, _className, methodName, Guid.NewGuid(), "Queues declared. Starting to consume messages."); + consumer.ReceivedAsync += async (model, eventArguments) => { var basicProperties = eventArguments.BasicProperties; @@ -122,28 +130,36 @@ await channel.QueueDeclareAsync( TMessage message = null!; try { + Logs.Debug(logger, _className, methodName, Guid.NewGuid(), "Message received. Deserializing."); + message = JsonSerializer.Deserialize(body)!; + Logs.Debug(logger, _className, methodName, message.CorrelationId, "Message deserialized. Validating."); + if (message == null || message.GetType() != typeof(TMessage)) { - Logs.ReceivedNullMessage(logger, _className, typeof(TMessage).Name); + Logs.Warning(logger, _className, methodName, Guid.NewGuid(), typeof(TMessage).Name + " is null or of incorrect type."); return; } } catch (JsonException ex) { - Logs.ErrorDeserializingMessage(logger, _className, basicProperties.CorrelationId, basicProperties.AppId, basicProperties.ClusterId, ex.Message, ex.StackTrace); + Logs.Error(logger, _className, methodName, Guid.NewGuid(), ex.Message); throw; } catch (Exception ex) { - Logs.UnexpectedError(logger, _className, basicProperties.CorrelationId, basicProperties.AppId, basicProperties.ClusterId, ex.Message, ex.StackTrace); + Logs.Error(logger, _className, methodName, Guid.NewGuid(), ex.Message); throw; } + Logs.Debug(logger, _className, methodName, message.CorrelationId, "Message validated. Handling use case."); + await handleAsync.Invoke(message, cancellationToken); + + Logs.Debug(logger, _className, methodName, message.CorrelationId, "Use case handled."); }; await channel.BasicConsumeAsync( diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 4cc1021b..00c885c0 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -44,7 +44,7 @@ public async Task HandleAsync( using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - Logs.PublishingMessage(_logger, _className, message.CorrelationId, typeof(TMessage).Name); + Logs.DebugStartingOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " publishing started."); await channel.BasicPublishAsync( exchange: exchange, @@ -53,7 +53,7 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.MessagePublished(_logger, _className, message.CorrelationId, typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds); + Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, _stopWatch.ElapsedMilliseconds, typeof(TMessage).Name + " published."); } public async Task HandleAsync( @@ -66,14 +66,16 @@ public async Task HandleAsync( await Task.Yield(); _stopWatch.Restart(); + + Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - var correlationIds = messages.Select(m => m.CorrelationId); - Logs.PublishingBatchMessages(_logger, _className, correlationIds, typeof(TMessage).Name); - foreach (var message in messages) + { + Logs.DebugStartingOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch publishing started."); + await channel.BasicPublishAsync( exchange: exchange, routingKey: queue, @@ -81,6 +83,9 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.BatchMessagesPublished(_logger, _className, correlationIds, typeof(TMessage).Name, _stopWatch.ElapsedMilliseconds); + Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, _stopWatch.ElapsedMilliseconds, typeof(TMessage).Name + " batch published."); + } + + Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); } } diff --git a/templates/Full/src/WebApp/Endpoints/OrderEndpoints.cs b/templates/Full/src/WebApp/Endpoints/OrderEndpoints.cs index 4c3ab0a7..0aa915a1 100644 --- a/templates/Full/src/WebApp/Endpoints/OrderEndpoints.cs +++ b/templates/Full/src/WebApp/Endpoints/OrderEndpoints.cs @@ -25,6 +25,7 @@ public static WebApplication MapOrderEndpoints(this WebApplication app) var response = cacheEnabled switch { true => await cache.GetOrCreateAsync( + correlationId, $"{nameof(OrderEndpoints)}-{id}", async (cancellationToken) => await useCase.HandleAsync(new(correlationId, id), cancellationToken), cancellationToken diff --git a/templates/Full/src/WebApp/GrpcServices/OrderService.cs b/templates/Full/src/WebApp/GrpcServices/OrderService.cs index 7430d007..f63b3a90 100644 --- a/templates/Full/src/WebApp/GrpcServices/OrderService.cs +++ b/templates/Full/src/WebApp/GrpcServices/OrderService.cs @@ -23,11 +23,12 @@ public override async Task Get( ServerCallContext context ) { + var correlationId = Guid.TryParse(request.CorrelationId, out var guid) ? guid : Guid.Empty; var response = await _cache.GetOrCreateAsync( + correlationId, $"{nameof(OrderService)}-{request.Id}", async cancellationToken => { - var correlationId = Guid.TryParse(request.CorrelationId, out var guid) ? guid : Guid.Empty; return await _useCase.HandleAsync(new(correlationId, request.Id), cancellationToken); }, context.CancellationToken diff --git a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs index 33b81f22..30d3b2dc 100644 --- a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs +++ b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -24,7 +24,7 @@ public async Task InvokeAsync(HttpContext context) private async Task HandleExceptionAsync(HttpContext context, Exception exception) { - Logs.ExceptionHandlerError(_logger, exception, _className, nameof(HandleExceptionAsync), exception.Message); + Logs.Error(_logger, _className, nameof(HandleExceptionAsync), exception.Message); BaseResponse response = new(false, exception.Message); diff --git a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs index 1ebf45e9..b8b0690b 100644 --- a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs +++ b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs @@ -88,12 +88,14 @@ public void SetFailedValidator(TRequest request) public void SetValidGetOrCreateAsync(TResult result) => MockCache .Setup(c => c.GetOrCreateAsync( + It.IsAny(), It.IsAny(), It.IsAny>>(), It.IsAny() )).ReturnsAsync(result); public void SetInvalidGetOrCreateAsync() => MockCache.Setup(c => c.GetOrCreateAsync( + It.IsAny(), It.IsAny(), It.IsAny>>(), It.IsAny() @@ -173,6 +175,7 @@ public void VerifyLogError(string message, int times = 1) => MockLogger.Verify( public void VerifyCache(int times) => MockCache.Verify( c => c.GetOrCreateAsync( + It.IsAny(), It.IsAny(), It.IsAny>>(), It.IsAny() From 38577e506ffb227e1aeff00854f2d07268019d1f Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Mon, 23 Feb 2026 07:55:33 -0300 Subject: [PATCH 071/161] test(53): fix logs on unit tests --- .../Common/BaseApplicationFixture.cs | 72 ------------------- .../Application/Common/LogMockExtensions.cs | 62 ++++++++++++++++ .../Common/RepositoryMockExtensions.cs | 2 +- .../CreateNotificationUseCaseTests.cs | 18 ++--- .../GetAllNotificationsUseCaseTests.cs | 18 ++--- .../GetNotificationUseCaseTest.cs | 18 ++--- .../Orders/CreateOrderUseCaseTests.cs | 41 +++++------ .../Orders/GetAllOrdersUseCaseTest.cs | 18 ++--- .../Orders/GetOrderUseCaseTests.cs | 24 +++---- 9 files changed, 127 insertions(+), 146 deletions(-) create mode 100644 templates/Full/tests/UnitTests/Application/Common/LogMockExtensions.cs diff --git a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs index b8b0690b..11ddb06f 100644 --- a/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs +++ b/templates/Full/tests/UnitTests/Application/Common/BaseApplicationFixture.cs @@ -101,78 +101,6 @@ public void SetInvalidGetOrCreateAsync() => MockCache.Setup(c => c.GetO It.IsAny() )); - public void VerifyStartUseCaseLog(int times = 1) => MockLogger.Verify( - x => x.Log( - LogLevel.Information, - It.Is(e => e.Id == 1), - It.Is((v, t) =>v.ToString()!.Contains("Start to execute use case")), - It.IsAny(), - It.IsAny>()), - Times.Exactly(times)); - - public void VerifyFinishUseCaseLog(int times = 1) => MockLogger.Verify( - x => x.Log( - LogLevel.Information, - It.Is(e => e.Id == 2), - It.Is((v, t) => v.ToString()!.Contains("Finished executing use case")), - It.IsAny(), - It.IsAny>()), - Times.Exactly(times)); - - public void VerifyFinishUseCaseWithCacheLog(int times = 1) => MockLogger.Verify( - x => x.Log( - LogLevel.Information, - It.IsAny(), - It.Is((v, t) => v.ToString()!.Contains("Finished executing use case with cache key")), - It.IsAny(), - It.IsAny>()), - Times.Exactly(times)); - - public void VerifyNotFoundLog(int times = 1) => MockLogger.Verify(l => l.Log( - LogLevel.Warning, - It.IsAny(), - It.Is((v, t) => v.ToString()!.Contains("not found.")), - null, - It.IsAny>()), - Times.Exactly(times) - ); - - public void VerifyOperationFailedLog(int times = 1) => MockLogger.Verify(l => l.Log( - LogLevel.Warning, - It.IsAny(), - It.Is((v, t) => v.ToString()!.Contains("Operation failed")), - null, - It.IsAny>()), - Times.Exactly(times) - ); - - public void VerifyLogInformation(string message, int times = 1) => MockLogger.Verify( - x => x.Log( - LogLevel.Information, - It.IsAny(), - It.Is((v, t) => v.ToString()!.Contains(message)), - It.IsAny(), - It.IsAny>()), - Times.Exactly(times)); - - public void VerifyLogWarning(string message, int times = 1) => MockLogger.Verify( - x => x.Log( - LogLevel.Warning, - It.IsAny(), - It.Is((v, t) => v.ToString()!.Contains(message)), - It.IsAny(), - It.IsAny>()), - Times.Exactly(times)); - - public void VerifyLogError(string message, int times = 1) => MockLogger.Verify( - x => x.Log( - LogLevel.Error, - It.IsAny(), - It.Is((v, t) => v.ToString()!.Contains(message)), - It.IsAny(), - It.IsAny>()), - Times.Exactly(times)); - public void VerifyCache(int times) => MockCache.Verify( c => c.GetOrCreateAsync( It.IsAny(), diff --git a/templates/Full/tests/UnitTests/Application/Common/LogMockExtensions.cs b/templates/Full/tests/UnitTests/Application/Common/LogMockExtensions.cs new file mode 100644 index 00000000..c054d1e0 --- /dev/null +++ b/templates/Full/tests/UnitTests/Application/Common/LogMockExtensions.cs @@ -0,0 +1,62 @@ +using Microsoft.Extensions.Logging; + +namespace UnitTests.Application.Common; + +internal static class LogMockExtensions +{ + public static void VerifyDebug(this Mock mockLogger, string expectedMessage, int times = 1) => mockLogger.Verify( + logger => logger.Log( + LogLevel.Debug, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains(expectedMessage)), + It.IsAny(), + It.Is>((v, t) => true) + ), + Times.Exactly(times) + ); + + public static void VerifyInformation(this Mock mockLogger, string expectedMessage, int times = 1) => mockLogger.Verify( + logger => logger.Log( + LogLevel.Information, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains(expectedMessage)), + It.IsAny(), + It.Is>((v, t) => true) + ), + Times.Exactly(times) + ); + + public static void VerifyError(this Mock mockLogger, string expectedMessage, int times = 1) => mockLogger.Verify( + logger => logger.Log( + LogLevel.Error, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains(expectedMessage)), + It.IsAny(), + It.Is>((v, t) => true) + ), + Times.Exactly(times) + ); + + public static void VerifyWarning(this Mock mockLogger, string expectedMessage, int times = 1) => mockLogger.Verify( + logger => logger.Log( + LogLevel.Warning, + It.IsAny(), + It.Is((v, t) => v.ToString()!.Contains(expectedMessage)), + It.IsAny(), + It.Is>((v, t) => true) + ), + Times.Exactly(times) + ); + + public static void VerifyStartOperation(this Mock mockLogger, int times = 1) => + VerifyInformation(mockLogger, $"Starting operation", times); + + public static void VerifyFinishOperation(this Mock mockLogger, int times = 1) => + VerifyInformation(mockLogger, $"Finished operation", times); + + public static void VerifyOperationFailed(this Mock mockLogger, int times = 1) => + VerifyWarning(mockLogger, "Failed operation", times); + + public static void VerifyNotFound(this Mock mockLogger, int times = 1) => + VerifyWarning(mockLogger, $"not found.", times); +} \ No newline at end of file diff --git a/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs b/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs index 7c5baf11..e61b1216 100644 --- a/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs +++ b/templates/Full/tests/UnitTests/Application/Common/RepositoryMockExtensions.cs @@ -5,7 +5,7 @@ namespace UnitTests.Application.Common; -public static class RepositoryMockExtensions +internal static class RepositoryMockExtensions { public static void SetSuccessfulAddAsync(this Mock mockRepository) where TEntity : DomainEntity => mockRepository .Setup(d => d.AddAsync(It.IsAny(), It.IsAny(), It.IsAny())) diff --git a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs index 05532c41..d4136aeb 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs @@ -83,9 +83,9 @@ public async Task GivenAValidRequestThenPass() await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyFinishUseCaseLog(); - _fixture.VerifyOperationFailedLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyFinishOperation(); + _fixture.MockLogger.VerifyOperationFailed(0); _fixture.MockRepository.VerifyAddAsync(1); } @@ -100,9 +100,9 @@ public async Task GivenAnInvalidRequestThenFails() await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyFinishUseCaseLog(0); - _fixture.VerifyOperationFailedLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyFinishOperation(0); + _fixture.MockLogger.VerifyOperationFailed(0); _fixture.MockRepository.VerifyAddAsync(0); } @@ -118,9 +118,9 @@ public async Task GivenAValidRequestWhenRepositoryFailsThenFails() await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); // Assert - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyFinishUseCaseLog(); - _fixture.VerifyOperationFailedLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyOperationFailed(); + _fixture.MockLogger.VerifyFinishOperation(); _fixture.MockRepository.VerifyAddAsync(1); } } diff --git a/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs index 10317bd5..17e6d410 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/GetAllNotificationsUseCaseTests.cs @@ -49,9 +49,9 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(1, result.TotalPages); Assert.Equal(totalRecords, result.TotalRecords); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(); } [Fact(DisplayName = nameof(GivenAnInvalidRequestThenFails))] @@ -69,9 +69,9 @@ public async Task GivenAnInvalidRequestThenFails() Assert.NotNull(result.Message); Assert.NotEmpty(result.Message); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(0); } [Fact(DisplayName = nameof(GivenAValidRequestWhenNoNotificationsFoundThenFails))] @@ -91,8 +91,8 @@ public async Task GivenAValidRequestWhenNoNotificationsFoundThenFails() Assert.NotEmpty(result.Message); Assert.Equal("No notifications found.", result.Message); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(1); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(1); + _fixture.MockLogger.VerifyFinishOperation(); } } diff --git a/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs index ab205af1..3fa32f0c 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/GetNotificationUseCaseTest.cs @@ -49,9 +49,9 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(expectedNotification.NotificationStatus, result.Data.NotificationStatus); _fixture.MockRepository.VerifyQueryable(); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(); } [Fact(DisplayName = nameof(GivenAnInvalidRequestThenFails))] @@ -70,9 +70,9 @@ public async Task GivenAnInvalidRequestThenFails() Assert.NotEmpty(result.Message); _fixture.MockRepository.VerifyQueryable(0); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(0); } [Fact(DisplayName = nameof(GivenAValidRequestWhenNotificationNotFoundThenFails))] @@ -93,8 +93,8 @@ public async Task GivenAValidRequestWhenNotificationNotFoundThenFails() Assert.Equal("Notification not found.", result.Message); _fixture.MockRepository.VerifyQueryable(); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(1); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(1); + _fixture.MockLogger.VerifyFinishOperation(); } } diff --git a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs index 819cfa21..15ab43be 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs @@ -3,7 +3,6 @@ using Domain.Orders; using FluentValidation; using FluentValidation.TestHelper; -using Microsoft.Extensions.Logging; using UnitTests.Application.Common; namespace UnitTests.Application.Orders; @@ -72,14 +71,6 @@ public CreateOrderRequest SetValidRequest() public static CreateOrderRequest SetInvalidRequestWithNoItems() => new(Guid.NewGuid(), "AwesomeComputer", []); - -#pragma warning disable CA1848 - public void VerifyCreateOrderLogNoItemsError(int times = 1) => - MockLogger.VerifyLog(l => l.LogWarning("*Order must have at least one item.*"), Times.Exactly(times)); - - public void VerifyFailedToCreateOrderLog(int times = 1) => - MockLogger.VerifyLog(l => l.LogWarning("*Failed to create order.*"), Times.Exactly(times)); -#pragma warning restore CA1848 } public sealed class CreateOrderUseCaseTest : IClassFixture @@ -108,10 +99,10 @@ public async Task GivenAValidRequestThenPass() Assert.True(result.Success); Assert.Null(result.Message); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyFinishUseCaseLog(); - _fixture.VerifyCreateOrderLogNoItemsError(0); - _fixture.VerifyFailedToCreateOrderLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyFinishOperation(); + _fixture.MockLogger.VerifyWarning("Order must have at least one item.", 0); + _fixture.MockLogger.VerifyWarning("Failed to create order.", 0); _fixture.MockRepository.VerifyAddAsync(1); _fixture.VerifyProduce(); } @@ -134,10 +125,10 @@ public async Task GivenAnInvalidRequestThenFails() Assert.NotNull(result.Message); Assert.NotEmpty(result.Message); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyFinishUseCaseLog(0); - _fixture.VerifyCreateOrderLogNoItemsError(0); - _fixture.VerifyFailedToCreateOrderLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyFinishOperation(0); + _fixture.MockLogger.VerifyWarning("Order must have at least one item.", 0); + _fixture.MockLogger.VerifyWarning("Failed to create order.", 0); _fixture.MockRepository.VerifyAddAsync(0); _fixture.VerifyProduce(0); } @@ -161,11 +152,11 @@ public async Task GivenAInvalidRequestThenFailsWhenThereIsNoItems() Assert.NotEmpty(result.Message); Assert.Equal("Order must have at least one item.", result.Message); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyCreateOrderLogNoItemsError(1); - _fixture.VerifyFailedToCreateOrderLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyFinishOperation(); + _fixture.MockLogger.VerifyWarning("Order must have at least one item.", 1); + _fixture.MockLogger.VerifyWarning("Failed to create order.", 0); _fixture.MockRepository.VerifyAddAsync(0); - _fixture.VerifyFinishUseCaseLog(); _fixture.VerifyProduce(); } @@ -189,11 +180,11 @@ public async Task GivenAValidRequestThenFailsWhenRepositoryReturnsZero() Assert.NotEmpty(result.Message); Assert.Equal("Failed to create order.", result.Message); - _fixture.VerifyStartUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyFinishOperation(); + _fixture.MockLogger.VerifyWarning("Order must have at least one item.", 0); + _fixture.MockLogger.VerifyWarning("Failed to create order.", 1); _fixture.MockRepository.VerifyAddAsync(1); - _fixture.VerifyCreateOrderLogNoItemsError(0); - _fixture.VerifyFailedToCreateOrderLog(1); - _fixture.VerifyFinishUseCaseLog(); _fixture.VerifyProduce(); } } diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs b/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs index 711b4397..7a6dbad7 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetAllOrdersUseCaseTest.cs @@ -43,10 +43,10 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(1, result.TotalPages); Assert.Equal(totalRecords, result.TotalRecords); - _fixture.VerifyStartUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(1); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(); } [Fact(DisplayName = nameof(GivenAValidRequestWhenNoOrdersFoundThenFails))] @@ -66,10 +66,10 @@ public async Task GivenAValidRequestWhenNoOrdersFoundThenFails() Assert.NotEmpty(result.Message); Assert.Equal("No orders found.", result.Message); - _fixture.VerifyStartUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(1); - _fixture.VerifyNotFoundLog(1); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyNotFound(1); + _fixture.MockLogger.VerifyFinishOperation(); } [Fact(DisplayName = nameof(GivenAnInvalidRequestThenFails))] @@ -87,9 +87,9 @@ public async Task GivenAnInvalidRequestThenFails() Assert.NotNull(result.Message); Assert.NotEmpty(result.Message); - _fixture.VerifyStartUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); _fixture.MockRepository.VerifyGetAllPaginatedNoIncludes(0); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(0); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(0); } } diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index 9daaaf3b..1189c39b 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -54,9 +54,9 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(expectedOrder.Items?.Count, result.Data.Items!.Count); _fixture.MockRepository.VerifyQueryable(); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(); } [Fact(DisplayName = nameof(GivenAValidRequestWithoutItemsThenPass))] @@ -89,9 +89,9 @@ public async Task GivenAValidRequestWithoutItemsThenPass() Assert.Equal(0, result.Data.Items?.Count); _fixture.MockRepository.VerifyQueryable(); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(); } [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] @@ -110,9 +110,9 @@ public async Task GivenAInvalidRequestThenFails() Assert.NotEmpty(result.Message); _fixture.MockRepository.VerifyQueryable(0); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(0); - _fixture.VerifyFinishUseCaseLog(0); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(0); + _fixture.MockLogger.VerifyFinishOperation(0); } [Fact(DisplayName = nameof(GivenAValidRequestWhenOrderNotFoundThenFails))] @@ -133,8 +133,8 @@ public async Task GivenAValidRequestWhenOrderNotFoundThenFails() Assert.Equal("Order not found.", result.Message); _fixture.MockRepository.VerifyQueryable(); - _fixture.VerifyStartUseCaseLog(); - _fixture.VerifyNotFoundLog(1); - _fixture.VerifyFinishUseCaseLog(); + _fixture.MockLogger.VerifyStartOperation(); + _fixture.MockLogger.VerifyNotFound(1); + _fixture.MockLogger.VerifyFinishOperation(); } } From 8be1245ce2d343213ab6e65cf4407e88c874c4e4 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 24 Feb 2026 07:30:09 -0300 Subject: [PATCH 072/161] chore(53): remove notification table migration and related files --- ...012134409_AddNotificationTable.Designer.cs | 165 ------------------ .../20251012134409_AddNotificationTable.cs | 95 ---------- .../Migrations/MyDbContextModelSnapshot.cs | 162 ----------------- .../src/Infrastructure/Data/MyDbContext.cs | 4 +- .../Infrastructure/Data/MyDbContextFactory.cs | 2 +- 5 files changed, 3 insertions(+), 425 deletions(-) delete mode 100644 templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs delete mode 100644 templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs delete mode 100644 templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs diff --git a/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs b/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs deleted file mode 100644 index 45ba980b..00000000 --- a/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.Designer.cs +++ /dev/null @@ -1,165 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(MyDbContext))] - [Migration("20251012134409_AddNotificationTable")] - partial class AddNotificationTable - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Domain.Notifications.Notification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Message") - .HasMaxLength(4000) - .HasColumnType("varchar"); - - b.Property("NotificationStatus") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.HasKey("Id"); - - b.ToTable("Notification"); - }); - - modelBuilder.Entity("Domain.Orders.Item", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar"); - - b.Property("OrderId") - .HasColumnType("int"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Value") - .HasPrecision(18, 2) - .HasColumnType("decimal(18,2)"); - - b.HasKey("Id"); - - b.HasIndex("OrderId"); - - b.ToTable("Item"); - }); - - modelBuilder.Entity("Domain.Orders.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar"); - - b.Property("Total") - .HasPrecision(18, 2) - .HasColumnType("decimal(18,2)"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.HasKey("Id"); - - b.ToTable("Order"); - }); - - modelBuilder.Entity("Domain.Orders.Item", b => - { - b.HasOne("Domain.Orders.Order", null) - .WithMany("Items") - .HasForeignKey("OrderId"); - }); - - modelBuilder.Entity("Domain.Orders.Order", b => - { - b.Navigation("Items"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs b/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs deleted file mode 100644 index d00d9ef1..00000000 --- a/templates/Full/src/Infrastructure/Data/Migrations/20251012134409_AddNotificationTable.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - /// - public partial class AddNotificationTable : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Notification", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - NotificationType = table.Column(type: "varchar(100)", maxLength: 100, nullable: false), - NotificationStatus = table.Column(type: "varchar(100)", maxLength: 100, nullable: false), - Message = table.Column(type: "varchar(4000)", maxLength: 4000, nullable: true), - CreatedAt = table.Column(type: "datetime2", nullable: false), - CreatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: true), - UpdatedAt = table.Column(type: "datetime2", nullable: false), - UpdatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Notification", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Order", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Description = table.Column(type: "varchar(255)", maxLength: 255, nullable: false), - Total = table.Column(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false), - CreatedAt = table.Column(type: "datetime2", nullable: false), - CreatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: true), - UpdatedAt = table.Column(type: "datetime2", nullable: false), - UpdatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Order", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Item", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Name = table.Column(type: "varchar(200)", maxLength: 200, nullable: false), - Description = table.Column(type: "varchar(255)", maxLength: 255, nullable: false), - Value = table.Column(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false), - OrderId = table.Column(type: "int", nullable: true), - CreatedAt = table.Column(type: "datetime2", nullable: false), - CreatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: true), - UpdatedAt = table.Column(type: "datetime2", nullable: false), - UpdatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Item", x => x.Id); - table.ForeignKey( - name: "FK_Item_Order_OrderId", - column: x => x.OrderId, - principalTable: "Order", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_Item_OrderId", - table: "Item", - column: "OrderId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Item"); - - migrationBuilder.DropTable( - name: "Notification"); - - migrationBuilder.DropTable( - name: "Order"); - } - } -} diff --git a/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs deleted file mode 100644 index aef3491b..00000000 --- a/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs +++ /dev/null @@ -1,162 +0,0 @@ -// -using System; -using Infrastructure.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Infrastructure.Data.Migrations -{ - [DbContext(typeof(MyDbContext))] - partial class MyDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Domain.Notifications.Notification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Message") - .HasMaxLength(4000) - .HasColumnType("varchar"); - - b.Property("NotificationStatus") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.HasKey("Id"); - - b.ToTable("Notification"); - }); - - modelBuilder.Entity("Domain.Orders.Item", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("varchar"); - - b.Property("OrderId") - .HasColumnType("int"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Value") - .HasPrecision(18, 2) - .HasColumnType("decimal(18,2)"); - - b.HasKey("Id"); - - b.HasIndex("OrderId"); - - b.ToTable("Item"); - }); - - modelBuilder.Entity("Domain.Orders.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("varchar"); - - b.Property("Total") - .HasPrecision(18, 2) - .HasColumnType("decimal(18,2)"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasMaxLength(100) - .HasColumnType("varchar"); - - b.HasKey("Id"); - - b.ToTable("Order"); - }); - - modelBuilder.Entity("Domain.Orders.Item", b => - { - b.HasOne("Domain.Orders.Order", null) - .WithMany("Items") - .HasForeignKey("OrderId"); - }); - - modelBuilder.Entity("Domain.Orders.Order", b => - { - b.Navigation("Items"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/templates/Full/src/Infrastructure/Data/MyDbContext.cs b/templates/Full/src/Infrastructure/Data/MyDbContext.cs index c839710e..9e7e8480 100644 --- a/templates/Full/src/Infrastructure/Data/MyDbContext.cs +++ b/templates/Full/src/Infrastructure/Data/MyDbContext.cs @@ -14,7 +14,7 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura { configurationBuilder .Properties() - .HaveColumnType("varchar") + .HaveColumnType("text") .HaveMaxLength(100); configurationBuilder @@ -27,6 +27,6 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura configurationBuilder .Properties() - .HaveColumnType("datetime2"); + .HaveColumnType("timestamp without time zone"); } } diff --git a/templates/Full/src/Infrastructure/Data/MyDbContextFactory.cs b/templates/Full/src/Infrastructure/Data/MyDbContextFactory.cs index 270434b9..e608ad61 100644 --- a/templates/Full/src/Infrastructure/Data/MyDbContextFactory.cs +++ b/templates/Full/src/Infrastructure/Data/MyDbContextFactory.cs @@ -15,7 +15,7 @@ public MyDbContext CreateDbContext(string[] args) { var builder = new DbContextOptionsBuilder(); - builder.UseSqlServer("Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=yourStrong(!)Password;TrustServerCertificate=true;"); + builder.UseNpgsql("Host=127.0.0.1;Port=5432;Database=OrderDb;Username=postgres;Password=yourStrong(!)Password"); return new MyDbContext(builder.Options); } From 84fc31d2c38d201d5b7d8512fa42fbdf8d5a8136 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 24 Feb 2026 07:33:43 -0300 Subject: [PATCH 073/161] feat(53): replace SQL Server health checks with PostgreSQL --- templates/Full/Directory.Packages.props | 5 +- .../src/Infrastructure/Infrastructure.csproj | 2 +- .../src/Infrastructure/packages.lock.json | 222 +++-------------- templates/Full/src/WebApp/WebApp.csproj | 2 +- templates/Full/src/WebApp/appsettings.json | 2 +- templates/Full/src/WebApp/packages.lock.json | 206 +++------------- .../tests/IntegrationTests/packages.lock.json | 223 +++--------------- .../Full/tests/UnitTests/packages.lock.json | 23 +- 8 files changed, 110 insertions(+), 575 deletions(-) diff --git a/templates/Full/Directory.Packages.props b/templates/Full/Directory.Packages.props index c070cd01..0144dab3 100644 --- a/templates/Full/Directory.Packages.props +++ b/templates/Full/Directory.Packages.props @@ -8,7 +8,7 @@ - + @@ -20,7 +20,8 @@ - + + diff --git a/templates/Full/src/Infrastructure/Infrastructure.csproj b/templates/Full/src/Infrastructure/Infrastructure.csproj index f760b0e3..5884fe46 100644 --- a/templates/Full/src/Infrastructure/Infrastructure.csproj +++ b/templates/Full/src/Infrastructure/Infrastructure.csproj @@ -7,7 +7,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json index 91088649..7e65cc64 100644 --- a/templates/Full/src/Infrastructure/packages.lock.json +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -22,19 +22,6 @@ "Newtonsoft.Json": "13.0.3" } }, - "Microsoft.EntityFrameworkCore.SqlServer": { - "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "x4ZO21dxzUeA2wvH5sdNBVVHuH+9+3DF3VV2pdENyHEZz+u0AyMi1a/NHzytPhn6IXBhqA8n6y9FAsdUWp8jyA==", - "dependencies": { - "Microsoft.Data.SqlClient": "6.1.1", - "Microsoft.EntityFrameworkCore.Relational": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" - } - }, "Microsoft.Extensions.Caching.Hybrid": { "type": "Direct", "requested": "[10.1.0, )", @@ -59,6 +46,17 @@ "StackExchange.Redis": "2.7.27" } }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "E2+uSWxSB8LdsUVwPaqRWOcGOP92biry2JEwc0KJMdLJF+aZdczeIdEXVwEyv4nSVMQJH0o8tLhyAMiR6VF0lw==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[10.0.0, 11.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[10.0.0, 11.0.0)", + "Npgsql": "10.0.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "Direct", "requested": "[1.14.0, )", @@ -148,41 +146,11 @@ "RabbitMQ.Client": "7.2.0" } }, - "Azure.Core": { - "type": "Transitive", - "resolved": "1.47.1", - "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.ClientModel": "1.5.1", - "System.Memory.Data": "8.0.1" - } - }, - "Azure.Identity": { - "type": "Transitive", - "resolved": "1.14.2", - "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", - "dependencies": { - "Azure.Core": "1.46.1", - "Microsoft.Identity.Client": "4.73.1", - "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" - } - }, "Humanizer.Core": { "type": "Transitive", "resolved": "2.14.1", "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" - }, - "Microsoft.Bcl.Cryptography": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" - }, "Microsoft.Build.Framework": { "type": "Transitive", "resolved": "18.0.2", @@ -253,28 +221,6 @@ "System.Composition": "9.0.0" } }, - "Microsoft.Data.SqlClient": { - "type": "Transitive", - "resolved": "6.1.1", - "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", - "dependencies": { - "Azure.Core": "1.47.1", - "Azure.Identity": "1.14.2", - "Microsoft.Bcl.Cryptography": "9.0.4", - "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", - "Microsoft.Extensions.Caching.Memory": "9.0.4", - "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", - "Microsoft.SqlServer.Server": "1.0.0", - "System.Configuration.ConfigurationManager": "9.0.4", - "System.Security.Cryptography.Pkcs": "9.0.4" - } - }, - "Microsoft.Data.SqlClient.SNI.runtime": { - "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" - }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -285,17 +231,6 @@ "resolved": "10.0.2", "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" - } - }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -430,74 +365,6 @@ "resolved": "10.0.2", "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, - "Microsoft.Identity.Client": { - "type": "Transitive", - "resolved": "4.73.1", - "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0" - } - }, - "Microsoft.Identity.Client.Extensions.Msal": { - "type": "Transitive", - "resolved": "4.73.1", - "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", - "dependencies": { - "Microsoft.Identity.Client": "4.73.1", - "System.Security.Cryptography.ProtectedData": "4.5.0" - } - }, - "Microsoft.IdentityModel.Abstractions": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" - }, - "Microsoft.IdentityModel.JsonWebTokens": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "Microsoft.IdentityModel.Logging": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "7.7.1" - } - }, - "Microsoft.IdentityModel.Protocols": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "7.7.1", - "System.IdentityModel.Tokens.Jwt": "7.7.1" - } - }, - "Microsoft.IdentityModel.Tokens": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "7.7.1" - } - }, - "Microsoft.SqlServer.Server": { - "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" - }, "Microsoft.VisualStudio.SolutionPersistence": { "type": "Transitive", "resolved": "1.0.52", @@ -511,6 +378,14 @@ "System.CodeDom": "6.0.0" } }, + "Npgsql": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "xZAYhPOU2rUIFpV48xsqhCx9vXs6Y+0jX2LCoSEfDFYMw9jtAOUk3iQsCnDLrFIv9NT3JGMihn7nnuZsPKqJmA==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "10.0.0" + } + }, "OpenTelemetry": { "type": "Transitive", "resolved": "1.14.0", @@ -549,15 +424,6 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.ClientModel": { - "type": "Transitive", - "resolved": "1.5.1", - "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "8.0.3", - "System.Memory.Data": "8.0.1" - } - }, "System.CodeDom": { "type": "Transitive", "resolved": "6.0.0", @@ -611,44 +477,6 @@ "System.Composition.Runtime": "9.0.0" } }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", - "dependencies": { - "System.Diagnostics.EventLog": "9.0.4", - "System.Security.Cryptography.ProtectedData": "9.0.4" - } - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "getRQEXD8idlpb1KW56XuxImMy0FKp2WJPDf3Qr0kI/QKxxJSftqfDFVo0DZ3HCJRLU73qHSruv5q2l5O47jQQ==" - }, - "System.IdentityModel.Tokens.Jwt": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", - "dependencies": { - "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "System.Memory.Data": { - "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" - }, "System.Threading.RateLimiting": { "type": "Transitive", "resolved": "8.0.0", @@ -698,6 +526,18 @@ "Microsoft.Extensions.Logging": "10.0.2" } }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "CentralTransitive", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" + } + }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", "requested": "[10.0.2, )", diff --git a/templates/Full/src/WebApp/WebApp.csproj b/templates/Full/src/WebApp/WebApp.csproj index 53d8f64a..077dffdd 100644 --- a/templates/Full/src/WebApp/WebApp.csproj +++ b/templates/Full/src/WebApp/WebApp.csproj @@ -1,7 +1,7 @@  - + diff --git a/templates/Full/src/WebApp/appsettings.json b/templates/Full/src/WebApp/appsettings.json index 770f1bb3..2c926dd0 100644 --- a/templates/Full/src/WebApp/appsettings.json +++ b/templates/Full/src/WebApp/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", + "OrderDb": "Host=127.0.0.1;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES", "Redis": "localhost:6379", "RabbitMq": "amqp://guest:guest@localhost:5672/" } diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json index 0109119f..ac352767 100644 --- a/templates/Full/src/WebApp/packages.lock.json +++ b/templates/Full/src/WebApp/packages.lock.json @@ -2,31 +2,31 @@ "version": 2, "dependencies": { "net10.0": { - "AspNetCore.HealthChecks.Rabbitmq": { + "AspNetCore.HealthChecks.NpgSql": { "type": "Direct", "requested": "[9.0.0, )", "resolved": "9.0.0", - "contentHash": "7WSQ7EwioA5niakzzLtGVcZMEOh+42fSwrI24vnNsT7gZuVGOViNekyz38G6wBPYKcpL/lUkMdg3ZaCiZTi/Dw==", + "contentHash": "npc58/AD5zuVxERdhCl2Kb7WnL37mwX42SJcXIwvmEig0/dugOLg3SIwtfvvh3TnvTwR/sk5LYNkkPaBdks61A==", "dependencies": { - "RabbitMQ.Client": "7.0.0" + "Npgsql": "8.0.3" } }, - "AspNetCore.HealthChecks.Redis": { + "AspNetCore.HealthChecks.Rabbitmq": { "type": "Direct", "requested": "[9.0.0, )", "resolved": "9.0.0", - "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", + "contentHash": "7WSQ7EwioA5niakzzLtGVcZMEOh+42fSwrI24vnNsT7gZuVGOViNekyz38G6wBPYKcpL/lUkMdg3ZaCiZTi/Dw==", "dependencies": { - "StackExchange.Redis": "2.7.4" + "RabbitMQ.Client": "7.0.0" } }, - "AspNetCore.HealthChecks.SqlServer": { + "AspNetCore.HealthChecks.Redis": { "type": "Direct", "requested": "[9.0.0, )", "resolved": "9.0.0", - "contentHash": "UxCf65iCF2nU1u7AcB320abjL4CRg5swCgJECY6mKk1j5lrGMfVtskWwriGs1T29pYdRig9Vra3SPnP+4G82pA==", + "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", "dependencies": { - "Microsoft.Data.SqlClient": "5.2.2" + "StackExchange.Redis": "2.7.4" } }, "AspNetCore.HealthChecks.UI.Client": { @@ -54,26 +54,6 @@ "resolved": "9.0.0", "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==" }, - "Azure.Core": { - "type": "Transitive", - "resolved": "1.47.1", - "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.ClientModel": "1.5.1", - "System.Memory.Data": "8.0.1" - } - }, - "Azure.Identity": { - "type": "Transitive", - "resolved": "1.14.2", - "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", - "dependencies": { - "Azure.Core": "1.46.1", - "Microsoft.Identity.Client": "4.73.1", - "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" - } - }, "Google.Protobuf": { "type": "Transitive", "resolved": "3.31.1", @@ -130,37 +110,6 @@ "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" - }, - "Microsoft.Bcl.Cryptography": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" - }, - "Microsoft.Data.SqlClient": { - "type": "Transitive", - "resolved": "6.1.1", - "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", - "dependencies": { - "Azure.Core": "1.47.1", - "Azure.Identity": "1.14.2", - "Microsoft.Bcl.Cryptography": "9.0.4", - "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", - "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", - "Microsoft.SqlServer.Server": "1.0.0", - "System.Configuration.ConfigurationManager": "9.0.4", - "System.Security.Cryptography.Pkcs": "9.0.4" - } - }, - "Microsoft.Data.SqlClient.SNI.runtime": { - "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" - }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -171,81 +120,10 @@ "resolved": "10.0.2", "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, - "Microsoft.EntityFrameworkCore.Relational": { + "Npgsql": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2" - } - }, - "Microsoft.Identity.Client": { - "type": "Transitive", - "resolved": "4.73.1", - "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0" - } - }, - "Microsoft.Identity.Client.Extensions.Msal": { - "type": "Transitive", - "resolved": "4.73.1", - "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", - "dependencies": { - "Microsoft.Identity.Client": "4.73.1", - "System.Security.Cryptography.ProtectedData": "4.5.0" - } - }, - "Microsoft.IdentityModel.Abstractions": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" - }, - "Microsoft.IdentityModel.JsonWebTokens": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "Microsoft.IdentityModel.Logging": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "7.7.1" - } - }, - "Microsoft.IdentityModel.Protocols": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "7.7.1", - "System.IdentityModel.Tokens.Jwt": "7.7.1" - } - }, - "Microsoft.IdentityModel.Tokens": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "7.7.1" - } - }, - "Microsoft.SqlServer.Server": { - "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + "resolved": "10.0.0", + "contentHash": "xZAYhPOU2rUIFpV48xsqhCx9vXs6Y+0jX2LCoSEfDFYMw9jtAOUk3iQsCnDLrFIv9NT3JGMihn7nnuZsPKqJmA==" }, "OpenTelemetry": { "type": "Transitive", @@ -281,46 +159,6 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.ClientModel": { - "type": "Transitive", - "resolved": "1.5.1", - "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", - "dependencies": { - "System.Memory.Data": "8.0.1" - } - }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", - "dependencies": { - "System.Security.Cryptography.ProtectedData": "9.0.4" - } - }, - "System.IdentityModel.Tokens.Jwt": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", - "dependencies": { - "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "System.Memory.Data": { - "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" - }, "application": { "type": "Project", "dependencies": { @@ -337,9 +175,9 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", @@ -376,14 +214,13 @@ "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2" } }, - "Microsoft.EntityFrameworkCore.SqlServer": { + "Microsoft.EntityFrameworkCore.Relational": { "type": "CentralTransitive", "requested": "[10.0.2, )", "resolved": "10.0.2", - "contentHash": "x4ZO21dxzUeA2wvH5sdNBVVHuH+9+3DF3VV2pdENyHEZz+u0AyMi1a/NHzytPhn6IXBhqA8n6y9FAsdUWp8jyA==", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", "dependencies": { - "Microsoft.Data.SqlClient": "6.1.1", - "Microsoft.EntityFrameworkCore.Relational": "10.0.2" + "Microsoft.EntityFrameworkCore": "10.0.2" } }, "Microsoft.Extensions.Caching.Hybrid": { @@ -401,6 +238,17 @@ "StackExchange.Redis": "2.7.27" } }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "E2+uSWxSB8LdsUVwPaqRWOcGOP92biry2JEwc0KJMdLJF+aZdczeIdEXVwEyv4nSVMQJH0o8tLhyAMiR6VF0lw==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[10.0.0, 11.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[10.0.0, 11.0.0)", + "Npgsql": "10.0.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "CentralTransitive", "requested": "[1.14.0, )", diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json index 2adc5196..07cd6abe 100644 --- a/templates/Full/tests/IntegrationTests/packages.lock.json +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -60,26 +60,6 @@ "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" } }, - "Azure.Core": { - "type": "Transitive", - "resolved": "1.47.1", - "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.ClientModel": "1.5.1", - "System.Memory.Data": "8.0.1" - } - }, - "Azure.Identity": { - "type": "Transitive", - "resolved": "1.14.2", - "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", - "dependencies": { - "Azure.Core": "1.46.1", - "Microsoft.Identity.Client": "4.73.1", - "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" - } - }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -146,43 +126,11 @@ "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" - }, - "Microsoft.Bcl.Cryptography": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" - }, "Microsoft.CodeCoverage": { "type": "Transitive", "resolved": "18.0.1", "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" }, - "Microsoft.Data.SqlClient": { - "type": "Transitive", - "resolved": "6.1.1", - "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", - "dependencies": { - "Azure.Core": "1.47.1", - "Azure.Identity": "1.14.2", - "Microsoft.Bcl.Cryptography": "9.0.4", - "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", - "Microsoft.Extensions.Caching.Memory": "9.0.4", - "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", - "Microsoft.SqlServer.Server": "1.0.0", - "System.Configuration.ConfigurationManager": "9.0.4", - "System.Security.Cryptography.Pkcs": "9.0.4" - } - }, - "Microsoft.Data.SqlClient.SNI.runtime": { - "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" - }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -193,17 +141,6 @@ "resolved": "10.0.2", "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" - } - }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -519,79 +456,11 @@ "resolved": "10.0.2", "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, - "Microsoft.Identity.Client": { - "type": "Transitive", - "resolved": "4.73.1", - "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0" - } - }, - "Microsoft.Identity.Client.Extensions.Msal": { - "type": "Transitive", - "resolved": "4.73.1", - "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", - "dependencies": { - "Microsoft.Identity.Client": "4.73.1", - "System.Security.Cryptography.ProtectedData": "4.5.0" - } - }, - "Microsoft.IdentityModel.Abstractions": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" - }, - "Microsoft.IdentityModel.JsonWebTokens": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "Microsoft.IdentityModel.Logging": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "7.7.1" - } - }, - "Microsoft.IdentityModel.Protocols": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "7.7.1", - "System.IdentityModel.Tokens.Jwt": "7.7.1" - } - }, - "Microsoft.IdentityModel.Tokens": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "7.7.1" - } - }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, - "Microsoft.SqlServer.Server": { - "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" - }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", "resolved": "18.0.1", @@ -614,6 +483,14 @@ "Microsoft.NETCore.Platforms": "1.1.0" } }, + "Npgsql": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "xZAYhPOU2rUIFpV48xsqhCx9vXs6Y+0jX2LCoSEfDFYMw9jtAOUk3iQsCnDLrFIv9NT3JGMihn7nnuZsPKqJmA==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "10.0.0" + } + }, "OpenTelemetry": { "type": "Transitive", "resolved": "1.14.0", @@ -652,53 +529,11 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.ClientModel": { - "type": "Transitive", - "resolved": "1.5.1", - "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "8.0.3", - "System.Memory.Data": "8.0.1" - } - }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", - "dependencies": { - "System.Diagnostics.EventLog": "9.0.4", - "System.Security.Cryptography.ProtectedData": "9.0.4" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "10.0.2", "contentHash": "TJPpTLF5MFPobq09c9BQ5X8QuviQfsKvH0Jbm7MkGylGvfIdRqJQLZDPC5sMRFkk9aZhmgir1NJKekip2NxfaA==" }, - "System.IdentityModel.Tokens.Jwt": { - "type": "Transitive", - "resolved": "7.7.1", - "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", - "dependencies": { - "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", - "Microsoft.IdentityModel.Tokens": "7.7.1" - } - }, - "System.Memory.Data": { - "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "9.0.4", - "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" - }, "System.Threading.RateLimiting": { "type": "Transitive", "resolved": "8.0.0", @@ -764,9 +599,9 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", @@ -781,42 +616,42 @@ "webapp": { "type": "Project", "dependencies": { + "AspNetCore.HealthChecks.Npgsql": "[9.0.0, )", "AspNetCore.HealthChecks.RabbitMQ": "[9.0.0, )", "AspNetCore.HealthChecks.Redis": "[9.0.0, )", - "AspNetCore.HealthChecks.SqlServer": "[9.0.0, )", "AspNetCore.HealthChecks.UI.Client": "[9.0.0, )", "Grpc.AspNetCore": "[2.76.0, )", "Infrastructure": "[1.0.0, )" } }, - "AspNetCore.HealthChecks.Rabbitmq": { + "AspNetCore.HealthChecks.NpgSql": { "type": "CentralTransitive", "requested": "[9.0.0, )", "resolved": "9.0.0", - "contentHash": "7WSQ7EwioA5niakzzLtGVcZMEOh+42fSwrI24vnNsT7gZuVGOViNekyz38G6wBPYKcpL/lUkMdg3ZaCiZTi/Dw==", + "contentHash": "npc58/AD5zuVxERdhCl2Kb7WnL37mwX42SJcXIwvmEig0/dugOLg3SIwtfvvh3TnvTwR/sk5LYNkkPaBdks61A==", "dependencies": { "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11", - "RabbitMQ.Client": "7.0.0" + "Npgsql": "8.0.3" } }, - "AspNetCore.HealthChecks.Redis": { + "AspNetCore.HealthChecks.Rabbitmq": { "type": "CentralTransitive", "requested": "[9.0.0, )", "resolved": "9.0.0", - "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", + "contentHash": "7WSQ7EwioA5niakzzLtGVcZMEOh+42fSwrI24vnNsT7gZuVGOViNekyz38G6wBPYKcpL/lUkMdg3ZaCiZTi/Dw==", "dependencies": { "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11", - "StackExchange.Redis": "2.7.4" + "RabbitMQ.Client": "7.0.0" } }, - "AspNetCore.HealthChecks.SqlServer": { + "AspNetCore.HealthChecks.Redis": { "type": "CentralTransitive", "requested": "[9.0.0, )", "resolved": "9.0.0", - "contentHash": "UxCf65iCF2nU1u7AcB320abjL4CRg5swCgJECY6mKk1j5lrGMfVtskWwriGs1T29pYdRig9Vra3SPnP+4G82pA==", + "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==", "dependencies": { - "Microsoft.Data.SqlClient": "5.2.2", - "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11", + "StackExchange.Redis": "2.7.4" } }, "AspNetCore.HealthChecks.UI.Client": { @@ -876,14 +711,13 @@ "Microsoft.Extensions.Logging": "10.0.2" } }, - "Microsoft.EntityFrameworkCore.SqlServer": { + "Microsoft.EntityFrameworkCore.Relational": { "type": "CentralTransitive", "requested": "[10.0.2, )", "resolved": "10.0.2", - "contentHash": "x4ZO21dxzUeA2wvH5sdNBVVHuH+9+3DF3VV2pdENyHEZz+u0AyMi1a/NHzytPhn6IXBhqA8n6y9FAsdUWp8jyA==", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", "dependencies": { - "Microsoft.Data.SqlClient": "6.1.1", - "Microsoft.EntityFrameworkCore.Relational": "10.0.2", + "Microsoft.EntityFrameworkCore": "10.0.2", "Microsoft.Extensions.Caching.Memory": "10.0.2", "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", "Microsoft.Extensions.Logging": "10.0.2" @@ -939,6 +773,17 @@ "resolved": "13.0.4", "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" }, + "Npgsql.EntityFrameworkCore.PostgreSQL": { + "type": "CentralTransitive", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "E2+uSWxSB8LdsUVwPaqRWOcGOP92biry2JEwc0KJMdLJF+aZdczeIdEXVwEyv4nSVMQJH0o8tLhyAMiR6VF0lw==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[10.0.0, 11.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[10.0.0, 11.0.0)", + "Npgsql": "10.0.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "CentralTransitive", "requested": "[1.14.0, )", diff --git a/templates/Full/tests/UnitTests/packages.lock.json b/templates/Full/tests/UnitTests/packages.lock.json index 0cbf03b3..dcc6b5d9 100644 --- a/templates/Full/tests/UnitTests/packages.lock.json +++ b/templates/Full/tests/UnitTests/packages.lock.json @@ -104,17 +104,6 @@ "resolved": "10.0.2", "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, - "Microsoft.EntityFrameworkCore.Relational": { - "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" - } - }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -315,6 +304,18 @@ "Microsoft.Extensions.Logging": "10.0.2" } }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "CentralTransitive", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.2", + "Microsoft.Extensions.Caching.Memory": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2" + } + }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", "requested": "[10.0.2, )", From e9b6f1c2cef0489195a361b162cc9492111d3671 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 24 Feb 2026 07:34:07 -0300 Subject: [PATCH 074/161] feat(53): migrate from SQL Server to PostgreSQL in docker setup --- templates/Full/docker-compose-load-tests.yml | 12 +- templates/Full/docker-compose.yml | 41 ++++-- templates/Full/scripts/migrations.sql | 123 +++++++++--------- templates/Full/scripts/seeds.sql | 74 +++++------ .../InfrastructureDataDependencyInjection.cs | 2 +- .../Extensions/HealthCheckExtensions.cs | 4 +- .../src/WebApp/Properties/launchSettings.json | 2 +- .../Common/CustomWebApplicationFactory.cs | 6 +- 8 files changed, 138 insertions(+), 126 deletions(-) diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 4fecc816..45fcf397 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -24,7 +24,7 @@ services: ports: - "5000:5000" depends_on: - sqlserver: + postgres: condition: service_healthy redis: condition: service_healthy @@ -51,21 +51,21 @@ services: - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - - ConnectionStrings__OrderDb=Server=sqlserver,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true; + - ConnectionStrings__OrderDb=Host=postgres;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES - ConnectionStrings__Redis=redis:6379 - ConnectionStrings__RabbitMQ=amqp://guest:guest@rabbitmq:5672/ networks: - hexagonal_solution_template_network - sqlserver: + postgres: extends: file: docker-compose.yml - service: sqlserver + service: postgres - mssqltools: + postgres-init: extends: file: docker-compose.yml - service: mssqltools + service: postgres-init aspire-dashboard: extends: diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index c82c113d..780e62f0 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -1,27 +1,40 @@ name: hexagonal-solution-template-full services: - sqlserver: - image: mcr.microsoft.com/mssql/server:2025-latest + postgres: + image: postgres:17-alpine environment: - SA_PASSWORD: "cY5VvZkkh4AzES" - ACCEPT_EULA: "Y" - MSSQL_PID: "Developer" + POSTGRES_PASSWORD: "cY5VvZkkh4AzES" + POSTGRES_USER: "postgres" + POSTGRES_DB: "OrderDb" networks: - hexagonal_solution_template_network healthcheck: - test: /opt/mssql-tools18/bin/sqlcmd -U sa -P "cY5VvZkkh4AzES" -C -Q "SELECT 1" || exit 1 + test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 3s timeout: 10s retries: 10 start_period: 15s ports: - - "1433:1433" + - "5432:5432" - mssqltools: - image: mcr.microsoft.com/mssql-tools + pgadmin: + image: dpage/pgadmin4:latest + environment: + PGADMIN_DEFAULT_EMAIL: "admin@admin.com" + PGADMIN_DEFAULT_PASSWORD: "admin" + depends_on: + postgres: + condition: service_healthy + ports: + - "5050:80" + networks: + - hexagonal_solution_template_network + + postgres-init: + image: postgres:17-alpine depends_on: - sqlserver: + postgres: condition: service_healthy networks: - hexagonal_solution_template_network @@ -29,11 +42,13 @@ services: - ./scripts/migrations.sql:/tmp/migrations.sql - ./scripts/seeds.sql:/tmp/seeds.sql command: - - /bin/bash + - /bin/sh - -c - | - ./opt/mssql-tools/bin/sqlcmd -S sqlserver -U sa -P "cY5VvZkkh4AzES" -C -d master -i ./tmp/migrations.sql && - ./opt/mssql-tools/bin/sqlcmd -S sqlserver -U sa -P "cY5VvZkkh4AzES" -C -d master -i ./tmp/seeds.sql + psql -h postgres -U postgres -d postgres -f /tmp/migrations.sql && + psql -h postgres -U postgres -d postgres -f /tmp/seeds.sql + environment: + PGPASSWORD: "cY5VvZkkh4AzES" aspire-dashboard: image: mcr.microsoft.com/dotnet/aspire-dashboard:13 diff --git a/templates/Full/scripts/migrations.sql b/templates/Full/scripts/migrations.sql index e1134f30..545354ba 100644 --- a/templates/Full/scripts/migrations.sql +++ b/templates/Full/scripts/migrations.sql @@ -1,73 +1,70 @@ -IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = 'OrderDb') -BEGIN - CREATE DATABASE [OrderDb] -END -GO - -USE [OrderDb] -GO +-- PostgreSQL Migration Script +-- Create database if not exists (handled by Docker environment variable POSTGRES_DB) +-- Create EFMigrationsHistory table if not exists +CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( + "MigrationId" VARCHAR(150) NOT NULL, + "ProductVersion" VARCHAR(32) NOT NULL, + CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") +); -IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL -BEGIN - CREATE TABLE [__EFMigrationsHistory] ( - [MigrationId] nvarchar(150) NOT NULL, - [ProductVersion] nvarchar(32) NOT NULL, - CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId]) - ); -END; -GO +-- Begin transaction +BEGIN; -BEGIN TRANSACTION; -IF NOT EXISTS ( - SELECT * FROM [__EFMigrationsHistory] - WHERE [MigrationId] = N'20251012134409_AddNotificationTable' -) +-- Check if migration already applied +DO $$ BEGIN - CREATE TABLE [Notification] ( - [Id] int NOT NULL IDENTITY, - [NotificationType] varchar(100) NOT NULL, - [NotificationStatus] varchar(100) NOT NULL, - [Message] varchar(4000) NULL, - [CreatedAt] datetime2 NOT NULL, - [CreatedBy] varchar(100) NULL, - [UpdatedAt] datetime2 NOT NULL, - [UpdatedBy] varchar(100) NULL, - CONSTRAINT [PK_Notification] PRIMARY KEY ([Id]) - ); + IF NOT EXISTS ( + SELECT 1 FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251012134409_AddNotificationTable' + ) THEN + -- Create Notification table + CREATE TABLE "Notification" ( + "Id" INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, + "NotificationType" VARCHAR(100) NOT NULL, + "NotificationStatus" VARCHAR(100) NOT NULL, + "Message" VARCHAR(4000) NULL, + "CreatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, + "CreatedBy" VARCHAR(100) NULL, + "UpdatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, + "UpdatedBy" VARCHAR(100) NULL, + CONSTRAINT "PK_Notification" PRIMARY KEY ("Id") + ); - CREATE TABLE [Order] ( - [Id] int NOT NULL IDENTITY, - [Description] varchar(255) NOT NULL, - [Total] decimal(18,2) NOT NULL, - [CreatedAt] datetime2 NOT NULL, - [CreatedBy] varchar(100) NULL, - [UpdatedAt] datetime2 NOT NULL, - [UpdatedBy] varchar(100) NULL, - CONSTRAINT [PK_Order] PRIMARY KEY ([Id]) - ); + -- Create Order table + CREATE TABLE "Order" ( + "Id" INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, + "Description" VARCHAR(255) NOT NULL, + "Total" NUMERIC(18,2) NOT NULL, + "CreatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, + "CreatedBy" VARCHAR(100) NULL, + "UpdatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, + "UpdatedBy" VARCHAR(100) NULL, + CONSTRAINT "PK_Order" PRIMARY KEY ("Id") + ); - CREATE TABLE [Item] ( - [Id] int NOT NULL IDENTITY, - [Name] varchar(200) NOT NULL, - [Description] varchar(255) NOT NULL, - [Value] decimal(18,2) NOT NULL, - [OrderId] int NULL, - [CreatedAt] datetime2 NOT NULL, - [CreatedBy] varchar(100) NULL, - [UpdatedAt] datetime2 NOT NULL, - [UpdatedBy] varchar(100) NULL, - CONSTRAINT [PK_Item] PRIMARY KEY ([Id]), - CONSTRAINT [FK_Item_Order_OrderId] FOREIGN KEY ([OrderId]) REFERENCES [Order] ([Id]) - ); + -- Create Item table + CREATE TABLE "Item" ( + "Id" INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, + "Name" VARCHAR(200) NOT NULL, + "Description" VARCHAR(255) NOT NULL, + "Value" NUMERIC(18,2) NOT NULL, + "OrderId" INTEGER NULL, + "CreatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, + "CreatedBy" VARCHAR(100) NULL, + "UpdatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, + "UpdatedBy" VARCHAR(100) NULL, + CONSTRAINT "PK_Item" PRIMARY KEY ("Id"), + CONSTRAINT "FK_Item_Order_OrderId" FOREIGN KEY ("OrderId") REFERENCES "Order" ("Id") + ); - CREATE INDEX [IX_Item_OrderId] ON [Item] ([OrderId]); + -- Create index + CREATE INDEX "IX_Item_OrderId" ON "Item" ("OrderId"); - INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) - VALUES (N'20251012134409_AddNotificationTable', N'9.0.8'); - -END; + -- Insert migration record + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251012134409_AddNotificationTable', '9.0.8'); + END IF; +END $$; COMMIT; -GO - diff --git a/templates/Full/scripts/seeds.sql b/templates/Full/scripts/seeds.sql index c0c42ea0..77b275fc 100644 --- a/templates/Full/scripts/seeds.sql +++ b/templates/Full/scripts/seeds.sql @@ -1,37 +1,37 @@ -USE [OrderDb] -GO - -SET IDENTITY_INSERT [Order] ON -GO - -IF NOT EXISTS (SELECT TOP 1 * FROM [Order] WHERE [Id] = 1) BEGIN - INSERT INTO [Order] ([Id], [Description], [Total], [CreatedAt], [UpdatedAt]) - VALUES (1, 'XPTO Client Computers', 1000.00, GETDATE(), GETDATE()); -END -GO - -SET IDENTITY_INSERT [Order] OFF -GO - -SET IDENTITY_INSERT [Item] ON -GO - -IF NOT EXISTS (SELECT TOP 1 * FROM [Item] WHERE [Id] = 1 ) BEGIN - INSERT INTO [Item] ([Id], [Name], [Description], [Value], [OrderId], [CreatedAt], [UpdatedAt]) - VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 999.00, 1, GETDATE(), GETDATE()); -END -GO - -SET IDENTITY_INSERT [Item] OFF -GO - -SET IDENTITY_INSERT [Notification] ON - -IF NOT EXISTS (SELECT TOP 1 * FROM [Notification] WHERE [Id] = 1 ) BEGIN - INSERT INTO [Notification] ([Id], [NotificationType], [NotificationStatus], [CreatedAt], [UpdatedAt]) - VALUES (1, 'OrderCreated', 'Created', GETDATE(), GETDATE()); -END -GO - -SET IDENTITY_INSERT [Notification] OFF -GO +-- PostgreSQL Seed Data Script + +-- Insert Order with explicit ID +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM "Order" WHERE "Id" = 1 LIMIT 1) THEN + INSERT INTO "Order" ("Id", "Description", "Total", "CreatedAt", "UpdatedAt") + VALUES (1, 'XPTO Client Computers', 1000.00, NOW(), NOW()); + + -- Update sequence to prevent ID conflicts + SELECT setval(pg_get_serial_sequence('"Order"', 'Id'), (SELECT MAX("Id") FROM "Order")); + END IF; +END $$; + +-- Insert Item with explicit ID +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM "Item" WHERE "Id" = 1 LIMIT 1) THEN + INSERT INTO "Item" ("Id", "Name", "Description", "Value", "OrderId", "CreatedAt", "UpdatedAt") + VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 999.00, 1, NOW(), NOW()); + + -- Update sequence to prevent ID conflicts + SELECT setval(pg_get_serial_sequence('"Item"', 'Id'), (SELECT MAX("Id") FROM "Item")); + END IF; +END $$; + +-- Insert Notification with explicit ID +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM "Notification" WHERE "Id" = 1 LIMIT 1) THEN + INSERT INTO "Notification" ("Id", "NotificationType", "NotificationStatus", "CreatedAt", "UpdatedAt") + VALUES (1, 'OrderCreated', 'Created', NOW(), NOW()); + + -- Update sequence to prevent ID conflicts + SELECT setval(pg_get_serial_sequence('"Notification"', 'Id'), (SELECT MAX("Id") FROM "Notification")); + END IF; +END $$; diff --git a/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs b/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs index 3d16e29a..028724a5 100644 --- a/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/Data/InfrastructureDataDependencyInjection.cs @@ -18,7 +18,7 @@ out var parsedValue services.AddPooledDbContextFactory(options => { - options.UseSqlServer( + options.UseNpgsql( configuration.GetConnectionString("OrderDb") ?? throw new InvalidOperationException("OrderDb connection string is not configured.") ); options.EnableSensitiveDataLogging(enableSensitiveDataLogging); diff --git a/templates/Full/src/WebApp/Extensions/HealthCheckExtensions.cs b/templates/Full/src/WebApp/Extensions/HealthCheckExtensions.cs index e9ec35d3..e52f312e 100644 --- a/templates/Full/src/WebApp/Extensions/HealthCheckExtensions.cs +++ b/templates/Full/src/WebApp/Extensions/HealthCheckExtensions.cs @@ -23,9 +23,9 @@ IConfiguration configuration }) .AddHealthChecks() .AddCheck("self", () => HealthCheckResult.Healthy()) - .AddSqlServer( + .AddNpgSql( configuration.GetConnectionString("OrderDb")!, - name: "SqlServer", + name: "PostgreSQL", tags: ["services"] ) .AddRedis( diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json index 58992051..e255b853 100644 --- a/templates/Full/src/WebApp/Properties/launchSettings.json +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -22,7 +22,7 @@ "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:18889", "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", - "ConnectionStrings__OrderDb": "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;", + "ConnectionStrings__OrderDb": "Host=127.0.0.1;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES", "ConnectionStrings__Redis": "127.0.0.1:6379", "ConnectionStrings__RabbitMQ": "amqp://guest:guest@127.0.0.1:5672/" diff --git a/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs b/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs index ca8398d1..fb8470ae 100644 --- a/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs +++ b/templates/Full/tests/IntegrationTests/Common/CustomWebApplicationFactory.cs @@ -14,7 +14,7 @@ public sealed class WebApplicationFactoryCollectionDefinition : IClassFixture : WebApplicationFactory, IDisposable where TProgram : class { - protected string? ConnectionString { get; } = "Server=127.0.0.1,1433;Database=OrderDb;User Id=sa;Password=cY5VvZkkh4AzES;TrustServerCertificate=true;"; + protected string? ConnectionString { get; } = "Host=127.0.0.1;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES"; public MyDbContext? MyDbContext { get; set; } @@ -34,14 +34,14 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) services.Remove(dbConnectionDescriptor!); - services.AddDbContextFactory((options) => options.UseSqlServer(ConnectionString)); + services.AddDbContextFactory((options) => options.UseNpgsql(ConnectionString)); }); } public void SetDbContext() { var contextOptions = new DbContextOptionsBuilder() - .UseSqlServer(ConnectionString) + .UseNpgsql(ConnectionString) .Options; MyDbContext = new(contextOptions); From 950c20f96b9b8571315086dfffab8f990fe111f9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 24 Feb 2026 07:37:20 -0300 Subject: [PATCH 075/161] chore(53): update pgadmin image version to 9.12.0 in docker-compose --- templates/Full/docker-compose-load-tests.yml | 13 +++++++++---- templates/Full/docker-compose.yml | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 45fcf397..53dc4bb3 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -66,11 +66,16 @@ services: extends: file: docker-compose.yml service: postgres-init + + # pgadmin: + # extends: + # file: docker-compose.yml + # service: pgadmin - aspire-dashboard: - extends: - file: docker-compose.yml - service: aspire-dashboard + # aspire-dashboard: + # extends: + # file: docker-compose.yml + # service: aspire-dashboard redis: extends: diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 780e62f0..0819e9ca 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -19,7 +19,7 @@ services: - "5432:5432" pgadmin: - image: dpage/pgadmin4:latest + image: dpage/pgadmin4:9.12.0 environment: PGADMIN_DEFAULT_EMAIL: "admin@admin.com" PGADMIN_DEFAULT_PASSWORD: "admin" From 6e43725463560028965ed51207eccda108c05437 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 25 Feb 2026 07:28:36 -0300 Subject: [PATCH 076/161] CMGO-53 docs: update migration commands in README for consistency --- templates/Full/Readme.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/templates/Full/Readme.md b/templates/Full/Readme.md index 87301605..694fd367 100644 --- a/templates/Full/Readme.md +++ b/templates/Full/Readme.md @@ -21,13 +21,19 @@ This repository provides a template for building applications using the Hexagona ### Run migrations ```bash -dotnet ef database update --project src/Infrastructure --startup-project src/WebApp --output-dir src/Infrastructure/Data/Migrations +dotnet ef database update --project src/Infrastructure --startup-project src/Infrastructure --output-dir src/Infrastructure/Data/Migrations ``` ### Create a new migration ```bash -dotnet ef migrations add --project src/Infrastructure --startup-project src/WebApp --output-dir src/Infrastructure/Data/Migrations +dotnet ef migrations add --project src/Infrastructure --startup-project src/Infrastructure --output-dir src/Infrastructure/Data/Migrations +``` + +### Generate SQL script for migrations + +```bash +dotnet ef migrations script --idempotent --project src/Infrastructure --startup-project src/Infrastructure --output scripts/migrations.sql ``` ### Run the application From 4369b4eded90375fd2bc935a7c5a7b70bb04e409 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 25 Feb 2026 07:29:03 -0300 Subject: [PATCH 077/161] 53 docs: update section title for generating SQL migration script --- templates/Full/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Full/Readme.md b/templates/Full/Readme.md index 694fd367..ecd9c0a1 100644 --- a/templates/Full/Readme.md +++ b/templates/Full/Readme.md @@ -30,7 +30,7 @@ dotnet ef database update --project src/Infrastructure --startup-project src/Inf dotnet ef migrations add --project src/Infrastructure --startup-project src/Infrastructure --output-dir src/Infrastructure/Data/Migrations ``` -### Generate SQL script for migrations +### Generate SQL script for migrations with idempotent option ```bash dotnet ef migrations script --idempotent --project src/Infrastructure --startup-project src/Infrastructure --output scripts/migrations.sql From 1459f4918788190aa690fcf6ef55b9ef7b1490ac Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 25 Feb 2026 07:29:16 -0300 Subject: [PATCH 078/161] CMGO-53 feat: create migration for Notification, Order, and Item tables --- templates/Full/scripts/migrations.sql | 130 ++++++------ .../20260225102145_CreateTables.Designer.cs | 192 ++++++++++++++++++ .../Migrations/20260225102145_CreateTables.cs | 100 +++++++++ .../Migrations/MyDbContextModelSnapshot.cs | 189 +++++++++++++++++ 4 files changed, 552 insertions(+), 59 deletions(-) create mode 100644 templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs create mode 100644 templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs create mode 100644 templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs diff --git a/templates/Full/scripts/migrations.sql b/templates/Full/scripts/migrations.sql index 545354ba..dc0555c8 100644 --- a/templates/Full/scripts/migrations.sql +++ b/templates/Full/scripts/migrations.sql @@ -1,70 +1,82 @@ --- PostgreSQL Migration Script --- Create database if not exists (handled by Docker environment variable POSTGRES_DB) - --- Create EFMigrationsHistory table if not exists -CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( - "MigrationId" VARCHAR(150) NOT NULL, - "ProductVersion" VARCHAR(32) NOT NULL, +CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( + "MigrationId" character varying(150) NOT NULL, + "ProductVersion" character varying(32) NOT NULL, CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") ); --- Begin transaction -BEGIN; +START TRANSACTION; --- Check if migration already applied -DO $$ +DO $EF$ BEGIN - IF NOT EXISTS ( - SELECT 1 FROM "__EFMigrationsHistory" - WHERE "MigrationId" = '20251012134409_AddNotificationTable' - ) THEN - -- Create Notification table - CREATE TABLE "Notification" ( - "Id" INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, - "NotificationType" VARCHAR(100) NOT NULL, - "NotificationStatus" VARCHAR(100) NOT NULL, - "Message" VARCHAR(4000) NULL, - "CreatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, - "CreatedBy" VARCHAR(100) NULL, - "UpdatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, - "UpdatedBy" VARCHAR(100) NULL, - CONSTRAINT "PK_Notification" PRIMARY KEY ("Id") - ); - - -- Create Order table - CREATE TABLE "Order" ( - "Id" INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, - "Description" VARCHAR(255) NOT NULL, - "Total" NUMERIC(18,2) NOT NULL, - "CreatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, - "CreatedBy" VARCHAR(100) NULL, - "UpdatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, - "UpdatedBy" VARCHAR(100) NULL, - CONSTRAINT "PK_Order" PRIMARY KEY ("Id") - ); + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + CREATE TABLE "Notification" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "NotificationType" text NOT NULL, + "NotificationStatus" text NOT NULL, + "Message" text, + "CreatedAt" timestamp without time zone NOT NULL, + "CreatedBy" text, + "CreatedByTimezoneId" text NOT NULL, + "UpdatedAt" timestamp without time zone NOT NULL, + "UpdatedBy" text, + "UpdatedByTimezoneId" text, + CONSTRAINT "PK_Notification" PRIMARY KEY ("Id") + ); + END IF; +END $EF$; - -- Create Item table - CREATE TABLE "Item" ( - "Id" INTEGER GENERATED ALWAYS AS IDENTITY NOT NULL, - "Name" VARCHAR(200) NOT NULL, - "Description" VARCHAR(255) NOT NULL, - "Value" NUMERIC(18,2) NOT NULL, - "OrderId" INTEGER NULL, - "CreatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, - "CreatedBy" VARCHAR(100) NULL, - "UpdatedAt" TIMESTAMP WITHOUT TIME ZONE NOT NULL, - "UpdatedBy" VARCHAR(100) NULL, - CONSTRAINT "PK_Item" PRIMARY KEY ("Id"), - CONSTRAINT "FK_Item_Order_OrderId" FOREIGN KEY ("OrderId") REFERENCES "Order" ("Id") - ); +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + CREATE TABLE "Order" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Description" text NOT NULL, + "Total" numeric(18,2) NOT NULL, + "CreatedAt" timestamp without time zone NOT NULL, + "CreatedBy" text, + "CreatedByTimezoneId" text NOT NULL, + "UpdatedAt" timestamp without time zone NOT NULL, + "UpdatedBy" text, + "UpdatedByTimezoneId" text, + CONSTRAINT "PK_Order" PRIMARY KEY ("Id") + ); + END IF; +END $EF$; - -- Create index - CREATE INDEX "IX_Item_OrderId" ON "Item" ("OrderId"); +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + CREATE TABLE "Item" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Description" text NOT NULL, + "Value" numeric(18,2) NOT NULL, + "OrderId" integer, + "CreatedAt" timestamp without time zone NOT NULL, + "CreatedBy" text, + "CreatedByTimezoneId" text NOT NULL, + "UpdatedAt" timestamp without time zone NOT NULL, + "UpdatedBy" text, + "UpdatedByTimezoneId" text, + CONSTRAINT "PK_Item" PRIMARY KEY ("Id"), + CONSTRAINT "FK_Item_Order_OrderId" FOREIGN KEY ("OrderId") REFERENCES "Order" ("Id") + ); + END IF; +END $EF$; - -- Insert migration record - INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") - VALUES ('20251012134409_AddNotificationTable', '9.0.8'); +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + CREATE INDEX "IX_Item_OrderId" ON "Item" ("OrderId"); END IF; -END $$; +END $EF$; +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260225102145_CreateTables', '10.0.2'); + END IF; +END $EF$; COMMIT; + diff --git a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs b/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs new file mode 100644 index 00000000..e242d7cd --- /dev/null +++ b/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs @@ -0,0 +1,192 @@ +// +using System; +using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.src.Infrastructure.Data.Migrations +{ + [DbContext(typeof(MyDbContext))] + [Migration("20260225102145_CreateTables")] + partial class CreateTables + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Notifications.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("CreatedByTimezoneId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Message") + .HasMaxLength(4000) + .HasColumnType("text"); + + b.Property("NotificationStatus") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("NotificationType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedByTimezoneId") + .HasMaxLength(100) + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Notification"); + }); + + modelBuilder.Entity("Domain.Orders.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("CreatedByTimezoneId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("text"); + + b.Property("OrderId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedByTimezoneId") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Value") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("Item"); + }); + + modelBuilder.Entity("Domain.Orders.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("CreatedByTimezoneId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("text"); + + b.Property("Total") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedByTimezoneId") + .HasMaxLength(100) + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Domain.Orders.Item", b => + { + b.HasOne("Domain.Orders.Order", null) + .WithMany("Items") + .HasForeignKey("OrderId"); + }); + + modelBuilder.Entity("Domain.Orders.Order", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs b/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs new file mode 100644 index 00000000..662166b2 --- /dev/null +++ b/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs @@ -0,0 +1,100 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.src.Infrastructure.Data.Migrations; + +/// +public partial class CreateTables : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Notification", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + NotificationType = table.Column(type: "text", maxLength: 100, nullable: false), + NotificationStatus = table.Column(type: "text", maxLength: 100, nullable: false), + Message = table.Column(type: "text", maxLength: 4000, nullable: true), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), + UpdatedAt = table.Column(type: "timestamp without time zone", nullable: false), + UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Notification", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Description = table.Column(type: "text", maxLength: 255, nullable: false), + Total = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), + UpdatedAt = table.Column(type: "timestamp without time zone", nullable: false), + UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Item", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", maxLength: 200, nullable: false), + Description = table.Column(type: "text", maxLength: 255, nullable: false), + Value = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + OrderId = table.Column(type: "integer", nullable: true), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), + UpdatedAt = table.Column(type: "timestamp without time zone", nullable: false), + UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Item", x => x.Id); + table.ForeignKey( + name: "FK_Item_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Item_OrderId", + table: "Item", + column: "OrderId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Item"); + + migrationBuilder.DropTable( + name: "Notification"); + + migrationBuilder.DropTable( + name: "Order"); + } +} diff --git a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs b/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs new file mode 100644 index 00000000..8232b9da --- /dev/null +++ b/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs @@ -0,0 +1,189 @@ +// +using System; +using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.src.Infrastructure.Data.Migrations +{ + [DbContext(typeof(MyDbContext))] + partial class MyDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Domain.Notifications.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("CreatedByTimezoneId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Message") + .HasMaxLength(4000) + .HasColumnType("text"); + + b.Property("NotificationStatus") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("NotificationType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedByTimezoneId") + .HasMaxLength(100) + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Notification"); + }); + + modelBuilder.Entity("Domain.Orders.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("CreatedByTimezoneId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("text"); + + b.Property("OrderId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedByTimezoneId") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Value") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("Item"); + }); + + modelBuilder.Entity("Domain.Orders.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("CreatedByTimezoneId") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("text"); + + b.Property("Total") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp without time zone"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("text"); + + b.Property("UpdatedByTimezoneId") + .HasMaxLength(100) + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Domain.Orders.Item", b => + { + b.HasOne("Domain.Orders.Order", null) + .WithMany("Items") + .HasForeignKey("OrderId"); + }); + + modelBuilder.Entity("Domain.Orders.Order", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} From 9ca32a085dd504577534fe6da18403402288a904 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 25 Feb 2026 07:33:23 -0300 Subject: [PATCH 079/161] 53 feat: update seed data to include CreatedBy fields and values --- templates/Full/scripts/seeds.sql | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/templates/Full/scripts/seeds.sql b/templates/Full/scripts/seeds.sql index 77b275fc..874c1440 100644 --- a/templates/Full/scripts/seeds.sql +++ b/templates/Full/scripts/seeds.sql @@ -4,8 +4,9 @@ DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Order" WHERE "Id" = 1 LIMIT 1) THEN - INSERT INTO "Order" ("Id", "Description", "Total", "CreatedAt", "UpdatedAt") - VALUES (1, 'XPTO Client Computers', 1000.00, NOW(), NOW()); + INSERT INTO "Order" ("Id", "Description", "Total", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") + VALUES (1, 'XPTO Client Computers', 2000.00, NOW(), NOW(), 'System', 'UTC'), + (2, 'Contoso', 1200.00, NOW(), NOW(), 'System', 'America/New_York'); -- Update sequence to prevent ID conflicts SELECT setval(pg_get_serial_sequence('"Order"', 'Id'), (SELECT MAX("Id") FROM "Order")); @@ -16,8 +17,9 @@ END $$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Item" WHERE "Id" = 1 LIMIT 1) THEN - INSERT INTO "Item" ("Id", "Name", "Description", "Value", "OrderId", "CreatedAt", "UpdatedAt") - VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 999.00, 1, NOW(), NOW()); + INSERT INTO "Item" ("Id", "Name", "Description", "Value", "OrderId", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") + VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 2000.00, 1, NOW(), NOW(), 'System', 'UTC'), + (3, 'Notebook', 'Notebook with Intel i7 and 16GB RAM', 1200.00, 2, NOW(), NOW(), 'System', 'America/New_York'); -- Update sequence to prevent ID conflicts SELECT setval(pg_get_serial_sequence('"Item"', 'Id'), (SELECT MAX("Id") FROM "Item")); @@ -28,8 +30,8 @@ END $$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Notification" WHERE "Id" = 1 LIMIT 1) THEN - INSERT INTO "Notification" ("Id", "NotificationType", "NotificationStatus", "CreatedAt", "UpdatedAt") - VALUES (1, 'OrderCreated', 'Created', NOW(), NOW()); + INSERT INTO "Notification" ("Id", "NotificationType", "NotificationStatus", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") + VALUES (1, 'OrderCreated', 'Created', NOW(), NOW(), 'System', 'UTC'); -- Update sequence to prevent ID conflicts SELECT setval(pg_get_serial_sequence('"Notification"', 'Id'), (SELECT MAX("Id") FROM "Notification")); From ddd645f293b4809d87cf1036bc9b9a38336d7b8f Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 25 Feb 2026 07:34:31 -0300 Subject: [PATCH 080/161] 53 style: remove commented lines from seed data script --- templates/Full/scripts/seeds.sql | 8 -------- 1 file changed, 8 deletions(-) diff --git a/templates/Full/scripts/seeds.sql b/templates/Full/scripts/seeds.sql index 874c1440..bbaaffcb 100644 --- a/templates/Full/scripts/seeds.sql +++ b/templates/Full/scripts/seeds.sql @@ -1,6 +1,3 @@ --- PostgreSQL Seed Data Script - --- Insert Order with explicit ID DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Order" WHERE "Id" = 1 LIMIT 1) THEN @@ -8,12 +5,10 @@ BEGIN VALUES (1, 'XPTO Client Computers', 2000.00, NOW(), NOW(), 'System', 'UTC'), (2, 'Contoso', 1200.00, NOW(), NOW(), 'System', 'America/New_York'); - -- Update sequence to prevent ID conflicts SELECT setval(pg_get_serial_sequence('"Order"', 'Id'), (SELECT MAX("Id") FROM "Order")); END IF; END $$; --- Insert Item with explicit ID DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Item" WHERE "Id" = 1 LIMIT 1) THEN @@ -21,19 +16,16 @@ BEGIN VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 2000.00, 1, NOW(), NOW(), 'System', 'UTC'), (3, 'Notebook', 'Notebook with Intel i7 and 16GB RAM', 1200.00, 2, NOW(), NOW(), 'System', 'America/New_York'); - -- Update sequence to prevent ID conflicts SELECT setval(pg_get_serial_sequence('"Item"', 'Id'), (SELECT MAX("Id") FROM "Item")); END IF; END $$; --- Insert Notification with explicit ID DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Notification" WHERE "Id" = 1 LIMIT 1) THEN INSERT INTO "Notification" ("Id", "NotificationType", "NotificationStatus", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") VALUES (1, 'OrderCreated', 'Created', NOW(), NOW(), 'System', 'UTC'); - -- Update sequence to prevent ID conflicts SELECT setval(pg_get_serial_sequence('"Notification"', 'Id'), (SELECT MAX("Id") FROM "Notification")); END IF; END $$; From 8f6da019eb28481a1354c5a11bdc62e848000ded Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 27 Feb 2026 15:59:58 -0300 Subject: [PATCH 081/161] 53 feat: reorganize pgadmin service and update seed data queries --- templates/Full/docker-compose.yml | 30 +++++++++++++++--------------- templates/Full/scripts/seeds.sql | 10 +--------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 0819e9ca..6c5d646a 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -18,19 +18,6 @@ services: ports: - "5432:5432" - pgadmin: - image: dpage/pgadmin4:9.12.0 - environment: - PGADMIN_DEFAULT_EMAIL: "admin@admin.com" - PGADMIN_DEFAULT_PASSWORD: "admin" - depends_on: - postgres: - condition: service_healthy - ports: - - "5050:80" - networks: - - hexagonal_solution_template_network - postgres-init: image: postgres:17-alpine depends_on: @@ -45,11 +32,24 @@ services: - /bin/sh - -c - | - psql -h postgres -U postgres -d postgres -f /tmp/migrations.sql && - psql -h postgres -U postgres -d postgres -f /tmp/seeds.sql + psql -h postgres -U postgres -d OrderDb -f /tmp/migrations.sql && + psql -h postgres -U postgres -d OrderDb -f /tmp/seeds.sql environment: PGPASSWORD: "cY5VvZkkh4AzES" + pgadmin: + image: dpage/pgadmin4:9.12.0 + environment: + PGADMIN_DEFAULT_EMAIL: "admin@admin.com" + PGADMIN_DEFAULT_PASSWORD: "admin" + depends_on: + postgres: + condition: service_healthy + ports: + - "5050:80" + networks: + - hexagonal_solution_template_network + aspire-dashboard: image: mcr.microsoft.com/dotnet/aspire-dashboard:13 ports: diff --git a/templates/Full/scripts/seeds.sql b/templates/Full/scripts/seeds.sql index bbaaffcb..adbcb3bb 100644 --- a/templates/Full/scripts/seeds.sql +++ b/templates/Full/scripts/seeds.sql @@ -4,19 +4,12 @@ BEGIN INSERT INTO "Order" ("Id", "Description", "Total", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") VALUES (1, 'XPTO Client Computers', 2000.00, NOW(), NOW(), 'System', 'UTC'), (2, 'Contoso', 1200.00, NOW(), NOW(), 'System', 'America/New_York'); - - SELECT setval(pg_get_serial_sequence('"Order"', 'Id'), (SELECT MAX("Id") FROM "Order")); END IF; -END $$; - -DO $$ -BEGIN + IF NOT EXISTS (SELECT 1 FROM "Item" WHERE "Id" = 1 LIMIT 1) THEN INSERT INTO "Item" ("Id", "Name", "Description", "Value", "OrderId", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 2000.00, 1, NOW(), NOW(), 'System', 'UTC'), (3, 'Notebook', 'Notebook with Intel i7 and 16GB RAM', 1200.00, 2, NOW(), NOW(), 'System', 'America/New_York'); - - SELECT setval(pg_get_serial_sequence('"Item"', 'Id'), (SELECT MAX("Id") FROM "Item")); END IF; END $$; @@ -26,6 +19,5 @@ BEGIN INSERT INTO "Notification" ("Id", "NotificationType", "NotificationStatus", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") VALUES (1, 'OrderCreated', 'Created', NOW(), NOW(), 'System', 'UTC'); - SELECT setval(pg_get_serial_sequence('"Notification"', 'Id'), (SELECT MAX("Id") FROM "Notification")); END IF; END $$; From 608c39b159fda13a71761991183bcc04121908f1 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 27 Feb 2026 16:01:13 -0300 Subject: [PATCH 082/161] 53 feat: add container names for all services in docker-compose --- templates/Full/docker-compose.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 6c5d646a..3bcc191a 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -3,6 +3,7 @@ name: hexagonal-solution-template-full services: postgres: image: postgres:17-alpine + container_name: postgres environment: POSTGRES_PASSWORD: "cY5VvZkkh4AzES" POSTGRES_USER: "postgres" @@ -20,6 +21,7 @@ services: postgres-init: image: postgres:17-alpine + container_name: postgres-init depends_on: postgres: condition: service_healthy @@ -39,6 +41,7 @@ services: pgadmin: image: dpage/pgadmin4:9.12.0 + container_name: pgadmin environment: PGADMIN_DEFAULT_EMAIL: "admin@admin.com" PGADMIN_DEFAULT_PASSWORD: "admin" @@ -52,6 +55,7 @@ services: aspire-dashboard: image: mcr.microsoft.com/dotnet/aspire-dashboard:13 + container_name: aspire-dashboard ports: - "18888:18888" - "18889:18889" @@ -60,6 +64,7 @@ services: redis: image: redis:8 + container_name: redis ports: - "6379:6379" healthcheck: @@ -73,6 +78,7 @@ services: rabbitmq: image: rabbitmq:management + container_name: rabbitmq ports: - "5672:5672" - "15672:15672" From 95828b0a785f549d9da56ca216b141ea42d548d2 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 27 Feb 2026 17:24:38 -0300 Subject: [PATCH 083/161] 53 feat: create migration for Notification, Order, and Item tables --- templates/Full/Readme.md | 2 +- templates/Full/scripts/migrations.sql | 12 ++++++------ .../20260227202315_CreateTables.Designer.cs} | 4 ++-- .../Migrations/20260227202315_CreateTables.cs} | 2 +- .../Data/Migrations/MyDbContextModelSnapshot.cs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) rename templates/Full/src/Infrastructure/{src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs => Data/Migrations/20260227202315_CreateTables.Designer.cs} (98%) rename templates/Full/src/Infrastructure/{src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs => Data/Migrations/20260227202315_CreateTables.cs} (98%) rename templates/Full/src/Infrastructure/{src/Infrastructure => }/Data/Migrations/MyDbContextModelSnapshot.cs (99%) diff --git a/templates/Full/Readme.md b/templates/Full/Readme.md index ecd9c0a1..65728a31 100644 --- a/templates/Full/Readme.md +++ b/templates/Full/Readme.md @@ -27,7 +27,7 @@ dotnet ef database update --project src/Infrastructure --startup-project src/Inf ### Create a new migration ```bash -dotnet ef migrations add --project src/Infrastructure --startup-project src/Infrastructure --output-dir src/Infrastructure/Data/Migrations +dotnet ef migrations add --project src/Infrastructure --startup-project src/Infrastructure --output-dir Data/Migrations ``` ### Generate SQL script for migrations with idempotent option diff --git a/templates/Full/scripts/migrations.sql b/templates/Full/scripts/migrations.sql index dc0555c8..e44020f1 100644 --- a/templates/Full/scripts/migrations.sql +++ b/templates/Full/scripts/migrations.sql @@ -8,7 +8,7 @@ START TRANSACTION; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN CREATE TABLE "Notification" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "NotificationType" text NOT NULL, @@ -27,7 +27,7 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN CREATE TABLE "Order" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "Description" text NOT NULL, @@ -45,7 +45,7 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN CREATE TABLE "Item" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "Name" text NOT NULL, @@ -66,16 +66,16 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN CREATE INDEX "IX_Item_OrderId" ON "Item" ("OrderId"); END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260225102145_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") - VALUES ('20260225102145_CreateTables', '10.0.2'); + VALUES ('20260227202315_CreateTables', '10.0.2'); END IF; END $EF$; COMMIT; diff --git a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs b/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.Designer.cs similarity index 98% rename from templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs rename to templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.Designer.cs index e242d7cd..bd870d96 100644 --- a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.Designer.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.Designer.cs @@ -9,10 +9,10 @@ #nullable disable -namespace Infrastructure.src.Infrastructure.Data.Migrations +namespace Infrastructure.Data.Migrations { [DbContext(typeof(MyDbContext))] - [Migration("20260225102145_CreateTables")] + [Migration("20260227202315_CreateTables")] partial class CreateTables { /// diff --git a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs b/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.cs similarity index 98% rename from templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs rename to templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.cs index 662166b2..0f005a05 100644 --- a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/20260225102145_CreateTables.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.cs @@ -3,7 +3,7 @@ #nullable disable -namespace Infrastructure.src.Infrastructure.Data.Migrations; +namespace Infrastructure.Data.Migrations; /// public partial class CreateTables : Migration diff --git a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs similarity index 99% rename from templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs rename to templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs index 8232b9da..c09a6a0d 100644 --- a/templates/Full/src/Infrastructure/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs @@ -8,7 +8,7 @@ #nullable disable -namespace Infrastructure.src.Infrastructure.Data.Migrations +namespace Infrastructure.Data.Migrations { [DbContext(typeof(MyDbContext))] partial class MyDbContextModelSnapshot : ModelSnapshot From b26ad2993a9c582b071a5d28b3c960db727a5f2a Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 27 Feb 2026 17:46:22 -0300 Subject: [PATCH 084/161] 53 feat: update migration and seed scripts for timestamp fields --- templates/Full/scripts/migrations.sql | 24 +++++++++---------- templates/Full/scripts/seeds.sql | 8 +++++++ ...> 20260227203704_CreateTables.Designer.cs} | 14 +++++------ ...bles.cs => 20260227203704_CreateTables.cs} | 12 +++++----- .../Migrations/MyDbContextModelSnapshot.cs | 12 +++++----- .../src/Infrastructure/Data/MyDbContext.cs | 2 +- 6 files changed, 40 insertions(+), 32 deletions(-) rename templates/Full/src/Infrastructure/Data/Migrations/{20260227202315_CreateTables.Designer.cs => 20260227203704_CreateTables.Designer.cs} (93%) rename templates/Full/src/Infrastructure/Data/Migrations/{20260227202315_CreateTables.cs => 20260227203704_CreateTables.cs} (95%) diff --git a/templates/Full/scripts/migrations.sql b/templates/Full/scripts/migrations.sql index e44020f1..8dc0bb54 100644 --- a/templates/Full/scripts/migrations.sql +++ b/templates/Full/scripts/migrations.sql @@ -8,16 +8,16 @@ START TRANSACTION; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN CREATE TABLE "Notification" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "NotificationType" text NOT NULL, "NotificationStatus" text NOT NULL, "Message" text, - "CreatedAt" timestamp without time zone NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, "CreatedBy" text, "CreatedByTimezoneId" text NOT NULL, - "UpdatedAt" timestamp without time zone NOT NULL, + "UpdatedAt" timestamp with time zone NOT NULL, "UpdatedBy" text, "UpdatedByTimezoneId" text, CONSTRAINT "PK_Notification" PRIMARY KEY ("Id") @@ -27,15 +27,15 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN CREATE TABLE "Order" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "Description" text NOT NULL, "Total" numeric(18,2) NOT NULL, - "CreatedAt" timestamp without time zone NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, "CreatedBy" text, "CreatedByTimezoneId" text NOT NULL, - "UpdatedAt" timestamp without time zone NOT NULL, + "UpdatedAt" timestamp with time zone NOT NULL, "UpdatedBy" text, "UpdatedByTimezoneId" text, CONSTRAINT "PK_Order" PRIMARY KEY ("Id") @@ -45,17 +45,17 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN CREATE TABLE "Item" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "Name" text NOT NULL, "Description" text NOT NULL, "Value" numeric(18,2) NOT NULL, "OrderId" integer, - "CreatedAt" timestamp without time zone NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, "CreatedBy" text, "CreatedByTimezoneId" text NOT NULL, - "UpdatedAt" timestamp without time zone NOT NULL, + "UpdatedAt" timestamp with time zone NOT NULL, "UpdatedBy" text, "UpdatedByTimezoneId" text, CONSTRAINT "PK_Item" PRIMARY KEY ("Id"), @@ -66,16 +66,16 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN CREATE INDEX "IX_Item_OrderId" ON "Item" ("OrderId"); END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202315_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") - VALUES ('20260227202315_CreateTables', '10.0.2'); + VALUES ('20260227202813_CreateTables', '10.0.2'); END IF; END $EF$; COMMIT; diff --git a/templates/Full/scripts/seeds.sql b/templates/Full/scripts/seeds.sql index adbcb3bb..b3c8288d 100644 --- a/templates/Full/scripts/seeds.sql +++ b/templates/Full/scripts/seeds.sql @@ -4,12 +4,18 @@ BEGIN INSERT INTO "Order" ("Id", "Description", "Total", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") VALUES (1, 'XPTO Client Computers', 2000.00, NOW(), NOW(), 'System', 'UTC'), (2, 'Contoso', 1200.00, NOW(), NOW(), 'System', 'America/New_York'); + + -- Reset the sequence to the maximum ID + 1 + PERFORM setval(pg_get_serial_sequence('"Order"', 'Id'), COALESCE(MAX("Id"), 1), true) FROM "Order"; END IF; IF NOT EXISTS (SELECT 1 FROM "Item" WHERE "Id" = 1 LIMIT 1) THEN INSERT INTO "Item" ("Id", "Name", "Description", "Value", "OrderId", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 2000.00, 1, NOW(), NOW(), 'System', 'UTC'), (3, 'Notebook', 'Notebook with Intel i7 and 16GB RAM', 1200.00, 2, NOW(), NOW(), 'System', 'America/New_York'); + + -- Reset the sequence to the maximum ID + 1 + PERFORM setval(pg_get_serial_sequence('"Item"', 'Id'), COALESCE(MAX("Id"), 1), true) FROM "Item"; END IF; END $$; @@ -19,5 +25,7 @@ BEGIN INSERT INTO "Notification" ("Id", "NotificationType", "NotificationStatus", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") VALUES (1, 'OrderCreated', 'Created', NOW(), NOW(), 'System', 'UTC'); + -- Reset the sequence to the maximum ID + 1 + PERFORM setval(pg_get_serial_sequence('"Notification"', 'Id'), COALESCE(MAX("Id"), 1), true) FROM "Notification"; END IF; END $$; diff --git a/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.Designer.cs b/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.Designer.cs similarity index 93% rename from templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.Designer.cs rename to templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.Designer.cs index bd870d96..e0130465 100644 --- a/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.Designer.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.Designer.cs @@ -12,7 +12,7 @@ namespace Infrastructure.Data.Migrations { [DbContext(typeof(MyDbContext))] - [Migration("20260227202315_CreateTables")] + [Migration("20260227203704_CreateTables")] partial class CreateTables { /// @@ -34,7 +34,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatedBy") .HasMaxLength(100) @@ -60,7 +60,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("UpdatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UpdatedBy") .HasMaxLength(100) @@ -84,7 +84,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatedBy") .HasMaxLength(100) @@ -109,7 +109,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("integer"); b.Property("UpdatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UpdatedBy") .HasMaxLength(100) @@ -139,7 +139,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatedBy") .HasMaxLength(100) @@ -160,7 +160,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("numeric(18,2)"); b.Property("UpdatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UpdatedBy") .HasMaxLength(100) diff --git a/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.cs b/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.cs similarity index 95% rename from templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.cs rename to templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.cs index 0f005a05..77b824a3 100644 --- a/templates/Full/src/Infrastructure/Data/Migrations/20260227202315_CreateTables.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.cs @@ -20,10 +20,10 @@ protected override void Up(MigrationBuilder migrationBuilder) NotificationType = table.Column(type: "text", maxLength: 100, nullable: false), NotificationStatus = table.Column(type: "text", maxLength: 100, nullable: false), Message = table.Column(type: "text", maxLength: 4000, nullable: true), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), - UpdatedAt = table.Column(type: "timestamp without time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) }, @@ -40,10 +40,10 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Description = table.Column(type: "text", maxLength: 255, nullable: false), Total = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), - UpdatedAt = table.Column(type: "timestamp without time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) }, @@ -62,10 +62,10 @@ protected override void Up(MigrationBuilder migrationBuilder) Description = table.Column(type: "text", maxLength: 255, nullable: false), Value = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), OrderId = table.Column(type: "integer", nullable: true), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), - UpdatedAt = table.Column(type: "timestamp without time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) }, diff --git a/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs index c09a6a0d..60b63967 100644 --- a/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs @@ -31,7 +31,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatedBy") .HasMaxLength(100) @@ -57,7 +57,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("UpdatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UpdatedBy") .HasMaxLength(100) @@ -81,7 +81,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatedBy") .HasMaxLength(100) @@ -106,7 +106,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("integer"); b.Property("UpdatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UpdatedBy") .HasMaxLength(100) @@ -136,7 +136,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatedBy") .HasMaxLength(100) @@ -157,7 +157,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("numeric(18,2)"); b.Property("UpdatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("UpdatedBy") .HasMaxLength(100) diff --git a/templates/Full/src/Infrastructure/Data/MyDbContext.cs b/templates/Full/src/Infrastructure/Data/MyDbContext.cs index 9e7e8480..0b978f1a 100644 --- a/templates/Full/src/Infrastructure/Data/MyDbContext.cs +++ b/templates/Full/src/Infrastructure/Data/MyDbContext.cs @@ -27,6 +27,6 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura configurationBuilder .Properties() - .HaveColumnType("timestamp without time zone"); + .HaveColumnType("timestamp with time zone"); } } From b6be639e54761dfefd8fa4df787d70a900c9cba1 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 27 Feb 2026 18:01:08 -0300 Subject: [PATCH 085/161] 53 feat: update seed data values and enhance order creation tests --- templates/Full/scripts/seeds.sql | 4 ++-- .../Infrastructure/Data/Common/BaseRepository.cs | 4 ++-- .../WebApp/Grpc/Orders/GetOrderGrpcTest.cs | 2 +- .../WebApp/Http/Orders/CreateOrderTest.cs | 14 +++++++++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/templates/Full/scripts/seeds.sql b/templates/Full/scripts/seeds.sql index b3c8288d..e4571e40 100644 --- a/templates/Full/scripts/seeds.sql +++ b/templates/Full/scripts/seeds.sql @@ -2,7 +2,7 @@ DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Order" WHERE "Id" = 1 LIMIT 1) THEN INSERT INTO "Order" ("Id", "Description", "Total", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") - VALUES (1, 'XPTO Client Computers', 2000.00, NOW(), NOW(), 'System', 'UTC'), + VALUES (1, 'XPTO Client Computers', 1000.00, NOW(), NOW(), 'System', 'UTC'), (2, 'Contoso', 1200.00, NOW(), NOW(), 'System', 'America/New_York'); -- Reset the sequence to the maximum ID + 1 @@ -11,7 +11,7 @@ BEGIN IF NOT EXISTS (SELECT 1 FROM "Item" WHERE "Id" = 1 LIMIT 1) THEN INSERT INTO "Item" ("Id", "Name", "Description", "Value", "OrderId", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") - VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 2000.00, 1, NOW(), NOW(), 'System', 'UTC'), + VALUES (1, 'Graphics Card 4090 Super', 'Nvidia Graphics Cards 24GB RX 4090 Super', 1000.00, 1, NOW(), NOW(), 'System', 'UTC'), (3, 'Notebook', 'Notebook with Intel i7 and 16GB RAM', 1200.00, 2, NOW(), NOW(), 'System', 'America/New_York'); -- Reset the sequence to the maximum ID + 1 diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 1e9aa135..5e26524f 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -132,7 +132,7 @@ params Expression>[]? includes if (searchByValues != null && searchByValues.Count != 0) foreach (var searchByValue in searchByValues) query = query.Where(e => - EF.Property(e, searchByValue.Key).Contains(searchByValue.Value.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase) + EF.Functions.ILike(EF.Property(e, searchByValue.Key), $"%{searchByValue.Value}%") ); var items = await query @@ -173,7 +173,7 @@ params Expression>[]? includes if (searchByValues != null && searchByValues.Count != 0) foreach (var searchByValue in searchByValues) query = query.Where(e => - EF.Property(e, searchByValue.Key).Contains(searchByValue.Value.ToLowerInvariant(), StringComparison.OrdinalIgnoreCase) + EF.Functions.ILike(EF.Property(e, searchByValue.Key), $"%{searchByValue.Value}%") ); var items = await query diff --git a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs index 363e4920..cb90bec8 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs @@ -45,7 +45,7 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(1, response.Data.Id); Assert.NotNull(response.Data.Items); Assert.NotEmpty(response.Data.Items); - Assert.Equal(1000.0, response.Data.Total); + Assert.Equal(2000.0, response.Data.Total); } [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] diff --git a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs index 89beff88..0d791355 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Http/Orders/CreateOrderTest.cs @@ -9,11 +9,23 @@ namespace IntegrationTests.WebApp.Http.Orders; public class CreateOrderTestFixture : BaseHttpFixture { - public CreateOrderRequest SetValidRequest() => AutoFixture.Create(); + public CreateOrderRequest SetValidRequest() + { + var items = AutoFixture.Build() + .With(i => i.Value, AutoFixture.Create() + 1) // Ensure non-zero value + .CreateMany(2) + .ToArray(); + + return AutoFixture.Build() + .With(r => r.Items, items) + .With(r => r.TimezoneId, "UTC") // Use valid timezone ID + .Create(); + } public CreateOrderRequest SetInvalidRequest() => AutoFixture .Build() .With(r => r.Description, string.Empty) + .With(r => r.TimezoneId, "UTC") .Create(); } From 71ab02cafe9399c7910b2eae6e767e8c5171f0be Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Mon, 9 Mar 2026 07:21:54 -0300 Subject: [PATCH 086/161] feat: add grafana with tempo support --- templates/Full/Directory.Packages.props | 19 +- templates/Full/docker-compose-grafana.yml | 76 +++ templates/Full/docker-compose.yml | 4 +- templates/Full/scripts/grafana/config.alloy | 38 ++ .../scripts/grafana/grafana-datasources.yaml | 55 ++ .../Full/scripts/grafana/loki-config.yaml | 40 ++ templates/Full/scripts/grafana/prometheus.yml | 15 + templates/Full/scripts/grafana/tempo.yaml | 90 ++++ .../Full/scripts/{ => sql}/migrations.sql | 0 templates/Full/scripts/{ => sql}/seeds.sql | 0 .../src/Infrastructure/Infrastructure.csproj | 13 +- ...ructureOpenTelemetryDependencyInjection.cs | 68 ++- .../src/Infrastructure/packages.lock.json | 455 +++++++++++++++-- templates/Full/src/WebApp/Program.cs | 2 + .../src/WebApp/Properties/launchSettings.json | 8 +- templates/Full/src/WebApp/packages.lock.json | 429 ++++++++++++++-- .../tests/IntegrationTests/packages.lock.json | 469 ++++++++++++++++-- 17 files changed, 1604 insertions(+), 177 deletions(-) create mode 100644 templates/Full/docker-compose-grafana.yml create mode 100644 templates/Full/scripts/grafana/config.alloy create mode 100644 templates/Full/scripts/grafana/grafana-datasources.yaml create mode 100644 templates/Full/scripts/grafana/loki-config.yaml create mode 100644 templates/Full/scripts/grafana/prometheus.yml create mode 100644 templates/Full/scripts/grafana/tempo.yaml rename templates/Full/scripts/{ => sql}/migrations.sql (100%) rename templates/Full/scripts/{ => sql}/seeds.sql (100%) diff --git a/templates/Full/Directory.Packages.props b/templates/Full/Directory.Packages.props index 0144dab3..a5235757 100644 --- a/templates/Full/Directory.Packages.props +++ b/templates/Full/Directory.Packages.props @@ -31,13 +31,18 @@ - - - - - - - + + + + + + + + + + + + diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml new file mode 100644 index 00000000..0902a055 --- /dev/null +++ b/templates/Full/docker-compose-grafana.yml @@ -0,0 +1,76 @@ +# Sample from: https://github.com/grafana/adventure/blob/main/docker-compose.yml +name: hexagonal-solution-template-grafana-full +services: + alloy: + image: grafana/alloy:v1.7.5 + ports: + - 12345:12345 + - 4318:4318 + - 4317:4317 + volumes: + - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + depends_on: + - loki + + prometheus: + image: prom/prometheus:v3.1.0 + ports: + - 9090:9090 + volumes: + - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml + command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage + + loki: + image: grafana/loki:3.4.2 + ports: + - "3100:3100" + volumes: + - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml + command: -config.file=/etc/loki/local-config.yaml + + grafana: + image: grafana/grafana:11.5.2 + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall + ports: + - 3000:3000/tcp + volumes: + - ./grafana:/etc/grafana/provisioning + - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + +# Tempo + # Tempo runs as user 10001, and docker compose creates the volume as root. + # As such, we need to chown the volume in order for Tempo to start correctly. + tempo-init: + image: &tempoImage grafana/tempo:2.7.1 + user: root + entrypoint: + - "chown" + - "10001:10001" + - "/var/tempo" + volumes: + - ./tempo-data:/var/tempo + + memcached: + image: memcached:1.6.29 + container_name: memcached + ports: + - "11211:11211" + environment: + - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage + - MEMCACHED_THREADS=4 # Number of threads to use + + tempo: + image: *tempoImage + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml + ports: + - "3200:3200" # tempo + depends_on: + - tempo-init + - memcached \ No newline at end of file diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 3bcc191a..62790e32 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -28,8 +28,8 @@ services: networks: - hexagonal_solution_template_network volumes: - - ./scripts/migrations.sql:/tmp/migrations.sql - - ./scripts/seeds.sql:/tmp/seeds.sql + - ./scripts/sql/migrations.sql:/tmp/migrations.sql + - ./scripts/sql/seeds.sql:/tmp/seeds.sql command: - /bin/sh - -c diff --git a/templates/Full/scripts/grafana/config.alloy b/templates/Full/scripts/grafana/config.alloy new file mode 100644 index 00000000..6577c47a --- /dev/null +++ b/templates/Full/scripts/grafana/config.alloy @@ -0,0 +1,38 @@ +otelcol.receiver.otlp "default" { + http { + endpoint = "0.0.0.0:4318" + + } + grpc { + endpoint = "0.0.0.0:4317" + } + + output { + metrics = [otelcol.exporter.otlphttp.metrics.input] + logs = [otelcol.exporter.otlphttp.logs.input] + traces = [otelcol.exporter.otlphttp.traces.input] + } +} + +otelcol.exporter.otlphttp "logs" { + client { + endpoint = "http://loki:3100/otlp" + } + +} + +otelcol.exporter.otlphttp "metrics" { + client { + endpoint = "http://prometheus:9090/api/v1/otlp" + } +} + +otelcol.exporter.otlphttp "traces" { + client { + endpoint = "http://tempo:4318" + } +} + +livedebugging { + enabled = true +} \ No newline at end of file diff --git a/templates/Full/scripts/grafana/grafana-datasources.yaml b/templates/Full/scripts/grafana/grafana-datasources.yaml new file mode 100644 index 00000000..b6e00255 --- /dev/null +++ b/templates/Full/scripts/grafana/grafana-datasources.yaml @@ -0,0 +1,55 @@ +apiVersion: 1 + +datasources: +- name: Tempo + type: tempo + access: proxy + orgId: 1 + url: http://tempo:3200 + basicAuth: false + isDefault: true + version: 1 + editable: false + apiVersion: 1 + uid: tempo + jsonData: + httpMethod: GET + serviceMap: + datasourceUid: prometheus + tracesToLogsV2: + # Field with an internal link pointing to a logs data source in Grafana. + # datasourceUid value must match the uid value of the logs data source. + datasourceUid: 'Loki' + spanStartTimeShift: '-1h' + spanEndTimeShift: '1h' + filterByTraceID: true + filterBySpanID: true + customQuery: false + query: 'method="$${__span.tags.method}"' +- name: Prometheus + type: prometheus + uid: prometheus + access: proxy + orgId: 1 + url: http://prometheus:9090 + basicAuth: false + isDefault: false + version: 1 + editable: false + jsonData: + httpMethod: GET +- name: Loki + type: loki + access: proxy + orgId: 1 + url: http://loki:3100 + basicAuth: false + isDefault: false + version: 1 + editable: false + jsonData: + derivedFields: + - datasourceUid: tempo + matcherRegex: tid=(\w+) + name: TraceId + url: $${__value.raw} diff --git a/templates/Full/scripts/grafana/loki-config.yaml b/templates/Full/scripts/grafana/loki-config.yaml new file mode 100644 index 00000000..a9eca0ee --- /dev/null +++ b/templates/Full/scripts/grafana/loki-config.yaml @@ -0,0 +1,40 @@ + +# This is a complete configuration to deploy Loki backed by the filesystem. +# The index will be shipped to the storage via tsdb-shipper. + +auth_enabled: false + +limits_config: + allow_structured_metadata: true + volume_enabled: true + +server: + http_listen_port: 3100 + +common: + ring: + instance_addr: 0.0.0.0 + kvstore: + store: inmemory + replication_factor: 1 + path_prefix: /tmp/loki + +schema_config: + configs: + - from: 2020-05-15 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +storage_config: + tsdb_shipper: + active_index_directory: /tmp/loki/index + cache_location: /tmp/loki/index_cache + filesystem: + directory: /tmp/loki/chunks + +pattern_ingester: + enabled: true \ No newline at end of file diff --git a/templates/Full/scripts/grafana/prometheus.yml b/templates/Full/scripts/grafana/prometheus.yml new file mode 100644 index 00000000..45fad92e --- /dev/null +++ b/templates/Full/scripts/grafana/prometheus.yml @@ -0,0 +1,15 @@ +global: + scrape_interval: 3s + evaluation_interval: 3s + +storage: + tsdb: + out_of_order_time_window: 30m + +scrape_configs: + - job_name: apicontagem-http + metrics_path: /metrics + scheme: http + static_configs: + - targets: ['localhost:5101'] + diff --git a/templates/Full/scripts/grafana/tempo.yaml b/templates/Full/scripts/grafana/tempo.yaml new file mode 100644 index 00000000..735326c2 --- /dev/null +++ b/templates/Full/scripts/grafana/tempo.yaml @@ -0,0 +1,90 @@ +stream_over_http_enabled: true +server: + http_listen_port: 3200 + log_level: info + + +cache: + background: + writeback_goroutines: 5 + caches: + - roles: + - frontend-search + memcached: + host: memcached:11211 + +query_frontend: + search: + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + metadata_slo: + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + trace_by_id: + duration_slo: 100ms + metrics: + max_duration: 120h # maximum duration of a metrics query, increase for local setups + query_backend_after: 5m + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + +distributor: + receivers: # this configuration will listen on all ports and protocols that tempo is capable of. + jaeger: # the receives all come from the OpenTelemetry collector. more configuration information can + protocols: # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver + thrift_http: # + endpoint: "tempo:14268" # for a production deployment you should only enable the receivers you need! + grpc: + endpoint: "tempo:14250" + thrift_binary: + endpoint: "tempo:6832" + thrift_compact: + endpoint: "tempo:6831" + zipkin: + endpoint: "tempo:9411" + otlp: + protocols: + grpc: + endpoint: "tempo:4317" + http: + endpoint: "tempo:4318" + opencensus: + endpoint: "tempo:55678" + +ingester: + max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally + +compactor: + compaction: + block_retention: 24h # overall Tempo trace retention. set for demo purposes + +metrics_generator: + registry: + external_labels: + source: tempo + cluster: docker-compose + storage: + path: /var/tempo/generator/wal + remote_write: + - url: http://prometheus:9090/api/v1/write + send_exemplars: true + traces_storage: + path: /var/tempo/generator/traces + processor: + local_blocks: + filter_server_spans: false + flush_to_storage: true + +storage: + trace: + backend: local # backend configuration to use + wal: + path: /var/tempo/wal # where to store the wal locally + local: + path: /var/tempo/blocks + +overrides: + defaults: + metrics_generator: + processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator + generate_native_histograms: both \ No newline at end of file diff --git a/templates/Full/scripts/migrations.sql b/templates/Full/scripts/sql/migrations.sql similarity index 100% rename from templates/Full/scripts/migrations.sql rename to templates/Full/scripts/sql/migrations.sql diff --git a/templates/Full/scripts/seeds.sql b/templates/Full/scripts/sql/seeds.sql similarity index 100% rename from templates/Full/scripts/seeds.sql rename to templates/Full/scripts/sql/seeds.sql diff --git a/templates/Full/src/Infrastructure/Infrastructure.csproj b/templates/Full/src/Infrastructure/Infrastructure.csproj index 5884fe46..605c7450 100644 --- a/templates/Full/src/Infrastructure/Infrastructure.csproj +++ b/templates/Full/src/Infrastructure/Infrastructure.csproj @@ -10,13 +10,18 @@ + + + + - - - - + + + + + diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 6a2dacc3..5f551c47 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -1,4 +1,5 @@ using Application.Common.Constants; +using Grafana.OpenTelemetry; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -36,32 +37,47 @@ public WebApplicationBuilder AddOpenTelemetry() builder.Services.AddOpenTelemetry() .ConfigureResource(resource => resource.AddEnvironmentVariableDetector()) - .WithMetrics(metrics => metrics - .AddAspNetCoreInstrumentation() - .AddMeter(DefaultConfigurations.Meter.Name) - .AddHttpClientInstrumentation() - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterMetricsEndpoint); - }) - ) - .WithTracing(tracing => tracing - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation(options => - { - options.RecordException = true; - }) - .AddEntityFrameworkCoreInstrumentation( - options => + .WithMetrics(metrics => + { + metrics.AddView( + "http.server.request.duration", + new ExplicitBucketHistogramConfiguration() { - options.SetDbStatementForText = true; - options.SetDbStatementForStoredProcedure = true; + Boundaries = [0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10] } - ) + ); + metrics.AddMeter( + DefaultConfigurations.Meter.Name, + "System.Diagnostics.Metrics", + "Microsoft.AspNetCore.Hosting", + "Microsoft.AspNetCore.Server.Kestrel", + "System.Net.Http" + ); + + metrics + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddProcessInstrumentation() + .AddSqlClientInstrumentation() + .AddPrometheusExporter() + .AddOtlpExporter(options => + { + options.Protocol = exporterProtocol; + options.Endpoint = new Uri(exporterMetricsEndpoint); + }) + .UseGrafana(); + } + ) + .WithTracing(tracing => tracing + .AddSqlClientInstrumentation() .AddRedisInstrumentation() .AddRabbitMQInstrumentation() .AddGrpcClientInstrumentation() + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddEntityFrameworkCoreInstrumentation() + .UseGrafana() .AddOtlpExporter(options => { options.Protocol = exporterProtocol; @@ -69,6 +85,7 @@ public WebApplicationBuilder AddOpenTelemetry() }) ) .WithLogging(logging => logging + .AddConsoleExporter() .AddOtlpExporter(options => { options.Protocol = exporterProtocol; @@ -76,10 +93,13 @@ public WebApplicationBuilder AddOpenTelemetry() }) ); - builder.Services.AddLogging(logging => logging.AddOpenTelemetry(openTelemetryLoggerOptions => + builder.Services.AddLogging(logging => logging.AddOpenTelemetry(options => { - openTelemetryLoggerOptions.IncludeScopes = true; - openTelemetryLoggerOptions.IncludeFormattedMessage = true; + options.IncludeFormattedMessage = true; + options.IncludeScopes = true; + options.ParseStateValues = true; + options.AttachLogsToActivityEvent(); + options.UseGrafana(); })); return builder; diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json index 7e65cc64..866cff48 100644 --- a/templates/Full/src/Infrastructure/packages.lock.json +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -2,6 +2,27 @@ "version": 2, "dependencies": { "net10.0": { + "Grafana.OpenTelemetry": { + "type": "Direct", + "requested": "[1.5.2, )", + "resolved": "1.5.2", + "contentHash": "5QDzhHq7vMd22uCXT7+TL7LRRxJ/wtsLcDosb81qKQxoxnKixLb0zjzEV157WcU3vbwlTXP07AIH3kpZzFrEWQ==", + "dependencies": { + "CassandraCSharpDriver": "3.22.0", + "Grafana.OpenTelemetry.Base": "1.5.2", + "OpenTelemetry.Extensions.Hosting": "1.15.0", + "OpenTelemetry.Instrumentation.AWS": "1.15.0", + "OpenTelemetry.Instrumentation.AWSLambda": "1.15.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.15.0", + "OpenTelemetry.Instrumentation.Cassandra": "1.0.0-beta.5", + "OpenTelemetry.Instrumentation.ElasticsearchClient": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Hangfire": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Quartz": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Wcf": "1.15.0-beta.1" + } + }, "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", "requested": "[10.0.2, )", @@ -57,74 +78,111 @@ "Npgsql": "10.0.0" } }, - "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "OpenTelemetry.Exporter.Console": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "Jweov3Q70xmy5U8bwab8xd+xAuaFBI4695q/IpH4/dcAwKytyB+WhV5HufmKfXiKZhRbSEo8piG+i1ENEmdFXw==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Exporter.Prometheus.AspNetCore": { + "type": "Direct", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "015RkS1PLohpIRXQA9mhi+KLUSIz4kdZ683ur1IIJ1YuCT1sf0maXTKZ3zNy6JtBzBXCIUmzt8ug1lRK8qLQ0A==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Extensions": { + "type": "Direct", + "requested": "[1.14.0-beta.1, )", + "resolved": "1.14.0-beta.1", + "contentHash": "4DxYvBgz3OaCD8DE2lutiMiaEZh/PvWW05ewhAeqS6lKAETultAzfCveDXxMcIkaVwc93PR/Z22/P5zZ2rmHZA==", + "dependencies": { + "OpenTelemetry": "[1.14.0, 2.0.0)" } }, "OpenTelemetry.Extensions.Hosting": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "RixjKyB1pbYGhWdvPto4KJs+exdQknJsnjUO9WszdLles5Vcd0EYzxPNJdwmLjYfP+Jfbr4B5nktM4ZgeHSWtg==", "dependencies": { "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" } }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.EntityFrameworkCore": { "type": "Direct", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "N01GzP+r8lpSBiqjRX0/WjSp17r+zk6dKvGKthiASyFzF44lrJo8cA3ihXnw3p4Rnqg1mVjOYy19R6iJ84NTpg==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.GrpcNetClient": { "type": "Direct", - "requested": "[1.12.0-beta.1, )", - "resolved": "1.12.0-beta.1", - "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "SBas5+C4kGUqoy8OPpQis+QIgJ7/aaJl4H3oLzHCJnZLCb8TXZmQL2/r753RXXJUH8oIeLIzdW+EXgujSy+cpQ==", "dependencies": { - "OpenTelemetry": "[1.12.0, 2.0.0)" + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.Http": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "uToc7bUp8IEdb0ny9mKsL6FrrYelINPzxxiSShJgOf4XmQc4Azww6S5RjRj24YhsOn2a1MABOrxfVTZXtDk4Eg==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.0", "Microsoft.Extensions.Options": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Process": { + "type": "Direct", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "99zSvpwsMpKextd3RkHGY8iRsmw1qg3PjqkjC5hZI0fZG6m+wPsssrX6z9RhwDGcZ8sdGNjoKyLpNOmR154zQg==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Runtime": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "OOvpqR/j2Pb6+tWhHNODIbSJ53Or/MDtTiXEyrsWI02K2lLAgvBFcxUOrHggS/8015cYR3AdSaXv6NZrkz5yQA==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.StackExchangeRedis": { "type": "Direct", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "Igg/3MlBZZ9lZCTzMcvoFKav263+zOcKx9s4LVIdq96YmBHCuPmDiyygAIPdeIVzwN08VwD3RG1nXHDuRF1Ssg==", "dependencies": { - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", - "StackExchange.Redis": "[2.6.122, 3.0.0)" + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)", + "StackExchange.Redis": "2.6.122" } }, "RabbitMQ.Client": { @@ -146,11 +204,102 @@ "RabbitMQ.Client": "7.2.0" } }, + "Amazon.Lambda.APIGatewayEvents": { + "type": "Transitive", + "resolved": "2.7.0", + "contentHash": "HAbdRKLzvUqRg/fItan7up/PWsj6vQfYF+1a4YV6sv1bq5TuK/ra+ndJwWMlzL+MFJzJaHwCmtkyBbh/vz8Oig==" + }, + "Amazon.Lambda.ApplicationLoadBalancerEvents": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UtCd9tFuVyED8+DmDKEkdXJbAb/TdHIz51R/CsJCexKerO6HGOG87FGId4Ce9IXE+qjlBGyNQsKCw9oLLNe0hw==" + }, + "Amazon.Lambda.Core": { + "type": "Transitive", + "resolved": "2.8.0", + "contentHash": "mZnqCrLuNvB9T9/bCIcu/7H8HWd3yi6sEDzRE76D9ym06D+CoEfJ/EG4w7FucclvpSXO7ui/MKpzqhEnR2FAVg==" + }, + "Amazon.Lambda.SNSEvents": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "w2PKM5eQ6DNCR07H6SwHdcPXAr/qKQhNoAD+8bWydpgh4QdS2O4heo9vxhxn1g5FMSKeOxVa2zMKUZ9xeSYPZw==" + }, + "Amazon.Lambda.SQSEvents": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "RyYLgEa6nh+VJrCfTDKxvTLS1+eLDIIDyHgwDY3TR1WQz1+UeDIcccOJvoDk2nmjkSA0TfWJmZX9Ld0ySf57lA==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "4.0.3.3", + "contentHash": "YQv10JuxnciWh0QwnkarSbge4gXQV1qTURf5jkBjNUH/3jYS9QrbxopA4TK1qdjfOfP37tqiJkLSrRRNqX81aw==" + }, + "AWSSDK.SimpleNotificationService": { + "type": "Transitive", + "resolved": "4.0.2.7", + "contentHash": "WO0xjj2demSh3hPKQtSS3XyncITIUlENCeLv/FZsteMJh4HpfGwxp1dbq8YnaR90ddzvyPU6uj675WGuMf6zgg==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)" + } + }, + "AWSSDK.SQS": { + "type": "Transitive", + "resolved": "4.0.2.5", + "contentHash": "bHA9m/2RZHNKt6NGvQ56rfEDj/pfUlcwPeCg2HmPJ3jZPyoerBuEsYvFkMP3YJNv3aoycNWZoiDk0/ULP0tEyA==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)" + } + }, + "CassandraCSharpDriver": { + "type": "Transitive", + "resolved": "3.22.0", + "contentHash": "T5vjErmZ42Q1TAzbYSJvOrAzOZmb/d+v3OoIYrKfbcqjDuvedd61e0bAaaLpgiujPBBO+qFpxR4T0WSXkMr/fA==", + "dependencies": { + "K4os.Compression.LZ4": "1.1.11", + "Microsoft.Extensions.Logging": "1.0.0", + "Microsoft.Extensions.Logging.Abstractions": "1.0.0", + "Newtonsoft.Json": "9.0.1", + "System.Management": "4.7.0" + } + }, + "Grafana.OpenTelemetry.Base": { + "type": "Transitive", + "resolved": "1.5.2", + "contentHash": "MS8yH+QFubR0Kllsxo7o2w9A2OwVeI2iB8gfGr7YPKEfwBYAcul4bHzPA9dyhtHE7hxGBuhh07bdNzMG8oRhyw==", + "dependencies": { + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.0", + "OpenTelemetry": "1.15.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.15.0", + "OpenTelemetry.Instrumentation.GrpcNetClient": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Http": "1.15.0", + "OpenTelemetry.Instrumentation.Process": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Runtime": "1.15.0", + "OpenTelemetry.Instrumentation.SqlClient": "1.15.0", + "OpenTelemetry.Resources.Container": "1.15.0-beta.1", + "OpenTelemetry.Resources.Host": "1.15.0-beta.2", + "OpenTelemetry.Resources.OperatingSystem": "1.15.0-beta.1", + "OpenTelemetry.Resources.Process": "1.15.0-beta.1", + "OpenTelemetry.Resources.ProcessRuntime": "1.15.0-beta.1" + } + }, + "Hangfire.Core": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "ZWfWcE+kOWjKy7l/rsVtZmxs5XdltJY5ndx+vy7enD85ZVgc0Z+nIOZ/PfBhQQ68vGOfwOudpQVHuwXlSn7/fQ==", + "dependencies": { + "Newtonsoft.Json": "11.0.1" + } + }, "Humanizer.Core": { "type": "Transitive", "resolved": "2.14.1", "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, + "K4os.Compression.LZ4": { + "type": "Transitive", + "resolved": "1.1.11", + "contentHash": "RNvJw0UGkedPhCqVBNIogtfQebY+bQt0PN7xDbVe5LWLra0ZEqPfjPSl7iStj3rgDnkqkkTTpm+vCX3hU1qKmA==" + }, "Microsoft.Build.Framework": { "type": "Transitive", "resolved": "18.0.2", @@ -277,6 +426,15 @@ "Microsoft.Extensions.Configuration.Abstractions": "10.0.0" } }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "TmFegsI/uCdwMBD4yKpmO+OkjVNHQL49Dh/ep83NI5rPUEoBK9OdsJo1zURc1A2FuS/R/Pos3wsTjlyLnguBLA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0" + } + }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -365,11 +523,24 @@ "resolved": "10.0.2", "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "3.1.4", + "contentHash": "9/y05/CuxE+j184Nr4KihhB9KcUkvGojmD4JV4Vt/mHhVZR+eOCD5WCM+CXye9K0OFMsaPXbN+IcaIpjgBGZmg==" + }, "Microsoft.VisualStudio.SolutionPersistence": { "type": "Transitive", "resolved": "1.0.52", "contentHash": "oNv2JtYXhpdJrX63nibx1JT3uCESOBQ1LAk7Dtz/sr0+laW0KRM6eKp4CZ3MHDR2siIkKsY8MmUkeP5DKkQQ5w==" }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0" + } + }, "Mono.TextTemplating": { "type": "Transitive", "resolved": "3.0.0", @@ -388,26 +559,164 @@ }, "OpenTelemetry": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "resolved": "1.15.0", + "contentHash": "7mS/oZFF8S6xyqGQfMU1btp0nXJQUPWV535Vp/XMLYwRAUv36xQN+U4vufWBF1+z4HnRTOwuFHtUSGnHbyN6FQ==", "dependencies": { "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", "Microsoft.Extensions.Logging.Configuration": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.0" } }, "OpenTelemetry.Api": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + "resolved": "1.15.0", + "contentHash": "vk5OGdf6K9kQScCWo3bRjhDWCv6Pqw92IpX4dlARZ8B1WL7/2NGTDtCkkw42eQf7UdwyoHKzVvMH/PtL8d6z7w==" }, "OpenTelemetry.Api.ProviderBuilderExtensions": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "resolved": "1.15.0", + "contentHash": "OnuSUlRpGvowkOzGFQfy+KZFu0cITfKfh2IYJJiZskxVJiOuexwOOuvfDAgpJdmTzVWAHjYdz2shcHZaJ06UjQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", - "OpenTelemetry.Api": "1.14.0" + "OpenTelemetry.Api": "1.15.0" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Extensions.AWS": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "XO2mKJSF3cjzNepezUcKdYqnk1Ex3VCQ8UMbNH8oo3eN/tZ6EXVnI/9daXgEoXIJLPHjeHHT/+aEy3wd74lF8A==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.AWS": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "CVI0H47mfFHAw4cFoUf6QO1H5O1fK7MQY8+T7CriEp4rOBto65ncZMnbEQtwQhcfeeNqbOTED8TayMPWY1yMRA==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)", + "AWSSDK.SQS": "[4.0.2.5, 5.0.0)", + "AWSSDK.SimpleNotificationService": "[4.0.2.7, 5.0.0)", + "OpenTelemetry.Extensions.AWS": "1.15.0" + } + }, + "OpenTelemetry.Instrumentation.AWSLambda": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "cO+iqR2lFriHg89QNSbpbFJm0o2BsMPxEThMbH9z/OLeVcxAh3EJ6PPAfw6NuicJhntN9I00iCYPpo22T5R+Zg==", + "dependencies": { + "Amazon.Lambda.APIGatewayEvents": "2.7.0", + "Amazon.Lambda.ApplicationLoadBalancerEvents": "2.2.0", + "Amazon.Lambda.Core": "2.8.0", + "Amazon.Lambda.SNSEvents": "2.1.0", + "Amazon.Lambda.SQSEvents": "2.2.0", + "OpenTelemetry.Extensions.AWS": "1.15.0" + } + }, + "OpenTelemetry.Instrumentation.Cassandra": { + "type": "Transitive", + "resolved": "1.0.0-beta.5", + "contentHash": "4FwPZqw/mQUfHlQsjwi3qG2OPFutOJSQsY+7oa5kI1Vu5U3tGiua+WNGxSyMsrid3MhXbC4Ax5013Fx6/vHVDg==", + "dependencies": { + "CassandraCSharpDriver": "3.17.0", + "Newtonsoft.Json": "13.0.1", + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.ElasticsearchClient": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "hETsgn/JfHPfiUa5mItGu0EiCai7PnGSD5IubdmOyZntqeG/Wf8YIx3rjlF6KVbBb+JAezgGKX4oqTTlrxJQbw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Hangfire": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "Rog5ojlXL+WB4Li/L1yisvfuKVmnPIOh62g1rfUJYgJGvPLQbm5FtGuIvvRJlMPLtu7f73tYmqouw9EiWExJIQ==", + "dependencies": { + "Hangfire.Core": "1.7.0", + "Microsoft.Extensions.Options": "10.0.0", + "Newtonsoft.Json": "13.0.1", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Quartz": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "C3TR/iDaXeZKpB8QTZjsSrbOlE6D+NYwMw3xDwMyt1T7s/1/4yqyZNQA3M5h3FKlinTvFdBxIbN6ghT/A1g5Jw==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.SqlClient": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "J0lI7lCngS4TJD4T7KNsAerOIjJHNV0T2MK0iuS2tK8wF7iqL1dp4MKW05FiyfvrIXkwsvFc1okKchxS8B0+SQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Wcf": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "2U34rv1JvdB8jDS1LC7yknm+iINO+hBIZeEtKyUgUUaJT1FOINSQpIhofEtdsTR+++cIHOnvWHcru60QmiT5Cw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)", + "System.Drawing.Common": "4.7.2", + "System.Security.Cryptography.Xml": "4.7.1", + "System.ServiceModel.Primitives": "4.7.0" + } + }, + "OpenTelemetry.Resources.Container": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "b3W+tt2P9E6MybwFv/5nGLcfwDFyNcKw9fLaXrqSt3PpXrE533f+Vas00VLH0Qs5Z1fqDZSFqzgNwxmmTxvKHg==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.Host": { + "type": "Transitive", + "resolved": "1.15.0-beta.2", + "contentHash": "Ars2cy8gRmQ4n29V9L58eiFYucB8QwTfagFlNfMl3JpgkEeTsNtMInNTTJBDtnLM//7187C70n6lkc8fr2aGIA==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.OperatingSystem": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "I32LVRlWDsBALEQKD2jQx69iGjE+PZ2RdD0LoWzaKx7IsfsXoykvH7XgfF8o+/bzkAwKDr0zVe1Yb6so2zw7lw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.Process": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "ZyrUlfsSusSZd9PYB90x6bQUCuCf+gbd18wu4y8FDscW1ASS2a9bM8BRiaFIyUHh8Ztou4Sjqes2Pob2s+mZkw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.ProcessRuntime": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "bejo+D0Fkt/IeLUtR1MaDQYbFKhMibdVEtlDm5s7CapDls+U8p20MCwkQwZBIeL+gzacWR7+IhKR1EozbvW2ew==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "Pipelines.Sockets.Unofficial": { @@ -477,11 +786,75 @@ "System.Composition.Runtime": "9.0.0" } }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "4.7.2", + "contentHash": "I2y4KBK3VCvU/WqE2xv7NjQ67maXHttkFSHYKgU2evrG9Yqh0oFjfORXt5hZTk+BVjdyFo2h0/YQZsca33BGmg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.4", + "Microsoft.Win32.SystemEvents": "4.7.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "IY+uuGhgzWiCg21i8IvQeY/Z7m1tX8VuPF+ludfn7iTCaccTtJo5HkjZbBEL8kbBubKhAKKtNXr7uMtmAc28Pw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0", + "System.CodeDom": "4.7.0" + } + }, + "System.Private.ServiceModel": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "BItrYCkoTV3VzVPsrew+uc34fmLb+3ncgspa7vbO3vkfY9JQCea4u34pHE+Bcv1Iy16MgRs3n2jKVRCDg0rPfg==", + "dependencies": { + "System.Security.Cryptography.Xml": "4.5.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "0Srzh6YlhjuMxaqMyeCCdZs22cucaUAG6SKDd3gNHBJmre0VZ71ekzWu9rvLD4YXPetyNdPvV6Qst+8C++9v3Q==" + }, + "System.Security.Cryptography.Xml": { + "type": "Transitive", + "resolved": "4.7.1", + "contentHash": "ddAre1QiT5cACLNWLLE3Smk61yhHr4IzQbt0FZiHsD63aFse0xSjbQU3+Fycc5elKhqNwgwk7ueOh3x9Rv9uIg==", + "dependencies": { + "System.Security.Cryptography.Pkcs": "4.7.0", + "System.Security.Permissions": "4.7.0" + } + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==", + "dependencies": { + "System.Windows.Extensions": "4.7.0" + } + }, + "System.ServiceModel.Primitives": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "YUXIMO4kL1v6dUVptJGixAx/8Ai5trQzVn3gbk0mpwxh77kGAs+MyBRoHN/5ZoxtwNn4E1dq3N4rJCAgAUaiJA==", + "dependencies": { + "System.Private.ServiceModel": "4.7.0" + } + }, "System.Threading.RateLimiting": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==", + "dependencies": { + "System.Drawing.Common": "4.7.0" + } + }, "application": { "type": "Project", "dependencies": { diff --git a/templates/Full/src/WebApp/Program.cs b/templates/Full/src/WebApp/Program.cs index 4b6b5685..977ee077 100644 --- a/templates/Full/src/WebApp/Program.cs +++ b/templates/Full/src/WebApp/Program.cs @@ -54,6 +54,8 @@ private static async Task Main(string[] args) .UseResponseCompression() .UseMiddleware(); + app.UseOpenTelemetryPrometheusScrapingEndpoint(); + await app.RunAsync(); } } diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json index e255b853..b67c97cc 100644 --- a/templates/Full/src/WebApp/Properties/launchSettings.json +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -16,10 +16,10 @@ "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND": "Information", "ENABLE_SENSITIVE_DATA_LOGGING": "true", "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:18889", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:18889", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:18889", - "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:18889", + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:4318", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost/api/v1/otlp:9090", + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost/otlp:3100", "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", "ConnectionStrings__OrderDb": "Host=127.0.0.1;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES", diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json index ac352767..2eecec66 100644 --- a/templates/Full/src/WebApp/packages.lock.json +++ b/templates/Full/src/WebApp/packages.lock.json @@ -49,16 +49,91 @@ "Grpc.Tools": "2.76.0" } }, + "Amazon.Lambda.APIGatewayEvents": { + "type": "Transitive", + "resolved": "2.7.0", + "contentHash": "HAbdRKLzvUqRg/fItan7up/PWsj6vQfYF+1a4YV6sv1bq5TuK/ra+ndJwWMlzL+MFJzJaHwCmtkyBbh/vz8Oig==" + }, + "Amazon.Lambda.ApplicationLoadBalancerEvents": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UtCd9tFuVyED8+DmDKEkdXJbAb/TdHIz51R/CsJCexKerO6HGOG87FGId4Ce9IXE+qjlBGyNQsKCw9oLLNe0hw==" + }, + "Amazon.Lambda.Core": { + "type": "Transitive", + "resolved": "2.8.0", + "contentHash": "mZnqCrLuNvB9T9/bCIcu/7H8HWd3yi6sEDzRE76D9ym06D+CoEfJ/EG4w7FucclvpSXO7ui/MKpzqhEnR2FAVg==" + }, + "Amazon.Lambda.SNSEvents": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "w2PKM5eQ6DNCR07H6SwHdcPXAr/qKQhNoAD+8bWydpgh4QdS2O4heo9vxhxn1g5FMSKeOxVa2zMKUZ9xeSYPZw==" + }, + "Amazon.Lambda.SQSEvents": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "RyYLgEa6nh+VJrCfTDKxvTLS1+eLDIIDyHgwDY3TR1WQz1+UeDIcccOJvoDk2nmjkSA0TfWJmZX9Ld0ySf57lA==" + }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==" }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "4.0.3.3", + "contentHash": "YQv10JuxnciWh0QwnkarSbge4gXQV1qTURf5jkBjNUH/3jYS9QrbxopA4TK1qdjfOfP37tqiJkLSrRRNqX81aw==" + }, + "AWSSDK.SimpleNotificationService": { + "type": "Transitive", + "resolved": "4.0.2.7", + "contentHash": "WO0xjj2demSh3hPKQtSS3XyncITIUlENCeLv/FZsteMJh4HpfGwxp1dbq8YnaR90ddzvyPU6uj675WGuMf6zgg==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)" + } + }, + "AWSSDK.SQS": { + "type": "Transitive", + "resolved": "4.0.2.5", + "contentHash": "bHA9m/2RZHNKt6NGvQ56rfEDj/pfUlcwPeCg2HmPJ3jZPyoerBuEsYvFkMP3YJNv3aoycNWZoiDk0/ULP0tEyA==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)" + } + }, + "CassandraCSharpDriver": { + "type": "Transitive", + "resolved": "3.22.0", + "contentHash": "T5vjErmZ42Q1TAzbYSJvOrAzOZmb/d+v3OoIYrKfbcqjDuvedd61e0bAaaLpgiujPBBO+qFpxR4T0WSXkMr/fA==", + "dependencies": { + "K4os.Compression.LZ4": "1.1.11", + "Newtonsoft.Json": "9.0.1", + "System.Management": "4.7.0" + } + }, "Google.Protobuf": { "type": "Transitive", "resolved": "3.31.1", "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" }, + "Grafana.OpenTelemetry.Base": { + "type": "Transitive", + "resolved": "1.5.2", + "contentHash": "MS8yH+QFubR0Kllsxo7o2w9A2OwVeI2iB8gfGr7YPKEfwBYAcul4bHzPA9dyhtHE7hxGBuhh07bdNzMG8oRhyw==", + "dependencies": { + "OpenTelemetry": "1.15.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.15.0", + "OpenTelemetry.Instrumentation.GrpcNetClient": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Http": "1.15.0", + "OpenTelemetry.Instrumentation.Process": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Runtime": "1.15.0", + "OpenTelemetry.Instrumentation.SqlClient": "1.15.0", + "OpenTelemetry.Resources.Container": "1.15.0-beta.1", + "OpenTelemetry.Resources.Host": "1.15.0-beta.2", + "OpenTelemetry.Resources.OperatingSystem": "1.15.0-beta.1", + "OpenTelemetry.Resources.Process": "1.15.0-beta.1", + "OpenTelemetry.Resources.ProcessRuntime": "1.15.0-beta.1" + } + }, "Grpc.AspNetCore.Server": { "type": "Transitive", "resolved": "2.76.0", @@ -110,6 +185,19 @@ "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" }, + "Hangfire.Core": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "ZWfWcE+kOWjKy7l/rsVtZmxs5XdltJY5ndx+vy7enD85ZVgc0Z+nIOZ/PfBhQQ68vGOfwOudpQVHuwXlSn7/fQ==", + "dependencies": { + "Newtonsoft.Json": "11.0.1" + } + }, + "K4os.Compression.LZ4": { + "type": "Transitive", + "resolved": "1.1.11", + "contentHash": "RNvJw0UGkedPhCqVBNIogtfQebY+bQt0PN7xDbVe5LWLra0ZEqPfjPSl7iStj3rgDnkqkkTTpm+vCX3hU1qKmA==" + }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -120,6 +208,19 @@ "resolved": "10.0.2", "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "3.1.4", + "contentHash": "9/y05/CuxE+j184Nr4KihhB9KcUkvGojmD4JV4Vt/mHhVZR+eOCD5WCM+CXye9K0OFMsaPXbN+IcaIpjgBGZmg==" + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0" + } + }, "Npgsql": { "type": "Transitive", "resolved": "10.0.0", @@ -127,23 +228,157 @@ }, "OpenTelemetry": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "resolved": "1.15.0", + "contentHash": "7mS/oZFF8S6xyqGQfMU1btp0nXJQUPWV535Vp/XMLYwRAUv36xQN+U4vufWBF1+z4HnRTOwuFHtUSGnHbyN6FQ==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.0" } }, "OpenTelemetry.Api": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + "resolved": "1.15.0", + "contentHash": "vk5OGdf6K9kQScCWo3bRjhDWCv6Pqw92IpX4dlARZ8B1WL7/2NGTDtCkkw42eQf7UdwyoHKzVvMH/PtL8d6z7w==" }, "OpenTelemetry.Api.ProviderBuilderExtensions": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "resolved": "1.15.0", + "contentHash": "OnuSUlRpGvowkOzGFQfy+KZFu0cITfKfh2IYJJiZskxVJiOuexwOOuvfDAgpJdmTzVWAHjYdz2shcHZaJ06UjQ==", + "dependencies": { + "OpenTelemetry.Api": "1.15.0" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Extensions.AWS": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "XO2mKJSF3cjzNepezUcKdYqnk1Ex3VCQ8UMbNH8oo3eN/tZ6EXVnI/9daXgEoXIJLPHjeHHT/+aEy3wd74lF8A==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.AWS": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "CVI0H47mfFHAw4cFoUf6QO1H5O1fK7MQY8+T7CriEp4rOBto65ncZMnbEQtwQhcfeeNqbOTED8TayMPWY1yMRA==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)", + "AWSSDK.SQS": "[4.0.2.5, 5.0.0)", + "AWSSDK.SimpleNotificationService": "[4.0.2.7, 5.0.0)", + "OpenTelemetry.Extensions.AWS": "1.15.0" + } + }, + "OpenTelemetry.Instrumentation.AWSLambda": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "cO+iqR2lFriHg89QNSbpbFJm0o2BsMPxEThMbH9z/OLeVcxAh3EJ6PPAfw6NuicJhntN9I00iCYPpo22T5R+Zg==", + "dependencies": { + "Amazon.Lambda.APIGatewayEvents": "2.7.0", + "Amazon.Lambda.ApplicationLoadBalancerEvents": "2.2.0", + "Amazon.Lambda.Core": "2.8.0", + "Amazon.Lambda.SNSEvents": "2.1.0", + "Amazon.Lambda.SQSEvents": "2.2.0", + "OpenTelemetry.Extensions.AWS": "1.15.0" + } + }, + "OpenTelemetry.Instrumentation.Cassandra": { + "type": "Transitive", + "resolved": "1.0.0-beta.5", + "contentHash": "4FwPZqw/mQUfHlQsjwi3qG2OPFutOJSQsY+7oa5kI1Vu5U3tGiua+WNGxSyMsrid3MhXbC4Ax5013Fx6/vHVDg==", + "dependencies": { + "CassandraCSharpDriver": "3.17.0", + "Newtonsoft.Json": "13.0.1", + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.ElasticsearchClient": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "hETsgn/JfHPfiUa5mItGu0EiCai7PnGSD5IubdmOyZntqeG/Wf8YIx3rjlF6KVbBb+JAezgGKX4oqTTlrxJQbw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Hangfire": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "Rog5ojlXL+WB4Li/L1yisvfuKVmnPIOh62g1rfUJYgJGvPLQbm5FtGuIvvRJlMPLtu7f73tYmqouw9EiWExJIQ==", + "dependencies": { + "Hangfire.Core": "1.7.0", + "Newtonsoft.Json": "13.0.1", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Quartz": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "C3TR/iDaXeZKpB8QTZjsSrbOlE6D+NYwMw3xDwMyt1T7s/1/4yqyZNQA3M5h3FKlinTvFdBxIbN6ghT/A1g5Jw==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.SqlClient": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "J0lI7lCngS4TJD4T7KNsAerOIjJHNV0T2MK0iuS2tK8wF7iqL1dp4MKW05FiyfvrIXkwsvFc1okKchxS8B0+SQ==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Wcf": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "2U34rv1JvdB8jDS1LC7yknm+iINO+hBIZeEtKyUgUUaJT1FOINSQpIhofEtdsTR+++cIHOnvWHcru60QmiT5Cw==", "dependencies": { - "OpenTelemetry.Api": "1.14.0" + "OpenTelemetry": "[1.15.0, 2.0.0)", + "System.Drawing.Common": "4.7.2", + "System.ServiceModel.Primitives": "4.7.0" + } + }, + "OpenTelemetry.Resources.Container": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "b3W+tt2P9E6MybwFv/5nGLcfwDFyNcKw9fLaXrqSt3PpXrE533f+Vas00VLH0Qs5Z1fqDZSFqzgNwxmmTxvKHg==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.Host": { + "type": "Transitive", + "resolved": "1.15.0-beta.2", + "contentHash": "Ars2cy8gRmQ4n29V9L58eiFYucB8QwTfagFlNfMl3JpgkEeTsNtMInNTTJBDtnLM//7187C70n6lkc8fr2aGIA==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.OperatingSystem": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "I32LVRlWDsBALEQKD2jQx69iGjE+PZ2RdD0LoWzaKx7IsfsXoykvH7XgfF8o+/bzkAwKDr0zVe1Yb6so2zw7lw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.Process": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "ZyrUlfsSusSZd9PYB90x6bQUCuCf+gbd18wu4y8FDscW1ASS2a9bM8BRiaFIyUHh8Ztou4Sjqes2Pob2s+mZkw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.ProcessRuntime": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "bejo+D0Fkt/IeLUtR1MaDQYbFKhMibdVEtlDm5s7CapDls+U8p20MCwkQwZBIeL+gzacWR7+IhKR1EozbvW2ew==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "Pipelines.Sockets.Unofficial": { @@ -159,6 +394,42 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "Hs9pw/kmvH3lXaZ1LFKj3pLQsiGfj2xo3sxSzwiLlRL6UcMZUTeCfoJ9Udalvn3yq5dLlPEZzYegrTQ1/LhPOQ==" + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "4.7.2", + "contentHash": "I2y4KBK3VCvU/WqE2xv7NjQ67maXHttkFSHYKgU2evrG9Yqh0oFjfORXt5hZTk+BVjdyFo2h0/YQZsca33BGmg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.4", + "Microsoft.Win32.SystemEvents": "4.7.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "IY+uuGhgzWiCg21i8IvQeY/Z7m1tX8VuPF+ludfn7iTCaccTtJo5HkjZbBEL8kbBubKhAKKtNXr7uMtmAc28Pw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0", + "System.CodeDom": "4.7.0" + } + }, + "System.Private.ServiceModel": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "BItrYCkoTV3VzVPsrew+uc34fmLb+3ncgspa7vbO3vkfY9JQCea4u34pHE+Bcv1Iy16MgRs3n2jKVRCDg0rPfg==" + }, + "System.ServiceModel.Primitives": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "YUXIMO4kL1v6dUVptJGixAx/8Ai5trQzVn3gbk0mpwxh77kGAs+MyBRoHN/5ZoxtwNn4E1dq3N4rJCAgAUaiJA==", + "dependencies": { + "System.Private.ServiceModel": "4.7.0" + } + }, "application": { "type": "Project", "dependencies": { @@ -175,16 +446,21 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", + "Grafana.OpenTelemetry": "[1.5.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", - "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", - "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", - "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", - "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", - "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )", + "OpenTelemetry.Exporter.Console": "[1.15.0, )", + "OpenTelemetry.Exporter.Prometheus.AspNetCore": "[1.15.0-beta.1, )", + "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", + "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", + "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Runtime": "[1.15.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.15.0-beta.1, )", "RabbitMQ.Client": "[7.2.0, )", "RabbitMQ.Client.OpenTelemetry": "[1.0.0-rc.2, )" } @@ -204,6 +480,27 @@ "FluentValidation": "12.1.1" } }, + "Grafana.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[1.5.2, )", + "resolved": "1.5.2", + "contentHash": "5QDzhHq7vMd22uCXT7+TL7LRRxJ/wtsLcDosb81qKQxoxnKixLb0zjzEV157WcU3vbwlTXP07AIH3kpZzFrEWQ==", + "dependencies": { + "CassandraCSharpDriver": "3.22.0", + "Grafana.OpenTelemetry.Base": "1.5.2", + "OpenTelemetry.Extensions.Hosting": "1.15.0", + "OpenTelemetry.Instrumentation.AWS": "1.15.0", + "OpenTelemetry.Instrumentation.AWSLambda": "1.15.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.15.0", + "OpenTelemetry.Instrumentation.Cassandra": "1.0.0-beta.5", + "OpenTelemetry.Instrumentation.ElasticsearchClient": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Hangfire": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Quartz": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Wcf": "1.15.0-beta.1" + } + }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", "requested": "[10.0.2, )", @@ -238,6 +535,12 @@ "StackExchange.Redis": "2.7.27" } }, + "Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "CentralTransitive", "requested": "[10.0.0, )", @@ -249,68 +552,104 @@ "Npgsql": "10.0.0" } }, - "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "OpenTelemetry.Exporter.Console": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "Jweov3Q70xmy5U8bwab8xd+xAuaFBI4695q/IpH4/dcAwKytyB+WhV5HufmKfXiKZhRbSEo8piG+i1ENEmdFXw==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Exporter.Prometheus.AspNetCore": { + "type": "CentralTransitive", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "015RkS1PLohpIRXQA9mhi+KLUSIz4kdZ683ur1IIJ1YuCT1sf0maXTKZ3zNy6JtBzBXCIUmzt8ug1lRK8qLQ0A==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Extensions": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "requested": "[1.14.0-beta.1, )", + "resolved": "1.14.0-beta.1", + "contentHash": "4DxYvBgz3OaCD8DE2lutiMiaEZh/PvWW05ewhAeqS6lKAETultAzfCveDXxMcIkaVwc93PR/Z22/P5zZ2rmHZA==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "[1.14.0, 2.0.0)" } }, "OpenTelemetry.Extensions.Hosting": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "RixjKyB1pbYGhWdvPto4KJs+exdQknJsnjUO9WszdLles5Vcd0EYzxPNJdwmLjYfP+Jfbr4B5nktM4ZgeHSWtg==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" } }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "N01GzP+r8lpSBiqjRX0/WjSp17r+zk6dKvGKthiASyFzF44lrJo8cA3ihXnw3p4Rnqg1mVjOYy19R6iJ84NTpg==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.GrpcNetClient": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.1, )", - "resolved": "1.12.0-beta.1", - "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "SBas5+C4kGUqoy8OPpQis+QIgJ7/aaJl4H3oLzHCJnZLCb8TXZmQL2/r753RXXJUH8oIeLIzdW+EXgujSy+cpQ==", "dependencies": { - "OpenTelemetry": "[1.12.0, 2.0.0)" + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.Http": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "uToc7bUp8IEdb0ny9mKsL6FrrYelINPzxxiSShJgOf4XmQc4Azww6S5RjRj24YhsOn2a1MABOrxfVTZXtDk4Eg==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Process": { + "type": "CentralTransitive", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "99zSvpwsMpKextd3RkHGY8iRsmw1qg3PjqkjC5hZI0fZG6m+wPsssrX6z9RhwDGcZ8sdGNjoKyLpNOmR154zQg==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Runtime": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "OOvpqR/j2Pb6+tWhHNODIbSJ53Or/MDtTiXEyrsWI02K2lLAgvBFcxUOrHggS/8015cYR3AdSaXv6NZrkz5yQA==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "Igg/3MlBZZ9lZCTzMcvoFKav263+zOcKx9s4LVIdq96YmBHCuPmDiyygAIPdeIVzwN08VwD3RG1nXHDuRF1Ssg==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", - "StackExchange.Redis": "[2.6.122, 3.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)", + "StackExchange.Redis": "2.6.122" } }, "RabbitMQ.Client": { diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json index 07cd6abe..9d3e4e33 100644 --- a/templates/Full/tests/IntegrationTests/packages.lock.json +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -52,6 +52,31 @@ "resolved": "3.1.5", "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, + "Amazon.Lambda.APIGatewayEvents": { + "type": "Transitive", + "resolved": "2.7.0", + "contentHash": "HAbdRKLzvUqRg/fItan7up/PWsj6vQfYF+1a4YV6sv1bq5TuK/ra+ndJwWMlzL+MFJzJaHwCmtkyBbh/vz8Oig==" + }, + "Amazon.Lambda.ApplicationLoadBalancerEvents": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "UtCd9tFuVyED8+DmDKEkdXJbAb/TdHIz51R/CsJCexKerO6HGOG87FGId4Ce9IXE+qjlBGyNQsKCw9oLLNe0hw==" + }, + "Amazon.Lambda.Core": { + "type": "Transitive", + "resolved": "2.8.0", + "contentHash": "mZnqCrLuNvB9T9/bCIcu/7H8HWd3yi6sEDzRE76D9ym06D+CoEfJ/EG4w7FucclvpSXO7ui/MKpzqhEnR2FAVg==" + }, + "Amazon.Lambda.SNSEvents": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "w2PKM5eQ6DNCR07H6SwHdcPXAr/qKQhNoAD+8bWydpgh4QdS2O4heo9vxhxn1g5FMSKeOxVa2zMKUZ9xeSYPZw==" + }, + "Amazon.Lambda.SQSEvents": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "RyYLgEa6nh+VJrCfTDKxvTLS1+eLDIIDyHgwDY3TR1WQz1+UeDIcccOJvoDk2nmjkSA0TfWJmZX9Ld0ySf57lA==" + }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", @@ -60,6 +85,39 @@ "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" } }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "4.0.3.3", + "contentHash": "YQv10JuxnciWh0QwnkarSbge4gXQV1qTURf5jkBjNUH/3jYS9QrbxopA4TK1qdjfOfP37tqiJkLSrRRNqX81aw==" + }, + "AWSSDK.SimpleNotificationService": { + "type": "Transitive", + "resolved": "4.0.2.7", + "contentHash": "WO0xjj2demSh3hPKQtSS3XyncITIUlENCeLv/FZsteMJh4HpfGwxp1dbq8YnaR90ddzvyPU6uj675WGuMf6zgg==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)" + } + }, + "AWSSDK.SQS": { + "type": "Transitive", + "resolved": "4.0.2.5", + "contentHash": "bHA9m/2RZHNKt6NGvQ56rfEDj/pfUlcwPeCg2HmPJ3jZPyoerBuEsYvFkMP3YJNv3aoycNWZoiDk0/ULP0tEyA==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)" + } + }, + "CassandraCSharpDriver": { + "type": "Transitive", + "resolved": "3.22.0", + "contentHash": "T5vjErmZ42Q1TAzbYSJvOrAzOZmb/d+v3OoIYrKfbcqjDuvedd61e0bAaaLpgiujPBBO+qFpxR4T0WSXkMr/fA==", + "dependencies": { + "K4os.Compression.LZ4": "1.1.11", + "Microsoft.Extensions.Logging": "1.0.0", + "Microsoft.Extensions.Logging.Abstractions": "1.0.0", + "Newtonsoft.Json": "9.0.1", + "System.Management": "4.7.0" + } + }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -73,6 +131,26 @@ "resolved": "3.31.1", "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" }, + "Grafana.OpenTelemetry.Base": { + "type": "Transitive", + "resolved": "1.5.2", + "contentHash": "MS8yH+QFubR0Kllsxo7o2w9A2OwVeI2iB8gfGr7YPKEfwBYAcul4bHzPA9dyhtHE7hxGBuhh07bdNzMG8oRhyw==", + "dependencies": { + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.0", + "OpenTelemetry": "1.15.0", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.15.0", + "OpenTelemetry.Instrumentation.GrpcNetClient": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Http": "1.15.0", + "OpenTelemetry.Instrumentation.Process": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Runtime": "1.15.0", + "OpenTelemetry.Instrumentation.SqlClient": "1.15.0", + "OpenTelemetry.Resources.Container": "1.15.0-beta.1", + "OpenTelemetry.Resources.Host": "1.15.0-beta.2", + "OpenTelemetry.Resources.OperatingSystem": "1.15.0-beta.1", + "OpenTelemetry.Resources.Process": "1.15.0-beta.1", + "OpenTelemetry.Resources.ProcessRuntime": "1.15.0-beta.1" + } + }, "Grpc.AspNetCore.Server": { "type": "Transitive", "resolved": "2.76.0", @@ -126,6 +204,19 @@ "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" }, + "Hangfire.Core": { + "type": "Transitive", + "resolved": "1.7.0", + "contentHash": "ZWfWcE+kOWjKy7l/rsVtZmxs5XdltJY5ndx+vy7enD85ZVgc0Z+nIOZ/PfBhQQ68vGOfwOudpQVHuwXlSn7/fQ==", + "dependencies": { + "Newtonsoft.Json": "11.0.1" + } + }, + "K4os.Compression.LZ4": { + "type": "Transitive", + "resolved": "1.1.11", + "contentHash": "RNvJw0UGkedPhCqVBNIogtfQebY+bQt0PN7xDbVe5LWLra0ZEqPfjPSl7iStj3rgDnkqkkTTpm+vCX3hU1qKmA==" + }, "Microsoft.CodeCoverage": { "type": "Transitive", "resolved": "18.0.1", @@ -458,8 +549,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + "resolved": "3.1.4", + "contentHash": "9/y05/CuxE+j184Nr4KihhB9KcUkvGojmD4JV4Vt/mHhVZR+eOCD5WCM+CXye9K0OFMsaPXbN+IcaIpjgBGZmg==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", @@ -475,6 +566,14 @@ "Newtonsoft.Json": "13.0.3" } }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0" + } + }, "NETStandard.Library": { "type": "Transitive", "resolved": "1.6.1", @@ -493,26 +592,164 @@ }, "OpenTelemetry": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "resolved": "1.15.0", + "contentHash": "7mS/oZFF8S6xyqGQfMU1btp0nXJQUPWV535Vp/XMLYwRAUv36xQN+U4vufWBF1+z4HnRTOwuFHtUSGnHbyN6FQ==", "dependencies": { "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", "Microsoft.Extensions.Logging.Configuration": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.0" } }, "OpenTelemetry.Api": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + "resolved": "1.15.0", + "contentHash": "vk5OGdf6K9kQScCWo3bRjhDWCv6Pqw92IpX4dlARZ8B1WL7/2NGTDtCkkw42eQf7UdwyoHKzVvMH/PtL8d6z7w==" }, "OpenTelemetry.Api.ProviderBuilderExtensions": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "resolved": "1.15.0", + "contentHash": "OnuSUlRpGvowkOzGFQfy+KZFu0cITfKfh2IYJJiZskxVJiOuexwOOuvfDAgpJdmTzVWAHjYdz2shcHZaJ06UjQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", - "OpenTelemetry.Api": "1.14.0" + "OpenTelemetry.Api": "1.15.0" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Extensions.AWS": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "XO2mKJSF3cjzNepezUcKdYqnk1Ex3VCQ8UMbNH8oo3eN/tZ6EXVnI/9daXgEoXIJLPHjeHHT/+aEy3wd74lF8A==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.AWS": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "CVI0H47mfFHAw4cFoUf6QO1H5O1fK7MQY8+T7CriEp4rOBto65ncZMnbEQtwQhcfeeNqbOTED8TayMPWY1yMRA==", + "dependencies": { + "AWSSDK.Core": "[4.0.3.3, 5.0.0)", + "AWSSDK.SQS": "[4.0.2.5, 5.0.0)", + "AWSSDK.SimpleNotificationService": "[4.0.2.7, 5.0.0)", + "OpenTelemetry.Extensions.AWS": "1.15.0" + } + }, + "OpenTelemetry.Instrumentation.AWSLambda": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "cO+iqR2lFriHg89QNSbpbFJm0o2BsMPxEThMbH9z/OLeVcxAh3EJ6PPAfw6NuicJhntN9I00iCYPpo22T5R+Zg==", + "dependencies": { + "Amazon.Lambda.APIGatewayEvents": "2.7.0", + "Amazon.Lambda.ApplicationLoadBalancerEvents": "2.2.0", + "Amazon.Lambda.Core": "2.8.0", + "Amazon.Lambda.SNSEvents": "2.1.0", + "Amazon.Lambda.SQSEvents": "2.2.0", + "OpenTelemetry.Extensions.AWS": "1.15.0" + } + }, + "OpenTelemetry.Instrumentation.Cassandra": { + "type": "Transitive", + "resolved": "1.0.0-beta.5", + "contentHash": "4FwPZqw/mQUfHlQsjwi3qG2OPFutOJSQsY+7oa5kI1Vu5U3tGiua+WNGxSyMsrid3MhXbC4Ax5013Fx6/vHVDg==", + "dependencies": { + "CassandraCSharpDriver": "3.17.0", + "Newtonsoft.Json": "13.0.1", + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.ElasticsearchClient": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "hETsgn/JfHPfiUa5mItGu0EiCai7PnGSD5IubdmOyZntqeG/Wf8YIx3rjlF6KVbBb+JAezgGKX4oqTTlrxJQbw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Hangfire": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "Rog5ojlXL+WB4Li/L1yisvfuKVmnPIOh62g1rfUJYgJGvPLQbm5FtGuIvvRJlMPLtu7f73tYmqouw9EiWExJIQ==", + "dependencies": { + "Hangfire.Core": "1.7.0", + "Microsoft.Extensions.Options": "10.0.0", + "Newtonsoft.Json": "13.0.1", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Quartz": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "C3TR/iDaXeZKpB8QTZjsSrbOlE6D+NYwMw3xDwMyt1T7s/1/4yqyZNQA3M5h3FKlinTvFdBxIbN6ghT/A1g5Jw==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.SqlClient": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "J0lI7lCngS4TJD4T7KNsAerOIjJHNV0T2MK0iuS2tK8wF7iqL1dp4MKW05FiyfvrIXkwsvFc1okKchxS8B0+SQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Wcf": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "2U34rv1JvdB8jDS1LC7yknm+iINO+hBIZeEtKyUgUUaJT1FOINSQpIhofEtdsTR+++cIHOnvWHcru60QmiT5Cw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)", + "System.Drawing.Common": "4.7.2", + "System.Security.Cryptography.Xml": "4.7.1", + "System.ServiceModel.Primitives": "4.7.0" + } + }, + "OpenTelemetry.Resources.Container": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "b3W+tt2P9E6MybwFv/5nGLcfwDFyNcKw9fLaXrqSt3PpXrE533f+Vas00VLH0Qs5Z1fqDZSFqzgNwxmmTxvKHg==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.Host": { + "type": "Transitive", + "resolved": "1.15.0-beta.2", + "contentHash": "Ars2cy8gRmQ4n29V9L58eiFYucB8QwTfagFlNfMl3JpgkEeTsNtMInNTTJBDtnLM//7187C70n6lkc8fr2aGIA==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.OperatingSystem": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "I32LVRlWDsBALEQKD2jQx69iGjE+PZ2RdD0LoWzaKx7IsfsXoykvH7XgfF8o+/bzkAwKDr0zVe1Yb6so2zw7lw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.Process": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "ZyrUlfsSusSZd9PYB90x6bQUCuCf+gbd18wu4y8FDscW1ASS2a9bM8BRiaFIyUHh8Ztou4Sjqes2Pob2s+mZkw==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Resources.ProcessRuntime": { + "type": "Transitive", + "resolved": "1.15.0-beta.1", + "contentHash": "bejo+D0Fkt/IeLUtR1MaDQYbFKhMibdVEtlDm5s7CapDls+U8p20MCwkQwZBIeL+gzacWR7+IhKR1EozbvW2ew==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "Pipelines.Sockets.Unofficial": { @@ -529,16 +766,85 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "Hs9pw/kmvH3lXaZ1LFKj3pLQsiGfj2xo3sxSzwiLlRL6UcMZUTeCfoJ9Udalvn3yq5dLlPEZzYegrTQ1/LhPOQ==" + }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "10.0.2", "contentHash": "TJPpTLF5MFPobq09c9BQ5X8QuviQfsKvH0Jbm7MkGylGvfIdRqJQLZDPC5sMRFkk9aZhmgir1NJKekip2NxfaA==" }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "4.7.2", + "contentHash": "I2y4KBK3VCvU/WqE2xv7NjQ67maXHttkFSHYKgU2evrG9Yqh0oFjfORXt5hZTk+BVjdyFo2h0/YQZsca33BGmg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.4", + "Microsoft.Win32.SystemEvents": "4.7.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "IY+uuGhgzWiCg21i8IvQeY/Z7m1tX8VuPF+ludfn7iTCaccTtJo5HkjZbBEL8kbBubKhAKKtNXr7uMtmAc28Pw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0", + "System.CodeDom": "4.7.0" + } + }, + "System.Private.ServiceModel": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "BItrYCkoTV3VzVPsrew+uc34fmLb+3ncgspa7vbO3vkfY9JQCea4u34pHE+Bcv1Iy16MgRs3n2jKVRCDg0rPfg==", + "dependencies": { + "System.Security.Cryptography.Xml": "4.5.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "0Srzh6YlhjuMxaqMyeCCdZs22cucaUAG6SKDd3gNHBJmre0VZ71ekzWu9rvLD4YXPetyNdPvV6Qst+8C++9v3Q==" + }, + "System.Security.Cryptography.Xml": { + "type": "Transitive", + "resolved": "4.7.1", + "contentHash": "ddAre1QiT5cACLNWLLE3Smk61yhHr4IzQbt0FZiHsD63aFse0xSjbQU3+Fycc5elKhqNwgwk7ueOh3x9Rv9uIg==", + "dependencies": { + "System.Security.Cryptography.Pkcs": "4.7.0", + "System.Security.Permissions": "4.7.0" + } + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==", + "dependencies": { + "System.Windows.Extensions": "4.7.0" + } + }, + "System.ServiceModel.Primitives": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "YUXIMO4kL1v6dUVptJGixAx/8Ai5trQzVn3gbk0mpwxh77kGAs+MyBRoHN/5ZoxtwNn4E1dq3N4rJCAgAUaiJA==", + "dependencies": { + "System.Private.ServiceModel": "4.7.0" + } + }, "System.Threading.RateLimiting": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==", + "dependencies": { + "System.Drawing.Common": "4.7.0" + } + }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -599,16 +905,21 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", + "Grafana.OpenTelemetry": "[1.5.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", - "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", - "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", - "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", - "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", - "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )", + "OpenTelemetry.Exporter.Console": "[1.15.0, )", + "OpenTelemetry.Exporter.Prometheus.AspNetCore": "[1.15.0-beta.1, )", + "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", + "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", + "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Runtime": "[1.15.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.15.0-beta.1, )", "RabbitMQ.Client": "[7.2.0, )", "RabbitMQ.Client.OpenTelemetry": "[1.0.0-rc.2, )" } @@ -688,6 +999,27 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" } }, + "Grafana.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[1.5.2, )", + "resolved": "1.5.2", + "contentHash": "5QDzhHq7vMd22uCXT7+TL7LRRxJ/wtsLcDosb81qKQxoxnKixLb0zjzEV157WcU3vbwlTXP07AIH3kpZzFrEWQ==", + "dependencies": { + "CassandraCSharpDriver": "3.22.0", + "Grafana.OpenTelemetry.Base": "1.5.2", + "OpenTelemetry.Extensions.Hosting": "1.15.0", + "OpenTelemetry.Instrumentation.AWS": "1.15.0", + "OpenTelemetry.Instrumentation.AWSLambda": "1.15.0", + "OpenTelemetry.Instrumentation.AspNetCore": "1.15.0", + "OpenTelemetry.Instrumentation.Cassandra": "1.0.0-beta.5", + "OpenTelemetry.Instrumentation.ElasticsearchClient": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Hangfire": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Quartz": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "1.15.0-beta.1", + "OpenTelemetry.Instrumentation.Wcf": "1.15.0-beta.1" + } + }, "Grpc.AspNetCore": { "type": "CentralTransitive", "requested": "[2.76.0, )", @@ -784,74 +1116,111 @@ "Npgsql": "10.0.0" } }, - "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "OpenTelemetry.Exporter.Console": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "Jweov3Q70xmy5U8bwab8xd+xAuaFBI4695q/IpH4/dcAwKytyB+WhV5HufmKfXiKZhRbSEo8piG+i1ENEmdFXw==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Exporter.Prometheus.AspNetCore": { + "type": "CentralTransitive", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "015RkS1PLohpIRXQA9mhi+KLUSIz4kdZ683ur1IIJ1YuCT1sf0maXTKZ3zNy6JtBzBXCIUmzt8ug1lRK8qLQ0A==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Extensions": { + "type": "CentralTransitive", + "requested": "[1.14.0-beta.1, )", + "resolved": "1.14.0-beta.1", + "contentHash": "4DxYvBgz3OaCD8DE2lutiMiaEZh/PvWW05ewhAeqS6lKAETultAzfCveDXxMcIkaVwc93PR/Z22/P5zZ2rmHZA==", + "dependencies": { + "OpenTelemetry": "[1.14.0, 2.0.0)" } }, "OpenTelemetry.Extensions.Hosting": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "RixjKyB1pbYGhWdvPto4KJs+exdQknJsnjUO9WszdLles5Vcd0EYzxPNJdwmLjYfP+Jfbr4B5nktM4ZgeHSWtg==", "dependencies": { "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" } }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "N01GzP+r8lpSBiqjRX0/WjSp17r+zk6dKvGKthiASyFzF44lrJo8cA3ihXnw3p4Rnqg1mVjOYy19R6iJ84NTpg==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.GrpcNetClient": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.1, )", - "resolved": "1.12.0-beta.1", - "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "SBas5+C4kGUqoy8OPpQis+QIgJ7/aaJl4H3oLzHCJnZLCb8TXZmQL2/r753RXXJUH8oIeLIzdW+EXgujSy+cpQ==", "dependencies": { - "OpenTelemetry": "[1.12.0, 2.0.0)" + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.Http": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "uToc7bUp8IEdb0ny9mKsL6FrrYelINPzxxiSShJgOf4XmQc4Azww6S5RjRj24YhsOn2a1MABOrxfVTZXtDk4Eg==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.0", "Microsoft.Extensions.Options": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Process": { + "type": "CentralTransitive", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "99zSvpwsMpKextd3RkHGY8iRsmw1qg3PjqkjC5hZI0fZG6m+wPsssrX6z9RhwDGcZ8sdGNjoKyLpNOmR154zQg==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Runtime": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "OOvpqR/j2Pb6+tWhHNODIbSJ53Or/MDtTiXEyrsWI02K2lLAgvBFcxUOrHggS/8015cYR3AdSaXv6NZrkz5yQA==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "Igg/3MlBZZ9lZCTzMcvoFKav263+zOcKx9s4LVIdq96YmBHCuPmDiyygAIPdeIVzwN08VwD3RG1nXHDuRF1Ssg==", "dependencies": { - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", - "StackExchange.Redis": "[2.6.122, 3.0.0)" + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)", + "StackExchange.Redis": "2.6.122" } }, "RabbitMQ.Client": { From cbd1d88c9efa7d45d9525415a5f73acb4b4585c4 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Mon, 9 Mar 2026 07:22:19 -0300 Subject: [PATCH 087/161] chore: remove aspire-dashboard service from docker-compose --- templates/Full/docker-compose.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 62790e32..2a9411f3 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -53,15 +53,6 @@ services: networks: - hexagonal_solution_template_network - aspire-dashboard: - image: mcr.microsoft.com/dotnet/aspire-dashboard:13 - container_name: aspire-dashboard - ports: - - "18888:18888" - - "18889:18889" - environment: - - DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true - redis: image: redis:8 container_name: redis From b2201293206b836273fdff35e80a649481fa5529 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Mon, 9 Mar 2026 07:23:19 -0300 Subject: [PATCH 088/161] chore: remove aspire-dashboard service from docker-compose --- templates/Full/docker-compose-load-tests.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 53dc4bb3..6d67c21f 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -72,11 +72,6 @@ services: # file: docker-compose.yml # service: pgadmin - # aspire-dashboard: - # extends: - # file: docker-compose.yml - # service: aspire-dashboard - redis: extends: file: docker-compose.yml From 77bbd5834617753604e4e8d11d58c1a9046a12a9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 10 Mar 2026 07:23:31 -0300 Subject: [PATCH 089/161] 53 feat: update docker-compose and configuration for observability --- templates/Full/docker-compose-load-tests.yml | 1 - .../Full/grafana/datasources/datasources.yaml | 0 .../Full/src/Infrastructure/Infrastructure.csproj | 1 - ...rastructureOpenTelemetryDependencyInjection.cs | 8 -------- .../src/WebApp/Properties/launchSettings.json | 15 ++------------- templates/Full/src/WebApp/appsettings.json | 12 +++++++++++- 6 files changed, 13 insertions(+), 24 deletions(-) create mode 100644 templates/Full/grafana/datasources/datasources.yaml diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 6d67c21f..e5bf3466 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -45,7 +45,6 @@ services: - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning - ENABLE_SENSITIVE_DATA_LOGGING=false - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp - - OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://aspire-dashboard:18889 - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://aspire-dashboard:18889 diff --git a/templates/Full/grafana/datasources/datasources.yaml b/templates/Full/grafana/datasources/datasources.yaml new file mode 100644 index 00000000..e69de29b diff --git a/templates/Full/src/Infrastructure/Infrastructure.csproj b/templates/Full/src/Infrastructure/Infrastructure.csproj index 605c7450..81bd7ae1 100644 --- a/templates/Full/src/Infrastructure/Infrastructure.csproj +++ b/templates/Full/src/Infrastructure/Infrastructure.csproj @@ -27,6 +27,5 @@ - \ No newline at end of file diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 5f551c47..54ee9435 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -39,13 +39,6 @@ public WebApplicationBuilder AddOpenTelemetry() .ConfigureResource(resource => resource.AddEnvironmentVariableDetector()) .WithMetrics(metrics => { - metrics.AddView( - "http.server.request.duration", - new ExplicitBucketHistogramConfiguration() - { - Boundaries = [0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10] - } - ); metrics.AddMeter( DefaultConfigurations.Meter.Name, "System.Diagnostics.Metrics", @@ -85,7 +78,6 @@ public WebApplicationBuilder AddOpenTelemetry() }) ) .WithLogging(logging => logging - .AddConsoleExporter() .AddOtlpExporter(options => { options.Protocol = exporterProtocol; diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json index b67c97cc..879115da 100644 --- a/templates/Full/src/WebApp/Properties/launchSettings.json +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -1,5 +1,5 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", + "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { "Development": { "commandName": "Project", @@ -9,23 +9,12 @@ "applicationUrl": "https://*:7175;http://*:5010", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "LOGGING__LOGLEVEL__DEFAULT": "Debug", - "LOGGING__LOGLEVEL__MICROSOFT": "Information", - "LOGGING__LOGLEVEL__MICROSOFT_HOSTING_LIFETIME": "Information", - "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE": "Information", - "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND": "Information", - "ENABLE_SENSITIVE_DATA_LOGGING": "true", "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:4318", "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost/api/v1/otlp:9090", "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost/otlp:3100", "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", - "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", - "ConnectionStrings__OrderDb": "Host=127.0.0.1;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES", - "ConnectionStrings__Redis": "127.0.0.1:6379", - "ConnectionStrings__RabbitMQ": "amqp://guest:guest@127.0.0.1:5672/" - + "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development" } } } diff --git a/templates/Full/src/WebApp/appsettings.json b/templates/Full/src/WebApp/appsettings.json index 2c926dd0..5d639f67 100644 --- a/templates/Full/src/WebApp/appsettings.json +++ b/templates/Full/src/WebApp/appsettings.json @@ -3,5 +3,15 @@ "OrderDb": "Host=127.0.0.1;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES", "Redis": "localhost:6379", "RabbitMq": "amqp://guest:guest@localhost:5672/" - } + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "Microsoft.EntityFrameworkCore": "Information", + "Microsoft.EntityFrameworkCore.Database.Command": "Information" + } + }, + "EnableSensitiveDataLogging": true } \ No newline at end of file From 4ed5fa4b39799089f3b55d21a64b00fb1c25c5a9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 10 Mar 2026 15:53:31 -0300 Subject: [PATCH 090/161] 53 style: format config and update launch settings for endpoints --- templates/Full/scripts/grafana/config.alloy | 6 +++--- templates/Full/src/WebApp/Properties/launchSettings.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/templates/Full/scripts/grafana/config.alloy b/templates/Full/scripts/grafana/config.alloy index 6577c47a..6e7d7f58 100644 --- a/templates/Full/scripts/grafana/config.alloy +++ b/templates/Full/scripts/grafana/config.alloy @@ -28,9 +28,9 @@ otelcol.exporter.otlphttp "metrics" { } otelcol.exporter.otlphttp "traces" { - client { - endpoint = "http://tempo:4318" - } + client { + endpoint = "http://tempo:4318" + } } livedebugging { diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json index 879115da..67474779 100644 --- a/templates/Full/src/WebApp/Properties/launchSettings.json +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -10,9 +10,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:4318", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost/api/v1/otlp:9090", - "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost/otlp:3100", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:4317", "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development" } From 64b6f0ee7f8bc01244c5acbe03102249937f738b Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 10 Mar 2026 16:48:38 -0300 Subject: [PATCH 091/161] 53 refactor: remove redundant use of grafana in telemetry setup --- .../InfrastructureOpenTelemetryDependencyInjection.cs | 11 ++++------- templates/Full/src/WebApp/Program.cs | 2 -- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 54ee9435..d83f2f73 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -58,10 +58,8 @@ public WebApplicationBuilder AddOpenTelemetry() { options.Protocol = exporterProtocol; options.Endpoint = new Uri(exporterMetricsEndpoint); - }) - .UseGrafana(); - } - ) + }); + }) .WithTracing(tracing => tracing .AddSqlClientInstrumentation() .AddRedisInstrumentation() @@ -70,7 +68,6 @@ public WebApplicationBuilder AddOpenTelemetry() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddEntityFrameworkCoreInstrumentation() - .UseGrafana() .AddOtlpExporter(options => { options.Protocol = exporterProtocol; @@ -83,7 +80,8 @@ public WebApplicationBuilder AddOpenTelemetry() options.Protocol = exporterProtocol; options.Endpoint = new Uri(exporterLogsEndpoint!); }) - ); + ) + .UseGrafana(); builder.Services.AddLogging(logging => logging.AddOpenTelemetry(options => { @@ -91,7 +89,6 @@ public WebApplicationBuilder AddOpenTelemetry() options.IncludeScopes = true; options.ParseStateValues = true; options.AttachLogsToActivityEvent(); - options.UseGrafana(); })); return builder; diff --git a/templates/Full/src/WebApp/Program.cs b/templates/Full/src/WebApp/Program.cs index 977ee077..4b6b5685 100644 --- a/templates/Full/src/WebApp/Program.cs +++ b/templates/Full/src/WebApp/Program.cs @@ -54,8 +54,6 @@ private static async Task Main(string[] args) .UseResponseCompression() .UseMiddleware(); - app.UseOpenTelemetryPrometheusScrapingEndpoint(); - await app.RunAsync(); } } From 00a6abd39157ff540f776e26018cd72cebcfc908 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 10 Mar 2026 16:57:43 -0300 Subject: [PATCH 092/161] 53 feat: add grafana integration to telemetry logging and metrics --- ...ructureOpenTelemetryDependencyInjection.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index d83f2f73..a405eea8 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -58,7 +58,8 @@ public WebApplicationBuilder AddOpenTelemetry() { options.Protocol = exporterProtocol; options.Endpoint = new Uri(exporterMetricsEndpoint); - }); + }) + .UseGrafana(); }) .WithTracing(tracing => tracing .AddSqlClientInstrumentation() @@ -73,22 +74,21 @@ public WebApplicationBuilder AddOpenTelemetry() options.Protocol = exporterProtocol; options.Endpoint = new Uri(exporterTracesEndpoint!); }) - ) - .WithLogging(logging => logging - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterLogsEndpoint!); - }) - ) - .UseGrafana(); + .UseGrafana() + ); builder.Services.AddLogging(logging => logging.AddOpenTelemetry(options => { options.IncludeFormattedMessage = true; options.IncludeScopes = true; options.ParseStateValues = true; - options.AttachLogsToActivityEvent(); + options + .AttachLogsToActivityEvent() + .AddOtlpExporter(exporterOptions => + { + exporterOptions.Protocol = exporterProtocol; + exporterOptions.Endpoint = new Uri(exporterLogsEndpoint!); + }).UseGrafana(); })); return builder; From c9f05c83a5470c9da12f52fa01ff58054cc36ece Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 07:43:56 -0300 Subject: [PATCH 093/161] 53 refactor: simplify open telemetry configuration and logging --- ...ructureOpenTelemetryDependencyInjection.cs | 111 +++++++----------- .../src/WebApp/Properties/launchSettings.json | 8 +- templates/Full/src/WebApp/appsettings.json | 3 +- 3 files changed, 48 insertions(+), 74 deletions(-) diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index a405eea8..2e0b94f9 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -3,8 +3,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using OpenTelemetry.Exporter; -using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -17,79 +15,58 @@ internal static class InfrastructureOpenTelemetryDependencyInjection { public WebApplicationBuilder AddOpenTelemetry() { - var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "grpc" - ? OtlpExportProtocol.Grpc - : OtlpExportProtocol.HttpProtobuf; - var exporterMetricsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"); - var exporterTracesEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"); - var exporterLogsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"); - - if ( - string.Equals(environment, "IntegrationTests", StringComparison.OrdinalIgnoreCase) || - string.IsNullOrWhiteSpace(exporterLogsEndpoint) || - string.IsNullOrWhiteSpace(exporterMetricsEndpoint) || - string.IsNullOrWhiteSpace(exporterTracesEndpoint) - ) - { - return builder; - } + var resourceBuilder = ResourceBuilder.CreateDefault() + .AddService("Hexagonal.Solution.Template.WebApp"); builder.Services.AddOpenTelemetry() - .ConfigureResource(resource => resource.AddEnvironmentVariableDetector()) - .WithMetrics(metrics => - { - metrics.AddMeter( - DefaultConfigurations.Meter.Name, - "System.Diagnostics.Metrics", - "Microsoft.AspNetCore.Hosting", - "Microsoft.AspNetCore.Server.Kestrel", - "System.Net.Http" - ); + .WithMetrics(metrics => + { + metrics.AddView( + "http.server.request.duration", + new ExplicitBucketHistogramConfiguration() + { + Boundaries = [0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10] + } + ); + metrics.AddMeter( + DefaultConfigurations.Meter.Name, + "System.Diagnostics.Metrics", + "Microsoft.AspNetCore.Hosting", + "Microsoft.AspNetCore.Server.Kestrel", + "System.Net.Http" + ); - metrics + metrics + .SetResourceBuilder(resourceBuilder) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddProcessInstrumentation() + .AddPrometheusExporter() + .UseGrafana(); + }) + .WithTracing(tracing => tracing + .AddSource("Hexagonal.Solution.Template.WebApp") + .SetResourceBuilder(resourceBuilder) + .AddRedisInstrumentation() + .AddRabbitMQInstrumentation() + .AddGrpcClientInstrumentation() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() - .AddRuntimeInstrumentation() - .AddProcessInstrumentation() - .AddSqlClientInstrumentation() - .AddPrometheusExporter() - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterMetricsEndpoint); - }) - .UseGrafana(); - }) - .WithTracing(tracing => tracing - .AddSqlClientInstrumentation() - .AddRedisInstrumentation() - .AddRabbitMQInstrumentation() - .AddGrpcClientInstrumentation() - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddEntityFrameworkCoreInstrumentation() - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterTracesEndpoint!); - }) - .UseGrafana() - ); + .AddEntityFrameworkCoreInstrumentation() + .UseGrafana() + ); - builder.Services.AddLogging(logging => logging.AddOpenTelemetry(options => + builder.Logging.AddOpenTelemetry(logging => { - options.IncludeFormattedMessage = true; - options.IncludeScopes = true; - options.ParseStateValues = true; - options + logging.SetResourceBuilder(resourceBuilder); + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + logging.ParseStateValues = true; + logging .AttachLogsToActivityEvent() - .AddOtlpExporter(exporterOptions => - { - exporterOptions.Protocol = exporterProtocol; - exporterOptions.Endpoint = new Uri(exporterLogsEndpoint!); - }).UseGrafana(); - })); + .UseGrafana(); + }); return builder; } diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json index 67474779..e069ed48 100644 --- a/templates/Full/src/WebApp/Properties/launchSettings.json +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -10,11 +10,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:4317", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:4317", - "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:4317", - "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", - "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development" + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", + "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", + "ENABLE_SENSITIVE_DATA_LOGGING": "true" } } } diff --git a/templates/Full/src/WebApp/appsettings.json b/templates/Full/src/WebApp/appsettings.json index 5d639f67..b3cdd8e8 100644 --- a/templates/Full/src/WebApp/appsettings.json +++ b/templates/Full/src/WebApp/appsettings.json @@ -12,6 +12,5 @@ "Microsoft.EntityFrameworkCore": "Information", "Microsoft.EntityFrameworkCore.Database.Command": "Information" } - }, - "EnableSensitiveDataLogging": true + } } \ No newline at end of file From bfd7764c07329ec63b6fbad6b8a56d0de2a4cce5 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 07:44:11 -0300 Subject: [PATCH 094/161] 53 refactor: simplify logging by removing elapsed time tracking --- .../Full/src/Application/Common/Helpers/Logs.cs | 12 +++++------- .../Common/UseCases/BaseInOutUseCase.cs | 11 ++++++----- .../Application/Common/UseCases/BaseInUseCase.cs | 5 +---- .../Common/UseCases/BaseOutUseCase.cs | 4 +--- .../Application/Common/UseCases/BaseUseCase.cs | 2 -- .../Cache/Services/HybridCacheService.cs | 14 +++----------- .../Infrastructure/Data/Common/BaseRepository.cs | 16 ++++++++++------ .../Messaging/Consumers/BaseConsumer.cs | 5 +---- .../Messaging/Producers/ProducerService.cs | 10 ++-------- 9 files changed, 29 insertions(+), 50 deletions(-) diff --git a/templates/Full/src/Application/Common/Helpers/Logs.cs b/templates/Full/src/Application/Common/Helpers/Logs.cs index b1d41490..4a812a6f 100644 --- a/templates/Full/src/Application/Common/Helpers/Logs.cs +++ b/templates/Full/src/Application/Common/Helpers/Logs.cs @@ -93,19 +93,18 @@ public partial class Logs public static partial void StartingOperation(ILogger logger, string className, string methodName, Guid correlationId); /// - /// Logs the completion of the execution of an operation, including the class name, method name, correlation ID, and elapsed time in milliseconds. + /// Logs the completion of the execution of an operation, including the class name, method name, and correlation ID. /// /// The logger instance to use for logging. /// The name of the class where the operation is executed. /// The name of the method where the operation is executed. /// The correlation ID for tracking the request. - /// The elapsed time in milliseconds for the execution of the operation. [LoggerMessage( EventId = 7, Level = LogLevel.Information, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Elapsed time: {ElapsedMilliseconds} ms | Finished operation" + Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Finished operation" )] - public static partial void FinishedOperation(ILogger logger, string className, string methodName, Guid correlationId, long elapsedMilliseconds); + public static partial void FinishedOperation(ILogger logger, string className, string methodName, Guid correlationId); /// /// Logs validation errors encountered during request validation. @@ -173,11 +172,10 @@ public partial class Logs /// The name of the class. /// The method name. /// The correlation ID for tracking the request. - /// The elapsed time in milliseconds. [LoggerMessage( EventId = 12, Level = LogLevel.Debug, - Message = "[{ClassName}] | [{Method}] | [{CorrelationId}] | Finished operation executed in {ElapsedMilliseconds} ms. | {Details}" + Message = "[{ClassName}] | [{Method}] | [{CorrelationId}] | Finished operation. | {Details}" )] - public static partial void DebugFinishedOperation(ILogger logger, string className, string method, Guid correlationId, long elapsedMilliseconds, string details = ""); + public static partial void DebugFinishedOperation(ILogger logger, string className, string method, Guid correlationId, string details = ""); } \ No newline at end of file diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index cbdc78b7..c2b831a4 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.Metrics; +using System.Diagnostics; +using System.Diagnostics.Metrics; using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Repositories; @@ -47,8 +48,8 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - StopWatch.Restart(); - + using var activity = new ActivitySource("Hexagonal.Solution.Template.WebApp").StartActivity($"{ClassName}.{HandleMethodName}")!; + Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); TResponseData response; @@ -73,10 +74,10 @@ CancellationToken cancellationToken response = await HandleInternalAsync(request, cancellationToken); - Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); _useCaseExecuted.Record(1); - _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); + activity?.SetTag("correlationId", request.CorrelationId); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index c1a54799..b3137337 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -43,8 +43,6 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - StopWatch.Restart(); - Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); if (_validator != null) @@ -61,10 +59,9 @@ CancellationToken cancellationToken await HandleInternalAsync(request, cancellationToken); - Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, StopWatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); _useCaseExecuted.Record(1); - _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); } public abstract Task HandleInternalAsync(TRequest request, CancellationToken cancellationToken); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 8b0f15b5..389058c7 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -34,16 +34,14 @@ protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvide public async Task HandleAsync(CancellationToken cancellationToken) { - StopWatch.Restart(); var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, ClassName, HandleMethodName, correlationId); var response = await HandleInternalAsync(cancellationToken); - Logs.FinishedOperation(Logger, ClassName, HandleMethodName, correlationId, StopWatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger, ClassName, HandleMethodName, correlationId); _useCaseExecuted.Record(1); - _useCaseExecutionElapsedTime.Record(StopWatch.ElapsedMilliseconds); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index 5be8384d..09dc8992 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -8,7 +7,6 @@ public abstract class BaseUseCase { protected IServiceProvider ServiceProvider { get; } protected ILogger Logger { get; } - protected Stopwatch StopWatch { get; } = new(); protected string ClassName { get; set; } protected BaseUseCase(IServiceProvider serviceProvider) diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index c74865ee..9aab8814 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Services; @@ -12,7 +11,6 @@ internal sealed class HybridCacheService(HybridCache cache, ILogger _logger = logger; private readonly string _className = nameof(HybridCacheService); - private readonly Stopwatch _stopwatch = new(); public async ValueTask GetOrCreateAsync( Guid correlationId, @@ -21,35 +19,29 @@ public async ValueTask GetOrCreateAsync( CancellationToken cancellationToken ) { - _stopwatch.Restart(); - Logs.DebugStartingOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, _stopwatch.ElapsedMilliseconds, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); return result; } public async ValueTask CreateAsync(Guid correlationId, string key, TResult value, CancellationToken cancellationToken) { - _stopwatch.Restart(); - Logs.DebugStartingOperation(_logger, _className, nameof(CreateAsync), correlationId, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, _stopwatch.ElapsedMilliseconds, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, key); } public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) { - _stopwatch.Restart(); - Logs.DebugStartingOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, _stopwatch.ElapsedMilliseconds, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); } } diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 5e26524f..d03c9908 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -14,7 +14,6 @@ public class BaseRepository( IDbContextFactory dbContextFactory ) : IBaseRepository { - private readonly Stopwatch _stopwatch = new(); private readonly IDbContextFactory _dbContextFactory = dbContextFactory; private readonly MyDbContext _dbContext = dbContextFactory.CreateDbContext(); private readonly string _className = nameof(BaseRepository); @@ -27,7 +26,7 @@ private async Task HandleBaseQueryAsync( string methodName = null! ) where TEntity : DomainEntity { - _stopwatch.Restart(); + using var activity = new ActivitySource("Hexagonal.Solution.Template.WebApp").StartActivity($"{_className}.{methodName}")!; Logs.DebugStartingOperation(logger, _className, methodName, correlationId); @@ -37,7 +36,10 @@ private async Task HandleBaseQueryAsync( var result = await query.Invoke(dbSet); - Logs.DebugFinishedOperation(logger, _className, methodName, correlationId, _stopwatch.ElapsedMilliseconds); + Logs.DebugFinishedOperation(logger, _className, methodName, correlationId); + + activity?.SetTag("correlationId", correlationId); + activity?.Stop(); return result; } @@ -49,15 +51,17 @@ public IQueryable GetQueryable( string methodName = null! ) where TEntity : DomainEntity { - _stopwatch.Restart(); + using var activity = new ActivitySource("Hexagonal.Solution.Template.WebApp").StartActivity($"{_className}.{nameof(GetQueryable)}")!; - Logs.DebugStartingOperation(logger, _className, methodName, correlationId); + Logs.DebugStartingOperation(logger, _className, nameof(GetQueryable), correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) dbSet = _dbContextFactory.CreateDbContext().Set(); - Logs.DebugFinishedOperation(logger, _className, methodName, correlationId, _stopwatch.ElapsedMilliseconds); + Logs.DebugFinishedOperation(logger, _className, nameof(GetQueryable), correlationId); + + activity?.SetTag("correlationId", correlationId); return dbSet; } diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index ee1f7439..ffa188f0 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Text.Json; using Application.Common.Messages; using Application.Common.Services; @@ -20,7 +19,6 @@ internal abstract class BaseConsumer : BaseBackgroundServic private readonly string _queueName; private readonly IDictionary _arguments; private readonly ConnectionFactory _factory; - private readonly Stopwatch _stopwatch = new(); protected IProduceService producerService = null!; public BaseConsumer( @@ -51,7 +49,6 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi try { - _stopwatch.Restart(); var hybridCacheService = serviceProvider.GetRequiredService(); @@ -77,7 +74,7 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi await hybridCacheService.CreateAsync(message.CorrelationId, isExecutedKey, true, cancellationToken); - Logs.DebugFinishedOperation(logger, _className, methodName, message.CorrelationId, _stopwatch.ElapsedMilliseconds, typeof(TMessage).Name + " processing finished."); + Logs.DebugFinishedOperation(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " processing finished."); } catch (Exception ex) { diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 00c885c0..983fdbaf 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Text.Json; using Application.Common.Messages; using Application.Common.Services; @@ -14,7 +13,6 @@ public sealed class ProducerService : IProduceService private readonly string _className = nameof(ProducerService); private readonly ILogger _logger; private readonly ConnectionFactory _factory; - private readonly Stopwatch _stopWatch = new(); public ProducerService(ILogger logger, IConfiguration configuration) { @@ -39,8 +37,6 @@ public async Task HandleAsync( { await Task.Yield(); - _stopWatch.Restart(); - using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); @@ -53,7 +49,7 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, _stopWatch.ElapsedMilliseconds, typeof(TMessage).Name + " published."); + Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " published."); } public async Task HandleAsync( @@ -65,8 +61,6 @@ public async Task HandleAsync( { await Task.Yield(); - _stopWatch.Restart(); - Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); using var connection = await _factory.CreateConnectionAsync(cancellationToken); @@ -83,7 +77,7 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, _stopWatch.ElapsedMilliseconds, typeof(TMessage).Name + " batch published."); + Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch published."); } Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); From 8ea66463f07b5901514d6627b270d63738dc8287 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 07:51:17 -0300 Subject: [PATCH 095/161] 53 refactor: streamline telemetry setup and remove elapsed time tracking --- .../Common/Constants/DefaultConfigurations.cs | 4 +++- .../src/Application/Common/UseCases/BaseInOutUseCase.cs | 9 ++------- .../src/Application/Common/UseCases/BaseInUseCase.cs | 8 ++++---- .../src/Application/Common/UseCases/BaseOutUseCase.cs | 8 ++++---- .../src/Infrastructure/Data/Common/BaseRepository.cs | 8 ++++---- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs b/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs index fc90b5dd..17abb91d 100644 --- a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs +++ b/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Diagnostics.Metrics; namespace Application.Common.Constants; @@ -5,6 +6,7 @@ namespace Application.Common.Constants; public static class DefaultConfigurations { public static string ApplicationName => "Hexagonal.Solution.Template"; + public static string Version => typeof(DefaultConfigurations).Assembly.GetName().Version!.ToString(); + public static ActivitySource ActivitySource => new(ApplicationName, Version); public static readonly Meter Meter = new("Application"); - } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index c2b831a4..0266cbc3 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Diagnostics.Metrics; +using System.Diagnostics.Metrics; using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Repositories; @@ -26,7 +25,6 @@ public abstract class BaseInOutUseCase : BaseUseCase, I protected IBaseRepository Repository { get; } private readonly IValidator _validator; private readonly Histogram _useCaseExecuted; - private readonly Gauge _useCaseExecutionElapsedTime; protected const string HandleMethodName = nameof(HandleAsync); protected BaseInOutUseCase(IServiceProvider serviceProvider) : base(serviceProvider) @@ -38,9 +36,6 @@ protected BaseInOutUseCase(IServiceProvider serviceProvider) : base(serviceProvi _useCaseExecuted = DefaultConfigurations.Meter .CreateHistogram($"{ClassName}.Executed", "total", "Number of times the use case was executed"); - - _useCaseExecutionElapsedTime = DefaultConfigurations.Meter - .CreateGauge($"{ClassName}.Elapsed", "elapsed", "Elapsed time taken to execute the use case"); } public async Task HandleAsync( @@ -48,7 +43,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = new ActivitySource("Hexagonal.Solution.Template.WebApp").StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); TResponseData response; diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index b3137337..000df0d0 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -21,7 +21,6 @@ public abstract class BaseInUseCase : BaseUseCase, IBaseInUseCase _validator; private readonly Histogram _useCaseExecuted; - private readonly Gauge _useCaseExecutionElapsedTime; protected const string HandleMethodName = nameof(HandleAsync); protected BaseInUseCase(IServiceProvider serviceProvider) : base(serviceProvider) @@ -33,9 +32,6 @@ protected BaseInUseCase(IServiceProvider serviceProvider) : base(serviceProvider _useCaseExecuted = DefaultConfigurations.Meter .CreateHistogram($"{ClassName}.Executed", "total", "Number of times the use case was executed"); - - _useCaseExecutionElapsedTime = DefaultConfigurations.Meter - .CreateGauge($"{ClassName}.Elapsed", "elapsed", "Elapsed time taken to execute the use case"); } public async Task HandleAsync( @@ -43,6 +39,8 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); if (_validator != null) @@ -62,6 +60,8 @@ CancellationToken cancellationToken Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); _useCaseExecuted.Record(1); + + activity?.SetTag("correlationId", request.CorrelationId); } public abstract Task HandleInternalAsync(TRequest request, CancellationToken cancellationToken); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 389058c7..de77782e 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -17,7 +17,6 @@ public abstract class BaseOutUseCase : BaseUseCase, IBaseOutUseCa protected IHybridCacheService Cache { get; } protected IProduceService ProduceService { get; } private readonly Histogram _useCaseExecuted; - private readonly Gauge _useCaseExecutionElapsedTime; protected const string HandleMethodName = nameof(HandleAsync); protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvider) @@ -27,13 +26,12 @@ protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvide _useCaseExecuted = DefaultConfigurations.Meter .CreateHistogram($"{ClassName}.Executed", "total", "Number of times the use case was executed"); - - _useCaseExecutionElapsedTime = DefaultConfigurations.Meter - .CreateGauge($"{ClassName}.Elapsed", "elapsed", "Elapsed time taken to execute the use case"); } public async Task HandleAsync(CancellationToken cancellationToken) { + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, ClassName, HandleMethodName, correlationId); @@ -43,6 +41,8 @@ public async Task HandleAsync(CancellationToken cancellationToken _useCaseExecuted.Record(1); + activity?.SetTag("correlationId", correlationId); + return response; } diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index d03c9908..329c44df 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -1,6 +1,6 @@ -using System.Diagnostics; -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Runtime.CompilerServices; +using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Repositories; using Domain.Common; @@ -26,7 +26,7 @@ private async Task HandleBaseQueryAsync( string methodName = null! ) where TEntity : DomainEntity { - using var activity = new ActivitySource("Hexagonal.Solution.Template.WebApp").StartActivity($"{_className}.{methodName}")!; + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{methodName}")!; Logs.DebugStartingOperation(logger, _className, methodName, correlationId); @@ -51,7 +51,7 @@ public IQueryable GetQueryable( string methodName = null! ) where TEntity : DomainEntity { - using var activity = new ActivitySource("Hexagonal.Solution.Template.WebApp").StartActivity($"{_className}.{nameof(GetQueryable)}")!; + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(GetQueryable)}")!; Logs.DebugStartingOperation(logger, _className, nameof(GetQueryable), correlationId); From 020b0deaa6dbae387fcddeaa61df53b515e580f5 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 15:49:21 -0300 Subject: [PATCH 096/161] 53 refactor: update activity tracking in use case classes --- .../Common/UseCases/BaseInOutUseCase.cs | 5 +++-- .../Common/UseCases/BaseInUseCase.cs | 5 +++-- .../Common/UseCases/BaseOutUseCase.cs | 5 +++-- .../Common/UseCases/BaseUseCase.cs | 6 ++++++ .../CreateNotificationUseCase.cs | 6 ++++-- .../GetAllNotificationsUseCase.cs | 7 +++++++ .../Notifications/GetNotificationUseCase.cs | 4 ++++ .../Application/Orders/CreateOrderUseCase.cs | 3 +++ .../Application/Orders/GetAllOrdersUseCase.cs | 9 +++++++- .../src/Application/Orders/GetOrderUseCase.cs | 3 +++ .../Cache/Services/HybridCacheService.cs | 21 ++++++++++++++++++- .../Messaging/Consumers/BaseConsumer.cs | 14 +++++++++++++ .../Messaging/Producers/ProducerService.cs | 12 ++++++++++- 13 files changed, 89 insertions(+), 11 deletions(-) diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 0266cbc3..0a765ceb 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -43,7 +43,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + Activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); TResponseData response; @@ -72,7 +72,8 @@ CancellationToken cancellationToken Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); _useCaseExecuted.Record(1); - activity?.SetTag("correlationId", request.CorrelationId); + Activity?.SetTag("correlationId", request.CorrelationId); + Activity?.Stop(); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 000df0d0..6d3f9641 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -39,7 +39,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + Activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); @@ -61,7 +61,8 @@ CancellationToken cancellationToken _useCaseExecuted.Record(1); - activity?.SetTag("correlationId", request.CorrelationId); + Activity?.SetTag("correlationId", request.CorrelationId); + Activity?.Stop(); } public abstract Task HandleInternalAsync(TRequest request, CancellationToken cancellationToken); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index de77782e..4963f48d 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -30,7 +30,7 @@ protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvide public async Task HandleAsync(CancellationToken cancellationToken) { - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + Activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, ClassName, HandleMethodName, correlationId); @@ -41,7 +41,8 @@ public async Task HandleAsync(CancellationToken cancellationToken _useCaseExecuted.Record(1); - activity?.SetTag("correlationId", correlationId); + Activity?.SetTag("correlationId", correlationId); + Activity?.Stop(); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index 09dc8992..bfae7f62 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; +using Application.Common.Constants; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -8,6 +10,8 @@ public abstract class BaseUseCase protected IServiceProvider ServiceProvider { get; } protected ILogger Logger { get; } protected string ClassName { get; set; } + protected ActivitySource ActivitySource { get; } + public Activity Activity { get; set; } protected BaseUseCase(IServiceProvider serviceProvider) { @@ -17,5 +21,7 @@ protected BaseUseCase(IServiceProvider serviceProvider) ServiceProvider = serviceProvider; Logger = serviceProvider.GetRequiredService().CreateLogger(classType); + + ActivitySource = DefaultConfigurations.ActivitySource; } } \ No newline at end of file diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 6655acad..02e7c66f 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -41,8 +41,10 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(notification, request.CorrelationId, cancellationToken); if (addResult == 0) - { Logs.FailedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); - } + + Activity?.SetTag("notificationId", notification.Id); + Activity?.SetTag("notificationType", notification.NotificationType); + Activity?.SetTag("notificationStatus", notification.NotificationStatus); } } diff --git a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs index ae29964e..59894533 100644 --- a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs @@ -40,6 +40,13 @@ CancellationToken cancellationToken var notificationDtos = notifications.Select(notification => notification); + Activity?.SetTag("page", request.Page); + Activity?.SetTag("pageSize", request.PageSize); + Activity?.SetTag("sortBy", request.SortBy); + Activity?.SetTag("sortDescending", request.SortDescending); + Activity?.SetTag("totalRecords", totalRecords); + Activity?.SetTag("totalPages", totalPages); + return new(true, totalPages, totalRecords, notificationDtos); } } diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index 95f0d730..f2ba0b6c 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -43,6 +43,10 @@ CancellationToken cancellationToken return new(false, null, "Notification not found."); } + Activity?.SetTag("notificationId", notification.Id); + Activity?.SetTag("notificationType", notification.NotificationType); + Activity?.SetTag("notificationStatus", notification.NotificationStatus); + return new(true, notification); } } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 66034f48..2eb282bf 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -97,6 +97,9 @@ CancellationToken cancellationToken CreateNotification(correlationId, "Success", response); + Activity?.SetTag("orderId", newOrder.Id); + Activity?.SetTag("orderTotal", newOrder.Total); + return response; } diff --git a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs index 4e886010..fa9febd2 100644 --- a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs +++ b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs @@ -34,7 +34,14 @@ CancellationToken cancellationToken return new(false, 0, 0, [], "No orders found."); } - var totalPages = (int)Math.Ceiling(totalRecords / (double)request.PageSize); + var totalPages = (int) Math.Ceiling(totalRecords / (double) request.PageSize); + + Activity?.SetTag("page", request.Page); + Activity?.SetTag("pageSize", request.PageSize); + Activity?.SetTag("sortBy", request.SortBy); + Activity?.SetTag("sortDescending", request.SortDescending); + Activity?.SetTag("totalRecords", totalRecords); + Activity?.SetTag("totalPages", totalPages); return new(true, totalPages, totalRecords, orders); } diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index e6926686..7a965bed 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -45,6 +45,9 @@ CancellationToken cancellationToken return new(false, null, "Order not found."); } + Activity?.SetTag("orderId", order.Id); + Activity?.SetTag("orderTotal", order.Total); + return new(true, order); } } diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 9aab8814..7343ba2e 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Services; @@ -11,6 +12,7 @@ internal sealed class HybridCacheService(HybridCache cache, ILogger _logger = logger; private readonly string _className = nameof(HybridCacheService); + private readonly ActivitySource _activitySource = DefaultConfigurations.ActivitySource; public async ValueTask GetOrCreateAsync( Guid correlationId, @@ -19,29 +21,46 @@ public async ValueTask GetOrCreateAsync( CancellationToken cancellationToken ) { + using var activity = _activitySource.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); + Logs.DebugStartingOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); - var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); + + activity?.SetTag("CorrelationId", correlationId.ToString()); + activity?.SetTag("cacheKey", key); + activity?.SetTag("cacheHit", result is not null); + return result; } public async ValueTask CreateAsync(Guid correlationId, string key, TResult value, CancellationToken cancellationToken) { + using var activity = _activitySource.StartActivity($"{_className}.{nameof(CreateAsync)}"); + Logs.DebugStartingOperation(_logger, _className, nameof(CreateAsync), correlationId, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, key); + + activity?.SetTag("CorrelationId", correlationId.ToString()); + activity?.SetTag("cacheKey", key); + activity?.SetTag("cacheHit", value is not null); } public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) { + using var activity = _activitySource.StartActivity($"{_className}.{nameof(DeleteAsync)}"); + Logs.DebugStartingOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); + + activity?.SetTag("CorrelationId", correlationId.ToString()); + activity?.SetTag("cacheKey", key); } } diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index ffa188f0..f7bd7ce8 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -1,4 +1,6 @@ +using System.Diagnostics; using System.Text.Json; +using Application.Common.Constants; using Application.Common.Messages; using Application.Common.Services; using Infrastructure.Common; @@ -20,6 +22,8 @@ internal abstract class BaseConsumer : BaseBackgroundServic private readonly IDictionary _arguments; private readonly ConnectionFactory _factory; protected IProduceService producerService = null!; + private readonly ActivitySource _activitySource = DefaultConfigurations.ActivitySource; + public BaseConsumer( ILogger> logger, @@ -93,6 +97,8 @@ private async Task HandleRabbitMqAsync( CancellationToken cancellationToken ) { + using var activity = _activitySource.StartActivity($"{_className}.{nameof(HandleRabbitMqAsync)}"); + var connection = await _factory.CreateConnectionAsync(cancellationToken); var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); var methodName = nameof(HandleRabbitMqAsync); @@ -157,6 +163,10 @@ await channel.QueueDeclareAsync( await handleAsync.Invoke(message, cancellationToken); Logs.Debug(logger, _className, methodName, message.CorrelationId, "Use case handled."); + + activity?.SetTag("CorrelationId", message.CorrelationId.ToString()); + activity?.SetTag("MessageType", typeof(TMessage).Name); + }; await channel.BasicConsumeAsync( @@ -165,6 +175,10 @@ await channel.BasicConsumeAsync( consumer: consumer, cancellationToken: cancellationToken ); + + activity?.SetTag("RabbitMQHostName", _factory.HostName); + activity?.SetTag("QueueName", _queueName); + } protected abstract Task HandleUseCaseAsync(IServiceProvider serviceProvider, TMessage message, CancellationToken cancellationToken); diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 983fdbaf..f9cdc15b 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using RabbitMQ.Client; +using Application.Common.Constants; namespace Infrastructure.Messaging.Producers; @@ -37,6 +38,8 @@ public async Task HandleAsync( { await Task.Yield(); + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(HandleAsync)}")!; + using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); @@ -50,6 +53,8 @@ await channel.BasicPublishAsync( ); Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " published."); + + activity?.SetTag("correlationId", message.CorrelationId); } public async Task HandleAsync( @@ -61,6 +66,8 @@ public async Task HandleAsync( { await Task.Yield(); + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(HandleAsync)}")!; + Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); using var connection = await _factory.CreateConnectionAsync(cancellationToken); @@ -78,8 +85,11 @@ await channel.BasicPublishAsync( ); Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch published."); + activity?.SetTag("correlationId", message.CorrelationId); } - + Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); + + activity?.SetTag("correlationId", messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty); } } From 757c9475a69c3e37ba70ce84fa535a9ae0ad68b9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 16:09:27 -0300 Subject: [PATCH 097/161] chore: remove unused OpenTelemetry dependencies --- templates/Full/Directory.Packages.props | 6 +- .../src/Infrastructure/Infrastructure.csproj | 6 +- .../InfrastructureDependencyInjection.cs | 1 - ...ructureOpenTelemetryDependencyInjection.cs | 77 ++-- .../src/Infrastructure/packages.lock.json | 345 +----------------- .../src/WebApp/Properties/launchSettings.json | 5 +- templates/Full/src/WebApp/packages.lock.json | 309 +--------------- .../tests/IntegrationTests/packages.lock.json | 342 +---------------- 8 files changed, 95 insertions(+), 996 deletions(-) diff --git a/templates/Full/Directory.Packages.props b/templates/Full/Directory.Packages.props index a5235757..9847919c 100644 --- a/templates/Full/Directory.Packages.props +++ b/templates/Full/Directory.Packages.props @@ -31,11 +31,11 @@ - - - + + + diff --git a/templates/Full/src/Infrastructure/Infrastructure.csproj b/templates/Full/src/Infrastructure/Infrastructure.csproj index 81bd7ae1..d6e2467c 100644 --- a/templates/Full/src/Infrastructure/Infrastructure.csproj +++ b/templates/Full/src/Infrastructure/Infrastructure.csproj @@ -10,11 +10,11 @@ - - - + + + diff --git a/templates/Full/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Full/src/Infrastructure/InfrastructureDependencyInjection.cs index 40f1fb7b..cbd83ae4 100644 --- a/templates/Full/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -1,6 +1,5 @@ using Infrastructure.Data; using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; using Infrastructure.OpenTelemetry; using Infrastructure.Cache; using Infrastructure.Messaging; diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 2e0b94f9..9548669a 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -1,8 +1,9 @@ using Application.Common.Constants; -using Grafana.OpenTelemetry; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using OpenTelemetry.Exporter; +using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -15,8 +16,28 @@ internal static class InfrastructureOpenTelemetryDependencyInjection { public WebApplicationBuilder AddOpenTelemetry() { + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "grpc" + ? OtlpExportProtocol.Grpc + : OtlpExportProtocol.HttpProtobuf; + var exporterMetricsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"); + var exporterTracesEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"); + var exporterLogsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"); + + if ( + string.Equals(environment, "IntegrationTests", StringComparison.OrdinalIgnoreCase) || + string.IsNullOrWhiteSpace(exporterLogsEndpoint) || + string.IsNullOrWhiteSpace(exporterMetricsEndpoint) || + string.IsNullOrWhiteSpace(exporterTracesEndpoint) + ) + { + return builder; + } + + var serviceName = DefaultConfigurations.ApplicationName; + var serviceVersion = DefaultConfigurations.Version; var resourceBuilder = ResourceBuilder.CreateDefault() - .AddService("Hexagonal.Solution.Template.WebApp"); + .AddService(serviceName, serviceVersion: serviceVersion); builder.Services.AddOpenTelemetry() .WithMetrics(metrics => @@ -27,26 +48,28 @@ public WebApplicationBuilder AddOpenTelemetry() { Boundaries = [0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10] } - ); - metrics.AddMeter( + ) + .AddMeter( DefaultConfigurations.Meter.Name, "System.Diagnostics.Metrics", "Microsoft.AspNetCore.Hosting", "Microsoft.AspNetCore.Server.Kestrel", "System.Net.Http" - ); - - metrics - .SetResourceBuilder(resourceBuilder) - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddRuntimeInstrumentation() - .AddProcessInstrumentation() - .AddPrometheusExporter() - .UseGrafana(); + ) + .SetResourceBuilder(resourceBuilder) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddProcessInstrumentation() + .AddPrometheusExporter() + .AddOtlpExporter(options => + { + options.Protocol = exporterProtocol; + options.Endpoint = new Uri(exporterMetricsEndpoint); + }); }) .WithTracing(tracing => tracing - .AddSource("Hexagonal.Solution.Template.WebApp") + .AddSource(serviceName) .SetResourceBuilder(resourceBuilder) .AddRedisInstrumentation() .AddRabbitMQInstrumentation() @@ -54,18 +77,26 @@ public WebApplicationBuilder AddOpenTelemetry() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddEntityFrameworkCoreInstrumentation() - .UseGrafana() + .AddOtlpExporter(options => + { + options.Protocol = exporterProtocol; + options.Endpoint = new Uri(exporterTracesEndpoint); + }) ); - builder.Logging.AddOpenTelemetry(logging => + builder.Logging.AddOpenTelemetry(options => { - logging.SetResourceBuilder(resourceBuilder); - logging.IncludeFormattedMessage = true; - logging.IncludeScopes = true; - logging.ParseStateValues = true; - logging + options.IncludeFormattedMessage = true; + options.IncludeScopes = true; + options.ParseStateValues = true; + options + .SetResourceBuilder(resourceBuilder) .AttachLogsToActivityEvent() - .UseGrafana(); + .AddOtlpExporter(exporterOptions => + { + exporterOptions.Protocol = exporterProtocol; + exporterOptions.Endpoint = new Uri(exporterLogsEndpoint!); + }); }); return builder; diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json index 866cff48..e402a72a 100644 --- a/templates/Full/src/Infrastructure/packages.lock.json +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -2,27 +2,6 @@ "version": 2, "dependencies": { "net10.0": { - "Grafana.OpenTelemetry": { - "type": "Direct", - "requested": "[1.5.2, )", - "resolved": "1.5.2", - "contentHash": "5QDzhHq7vMd22uCXT7+TL7LRRxJ/wtsLcDosb81qKQxoxnKixLb0zjzEV157WcU3vbwlTXP07AIH3kpZzFrEWQ==", - "dependencies": { - "CassandraCSharpDriver": "3.22.0", - "Grafana.OpenTelemetry.Base": "1.5.2", - "OpenTelemetry.Extensions.Hosting": "1.15.0", - "OpenTelemetry.Instrumentation.AWS": "1.15.0", - "OpenTelemetry.Instrumentation.AWSLambda": "1.15.0", - "OpenTelemetry.Instrumentation.AspNetCore": "1.15.0", - "OpenTelemetry.Instrumentation.Cassandra": "1.0.0-beta.5", - "OpenTelemetry.Instrumentation.ElasticsearchClient": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Hangfire": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Quartz": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.StackExchangeRedis": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Wcf": "1.15.0-beta.1" - } - }, "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", "requested": "[10.0.2, )", @@ -87,6 +66,15 @@ "OpenTelemetry": "1.15.0" } }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, "OpenTelemetry.Exporter.Prometheus.AspNetCore": { "type": "Direct", "requested": "[1.15.0-beta.1, )", @@ -204,102 +192,11 @@ "RabbitMQ.Client": "7.2.0" } }, - "Amazon.Lambda.APIGatewayEvents": { - "type": "Transitive", - "resolved": "2.7.0", - "contentHash": "HAbdRKLzvUqRg/fItan7up/PWsj6vQfYF+1a4YV6sv1bq5TuK/ra+ndJwWMlzL+MFJzJaHwCmtkyBbh/vz8Oig==" - }, - "Amazon.Lambda.ApplicationLoadBalancerEvents": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "UtCd9tFuVyED8+DmDKEkdXJbAb/TdHIz51R/CsJCexKerO6HGOG87FGId4Ce9IXE+qjlBGyNQsKCw9oLLNe0hw==" - }, - "Amazon.Lambda.Core": { - "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "mZnqCrLuNvB9T9/bCIcu/7H8HWd3yi6sEDzRE76D9ym06D+CoEfJ/EG4w7FucclvpSXO7ui/MKpzqhEnR2FAVg==" - }, - "Amazon.Lambda.SNSEvents": { - "type": "Transitive", - "resolved": "2.1.0", - "contentHash": "w2PKM5eQ6DNCR07H6SwHdcPXAr/qKQhNoAD+8bWydpgh4QdS2O4heo9vxhxn1g5FMSKeOxVa2zMKUZ9xeSYPZw==" - }, - "Amazon.Lambda.SQSEvents": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "RyYLgEa6nh+VJrCfTDKxvTLS1+eLDIIDyHgwDY3TR1WQz1+UeDIcccOJvoDk2nmjkSA0TfWJmZX9Ld0ySf57lA==" - }, - "AWSSDK.Core": { - "type": "Transitive", - "resolved": "4.0.3.3", - "contentHash": "YQv10JuxnciWh0QwnkarSbge4gXQV1qTURf5jkBjNUH/3jYS9QrbxopA4TK1qdjfOfP37tqiJkLSrRRNqX81aw==" - }, - "AWSSDK.SimpleNotificationService": { - "type": "Transitive", - "resolved": "4.0.2.7", - "contentHash": "WO0xjj2demSh3hPKQtSS3XyncITIUlENCeLv/FZsteMJh4HpfGwxp1dbq8YnaR90ddzvyPU6uj675WGuMf6zgg==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)" - } - }, - "AWSSDK.SQS": { - "type": "Transitive", - "resolved": "4.0.2.5", - "contentHash": "bHA9m/2RZHNKt6NGvQ56rfEDj/pfUlcwPeCg2HmPJ3jZPyoerBuEsYvFkMP3YJNv3aoycNWZoiDk0/ULP0tEyA==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)" - } - }, - "CassandraCSharpDriver": { - "type": "Transitive", - "resolved": "3.22.0", - "contentHash": "T5vjErmZ42Q1TAzbYSJvOrAzOZmb/d+v3OoIYrKfbcqjDuvedd61e0bAaaLpgiujPBBO+qFpxR4T0WSXkMr/fA==", - "dependencies": { - "K4os.Compression.LZ4": "1.1.11", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Abstractions": "1.0.0", - "Newtonsoft.Json": "9.0.1", - "System.Management": "4.7.0" - } - }, - "Grafana.OpenTelemetry.Base": { - "type": "Transitive", - "resolved": "1.5.2", - "contentHash": "MS8yH+QFubR0Kllsxo7o2w9A2OwVeI2iB8gfGr7YPKEfwBYAcul4bHzPA9dyhtHE7hxGBuhh07bdNzMG8oRhyw==", - "dependencies": { - "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.0", - "OpenTelemetry": "1.15.0", - "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.15.0", - "OpenTelemetry.Instrumentation.GrpcNetClient": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Http": "1.15.0", - "OpenTelemetry.Instrumentation.Process": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Runtime": "1.15.0", - "OpenTelemetry.Instrumentation.SqlClient": "1.15.0", - "OpenTelemetry.Resources.Container": "1.15.0-beta.1", - "OpenTelemetry.Resources.Host": "1.15.0-beta.2", - "OpenTelemetry.Resources.OperatingSystem": "1.15.0-beta.1", - "OpenTelemetry.Resources.Process": "1.15.0-beta.1", - "OpenTelemetry.Resources.ProcessRuntime": "1.15.0-beta.1" - } - }, - "Hangfire.Core": { - "type": "Transitive", - "resolved": "1.7.0", - "contentHash": "ZWfWcE+kOWjKy7l/rsVtZmxs5XdltJY5ndx+vy7enD85ZVgc0Z+nIOZ/PfBhQQ68vGOfwOudpQVHuwXlSn7/fQ==", - "dependencies": { - "Newtonsoft.Json": "11.0.1" - } - }, "Humanizer.Core": { "type": "Transitive", "resolved": "2.14.1", "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, - "K4os.Compression.LZ4": { - "type": "Transitive", - "resolved": "1.1.11", - "contentHash": "RNvJw0UGkedPhCqVBNIogtfQebY+bQt0PN7xDbVe5LWLra0ZEqPfjPSl7iStj3rgDnkqkkTTpm+vCX3hU1qKmA==" - }, "Microsoft.Build.Framework": { "type": "Transitive", "resolved": "18.0.2", @@ -426,15 +323,6 @@ "Microsoft.Extensions.Configuration.Abstractions": "10.0.0" } }, - "Microsoft.Extensions.Configuration.EnvironmentVariables": { - "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "TmFegsI/uCdwMBD4yKpmO+OkjVNHQL49Dh/ep83NI5rPUEoBK9OdsJo1zURc1A2FuS/R/Pos3wsTjlyLnguBLA==", - "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.0" - } - }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -523,24 +411,11 @@ "resolved": "10.0.2", "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.1.4", - "contentHash": "9/y05/CuxE+j184Nr4KihhB9KcUkvGojmD4JV4Vt/mHhVZR+eOCD5WCM+CXye9K0OFMsaPXbN+IcaIpjgBGZmg==" - }, "Microsoft.VisualStudio.SolutionPersistence": { "type": "Transitive", "resolved": "1.0.52", "contentHash": "oNv2JtYXhpdJrX63nibx1JT3uCESOBQ1LAk7Dtz/sr0+laW0KRM6eKp4CZ3MHDR2siIkKsY8MmUkeP5DKkQQ5w==" }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0" - } - }, "Mono.TextTemplating": { "type": "Transitive", "resolved": "3.0.0", @@ -581,144 +456,6 @@ "OpenTelemetry.Api": "1.15.0" } }, - "OpenTelemetry.Exporter.OpenTelemetryProtocol": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", - "dependencies": { - "OpenTelemetry": "1.15.0" - } - }, - "OpenTelemetry.Extensions.AWS": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "XO2mKJSF3cjzNepezUcKdYqnk1Ex3VCQ8UMbNH8oo3eN/tZ6EXVnI/9daXgEoXIJLPHjeHHT/+aEy3wd74lF8A==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.AWS": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "CVI0H47mfFHAw4cFoUf6QO1H5O1fK7MQY8+T7CriEp4rOBto65ncZMnbEQtwQhcfeeNqbOTED8TayMPWY1yMRA==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)", - "AWSSDK.SQS": "[4.0.2.5, 5.0.0)", - "AWSSDK.SimpleNotificationService": "[4.0.2.7, 5.0.0)", - "OpenTelemetry.Extensions.AWS": "1.15.0" - } - }, - "OpenTelemetry.Instrumentation.AWSLambda": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "cO+iqR2lFriHg89QNSbpbFJm0o2BsMPxEThMbH9z/OLeVcxAh3EJ6PPAfw6NuicJhntN9I00iCYPpo22T5R+Zg==", - "dependencies": { - "Amazon.Lambda.APIGatewayEvents": "2.7.0", - "Amazon.Lambda.ApplicationLoadBalancerEvents": "2.2.0", - "Amazon.Lambda.Core": "2.8.0", - "Amazon.Lambda.SNSEvents": "2.1.0", - "Amazon.Lambda.SQSEvents": "2.2.0", - "OpenTelemetry.Extensions.AWS": "1.15.0" - } - }, - "OpenTelemetry.Instrumentation.Cassandra": { - "type": "Transitive", - "resolved": "1.0.0-beta.5", - "contentHash": "4FwPZqw/mQUfHlQsjwi3qG2OPFutOJSQsY+7oa5kI1Vu5U3tGiua+WNGxSyMsrid3MhXbC4Ax5013Fx6/vHVDg==", - "dependencies": { - "CassandraCSharpDriver": "3.17.0", - "Newtonsoft.Json": "13.0.1", - "OpenTelemetry.Api": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.ElasticsearchClient": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "hETsgn/JfHPfiUa5mItGu0EiCai7PnGSD5IubdmOyZntqeG/Wf8YIx3rjlF6KVbBb+JAezgGKX4oqTTlrxJQbw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Hangfire": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "Rog5ojlXL+WB4Li/L1yisvfuKVmnPIOh62g1rfUJYgJGvPLQbm5FtGuIvvRJlMPLtu7f73tYmqouw9EiWExJIQ==", - "dependencies": { - "Hangfire.Core": "1.7.0", - "Microsoft.Extensions.Options": "10.0.0", - "Newtonsoft.Json": "13.0.1", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Quartz": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "C3TR/iDaXeZKpB8QTZjsSrbOlE6D+NYwMw3xDwMyt1T7s/1/4yqyZNQA3M5h3FKlinTvFdBxIbN6ghT/A1g5Jw==", - "dependencies": { - "OpenTelemetry.Api": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.SqlClient": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "J0lI7lCngS4TJD4T7KNsAerOIjJHNV0T2MK0iuS2tK8wF7iqL1dp4MKW05FiyfvrIXkwsvFc1okKchxS8B0+SQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.0", - "Microsoft.Extensions.Options": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Wcf": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "2U34rv1JvdB8jDS1LC7yknm+iINO+hBIZeEtKyUgUUaJT1FOINSQpIhofEtdsTR+++cIHOnvWHcru60QmiT5Cw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)", - "System.Drawing.Common": "4.7.2", - "System.Security.Cryptography.Xml": "4.7.1", - "System.ServiceModel.Primitives": "4.7.0" - } - }, - "OpenTelemetry.Resources.Container": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "b3W+tt2P9E6MybwFv/5nGLcfwDFyNcKw9fLaXrqSt3PpXrE533f+Vas00VLH0Qs5Z1fqDZSFqzgNwxmmTxvKHg==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.Host": { - "type": "Transitive", - "resolved": "1.15.0-beta.2", - "contentHash": "Ars2cy8gRmQ4n29V9L58eiFYucB8QwTfagFlNfMl3JpgkEeTsNtMInNTTJBDtnLM//7187C70n6lkc8fr2aGIA==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.OperatingSystem": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "I32LVRlWDsBALEQKD2jQx69iGjE+PZ2RdD0LoWzaKx7IsfsXoykvH7XgfF8o+/bzkAwKDr0zVe1Yb6so2zw7lw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.Process": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "ZyrUlfsSusSZd9PYB90x6bQUCuCf+gbd18wu4y8FDscW1ASS2a9bM8BRiaFIyUHh8Ztou4Sjqes2Pob2s+mZkw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.ProcessRuntime": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "bejo+D0Fkt/IeLUtR1MaDQYbFKhMibdVEtlDm5s7CapDls+U8p20MCwkQwZBIeL+gzacWR7+IhKR1EozbvW2ew==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -786,75 +523,11 @@ "System.Composition.Runtime": "9.0.0" } }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "I2y4KBK3VCvU/WqE2xv7NjQ67maXHttkFSHYKgU2evrG9Yqh0oFjfORXt5hZTk+BVjdyFo2h0/YQZsca33BGmg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.4", - "Microsoft.Win32.SystemEvents": "4.7.0" - } - }, - "System.Management": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "IY+uuGhgzWiCg21i8IvQeY/Z7m1tX8VuPF+ludfn7iTCaccTtJo5HkjZbBEL8kbBubKhAKKtNXr7uMtmAc28Pw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0", - "System.CodeDom": "4.7.0" - } - }, - "System.Private.ServiceModel": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "BItrYCkoTV3VzVPsrew+uc34fmLb+3ncgspa7vbO3vkfY9JQCea4u34pHE+Bcv1Iy16MgRs3n2jKVRCDg0rPfg==", - "dependencies": { - "System.Security.Cryptography.Xml": "4.5.0" - } - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "0Srzh6YlhjuMxaqMyeCCdZs22cucaUAG6SKDd3gNHBJmre0VZ71ekzWu9rvLD4YXPetyNdPvV6Qst+8C++9v3Q==" - }, - "System.Security.Cryptography.Xml": { - "type": "Transitive", - "resolved": "4.7.1", - "contentHash": "ddAre1QiT5cACLNWLLE3Smk61yhHr4IzQbt0FZiHsD63aFse0xSjbQU3+Fycc5elKhqNwgwk7ueOh3x9Rv9uIg==", - "dependencies": { - "System.Security.Cryptography.Pkcs": "4.7.0", - "System.Security.Permissions": "4.7.0" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==", - "dependencies": { - "System.Windows.Extensions": "4.7.0" - } - }, - "System.ServiceModel.Primitives": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "YUXIMO4kL1v6dUVptJGixAx/8Ai5trQzVn3gbk0mpwxh77kGAs+MyBRoHN/5ZoxtwNn4E1dq3N4rJCAgAUaiJA==", - "dependencies": { - "System.Private.ServiceModel": "4.7.0" - } - }, "System.Threading.RateLimiting": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==", - "dependencies": { - "System.Drawing.Common": "4.7.0" - } - }, "application": { "type": "Project", "dependencies": { diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json index e069ed48..459ebd12 100644 --- a/templates/Full/src/WebApp/Properties/launchSettings.json +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -10,7 +10,10 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", "ENABLE_SENSITIVE_DATA_LOGGING": "true" } diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json index 2eecec66..334b8df9 100644 --- a/templates/Full/src/WebApp/packages.lock.json +++ b/templates/Full/src/WebApp/packages.lock.json @@ -49,91 +49,16 @@ "Grpc.Tools": "2.76.0" } }, - "Amazon.Lambda.APIGatewayEvents": { - "type": "Transitive", - "resolved": "2.7.0", - "contentHash": "HAbdRKLzvUqRg/fItan7up/PWsj6vQfYF+1a4YV6sv1bq5TuK/ra+ndJwWMlzL+MFJzJaHwCmtkyBbh/vz8Oig==" - }, - "Amazon.Lambda.ApplicationLoadBalancerEvents": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "UtCd9tFuVyED8+DmDKEkdXJbAb/TdHIz51R/CsJCexKerO6HGOG87FGId4Ce9IXE+qjlBGyNQsKCw9oLLNe0hw==" - }, - "Amazon.Lambda.Core": { - "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "mZnqCrLuNvB9T9/bCIcu/7H8HWd3yi6sEDzRE76D9ym06D+CoEfJ/EG4w7FucclvpSXO7ui/MKpzqhEnR2FAVg==" - }, - "Amazon.Lambda.SNSEvents": { - "type": "Transitive", - "resolved": "2.1.0", - "contentHash": "w2PKM5eQ6DNCR07H6SwHdcPXAr/qKQhNoAD+8bWydpgh4QdS2O4heo9vxhxn1g5FMSKeOxVa2zMKUZ9xeSYPZw==" - }, - "Amazon.Lambda.SQSEvents": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "RyYLgEa6nh+VJrCfTDKxvTLS1+eLDIIDyHgwDY3TR1WQz1+UeDIcccOJvoDk2nmjkSA0TfWJmZX9Ld0ySf57lA==" - }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", "contentHash": "TVriy4hgYnhfqz6NAzv8qe62Q8wf82iKUL6WV9selqeFZTq1ILi39Sic6sFQegRysvAVcnxKP/vY8z9Fk8x6XQ==" }, - "AWSSDK.Core": { - "type": "Transitive", - "resolved": "4.0.3.3", - "contentHash": "YQv10JuxnciWh0QwnkarSbge4gXQV1qTURf5jkBjNUH/3jYS9QrbxopA4TK1qdjfOfP37tqiJkLSrRRNqX81aw==" - }, - "AWSSDK.SimpleNotificationService": { - "type": "Transitive", - "resolved": "4.0.2.7", - "contentHash": "WO0xjj2demSh3hPKQtSS3XyncITIUlENCeLv/FZsteMJh4HpfGwxp1dbq8YnaR90ddzvyPU6uj675WGuMf6zgg==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)" - } - }, - "AWSSDK.SQS": { - "type": "Transitive", - "resolved": "4.0.2.5", - "contentHash": "bHA9m/2RZHNKt6NGvQ56rfEDj/pfUlcwPeCg2HmPJ3jZPyoerBuEsYvFkMP3YJNv3aoycNWZoiDk0/ULP0tEyA==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)" - } - }, - "CassandraCSharpDriver": { - "type": "Transitive", - "resolved": "3.22.0", - "contentHash": "T5vjErmZ42Q1TAzbYSJvOrAzOZmb/d+v3OoIYrKfbcqjDuvedd61e0bAaaLpgiujPBBO+qFpxR4T0WSXkMr/fA==", - "dependencies": { - "K4os.Compression.LZ4": "1.1.11", - "Newtonsoft.Json": "9.0.1", - "System.Management": "4.7.0" - } - }, "Google.Protobuf": { "type": "Transitive", "resolved": "3.31.1", "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" }, - "Grafana.OpenTelemetry.Base": { - "type": "Transitive", - "resolved": "1.5.2", - "contentHash": "MS8yH+QFubR0Kllsxo7o2w9A2OwVeI2iB8gfGr7YPKEfwBYAcul4bHzPA9dyhtHE7hxGBuhh07bdNzMG8oRhyw==", - "dependencies": { - "OpenTelemetry": "1.15.0", - "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.15.0", - "OpenTelemetry.Instrumentation.GrpcNetClient": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Http": "1.15.0", - "OpenTelemetry.Instrumentation.Process": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Runtime": "1.15.0", - "OpenTelemetry.Instrumentation.SqlClient": "1.15.0", - "OpenTelemetry.Resources.Container": "1.15.0-beta.1", - "OpenTelemetry.Resources.Host": "1.15.0-beta.2", - "OpenTelemetry.Resources.OperatingSystem": "1.15.0-beta.1", - "OpenTelemetry.Resources.Process": "1.15.0-beta.1", - "OpenTelemetry.Resources.ProcessRuntime": "1.15.0-beta.1" - } - }, "Grpc.AspNetCore.Server": { "type": "Transitive", "resolved": "2.76.0", @@ -185,19 +110,6 @@ "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" }, - "Hangfire.Core": { - "type": "Transitive", - "resolved": "1.7.0", - "contentHash": "ZWfWcE+kOWjKy7l/rsVtZmxs5XdltJY5ndx+vy7enD85ZVgc0Z+nIOZ/PfBhQQ68vGOfwOudpQVHuwXlSn7/fQ==", - "dependencies": { - "Newtonsoft.Json": "11.0.1" - } - }, - "K4os.Compression.LZ4": { - "type": "Transitive", - "resolved": "1.1.11", - "contentHash": "RNvJw0UGkedPhCqVBNIogtfQebY+bQt0PN7xDbVe5LWLra0ZEqPfjPSl7iStj3rgDnkqkkTTpm+vCX3hU1qKmA==" - }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", "resolved": "10.0.2", @@ -208,19 +120,6 @@ "resolved": "10.0.2", "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.1.4", - "contentHash": "9/y05/CuxE+j184Nr4KihhB9KcUkvGojmD4JV4Vt/mHhVZR+eOCD5WCM+CXye9K0OFMsaPXbN+IcaIpjgBGZmg==" - }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0" - } - }, "Npgsql": { "type": "Transitive", "resolved": "10.0.0", @@ -247,140 +146,6 @@ "OpenTelemetry.Api": "1.15.0" } }, - "OpenTelemetry.Exporter.OpenTelemetryProtocol": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", - "dependencies": { - "OpenTelemetry": "1.15.0" - } - }, - "OpenTelemetry.Extensions.AWS": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "XO2mKJSF3cjzNepezUcKdYqnk1Ex3VCQ8UMbNH8oo3eN/tZ6EXVnI/9daXgEoXIJLPHjeHHT/+aEy3wd74lF8A==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.AWS": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "CVI0H47mfFHAw4cFoUf6QO1H5O1fK7MQY8+T7CriEp4rOBto65ncZMnbEQtwQhcfeeNqbOTED8TayMPWY1yMRA==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)", - "AWSSDK.SQS": "[4.0.2.5, 5.0.0)", - "AWSSDK.SimpleNotificationService": "[4.0.2.7, 5.0.0)", - "OpenTelemetry.Extensions.AWS": "1.15.0" - } - }, - "OpenTelemetry.Instrumentation.AWSLambda": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "cO+iqR2lFriHg89QNSbpbFJm0o2BsMPxEThMbH9z/OLeVcxAh3EJ6PPAfw6NuicJhntN9I00iCYPpo22T5R+Zg==", - "dependencies": { - "Amazon.Lambda.APIGatewayEvents": "2.7.0", - "Amazon.Lambda.ApplicationLoadBalancerEvents": "2.2.0", - "Amazon.Lambda.Core": "2.8.0", - "Amazon.Lambda.SNSEvents": "2.1.0", - "Amazon.Lambda.SQSEvents": "2.2.0", - "OpenTelemetry.Extensions.AWS": "1.15.0" - } - }, - "OpenTelemetry.Instrumentation.Cassandra": { - "type": "Transitive", - "resolved": "1.0.0-beta.5", - "contentHash": "4FwPZqw/mQUfHlQsjwi3qG2OPFutOJSQsY+7oa5kI1Vu5U3tGiua+WNGxSyMsrid3MhXbC4Ax5013Fx6/vHVDg==", - "dependencies": { - "CassandraCSharpDriver": "3.17.0", - "Newtonsoft.Json": "13.0.1", - "OpenTelemetry.Api": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.ElasticsearchClient": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "hETsgn/JfHPfiUa5mItGu0EiCai7PnGSD5IubdmOyZntqeG/Wf8YIx3rjlF6KVbBb+JAezgGKX4oqTTlrxJQbw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Hangfire": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "Rog5ojlXL+WB4Li/L1yisvfuKVmnPIOh62g1rfUJYgJGvPLQbm5FtGuIvvRJlMPLtu7f73tYmqouw9EiWExJIQ==", - "dependencies": { - "Hangfire.Core": "1.7.0", - "Newtonsoft.Json": "13.0.1", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Quartz": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "C3TR/iDaXeZKpB8QTZjsSrbOlE6D+NYwMw3xDwMyt1T7s/1/4yqyZNQA3M5h3FKlinTvFdBxIbN6ghT/A1g5Jw==", - "dependencies": { - "OpenTelemetry.Api": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.SqlClient": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "J0lI7lCngS4TJD4T7KNsAerOIjJHNV0T2MK0iuS2tK8wF7iqL1dp4MKW05FiyfvrIXkwsvFc1okKchxS8B0+SQ==", - "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Wcf": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "2U34rv1JvdB8jDS1LC7yknm+iINO+hBIZeEtKyUgUUaJT1FOINSQpIhofEtdsTR+++cIHOnvWHcru60QmiT5Cw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)", - "System.Drawing.Common": "4.7.2", - "System.ServiceModel.Primitives": "4.7.0" - } - }, - "OpenTelemetry.Resources.Container": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "b3W+tt2P9E6MybwFv/5nGLcfwDFyNcKw9fLaXrqSt3PpXrE533f+Vas00VLH0Qs5Z1fqDZSFqzgNwxmmTxvKHg==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.Host": { - "type": "Transitive", - "resolved": "1.15.0-beta.2", - "contentHash": "Ars2cy8gRmQ4n29V9L58eiFYucB8QwTfagFlNfMl3JpgkEeTsNtMInNTTJBDtnLM//7187C70n6lkc8fr2aGIA==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.OperatingSystem": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "I32LVRlWDsBALEQKD2jQx69iGjE+PZ2RdD0LoWzaKx7IsfsXoykvH7XgfF8o+/bzkAwKDr0zVe1Yb6so2zw7lw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.Process": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "ZyrUlfsSusSZd9PYB90x6bQUCuCf+gbd18wu4y8FDscW1ASS2a9bM8BRiaFIyUHh8Ztou4Sjqes2Pob2s+mZkw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.ProcessRuntime": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "bejo+D0Fkt/IeLUtR1MaDQYbFKhMibdVEtlDm5s7CapDls+U8p20MCwkQwZBIeL+gzacWR7+IhKR1EozbvW2ew==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -394,42 +159,6 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "Hs9pw/kmvH3lXaZ1LFKj3pLQsiGfj2xo3sxSzwiLlRL6UcMZUTeCfoJ9Udalvn3yq5dLlPEZzYegrTQ1/LhPOQ==" - }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "I2y4KBK3VCvU/WqE2xv7NjQ67maXHttkFSHYKgU2evrG9Yqh0oFjfORXt5hZTk+BVjdyFo2h0/YQZsca33BGmg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.4", - "Microsoft.Win32.SystemEvents": "4.7.0" - } - }, - "System.Management": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "IY+uuGhgzWiCg21i8IvQeY/Z7m1tX8VuPF+ludfn7iTCaccTtJo5HkjZbBEL8kbBubKhAKKtNXr7uMtmAc28Pw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0", - "System.CodeDom": "4.7.0" - } - }, - "System.Private.ServiceModel": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "BItrYCkoTV3VzVPsrew+uc34fmLb+3ncgspa7vbO3vkfY9JQCea4u34pHE+Bcv1Iy16MgRs3n2jKVRCDg0rPfg==" - }, - "System.ServiceModel.Primitives": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "YUXIMO4kL1v6dUVptJGixAx/8Ai5trQzVn3gbk0mpwxh77kGAs+MyBRoHN/5ZoxtwNn4E1dq3N4rJCAgAUaiJA==", - "dependencies": { - "System.Private.ServiceModel": "4.7.0" - } - }, "application": { "type": "Project", "dependencies": { @@ -446,11 +175,11 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Grafana.OpenTelemetry": "[1.5.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", "OpenTelemetry.Exporter.Prometheus.AspNetCore": "[1.15.0-beta.1, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", @@ -480,27 +209,6 @@ "FluentValidation": "12.1.1" } }, - "Grafana.OpenTelemetry": { - "type": "CentralTransitive", - "requested": "[1.5.2, )", - "resolved": "1.5.2", - "contentHash": "5QDzhHq7vMd22uCXT7+TL7LRRxJ/wtsLcDosb81qKQxoxnKixLb0zjzEV157WcU3vbwlTXP07AIH3kpZzFrEWQ==", - "dependencies": { - "CassandraCSharpDriver": "3.22.0", - "Grafana.OpenTelemetry.Base": "1.5.2", - "OpenTelemetry.Extensions.Hosting": "1.15.0", - "OpenTelemetry.Instrumentation.AWS": "1.15.0", - "OpenTelemetry.Instrumentation.AWSLambda": "1.15.0", - "OpenTelemetry.Instrumentation.AspNetCore": "1.15.0", - "OpenTelemetry.Instrumentation.Cassandra": "1.0.0-beta.5", - "OpenTelemetry.Instrumentation.ElasticsearchClient": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Hangfire": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Quartz": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.StackExchangeRedis": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Wcf": "1.15.0-beta.1" - } - }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", "requested": "[10.0.2, )", @@ -535,12 +243,6 @@ "StackExchange.Redis": "2.7.27" } }, - "Newtonsoft.Json": { - "type": "CentralTransitive", - "requested": "[13.0.4, )", - "resolved": "13.0.4", - "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" - }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "CentralTransitive", "requested": "[10.0.0, )", @@ -561,6 +263,15 @@ "OpenTelemetry": "1.15.0" } }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, "OpenTelemetry.Exporter.Prometheus.AspNetCore": { "type": "CentralTransitive", "requested": "[1.15.0-beta.1, )", diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json index 9d3e4e33..e006a40c 100644 --- a/templates/Full/tests/IntegrationTests/packages.lock.json +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -52,31 +52,6 @@ "resolved": "3.1.5", "contentHash": "tKi7dSTwP4m5m9eXPM2Ime4Kn7xNf4x4zT9sdLO/G4hZVnQCRiMTWoSZqI/pYTVeI27oPPqHBKYI/DjJ9GsYgA==" }, - "Amazon.Lambda.APIGatewayEvents": { - "type": "Transitive", - "resolved": "2.7.0", - "contentHash": "HAbdRKLzvUqRg/fItan7up/PWsj6vQfYF+1a4YV6sv1bq5TuK/ra+ndJwWMlzL+MFJzJaHwCmtkyBbh/vz8Oig==" - }, - "Amazon.Lambda.ApplicationLoadBalancerEvents": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "UtCd9tFuVyED8+DmDKEkdXJbAb/TdHIz51R/CsJCexKerO6HGOG87FGId4Ce9IXE+qjlBGyNQsKCw9oLLNe0hw==" - }, - "Amazon.Lambda.Core": { - "type": "Transitive", - "resolved": "2.8.0", - "contentHash": "mZnqCrLuNvB9T9/bCIcu/7H8HWd3yi6sEDzRE76D9ym06D+CoEfJ/EG4w7FucclvpSXO7ui/MKpzqhEnR2FAVg==" - }, - "Amazon.Lambda.SNSEvents": { - "type": "Transitive", - "resolved": "2.1.0", - "contentHash": "w2PKM5eQ6DNCR07H6SwHdcPXAr/qKQhNoAD+8bWydpgh4QdS2O4heo9vxhxn1g5FMSKeOxVa2zMKUZ9xeSYPZw==" - }, - "Amazon.Lambda.SQSEvents": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "RyYLgEa6nh+VJrCfTDKxvTLS1+eLDIIDyHgwDY3TR1WQz1+UeDIcccOJvoDk2nmjkSA0TfWJmZX9Ld0ySf57lA==" - }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", @@ -85,39 +60,6 @@ "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11" } }, - "AWSSDK.Core": { - "type": "Transitive", - "resolved": "4.0.3.3", - "contentHash": "YQv10JuxnciWh0QwnkarSbge4gXQV1qTURf5jkBjNUH/3jYS9QrbxopA4TK1qdjfOfP37tqiJkLSrRRNqX81aw==" - }, - "AWSSDK.SimpleNotificationService": { - "type": "Transitive", - "resolved": "4.0.2.7", - "contentHash": "WO0xjj2demSh3hPKQtSS3XyncITIUlENCeLv/FZsteMJh4HpfGwxp1dbq8YnaR90ddzvyPU6uj675WGuMf6zgg==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)" - } - }, - "AWSSDK.SQS": { - "type": "Transitive", - "resolved": "4.0.2.5", - "contentHash": "bHA9m/2RZHNKt6NGvQ56rfEDj/pfUlcwPeCg2HmPJ3jZPyoerBuEsYvFkMP3YJNv3aoycNWZoiDk0/ULP0tEyA==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)" - } - }, - "CassandraCSharpDriver": { - "type": "Transitive", - "resolved": "3.22.0", - "contentHash": "T5vjErmZ42Q1TAzbYSJvOrAzOZmb/d+v3OoIYrKfbcqjDuvedd61e0bAaaLpgiujPBBO+qFpxR4T0WSXkMr/fA==", - "dependencies": { - "K4os.Compression.LZ4": "1.1.11", - "Microsoft.Extensions.Logging": "1.0.0", - "Microsoft.Extensions.Logging.Abstractions": "1.0.0", - "Newtonsoft.Json": "9.0.1", - "System.Management": "4.7.0" - } - }, "Fare": { "type": "Transitive", "resolved": "2.1.1", @@ -131,26 +73,6 @@ "resolved": "3.31.1", "contentHash": "gSnJbUmGiOTdWddPhqzrEscHq9Ls6sqRDPB9WptckyjTUyx70JOOAaDLkFff8gManZNN3hllQ4aQInnQyq/Z/A==" }, - "Grafana.OpenTelemetry.Base": { - "type": "Transitive", - "resolved": "1.5.2", - "contentHash": "MS8yH+QFubR0Kllsxo7o2w9A2OwVeI2iB8gfGr7YPKEfwBYAcul4bHzPA9dyhtHE7hxGBuhh07bdNzMG8oRhyw==", - "dependencies": { - "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.0", - "OpenTelemetry": "1.15.0", - "OpenTelemetry.Exporter.OpenTelemetryProtocol": "1.15.0", - "OpenTelemetry.Instrumentation.GrpcNetClient": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Http": "1.15.0", - "OpenTelemetry.Instrumentation.Process": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Runtime": "1.15.0", - "OpenTelemetry.Instrumentation.SqlClient": "1.15.0", - "OpenTelemetry.Resources.Container": "1.15.0-beta.1", - "OpenTelemetry.Resources.Host": "1.15.0-beta.2", - "OpenTelemetry.Resources.OperatingSystem": "1.15.0-beta.1", - "OpenTelemetry.Resources.Process": "1.15.0-beta.1", - "OpenTelemetry.Resources.ProcessRuntime": "1.15.0-beta.1" - } - }, "Grpc.AspNetCore.Server": { "type": "Transitive", "resolved": "2.76.0", @@ -204,19 +126,6 @@ "resolved": "2.76.0", "contentHash": "goRzYZVMgQtyLvkWdgTnPycg49hlNxnMkqGGgR3l7nCOm0bUh0YeAneiJ9JFk3XLgF4suQUdETYkl2Mg/TBr0w==" }, - "Hangfire.Core": { - "type": "Transitive", - "resolved": "1.7.0", - "contentHash": "ZWfWcE+kOWjKy7l/rsVtZmxs5XdltJY5ndx+vy7enD85ZVgc0Z+nIOZ/PfBhQQ68vGOfwOudpQVHuwXlSn7/fQ==", - "dependencies": { - "Newtonsoft.Json": "11.0.1" - } - }, - "K4os.Compression.LZ4": { - "type": "Transitive", - "resolved": "1.1.11", - "contentHash": "RNvJw0UGkedPhCqVBNIogtfQebY+bQt0PN7xDbVe5LWLra0ZEqPfjPSl7iStj3rgDnkqkkTTpm+vCX3hU1qKmA==" - }, "Microsoft.CodeCoverage": { "type": "Transitive", "resolved": "18.0.1", @@ -549,8 +458,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "3.1.4", - "contentHash": "9/y05/CuxE+j184Nr4KihhB9KcUkvGojmD4JV4Vt/mHhVZR+eOCD5WCM+CXye9K0OFMsaPXbN+IcaIpjgBGZmg==" + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", @@ -566,14 +475,6 @@ "Newtonsoft.Json": "13.0.3" } }, - "Microsoft.Win32.SystemEvents": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0" - } - }, "NETStandard.Library": { "type": "Transitive", "resolved": "1.6.1", @@ -614,144 +515,6 @@ "OpenTelemetry.Api": "1.15.0" } }, - "OpenTelemetry.Exporter.OpenTelemetryProtocol": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", - "dependencies": { - "OpenTelemetry": "1.15.0" - } - }, - "OpenTelemetry.Extensions.AWS": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "XO2mKJSF3cjzNepezUcKdYqnk1Ex3VCQ8UMbNH8oo3eN/tZ6EXVnI/9daXgEoXIJLPHjeHHT/+aEy3wd74lF8A==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.AWS": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "CVI0H47mfFHAw4cFoUf6QO1H5O1fK7MQY8+T7CriEp4rOBto65ncZMnbEQtwQhcfeeNqbOTED8TayMPWY1yMRA==", - "dependencies": { - "AWSSDK.Core": "[4.0.3.3, 5.0.0)", - "AWSSDK.SQS": "[4.0.2.5, 5.0.0)", - "AWSSDK.SimpleNotificationService": "[4.0.2.7, 5.0.0)", - "OpenTelemetry.Extensions.AWS": "1.15.0" - } - }, - "OpenTelemetry.Instrumentation.AWSLambda": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "cO+iqR2lFriHg89QNSbpbFJm0o2BsMPxEThMbH9z/OLeVcxAh3EJ6PPAfw6NuicJhntN9I00iCYPpo22T5R+Zg==", - "dependencies": { - "Amazon.Lambda.APIGatewayEvents": "2.7.0", - "Amazon.Lambda.ApplicationLoadBalancerEvents": "2.2.0", - "Amazon.Lambda.Core": "2.8.0", - "Amazon.Lambda.SNSEvents": "2.1.0", - "Amazon.Lambda.SQSEvents": "2.2.0", - "OpenTelemetry.Extensions.AWS": "1.15.0" - } - }, - "OpenTelemetry.Instrumentation.Cassandra": { - "type": "Transitive", - "resolved": "1.0.0-beta.5", - "contentHash": "4FwPZqw/mQUfHlQsjwi3qG2OPFutOJSQsY+7oa5kI1Vu5U3tGiua+WNGxSyMsrid3MhXbC4Ax5013Fx6/vHVDg==", - "dependencies": { - "CassandraCSharpDriver": "3.17.0", - "Newtonsoft.Json": "13.0.1", - "OpenTelemetry.Api": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.ElasticsearchClient": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "hETsgn/JfHPfiUa5mItGu0EiCai7PnGSD5IubdmOyZntqeG/Wf8YIx3rjlF6KVbBb+JAezgGKX4oqTTlrxJQbw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Hangfire": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "Rog5ojlXL+WB4Li/L1yisvfuKVmnPIOh62g1rfUJYgJGvPLQbm5FtGuIvvRJlMPLtu7f73tYmqouw9EiWExJIQ==", - "dependencies": { - "Hangfire.Core": "1.7.0", - "Microsoft.Extensions.Options": "10.0.0", - "Newtonsoft.Json": "13.0.1", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Quartz": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "C3TR/iDaXeZKpB8QTZjsSrbOlE6D+NYwMw3xDwMyt1T7s/1/4yqyZNQA3M5h3FKlinTvFdBxIbN6ghT/A1g5Jw==", - "dependencies": { - "OpenTelemetry.Api": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.SqlClient": { - "type": "Transitive", - "resolved": "1.15.0", - "contentHash": "J0lI7lCngS4TJD4T7KNsAerOIjJHNV0T2MK0iuS2tK8wF7iqL1dp4MKW05FiyfvrIXkwsvFc1okKchxS8B0+SQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.0", - "Microsoft.Extensions.Options": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Instrumentation.Wcf": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "2U34rv1JvdB8jDS1LC7yknm+iINO+hBIZeEtKyUgUUaJT1FOINSQpIhofEtdsTR+++cIHOnvWHcru60QmiT5Cw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)", - "System.Drawing.Common": "4.7.2", - "System.Security.Cryptography.Xml": "4.7.1", - "System.ServiceModel.Primitives": "4.7.0" - } - }, - "OpenTelemetry.Resources.Container": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "b3W+tt2P9E6MybwFv/5nGLcfwDFyNcKw9fLaXrqSt3PpXrE533f+Vas00VLH0Qs5Z1fqDZSFqzgNwxmmTxvKHg==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.Host": { - "type": "Transitive", - "resolved": "1.15.0-beta.2", - "contentHash": "Ars2cy8gRmQ4n29V9L58eiFYucB8QwTfagFlNfMl3JpgkEeTsNtMInNTTJBDtnLM//7187C70n6lkc8fr2aGIA==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.OperatingSystem": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "I32LVRlWDsBALEQKD2jQx69iGjE+PZ2RdD0LoWzaKx7IsfsXoykvH7XgfF8o+/bzkAwKDr0zVe1Yb6so2zw7lw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.Process": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "ZyrUlfsSusSZd9PYB90x6bQUCuCf+gbd18wu4y8FDscW1ASS2a9bM8BRiaFIyUHh8Ztou4Sjqes2Pob2s+mZkw==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, - "OpenTelemetry.Resources.ProcessRuntime": { - "type": "Transitive", - "resolved": "1.15.0-beta.1", - "contentHash": "bejo+D0Fkt/IeLUtR1MaDQYbFKhMibdVEtlDm5s7CapDls+U8p20MCwkQwZBIeL+gzacWR7+IhKR1EozbvW2ew==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -766,85 +529,16 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "Hs9pw/kmvH3lXaZ1LFKj3pLQsiGfj2xo3sxSzwiLlRL6UcMZUTeCfoJ9Udalvn3yq5dLlPEZzYegrTQ1/LhPOQ==" - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "10.0.2", "contentHash": "TJPpTLF5MFPobq09c9BQ5X8QuviQfsKvH0Jbm7MkGylGvfIdRqJQLZDPC5sMRFkk9aZhmgir1NJKekip2NxfaA==" }, - "System.Drawing.Common": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "I2y4KBK3VCvU/WqE2xv7NjQ67maXHttkFSHYKgU2evrG9Yqh0oFjfORXt5hZTk+BVjdyFo2h0/YQZsca33BGmg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.4", - "Microsoft.Win32.SystemEvents": "4.7.0" - } - }, - "System.Management": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "IY+uuGhgzWiCg21i8IvQeY/Z7m1tX8VuPF+ludfn7iTCaccTtJo5HkjZbBEL8kbBubKhAKKtNXr7uMtmAc28Pw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0", - "System.CodeDom": "4.7.0" - } - }, - "System.Private.ServiceModel": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "BItrYCkoTV3VzVPsrew+uc34fmLb+3ncgspa7vbO3vkfY9JQCea4u34pHE+Bcv1Iy16MgRs3n2jKVRCDg0rPfg==", - "dependencies": { - "System.Security.Cryptography.Xml": "4.5.0" - } - }, - "System.Security.Cryptography.Pkcs": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "0Srzh6YlhjuMxaqMyeCCdZs22cucaUAG6SKDd3gNHBJmre0VZ71ekzWu9rvLD4YXPetyNdPvV6Qst+8C++9v3Q==" - }, - "System.Security.Cryptography.Xml": { - "type": "Transitive", - "resolved": "4.7.1", - "contentHash": "ddAre1QiT5cACLNWLLE3Smk61yhHr4IzQbt0FZiHsD63aFse0xSjbQU3+Fycc5elKhqNwgwk7ueOh3x9Rv9uIg==", - "dependencies": { - "System.Security.Cryptography.Pkcs": "4.7.0", - "System.Security.Permissions": "4.7.0" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==", - "dependencies": { - "System.Windows.Extensions": "4.7.0" - } - }, - "System.ServiceModel.Primitives": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "YUXIMO4kL1v6dUVptJGixAx/8Ai5trQzVn3gbk0mpwxh77kGAs+MyBRoHN/5ZoxtwNn4E1dq3N4rJCAgAUaiJA==", - "dependencies": { - "System.Private.ServiceModel": "4.7.0" - } - }, "System.Threading.RateLimiting": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==" }, - "System.Windows.Extensions": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==", - "dependencies": { - "System.Drawing.Common": "4.7.0" - } - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -905,11 +599,11 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Grafana.OpenTelemetry": "[1.5.2, )", "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", "OpenTelemetry.Exporter.Prometheus.AspNetCore": "[1.15.0-beta.1, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", @@ -999,27 +693,6 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" } }, - "Grafana.OpenTelemetry": { - "type": "CentralTransitive", - "requested": "[1.5.2, )", - "resolved": "1.5.2", - "contentHash": "5QDzhHq7vMd22uCXT7+TL7LRRxJ/wtsLcDosb81qKQxoxnKixLb0zjzEV157WcU3vbwlTXP07AIH3kpZzFrEWQ==", - "dependencies": { - "CassandraCSharpDriver": "3.22.0", - "Grafana.OpenTelemetry.Base": "1.5.2", - "OpenTelemetry.Extensions.Hosting": "1.15.0", - "OpenTelemetry.Instrumentation.AWS": "1.15.0", - "OpenTelemetry.Instrumentation.AWSLambda": "1.15.0", - "OpenTelemetry.Instrumentation.AspNetCore": "1.15.0", - "OpenTelemetry.Instrumentation.Cassandra": "1.0.0-beta.5", - "OpenTelemetry.Instrumentation.ElasticsearchClient": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Hangfire": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Quartz": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.StackExchangeRedis": "1.15.0-beta.1", - "OpenTelemetry.Instrumentation.Wcf": "1.15.0-beta.1" - } - }, "Grpc.AspNetCore": { "type": "CentralTransitive", "requested": "[2.76.0, )", @@ -1125,6 +798,15 @@ "OpenTelemetry": "1.15.0" } }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, "OpenTelemetry.Exporter.Prometheus.AspNetCore": { "type": "CentralTransitive", "requested": "[1.15.0-beta.1, )", From 0366757174bc86f7b4dfea3e6a11724dad3b4392 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 16:23:49 -0300 Subject: [PATCH 098/161] 53 refactor: replace Activity with using statement in use cases --- .../Application/Common/UseCases/BaseInOutUseCase.cs | 5 ++--- .../src/Application/Common/UseCases/BaseInUseCase.cs | 5 ++--- .../src/Application/Common/UseCases/BaseOutUseCase.cs | 5 ++--- .../src/Application/Common/UseCases/BaseUseCase.cs | 1 - .../Notifications/CreateNotificationUseCase.cs | 4 ---- .../Notifications/GetAllNotificationsUseCase.cs | 7 ------- .../Notifications/GetNotificationUseCase.cs | 4 ---- .../Full/src/Application/Orders/CreateOrderUseCase.cs | 3 --- .../src/Application/Orders/GetAllOrdersUseCase.cs | 7 ------- .../Full/src/Application/Orders/GetOrderUseCase.cs | 3 --- .../Cache/Services/HybridCacheService.cs | 11 ----------- 11 files changed, 6 insertions(+), 49 deletions(-) diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 0a765ceb..94be5c25 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -43,7 +43,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - Activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); TResponseData response; @@ -72,8 +72,7 @@ CancellationToken cancellationToken Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); _useCaseExecuted.Record(1); - Activity?.SetTag("correlationId", request.CorrelationId); - Activity?.Stop(); + activity?.SetTag("correlationId", request.CorrelationId); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 6d3f9641..6fa013ae 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -39,7 +39,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - Activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); @@ -61,8 +61,7 @@ CancellationToken cancellationToken _useCaseExecuted.Record(1); - Activity?.SetTag("correlationId", request.CorrelationId); - Activity?.Stop(); + activity?.SetTag("correlationId", request.CorrelationId); } public abstract Task HandleInternalAsync(TRequest request, CancellationToken cancellationToken); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 4963f48d..9ff74728 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -30,7 +30,7 @@ protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvide public async Task HandleAsync(CancellationToken cancellationToken) { - Activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, ClassName, HandleMethodName, correlationId); @@ -41,8 +41,7 @@ public async Task HandleAsync(CancellationToken cancellationToken _useCaseExecuted.Record(1); - Activity?.SetTag("correlationId", correlationId); - Activity?.Stop(); + activity?.SetTag("correlationId", correlationId); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index bfae7f62..9d704b3b 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -11,7 +11,6 @@ public abstract class BaseUseCase protected ILogger Logger { get; } protected string ClassName { get; set; } protected ActivitySource ActivitySource { get; } - public Activity Activity { get; set; } protected BaseUseCase(IServiceProvider serviceProvider) { diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 02e7c66f..2d730807 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -42,9 +42,5 @@ CancellationToken cancellationToken if (addResult == 0) Logs.FailedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); - - Activity?.SetTag("notificationId", notification.Id); - Activity?.SetTag("notificationType", notification.NotificationType); - Activity?.SetTag("notificationStatus", notification.NotificationStatus); } } diff --git a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs index 59894533..ae29964e 100644 --- a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs @@ -40,13 +40,6 @@ CancellationToken cancellationToken var notificationDtos = notifications.Select(notification => notification); - Activity?.SetTag("page", request.Page); - Activity?.SetTag("pageSize", request.PageSize); - Activity?.SetTag("sortBy", request.SortBy); - Activity?.SetTag("sortDescending", request.SortDescending); - Activity?.SetTag("totalRecords", totalRecords); - Activity?.SetTag("totalPages", totalPages); - return new(true, totalPages, totalRecords, notificationDtos); } } diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index f2ba0b6c..95f0d730 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -43,10 +43,6 @@ CancellationToken cancellationToken return new(false, null, "Notification not found."); } - Activity?.SetTag("notificationId", notification.Id); - Activity?.SetTag("notificationType", notification.NotificationType); - Activity?.SetTag("notificationStatus", notification.NotificationStatus); - return new(true, notification); } } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 2eb282bf..66034f48 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -97,9 +97,6 @@ CancellationToken cancellationToken CreateNotification(correlationId, "Success", response); - Activity?.SetTag("orderId", newOrder.Id); - Activity?.SetTag("orderTotal", newOrder.Total); - return response; } diff --git a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs index fa9febd2..8335e10c 100644 --- a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs +++ b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs @@ -36,13 +36,6 @@ CancellationToken cancellationToken var totalPages = (int) Math.Ceiling(totalRecords / (double) request.PageSize); - Activity?.SetTag("page", request.Page); - Activity?.SetTag("pageSize", request.PageSize); - Activity?.SetTag("sortBy", request.SortBy); - Activity?.SetTag("sortDescending", request.SortDescending); - Activity?.SetTag("totalRecords", totalRecords); - Activity?.SetTag("totalPages", totalPages); - return new(true, totalPages, totalRecords, orders); } } diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index 7a965bed..e6926686 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -45,9 +45,6 @@ CancellationToken cancellationToken return new(false, null, "Order not found."); } - Activity?.SetTag("orderId", order.Id); - Activity?.SetTag("orderTotal", order.Total); - return new(true, order); } } diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 7343ba2e..f2ff2b9b 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -28,10 +28,6 @@ CancellationToken cancellationToken Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); - activity?.SetTag("CorrelationId", correlationId.ToString()); - activity?.SetTag("cacheKey", key); - activity?.SetTag("cacheHit", result is not null); - return result; } @@ -44,10 +40,6 @@ public async ValueTask CreateAsync(Guid correlationId, string key, TRes await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, key); - - activity?.SetTag("CorrelationId", correlationId.ToString()); - activity?.SetTag("cacheKey", key); - activity?.SetTag("cacheHit", value is not null); } public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) @@ -59,8 +51,5 @@ public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationT await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); - - activity?.SetTag("CorrelationId", correlationId.ToString()); - activity?.SetTag("cacheKey", key); } } From 64a0413c21bb2565a1c981b840f7fa108cce308b Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 16:25:21 -0300 Subject: [PATCH 099/161] 53 refactor: enhance logging for cache operations with detailed messages --- .../src/Infrastructure/Cache/Services/HybridCacheService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index f2ff2b9b..79107409 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -26,7 +26,7 @@ CancellationToken cancellationToken Logs.DebugStartingOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, $"Cache hit: {result != null} for key: {key}"); return result; } @@ -39,7 +39,7 @@ public async ValueTask CreateAsync(Guid correlationId, string key, TRes await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, $"Cached hit: {value != null} for key: {key}"); } public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) @@ -50,6 +50,6 @@ public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationT await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); + Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, $"Cache entry removed for key: {key}"); } } From 66a3c561c29d80965b5ea24ff4c02b9368dcec6c Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 16:25:56 -0300 Subject: [PATCH 100/161] 53 refactor: remove unused activity tags from message handling --- .../Infrastructure/Messaging/Consumers/BaseConsumer.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index f7bd7ce8..29d8d199 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -163,10 +163,6 @@ await channel.QueueDeclareAsync( await handleAsync.Invoke(message, cancellationToken); Logs.Debug(logger, _className, methodName, message.CorrelationId, "Use case handled."); - - activity?.SetTag("CorrelationId", message.CorrelationId.ToString()); - activity?.SetTag("MessageType", typeof(TMessage).Name); - }; await channel.BasicConsumeAsync( @@ -175,10 +171,6 @@ await channel.BasicConsumeAsync( consumer: consumer, cancellationToken: cancellationToken ); - - activity?.SetTag("RabbitMQHostName", _factory.HostName); - activity?.SetTag("QueueName", _queueName); - } protected abstract Task HandleUseCaseAsync(IServiceProvider serviceProvider, TMessage message, CancellationToken cancellationToken); From 043bf8bdda2f78b6739325df98892a0d70429c40 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 17:01:46 -0300 Subject: [PATCH 101/161] 53 refactor: remove class name from logging parameters --- .../src/Application/Common/Helpers/Logs.cs | 64 ++++++++----------- .../Common/UseCases/BaseInOutUseCase.cs | 6 +- .../Common/UseCases/BaseInUseCase.cs | 6 +- .../Common/UseCases/BaseOutUseCase.cs | 4 +- .../CreateNotificationUseCase.cs | 2 +- .../GetAllNotificationsUseCase.cs | 2 +- .../Notifications/GetNotificationUseCase.cs | 2 +- .../Application/Orders/CreateOrderUseCase.cs | 4 +- .../Application/Orders/GetAllOrdersUseCase.cs | 2 +- .../src/Application/Orders/GetOrderUseCase.cs | 2 +- .../Cache/Services/HybridCacheService.cs | 12 ++-- .../Common/BaseBackgroundService.cs | 2 +- .../Data/Common/BaseRepository.cs | 8 +-- .../Messaging/Consumers/BaseConsumer.cs | 34 +++++----- .../Messaging/Producers/ProducerService.cs | 17 +++-- .../ExceptionHandlingMiddleware.cs | 3 +- 16 files changed, 78 insertions(+), 92 deletions(-) diff --git a/templates/Full/src/Application/Common/Helpers/Logs.cs b/templates/Full/src/Application/Common/Helpers/Logs.cs index 4a812a6f..67f3d2bd 100644 --- a/templates/Full/src/Application/Common/Helpers/Logs.cs +++ b/templates/Full/src/Application/Common/Helpers/Logs.cs @@ -8,174 +8,162 @@ public partial class Logs /// Logs a generic debug message with a custom message. /// /// The logger instance to use for logging. - /// The name of the class. /// The name of the method. /// The correlation ID for tracking the request. /// The debug message. [LoggerMessage( EventId = 1, Level = LogLevel.Debug, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {Message}" + Message = "[{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void Debug(ILogger logger, string className, string methodName, Guid correlationId, string message); + public static partial void Debug(ILogger logger, string methodName, Guid correlationId, string message); /// /// Logs a generic information message with a custom message. /// /// The logger instance to use for logging. - /// The name of the class. /// The name of the method. /// The correlation ID for tracking the request. /// The information message. [LoggerMessage( EventId = 2, Level = LogLevel.Information, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {Message}" + Message = "[{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void Information(ILogger logger, string className, string methodName, Guid correlationId, string message); + public static partial void Information(ILogger logger, string methodName, Guid correlationId, string message); /// /// Logs a generic warning message with a custom message. /// /// The logger instance to use for logging. - /// The name of the class. /// The name of the method. /// The correlation ID for tracking the request. /// The warning message. [LoggerMessage( EventId = 3, Level = LogLevel.Warning, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {Message}" + Message = "[{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void Warning(ILogger logger, string className, string methodName, Guid correlationId, string message); + public static partial void Warning(ILogger logger, string methodName, Guid correlationId, string message); /// /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. - /// The name of the class. /// The name of the method. /// The failure message. [LoggerMessage( EventId = 4, Level = LogLevel.Error, - Message = "[{ClassName}] | [{MethodName}] | Error: {Message}" + Message = "[{MethodName}] | Error: {Message}" )] - public static partial void Error(ILogger logger, string className, string methodName, string message); + public static partial void Error(ILogger logger, string methodName, string message); /// /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. - /// The name of the class. /// The name of the method. /// The correlation ID for tracking the request. /// The failure message. [LoggerMessage( EventId = 5, Level = LogLevel.Error, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Error: {Message}" + Message = "[{MethodName}] | [{CorrelationId}] | Error: {Message}" )] - public static partial void Error(ILogger logger, string className, string methodName, Guid correlationId, string message); + public static partial void Error(ILogger logger, string methodName, Guid correlationId, string message); /// - /// Logs the start of the execution of an operation, including the class name, method name, and correlation ID. + /// Logs the start of the execution of an operation, including the method name and correlation ID. /// /// The logger instance to use for logging. - /// The name of the class where the operation is executed. /// The name of the method where the operation is executed. /// The correlation ID for tracking the request. [LoggerMessage( EventId = 6, Level = LogLevel.Information, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Starting operation" + Message = "[{MethodName}] | [{CorrelationId}] | Starting operation" )] - public static partial void StartingOperation(ILogger logger, string className, string methodName, Guid correlationId); + public static partial void StartingOperation(ILogger logger, string methodName, Guid correlationId); /// - /// Logs the completion of the execution of an operation, including the class name, method name, and correlation ID. + /// Logs the completion of the execution of an operation, including the method name and correlation ID. /// /// The logger instance to use for logging. - /// The name of the class where the operation is executed. /// The name of the method where the operation is executed. /// The correlation ID for tracking the request. [LoggerMessage( EventId = 7, Level = LogLevel.Information, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Finished operation" + Message = "[{MethodName}] | [{CorrelationId}] | Finished operation" )] - public static partial void FinishedOperation(ILogger logger, string className, string methodName, Guid correlationId); + public static partial void FinishedOperation(ILogger logger, string methodName, Guid correlationId); /// /// Logs validation errors encountered during request validation. /// /// The logger instance to use for logging. - /// The name of the class where the validation occurred. /// The name of the method where the validation occurred. /// The correlation ID for tracking the request. /// The validation errors. [LoggerMessage( EventId = 8, Level = LogLevel.Error, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Validation errors: {Errors}" + Message = "[{MethodName}] | [{CorrelationId}] | Validation errors: {Errors}" )] - public static partial void ValidationErrors(ILogger logger, string className, string methodName, Guid correlationId, string errors); + public static partial void ValidationErrors(ILogger logger, string methodName, Guid correlationId, string errors); /// /// Logs when an entity is not found. /// /// The logger instance to use for logging. - /// The name of the class. /// The name of the method. /// The correlation ID for tracking the request. /// The name of the entity that was not found. [LoggerMessage( EventId = 9, Level = LogLevel.Warning, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | {EntityName} not found." + Message = "[{MethodName}] | [{CorrelationId}] | {EntityName} not found." )] - public static partial void NotFound(ILogger logger, string className, string methodName, Guid correlationId, string entityName); + public static partial void NotFound(ILogger logger, string methodName, Guid correlationId, string entityName); /// /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. - /// The name of the class. /// The name of the method. /// The correlation ID for tracking the request. /// The failure message. [LoggerMessage( EventId = 10, Level = LogLevel.Warning, - Message = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Failed operation: {Message}" + Message = "[{MethodName}] | [{CorrelationId}] | Failed operation: {Message}" )] - public static partial void FailedOperation(ILogger logger, string className, string methodName, Guid correlationId, string message); + public static partial void FailedOperation(ILogger logger, string methodName, Guid correlationId, string message); /// /// Logs the start of an operation. /// /// The logger instance to use for logging. - /// The name of the class. /// The method name. /// The correlation ID for tracking the request. [LoggerMessage( EventId = 11, Level = LogLevel.Debug, - Message = "[{ClassName}] | [{Method}] | [{CorrelationId}] | Starting operation. | {Details}" + Message = "[{Method}] | [{CorrelationId}] | Starting operation. | {Details}" )] - public static partial void DebugStartingOperation(ILogger logger, string className, string method, Guid correlationId, string details = ""); + public static partial void DebugStartingOperation(ILogger logger, string method, Guid correlationId, string details = ""); /// /// Logs the completion of an operation. /// /// The logger instance to use for logging. - /// The name of the class. /// The method name. /// The correlation ID for tracking the request. [LoggerMessage( EventId = 12, Level = LogLevel.Debug, - Message = "[{ClassName}] | [{Method}] | [{CorrelationId}] | Finished operation. | {Details}" + Message = "[{Method}] | [{CorrelationId}] | Finished operation. | {Details}" )] - public static partial void DebugFinishedOperation(ILogger logger, string className, string method, Guid correlationId, string details = ""); + public static partial void DebugFinishedOperation(ILogger logger, string method, Guid correlationId, string details = ""); } \ No newline at end of file diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 94be5c25..ced1dfe0 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -45,7 +45,7 @@ CancellationToken cancellationToken { using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; - Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.StartingOperation(Logger, HandleMethodName, request.CorrelationId); TResponseData response; if (_validator != null) @@ -54,7 +54,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { string errors = string.Join(", ", validationResult.Errors); - Logs.ValidationErrors(Logger, ClassName, HandleMethodName, request.CorrelationId, errors); + Logs.ValidationErrors(Logger, HandleMethodName, request.CorrelationId, errors); response = Activator.CreateInstance(); response = response with @@ -69,7 +69,7 @@ CancellationToken cancellationToken response = await HandleInternalAsync(request, cancellationToken); - Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.FinishedOperation(Logger, HandleMethodName, request.CorrelationId); _useCaseExecuted.Record(1); activity?.SetTag("correlationId", request.CorrelationId); diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 6fa013ae..c392067f 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -41,7 +41,7 @@ CancellationToken cancellationToken { using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; - Logs.StartingOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.StartingOperation(Logger, HandleMethodName, request.CorrelationId); if (_validator != null) { @@ -49,7 +49,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { var errors = string.Join(", ", validationResult.Errors); - Logs.ValidationErrors(Logger, ClassName, HandleMethodName, request.CorrelationId, errors); + Logs.ValidationErrors(Logger, HandleMethodName, request.CorrelationId, errors); return; } @@ -57,7 +57,7 @@ CancellationToken cancellationToken await HandleInternalAsync(request, cancellationToken); - Logs.FinishedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId); + Logs.FinishedOperation(Logger, HandleMethodName, request.CorrelationId); _useCaseExecuted.Record(1); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 9ff74728..bb4ada7e 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -33,11 +33,11 @@ public async Task HandleAsync(CancellationToken cancellationToken using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; var correlationId = Guid.NewGuid(); - Logs.StartingOperation(Logger, ClassName, HandleMethodName, correlationId); + Logs.StartingOperation(Logger, HandleMethodName, correlationId); var response = await HandleInternalAsync(cancellationToken); - Logs.FinishedOperation(Logger, ClassName, HandleMethodName, correlationId); + Logs.FinishedOperation(Logger, HandleMethodName, correlationId); _useCaseExecuted.Record(1); diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 2d730807..6f26bb5a 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -41,6 +41,6 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(notification, request.CorrelationId, cancellationToken); if (addResult == 0) - Logs.FailedOperation(Logger, ClassName, HandleMethodName, request.CorrelationId, "Failed to create notification"); + Logs.FailedOperation(Logger, HandleMethodName, request.CorrelationId, "Failed to create notification"); } } diff --git a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs index ae29964e..5aed4b91 100644 --- a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs @@ -32,7 +32,7 @@ CancellationToken cancellationToken if (notifications is null || !notifications.Any()) { - Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(Notification)); + Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(Notification)); return new(false, 0, 0, [], "No notifications found."); } diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index 95f0d730..e6ed4b1a 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -39,7 +39,7 @@ CancellationToken cancellationToken if (notification is null) { - Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(notification)); + Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(notification)); return new(false, null, "Notification not found."); } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 66034f48..34d4db8d 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -61,7 +61,7 @@ CancellationToken cancellationToken var createResult = newOrder.SetTotal(); if (createResult.IsFailure) { - Logs.FailedOperation(Logger, ClassName, HandleMethodName, correlationId, createResult.Message); + Logs.FailedOperation(Logger, HandleMethodName, correlationId, createResult.Message); response = new(false, null, createResult.Message); @@ -73,7 +73,7 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(newOrder, correlationId, cancellationToken); if (addResult == 0) { - Logs.FailedOperation(Logger, ClassName, HandleMethodName, correlationId, "Failed to create order."); + Logs.FailedOperation(Logger, HandleMethodName, correlationId, "Failed to create order."); response = new(false, null, "Failed to create order."); diff --git a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs index 8335e10c..58050e23 100644 --- a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs +++ b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs @@ -30,7 +30,7 @@ CancellationToken cancellationToken if (orders is null || !orders.Any()) { - Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(orders)); + Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(orders)); return new(false, 0, 0, [], "No orders found."); } diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index e6926686..a32786c1 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -41,7 +41,7 @@ CancellationToken cancellationToken if (order is null) { - Logs.NotFound(Logger, ClassName, HandleMethodName, request.CorrelationId, nameof(order)); + Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(order)); return new(false, null, "Order not found."); } diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 79107409..3d6b0585 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -23,10 +23,10 @@ CancellationToken cancellationToken { using var activity = _activitySource.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); - Logs.DebugStartingOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, key); + Logs.DebugStartingOperation(_logger, nameof(GetOrCreateAsync), correlationId, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(GetOrCreateAsync), correlationId, $"Cache hit: {result != null} for key: {key}"); + Logs.DebugFinishedOperation(_logger, nameof(GetOrCreateAsync), correlationId, $"Cache hit: {result != null} for key: {key}"); return result; } @@ -35,21 +35,21 @@ public async ValueTask CreateAsync(Guid correlationId, string key, TRes { using var activity = _activitySource.StartActivity($"{_className}.{nameof(CreateAsync)}"); - Logs.DebugStartingOperation(_logger, _className, nameof(CreateAsync), correlationId, key); + Logs.DebugStartingOperation(_logger, nameof(CreateAsync), correlationId, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(CreateAsync), correlationId, $"Cached hit: {value != null} for key: {key}"); + Logs.DebugFinishedOperation(_logger, nameof(CreateAsync), correlationId, $"Cached hit: {value != null} for key: {key}"); } public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) { using var activity = _activitySource.StartActivity($"{_className}.{nameof(DeleteAsync)}"); - Logs.DebugStartingOperation(_logger, _className, nameof(DeleteAsync), correlationId, key); + Logs.DebugStartingOperation(_logger, nameof(DeleteAsync), correlationId, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - Logs.DebugFinishedOperation(_logger, _className, nameof(DeleteAsync), correlationId, $"Cache entry removed for key: {key}"); + Logs.DebugFinishedOperation(_logger, nameof(DeleteAsync), correlationId, $"Cache entry removed for key: {key}"); } } diff --git a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs index 69520130..6a231e47 100644 --- a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs +++ b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs @@ -28,7 +28,7 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken) } catch (Exception ex) { - Logs.Error(logger, nameof(BaseBackgroundService<>), nameof(ExecuteAsync), Guid.NewGuid(), ex.Message); + Logs.Error(logger, nameof(ExecuteAsync), ex.Message); throw; } diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 329c44df..97b314ed 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -28,7 +28,7 @@ private async Task HandleBaseQueryAsync( { using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{methodName}")!; - Logs.DebugStartingOperation(logger, _className, methodName, correlationId); + Logs.DebugStartingOperation(logger, methodName, correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) @@ -36,7 +36,7 @@ private async Task HandleBaseQueryAsync( var result = await query.Invoke(dbSet); - Logs.DebugFinishedOperation(logger, _className, methodName, correlationId); + Logs.DebugFinishedOperation(logger, methodName, correlationId); activity?.SetTag("correlationId", correlationId); activity?.Stop(); @@ -53,13 +53,13 @@ public IQueryable GetQueryable( { using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(GetQueryable)}")!; - Logs.DebugStartingOperation(logger, _className, nameof(GetQueryable), correlationId); + Logs.DebugStartingOperation(logger, nameof(GetQueryable), correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) dbSet = _dbContextFactory.CreateDbContext().Set(); - Logs.DebugFinishedOperation(logger, _className, nameof(GetQueryable), correlationId); + Logs.DebugFinishedOperation(logger, nameof(GetQueryable), correlationId); activity?.SetTag("correlationId", correlationId); diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index 29d8d199..934d7570 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -17,7 +17,7 @@ namespace Infrastructure.Messaging.Consumers; internal abstract class BaseConsumer : BaseBackgroundService> where TMessage : BaseMessage { - private readonly string _className = typeof(TConsumer).Name; + private readonly string _consumerName = typeof(TConsumer).Name; private readonly string _queueName; private readonly IDictionary _arguments; private readonly ConnectionFactory _factory; @@ -56,9 +56,9 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi var hybridCacheService = serviceProvider.GetRequiredService(); - Logs.Debug(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " received. Checking if it has already been processed."); + Logs.Debug(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " received. Checking if it has already been processed."); - var isExecutedKey = _className + "-" + message.CorrelationId; + var isExecutedKey = _consumerName + "-" + message.CorrelationId; var isExecuted = await hybridCacheService.GetOrCreateAsync( message.CorrelationId, isExecutedKey, @@ -68,21 +68,21 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi if (isExecuted) { - Logs.Warning(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " has already been processed. Skipping."); + Logs.Warning(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " has already been processed. Skipping."); return; } - Logs.DebugStartingOperation(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " processing started."); + Logs.DebugStartingOperation(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " processing started."); await HandleUseCaseAsync(serviceProvider, message, cancellationToken); await hybridCacheService.CreateAsync(message.CorrelationId, isExecutedKey, true, cancellationToken); - Logs.DebugFinishedOperation(logger, _className, methodName, message.CorrelationId, typeof(TMessage).Name + " processing finished."); + Logs.DebugFinishedOperation(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " processing finished."); } catch (Exception ex) { - Logs.Error(logger, _className, methodName, message.CorrelationId, ex.Message); + Logs.Error(logger, methodName, message.CorrelationId, ex.Message); _ = producerService.HandleAsync(message!, CancellationToken.None, _queueName + "_deadLetter"); @@ -97,13 +97,13 @@ private async Task HandleRabbitMqAsync( CancellationToken cancellationToken ) { - using var activity = _activitySource.StartActivity($"{_className}.{nameof(HandleRabbitMqAsync)}"); + using var activity = _activitySource.StartActivity($"{_consumerName}.{nameof(HandleRabbitMqAsync)}"); var connection = await _factory.CreateConnectionAsync(cancellationToken); var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); var methodName = nameof(HandleRabbitMqAsync); - Logs.Debug(logger, _className, methodName, Guid.NewGuid(), "Connected to RabbitMQ. Declaring queues."); + Logs.Debug(logger, methodName, Guid.NewGuid(), "Connected to RabbitMQ. Declaring queues."); await channel.QueueDeclareAsync( queue: _queueName, @@ -123,7 +123,7 @@ await channel.QueueDeclareAsync( AsyncEventingBasicConsumer consumer = new(channel); - Logs.Debug(logger, _className, methodName, Guid.NewGuid(), "Queues declared. Starting to consume messages."); + Logs.Debug(logger, methodName, Guid.NewGuid(), "Queues declared. Starting to consume messages."); consumer.ReceivedAsync += async (model, eventArguments) => { @@ -133,36 +133,36 @@ await channel.QueueDeclareAsync( TMessage message = null!; try { - Logs.Debug(logger, _className, methodName, Guid.NewGuid(), "Message received. Deserializing."); + Logs.Debug(logger, methodName, Guid.NewGuid(), "Message received. Deserializing."); message = JsonSerializer.Deserialize(body)!; - Logs.Debug(logger, _className, methodName, message.CorrelationId, "Message deserialized. Validating."); + Logs.Debug(logger, methodName, message.CorrelationId, "Message deserialized. Validating."); if (message == null || message.GetType() != typeof(TMessage)) { - Logs.Warning(logger, _className, methodName, Guid.NewGuid(), typeof(TMessage).Name + " is null or of incorrect type."); + Logs.Warning(logger, methodName, Guid.NewGuid(), typeof(TMessage).Name + " is null or of incorrect type."); return; } } catch (JsonException ex) { - Logs.Error(logger, _className, methodName, Guid.NewGuid(), ex.Message); + Logs.Error(logger, methodName, Guid.NewGuid(), ex.Message); throw; } catch (Exception ex) { - Logs.Error(logger, _className, methodName, Guid.NewGuid(), ex.Message); + Logs.Error(logger, methodName, Guid.NewGuid(), ex.Message); throw; } - Logs.Debug(logger, _className, methodName, message.CorrelationId, "Message validated. Handling use case."); + Logs.Debug(logger, methodName, message.CorrelationId, "Message validated. Handling use case."); await handleAsync.Invoke(message, cancellationToken); - Logs.Debug(logger, _className, methodName, message.CorrelationId, "Use case handled."); + Logs.Debug(logger, methodName, message.CorrelationId, "Use case handled."); }; await channel.BasicConsumeAsync( diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index f9cdc15b..6f6bb236 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -11,7 +11,6 @@ namespace Infrastructure.Messaging.Producers; public sealed class ProducerService : IProduceService { - private readonly string _className = nameof(ProducerService); private readonly ILogger _logger; private readonly ConnectionFactory _factory; @@ -38,12 +37,12 @@ public async Task HandleAsync( { await Task.Yield(); - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(HandleAsync)}")!; + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}")!; using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - Logs.DebugStartingOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " publishing started."); + Logs.DebugStartingOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " publishing started."); await channel.BasicPublishAsync( exchange: exchange, @@ -52,7 +51,7 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " published."); + Logs.DebugFinishedOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " published."); activity?.SetTag("correlationId", message.CorrelationId); } @@ -66,16 +65,16 @@ public async Task HandleAsync( { await Task.Yield(); - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(HandleAsync)}")!; + using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}")!; - Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); + Logs.Debug(_logger, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); foreach (var message in messages) { - Logs.DebugStartingOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch publishing started."); + Logs.DebugStartingOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch publishing started."); await channel.BasicPublishAsync( exchange: exchange, @@ -84,11 +83,11 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.DebugFinishedOperation(_logger, _className, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch published."); + Logs.DebugFinishedOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch published."); activity?.SetTag("correlationId", message.CorrelationId); } - Logs.Debug(_logger, _className, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); + Logs.Debug(_logger, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); activity?.SetTag("correlationId", messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty); } diff --git a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs index 30d3b2dc..9fc1688c 100644 --- a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs +++ b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -8,7 +8,6 @@ internal sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger< { private readonly RequestDelegate _next = next; private readonly ILogger _logger = logger; - private readonly string _className = nameof(ExceptionHandlingMiddleware); public async Task InvokeAsync(HttpContext context) { @@ -24,7 +23,7 @@ public async Task InvokeAsync(HttpContext context) private async Task HandleExceptionAsync(HttpContext context, Exception exception) { - Logs.Error(_logger, _className, nameof(HandleExceptionAsync), exception.Message); + Logs.Error(_logger, nameof(HandleExceptionAsync), exception.Message); BaseResponse response = new(false, exception.Message); From ef2c06e84f9d2b9bf362937845d7d6c5f70a020d Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 17:23:01 -0300 Subject: [PATCH 102/161] 53 refactor: simplify logging parameters by removing method names --- .../src/Application/Common/Helpers/Logs.cs | 51 ++++++++++--------- .../Common/UseCases/BaseInOutUseCase.cs | 6 +-- .../Common/UseCases/BaseInUseCase.cs | 6 +-- .../Common/UseCases/BaseOutUseCase.cs | 4 +- .../CreateNotificationUseCase.cs | 2 +- .../GetAllNotificationsUseCase.cs | 2 +- .../Notifications/GetNotificationUseCase.cs | 2 +- .../Application/Orders/CreateOrderUseCase.cs | 4 +- .../Application/Orders/GetAllOrdersUseCase.cs | 2 +- .../src/Application/Orders/GetOrderUseCase.cs | 2 +- .../Cache/Services/HybridCacheService.cs | 12 ++--- .../Common/BaseBackgroundService.cs | 2 +- .../Data/Common/BaseRepository.cs | 8 +-- .../Messaging/Consumers/BaseConsumer.cs | 30 +++++------ .../Messaging/Producers/ProducerService.cs | 12 ++--- .../ExceptionHandlingMiddleware.cs | 2 +- 16 files changed, 74 insertions(+), 73 deletions(-) diff --git a/templates/Full/src/Application/Common/Helpers/Logs.cs b/templates/Full/src/Application/Common/Helpers/Logs.cs index 67f3d2bd..acb0a91d 100644 --- a/templates/Full/src/Application/Common/Helpers/Logs.cs +++ b/templates/Full/src/Application/Common/Helpers/Logs.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Microsoft.Extensions.Logging; namespace Application.Common.Helpers; @@ -8,162 +9,164 @@ public partial class Logs /// Logs a generic debug message with a custom message. /// /// The logger instance to use for logging. - /// The name of the method. /// The correlation ID for tracking the request. /// The debug message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 1, Level = LogLevel.Debug, Message = "[{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void Debug(ILogger logger, string methodName, Guid correlationId, string message); + public static partial void Debug(ILogger logger, Guid correlationId, string message, [CallerMemberName] string methodName = null!); /// /// Logs a generic information message with a custom message. /// /// The logger instance to use for logging. - /// The name of the method. /// The correlation ID for tracking the request. /// The information message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 2, Level = LogLevel.Information, Message = "[{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void Information(ILogger logger, string methodName, Guid correlationId, string message); + public static partial void Information(ILogger logger, Guid correlationId, string message, [CallerMemberName] string methodName = null!); /// /// Logs a generic warning message with a custom message. /// /// The logger instance to use for logging. - /// The name of the method. /// The correlation ID for tracking the request. /// The warning message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 3, Level = LogLevel.Warning, Message = "[{MethodName}] | [{CorrelationId}] | {Message}" )] - public static partial void Warning(ILogger logger, string methodName, Guid correlationId, string message); + public static partial void Warning(ILogger logger, Guid correlationId, string message, [CallerMemberName] string methodName = null!); /// /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. - /// The name of the method. /// The failure message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 4, Level = LogLevel.Error, Message = "[{MethodName}] | Error: {Message}" )] - public static partial void Error(ILogger logger, string methodName, string message); + public static partial void Error(ILogger logger, string message, [CallerMemberName] string methodName = null!); /// /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. - /// The name of the method. /// The correlation ID for tracking the request. /// The failure message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 5, Level = LogLevel.Error, Message = "[{MethodName}] | [{CorrelationId}] | Error: {Message}" )] - public static partial void Error(ILogger logger, string methodName, Guid correlationId, string message); + public static partial void Error(ILogger logger, Guid correlationId, string message, [CallerMemberName] string methodName = null!); /// /// Logs the start of the execution of an operation, including the method name and correlation ID. /// /// The logger instance to use for logging. - /// The name of the method where the operation is executed. /// The correlation ID for tracking the request. + /// The name of the method where the operation is executed (auto-captured). [LoggerMessage( EventId = 6, Level = LogLevel.Information, Message = "[{MethodName}] | [{CorrelationId}] | Starting operation" )] - public static partial void StartingOperation(ILogger logger, string methodName, Guid correlationId); + public static partial void StartingOperation(ILogger logger, Guid correlationId, [CallerMemberName] string methodName = null!); /// /// Logs the completion of the execution of an operation, including the method name and correlation ID. /// /// The logger instance to use for logging. - /// The name of the method where the operation is executed. /// The correlation ID for tracking the request. + /// The name of the method where the operation is executed (auto-captured). [LoggerMessage( EventId = 7, Level = LogLevel.Information, Message = "[{MethodName}] | [{CorrelationId}] | Finished operation" )] - public static partial void FinishedOperation(ILogger logger, string methodName, Guid correlationId); + public static partial void FinishedOperation(ILogger logger, Guid correlationId, [CallerMemberName] string methodName = null!); /// /// Logs validation errors encountered during request validation. /// /// The logger instance to use for logging. - /// The name of the method where the validation occurred. /// The correlation ID for tracking the request. /// The validation errors. + /// The name of the method where the validation occurred (auto-captured). [LoggerMessage( EventId = 8, Level = LogLevel.Error, Message = "[{MethodName}] | [{CorrelationId}] | Validation errors: {Errors}" )] - public static partial void ValidationErrors(ILogger logger, string methodName, Guid correlationId, string errors); + public static partial void ValidationErrors(ILogger logger, Guid correlationId, string errors, [CallerMemberName] string methodName = null!); /// /// Logs when an entity is not found. /// /// The logger instance to use for logging. - /// The name of the method. /// The correlation ID for tracking the request. /// The name of the entity that was not found. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 9, Level = LogLevel.Warning, Message = "[{MethodName}] | [{CorrelationId}] | {EntityName} not found." )] - public static partial void NotFound(ILogger logger, string methodName, Guid correlationId, string entityName); + public static partial void NotFound(ILogger logger, Guid correlationId, string entityName, [CallerMemberName] string methodName = null!); /// /// Logs a generic operation failure with a custom message. /// /// The logger instance to use for logging. - /// The name of the method. /// The correlation ID for tracking the request. /// The failure message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 10, Level = LogLevel.Warning, Message = "[{MethodName}] | [{CorrelationId}] | Failed operation: {Message}" )] - public static partial void FailedOperation(ILogger logger, string methodName, Guid correlationId, string message); + public static partial void FailedOperation(ILogger logger, Guid correlationId, string message, [CallerMemberName] string methodName = null!); /// /// Logs the start of an operation. /// /// The logger instance to use for logging. - /// The method name. /// The correlation ID for tracking the request. + /// Optional details about the operation. + /// The method name (auto-captured). [LoggerMessage( EventId = 11, Level = LogLevel.Debug, Message = "[{Method}] | [{CorrelationId}] | Starting operation. | {Details}" )] - public static partial void DebugStartingOperation(ILogger logger, string method, Guid correlationId, string details = ""); + public static partial void DebugStartingOperation(ILogger logger, Guid correlationId, string details = "", [CallerMemberName] string method = null!); /// /// Logs the completion of an operation. /// /// The logger instance to use for logging. - /// The method name. /// The correlation ID for tracking the request. + /// Optional details about the operation. + /// The method name (auto-captured). [LoggerMessage( EventId = 12, Level = LogLevel.Debug, Message = "[{Method}] | [{CorrelationId}] | Finished operation. | {Details}" )] - public static partial void DebugFinishedOperation(ILogger logger, string method, Guid correlationId, string details = ""); + public static partial void DebugFinishedOperation(ILogger logger, Guid correlationId, string details = "", [CallerMemberName] string method = null!); } \ No newline at end of file diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index ced1dfe0..ae188c9d 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -45,7 +45,7 @@ CancellationToken cancellationToken { using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; - Logs.StartingOperation(Logger, HandleMethodName, request.CorrelationId); + Logs.StartingOperation(Logger, request.CorrelationId); TResponseData response; if (_validator != null) @@ -54,7 +54,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { string errors = string.Join(", ", validationResult.Errors); - Logs.ValidationErrors(Logger, HandleMethodName, request.CorrelationId, errors); + Logs.ValidationErrors(Logger, request.CorrelationId, errors); response = Activator.CreateInstance(); response = response with @@ -69,7 +69,7 @@ CancellationToken cancellationToken response = await HandleInternalAsync(request, cancellationToken); - Logs.FinishedOperation(Logger, HandleMethodName, request.CorrelationId); + Logs.FinishedOperation(Logger, request.CorrelationId); _useCaseExecuted.Record(1); activity?.SetTag("correlationId", request.CorrelationId); diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index c392067f..84d9902e 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -41,7 +41,7 @@ CancellationToken cancellationToken { using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; - Logs.StartingOperation(Logger, HandleMethodName, request.CorrelationId); + Logs.StartingOperation(Logger, request.CorrelationId); if (_validator != null) { @@ -49,7 +49,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { var errors = string.Join(", ", validationResult.Errors); - Logs.ValidationErrors(Logger, HandleMethodName, request.CorrelationId, errors); + Logs.ValidationErrors(Logger, request.CorrelationId, errors); return; } @@ -57,7 +57,7 @@ CancellationToken cancellationToken await HandleInternalAsync(request, cancellationToken); - Logs.FinishedOperation(Logger, HandleMethodName, request.CorrelationId); + Logs.FinishedOperation(Logger, request.CorrelationId); _useCaseExecuted.Record(1); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index bb4ada7e..b9b6ea8c 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -33,11 +33,11 @@ public async Task HandleAsync(CancellationToken cancellationToken using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; var correlationId = Guid.NewGuid(); - Logs.StartingOperation(Logger, HandleMethodName, correlationId); + Logs.StartingOperation(Logger, correlationId); var response = await HandleInternalAsync(cancellationToken); - Logs.FinishedOperation(Logger, HandleMethodName, correlationId); + Logs.FinishedOperation(Logger, correlationId); _useCaseExecuted.Record(1); diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 6f26bb5a..efa09477 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -41,6 +41,6 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(notification, request.CorrelationId, cancellationToken); if (addResult == 0) - Logs.FailedOperation(Logger, HandleMethodName, request.CorrelationId, "Failed to create notification"); + Logs.FailedOperation(Logger, request.CorrelationId, "Failed to create notification"); } } diff --git a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs index 5aed4b91..678aa011 100644 --- a/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetAllNotificationsUseCase.cs @@ -32,7 +32,7 @@ CancellationToken cancellationToken if (notifications is null || !notifications.Any()) { - Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(Notification)); + Logs.NotFound(Logger, request.CorrelationId, nameof(Notification)); return new(false, 0, 0, [], "No notifications found."); } diff --git a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs index e6ed4b1a..e047fe94 100644 --- a/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/GetNotificationUseCase.cs @@ -39,7 +39,7 @@ CancellationToken cancellationToken if (notification is null) { - Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(notification)); + Logs.NotFound(Logger, request.CorrelationId, nameof(notification)); return new(false, null, "Notification not found."); } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 34d4db8d..21c54b54 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -61,7 +61,7 @@ CancellationToken cancellationToken var createResult = newOrder.SetTotal(); if (createResult.IsFailure) { - Logs.FailedOperation(Logger, HandleMethodName, correlationId, createResult.Message); + Logs.FailedOperation(Logger, correlationId, createResult.Message); response = new(false, null, createResult.Message); @@ -73,7 +73,7 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(newOrder, correlationId, cancellationToken); if (addResult == 0) { - Logs.FailedOperation(Logger, HandleMethodName, correlationId, "Failed to create order."); + Logs.FailedOperation(Logger, correlationId, "Failed to create order."); response = new(false, null, "Failed to create order."); diff --git a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs index 58050e23..e55965b1 100644 --- a/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs +++ b/templates/Full/src/Application/Orders/GetAllOrdersUseCase.cs @@ -30,7 +30,7 @@ CancellationToken cancellationToken if (orders is null || !orders.Any()) { - Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(orders)); + Logs.NotFound(Logger, request.CorrelationId, nameof(orders)); return new(false, 0, 0, [], "No orders found."); } diff --git a/templates/Full/src/Application/Orders/GetOrderUseCase.cs b/templates/Full/src/Application/Orders/GetOrderUseCase.cs index a32786c1..8ecc4919 100644 --- a/templates/Full/src/Application/Orders/GetOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/GetOrderUseCase.cs @@ -41,7 +41,7 @@ CancellationToken cancellationToken if (order is null) { - Logs.NotFound(Logger, HandleMethodName, request.CorrelationId, nameof(order)); + Logs.NotFound(Logger, request.CorrelationId, nameof(order)); return new(false, null, "Order not found."); } diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 3d6b0585..1f2e0e02 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -23,10 +23,10 @@ CancellationToken cancellationToken { using var activity = _activitySource.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); - Logs.DebugStartingOperation(_logger, nameof(GetOrCreateAsync), correlationId, key); + Logs.DebugStartingOperation(_logger, correlationId, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, nameof(GetOrCreateAsync), correlationId, $"Cache hit: {result != null} for key: {key}"); + Logs.DebugFinishedOperation(_logger, correlationId, $"Cache hit: {result != null} for key: {key}"); return result; } @@ -35,21 +35,21 @@ public async ValueTask CreateAsync(Guid correlationId, string key, TRes { using var activity = _activitySource.StartActivity($"{_className}.{nameof(CreateAsync)}"); - Logs.DebugStartingOperation(_logger, nameof(CreateAsync), correlationId, key); + Logs.DebugStartingOperation(_logger, correlationId, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - Logs.DebugFinishedOperation(_logger, nameof(CreateAsync), correlationId, $"Cached hit: {value != null} for key: {key}"); + Logs.DebugFinishedOperation(_logger, correlationId, $"Cached hit: {value != null} for key: {key}"); } public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) { using var activity = _activitySource.StartActivity($"{_className}.{nameof(DeleteAsync)}"); - Logs.DebugStartingOperation(_logger, nameof(DeleteAsync), correlationId, key); + Logs.DebugStartingOperation(_logger, correlationId, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - Logs.DebugFinishedOperation(_logger, nameof(DeleteAsync), correlationId, $"Cache entry removed for key: {key}"); + Logs.DebugFinishedOperation(_logger, correlationId, $"Cache entry removed for key: {key}"); } } diff --git a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs index 6a231e47..45aae6e7 100644 --- a/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs +++ b/templates/Full/src/Infrastructure/Common/BaseBackgroundService.cs @@ -28,7 +28,7 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken) } catch (Exception ex) { - Logs.Error(logger, nameof(ExecuteAsync), ex.Message); + Logs.Error(logger, ex.Message); throw; } diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 97b314ed..9f48fb3d 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -28,7 +28,7 @@ private async Task HandleBaseQueryAsync( { using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{methodName}")!; - Logs.DebugStartingOperation(logger, methodName, correlationId); + Logs.DebugStartingOperation(logger, correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) @@ -36,7 +36,7 @@ private async Task HandleBaseQueryAsync( var result = await query.Invoke(dbSet); - Logs.DebugFinishedOperation(logger, methodName, correlationId); + Logs.DebugFinishedOperation(logger, correlationId); activity?.SetTag("correlationId", correlationId); activity?.Stop(); @@ -53,13 +53,13 @@ public IQueryable GetQueryable( { using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(GetQueryable)}")!; - Logs.DebugStartingOperation(logger, nameof(GetQueryable), correlationId); + Logs.DebugStartingOperation(logger, correlationId); var dbSet = _dbContext.Set(); if (newContext.GetValueOrDefault()) dbSet = _dbContextFactory.CreateDbContext().Set(); - Logs.DebugFinishedOperation(logger, nameof(GetQueryable), correlationId); + Logs.DebugFinishedOperation(logger, correlationId); activity?.SetTag("correlationId", correlationId); diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index 934d7570..4a227f2b 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -49,14 +49,13 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi async (message, cancellationToken) => { producerService = serviceProvider.GetRequiredService(); - var methodName = nameof(ExecuteInternalAsync); try { var hybridCacheService = serviceProvider.GetRequiredService(); - Logs.Debug(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " received. Checking if it has already been processed."); + Logs.Debug(logger, message.CorrelationId, typeof(TMessage).Name + " received. Checking if it has already been processed."); var isExecutedKey = _consumerName + "-" + message.CorrelationId; var isExecuted = await hybridCacheService.GetOrCreateAsync( @@ -68,21 +67,21 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi if (isExecuted) { - Logs.Warning(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " has already been processed. Skipping."); + Logs.Warning(logger, message.CorrelationId, typeof(TMessage).Name + " has already been processed. Skipping."); return; } - Logs.DebugStartingOperation(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " processing started."); + Logs.DebugStartingOperation(logger, message.CorrelationId, typeof(TMessage).Name + " processing started."); await HandleUseCaseAsync(serviceProvider, message, cancellationToken); await hybridCacheService.CreateAsync(message.CorrelationId, isExecutedKey, true, cancellationToken); - Logs.DebugFinishedOperation(logger, methodName, message.CorrelationId, typeof(TMessage).Name + " processing finished."); + Logs.DebugFinishedOperation(logger, message.CorrelationId, typeof(TMessage).Name + " processing finished."); } catch (Exception ex) { - Logs.Error(logger, methodName, message.CorrelationId, ex.Message); + Logs.Error(logger, message.CorrelationId, ex.Message); _ = producerService.HandleAsync(message!, CancellationToken.None, _queueName + "_deadLetter"); @@ -101,9 +100,8 @@ CancellationToken cancellationToken var connection = await _factory.CreateConnectionAsync(cancellationToken); var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - var methodName = nameof(HandleRabbitMqAsync); - Logs.Debug(logger, methodName, Guid.NewGuid(), "Connected to RabbitMQ. Declaring queues."); + Logs.Debug(logger, Guid.NewGuid(), "Connected to RabbitMQ. Declaring queues."); await channel.QueueDeclareAsync( queue: _queueName, @@ -123,7 +121,7 @@ await channel.QueueDeclareAsync( AsyncEventingBasicConsumer consumer = new(channel); - Logs.Debug(logger, methodName, Guid.NewGuid(), "Queues declared. Starting to consume messages."); + Logs.Debug(logger, Guid.NewGuid(), "Queues declared. Starting to consume messages."); consumer.ReceivedAsync += async (model, eventArguments) => { @@ -133,36 +131,36 @@ await channel.QueueDeclareAsync( TMessage message = null!; try { - Logs.Debug(logger, methodName, Guid.NewGuid(), "Message received. Deserializing."); + Logs.Debug(logger, Guid.NewGuid(), "Message received. Deserializing."); message = JsonSerializer.Deserialize(body)!; - Logs.Debug(logger, methodName, message.CorrelationId, "Message deserialized. Validating."); + Logs.Debug(logger, message.CorrelationId, "Message deserialized. Validating."); if (message == null || message.GetType() != typeof(TMessage)) { - Logs.Warning(logger, methodName, Guid.NewGuid(), typeof(TMessage).Name + " is null or of incorrect type."); + Logs.Warning(logger, Guid.NewGuid(), typeof(TMessage).Name + " is null or of incorrect type."); return; } } catch (JsonException ex) { - Logs.Error(logger, methodName, Guid.NewGuid(), ex.Message); + Logs.Error(logger, Guid.NewGuid(), ex.Message); throw; } catch (Exception ex) { - Logs.Error(logger, methodName, Guid.NewGuid(), ex.Message); + Logs.Error(logger, Guid.NewGuid(), ex.Message); throw; } - Logs.Debug(logger, methodName, message.CorrelationId, "Message validated. Handling use case."); + Logs.Debug(logger, message.CorrelationId, "Message validated. Handling use case."); await handleAsync.Invoke(message, cancellationToken); - Logs.Debug(logger, methodName, message.CorrelationId, "Use case handled."); + Logs.Debug(logger, message.CorrelationId, "Use case handled."); }; await channel.BasicConsumeAsync( diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 6f6bb236..234c9bcc 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -42,7 +42,7 @@ public async Task HandleAsync( using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); - Logs.DebugStartingOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " publishing started."); + Logs.DebugStartingOperation(_logger, message.CorrelationId, typeof(TMessage).Name + " publishing started."); await channel.BasicPublishAsync( exchange: exchange, @@ -51,7 +51,7 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.DebugFinishedOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " published."); + Logs.DebugFinishedOperation(_logger, message.CorrelationId, typeof(TMessage).Name + " published."); activity?.SetTag("correlationId", message.CorrelationId); } @@ -67,14 +67,14 @@ public async Task HandleAsync( using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}")!; - Logs.Debug(_logger, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); + Logs.Debug(_logger, messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); foreach (var message in messages) { - Logs.DebugStartingOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch publishing started."); + Logs.DebugStartingOperation(_logger, message.CorrelationId, typeof(TMessage).Name + " batch publishing started."); await channel.BasicPublishAsync( exchange: exchange, @@ -83,11 +83,11 @@ await channel.BasicPublishAsync( cancellationToken: cancellationToken ); - Logs.DebugFinishedOperation(_logger, nameof(HandleAsync), message.CorrelationId, typeof(TMessage).Name + " batch published."); + Logs.DebugFinishedOperation(_logger, message.CorrelationId, typeof(TMessage).Name + " batch published."); activity?.SetTag("correlationId", message.CorrelationId); } - Logs.Debug(_logger, nameof(HandleAsync), messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); + Logs.Debug(_logger, messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); activity?.SetTag("correlationId", messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty); } diff --git a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs index 9fc1688c..dd8ce652 100644 --- a/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs +++ b/templates/Full/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -23,7 +23,7 @@ public async Task InvokeAsync(HttpContext context) private async Task HandleExceptionAsync(HttpContext context, Exception exception) { - Logs.Error(_logger, nameof(HandleExceptionAsync), exception.Message); + Logs.Error(_logger, exception.Message); BaseResponse response = new(false, exception.Message); From d31d6e5513c17a4aefc4810865af1660bd081631 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 17:27:21 -0300 Subject: [PATCH 103/161] 53 refactor: simplify metrics configuration in OpenTelemetry setup --- ...frastructureOpenTelemetryDependencyInjection.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 9548669a..9ebd4459 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -40,15 +40,7 @@ public WebApplicationBuilder AddOpenTelemetry() .AddService(serviceName, serviceVersion: serviceVersion); builder.Services.AddOpenTelemetry() - .WithMetrics(metrics => - { - metrics.AddView( - "http.server.request.duration", - new ExplicitBucketHistogramConfiguration() - { - Boundaries = [0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10] - } - ) + .WithMetrics(metrics => metrics .AddMeter( DefaultConfigurations.Meter.Name, "System.Diagnostics.Metrics", @@ -66,8 +58,8 @@ public WebApplicationBuilder AddOpenTelemetry() { options.Protocol = exporterProtocol; options.Endpoint = new Uri(exporterMetricsEndpoint); - }); - }) + }) + ) .WithTracing(tracing => tracing .AddSource(serviceName) .SetResourceBuilder(resourceBuilder) From d246df5fe7da43889909bf05574536176d0414fd Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 17:33:45 -0300 Subject: [PATCH 104/161] 53 refactor: update docker-compose files to use full network --- templates/Full/docker-compose-grafana.yml | 6 +++++- templates/Full/docker-compose-load-tests.yml | 22 ++++++++++---------- templates/Full/docker-compose.yml | 12 +++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index 0902a055..31cd474e 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -73,4 +73,8 @@ services: - "3200:3200" # tempo depends_on: - tempo-init - - memcached \ No newline at end of file + - memcached + +networks: + hexagonal_solution_template_full_network: + driver: bridge \ No newline at end of file diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index e5bf3466..8c700734 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -1,4 +1,4 @@ -name: hexagonal-solution-template-load-tests +name: hexagonal-solution-template-full-load-tests services: k6: @@ -15,7 +15,7 @@ services: volumes: - ./tests/LoadTests:/LoadTests networks: - - hexagonal_solution_template_network + - hexagonal_solution_template_full_network webapp: build: @@ -45,16 +45,16 @@ services: - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning - ENABLE_SENSITIVE_DATA_LOGGING=false - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp - - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://aspire-dashboard:18889 - - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://aspire-dashboard:18889 - - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://aspire-dashboard:18889 + - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://alloy:4317 - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - ConnectionStrings__OrderDb=Host=postgres;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES - ConnectionStrings__Redis=redis:6379 - ConnectionStrings__RabbitMQ=amqp://guest:guest@rabbitmq:5672/ networks: - - hexagonal_solution_template_network + - hexagonal_solution_template_full_network postgres: extends: @@ -66,10 +66,10 @@ services: file: docker-compose.yml service: postgres-init - # pgadmin: - # extends: - # file: docker-compose.yml - # service: pgadmin + pgadmin: + extends: + file: docker-compose.yml + service: pgadmin redis: extends: @@ -82,5 +82,5 @@ services: service: rabbitmq networks: - hexagonal_solution_template_network: + hexagonal_solution_template_full_network: driver: bridge diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 2a9411f3..4dcd1a65 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -9,7 +9,7 @@ services: POSTGRES_USER: "postgres" POSTGRES_DB: "OrderDb" networks: - - hexagonal_solution_template_network + - hexagonal_solution_template_full_network healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 3s @@ -26,7 +26,7 @@ services: postgres: condition: service_healthy networks: - - hexagonal_solution_template_network + - hexagonal_solution_template_full_network volumes: - ./scripts/sql/migrations.sql:/tmp/migrations.sql - ./scripts/sql/seeds.sql:/tmp/seeds.sql @@ -51,7 +51,7 @@ services: ports: - "5050:80" networks: - - hexagonal_solution_template_network + - hexagonal_solution_template_full_network redis: image: redis:8 @@ -65,7 +65,7 @@ services: retries: 5 start_period: 15s networks: - - hexagonal_solution_template_network + - hexagonal_solution_template_full_network rabbitmq: image: rabbitmq:management @@ -83,8 +83,8 @@ services: retries: 5 start_period: 15s networks: - - hexagonal_solution_template_network + - hexagonal_solution_template_full_network networks: - hexagonal_solution_template_network: + hexagonal_solution_template_full_network: driver: bridge From 4dd3b3e1ce6338d44fd4d0d77296511224300090 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 11 Mar 2026 17:36:08 -0300 Subject: [PATCH 105/161] 53 refactor: update k6 service environment variables for dashboard --- templates/Full/docker-compose-load-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 8c700734..91c349ac 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -8,9 +8,12 @@ services: condition: service_healthy ports: - "6565:6565" + - "5665:5665" environment: - WEBAPP_URL=http://webapp:5000 - K6_SUMMARY_MODE=full + - K6_WEB_DASHBOARD=true + - K6_WEB_DASHBOARD_EXPORT=html-report.html command: ["run", "/LoadTests/scriptHttp.js", ] volumes: - ./tests/LoadTests:/LoadTests From 53c8b05822aaa285a653af857ae914e955b61769 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 06:54:43 -0300 Subject: [PATCH 106/161] 53 refactor: replace ActivitySource with Activities helper class --- .../Common/Constants/DefaultConfigurations.cs | 2 -- .../src/Application/Common/Helpers/Activities.cs | 10 ++++++++++ .../Common/UseCases/BaseInOutUseCase.cs | 3 +-- .../Application/Common/UseCases/BaseInUseCase.cs | 4 +--- .../Application/Common/UseCases/BaseOutUseCase.cs | 4 +--- .../Application/Common/UseCases/BaseUseCase.cs | 5 ----- .../Cache/Services/HybridCacheService.cs | 15 +++++++++------ .../Infrastructure/Data/Common/BaseRepository.cs | 5 ++--- .../Messaging/Consumers/BaseConsumer.cs | 10 +++++----- .../Messaging/Producers/ProducerService.cs | 10 ++-------- 10 files changed, 31 insertions(+), 37 deletions(-) create mode 100644 templates/Full/src/Application/Common/Helpers/Activities.cs diff --git a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs b/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs index 17abb91d..e5085778 100644 --- a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs +++ b/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Diagnostics.Metrics; namespace Application.Common.Constants; @@ -7,6 +6,5 @@ public static class DefaultConfigurations { public static string ApplicationName => "Hexagonal.Solution.Template"; public static string Version => typeof(DefaultConfigurations).Assembly.GetName().Version!.ToString(); - public static ActivitySource ActivitySource => new(ApplicationName, Version); public static readonly Meter Meter = new("Application"); } diff --git a/templates/Full/src/Application/Common/Helpers/Activities.cs b/templates/Full/src/Application/Common/Helpers/Activities.cs new file mode 100644 index 00000000..d6f66cca --- /dev/null +++ b/templates/Full/src/Application/Common/Helpers/Activities.cs @@ -0,0 +1,10 @@ +using System.Diagnostics; +using Application.Common.Constants; + +namespace Application.Common.Helpers; + +public static class Activities +{ + public static ActivitySource ActivitySource => new(DefaultConfigurations.ApplicationName, DefaultConfigurations.Version); + public static Activity StartActivity(string name) => ActivitySource.StartActivity(name) ?? new Activity(name); +} \ No newline at end of file diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index ae188c9d..f7b70935 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -43,7 +43,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = Activities.StartActivity($"{ClassName}.{HandleMethodName}")!; Logs.StartingOperation(Logger, request.CorrelationId); TResponseData response; @@ -72,7 +72,6 @@ CancellationToken cancellationToken Logs.FinishedOperation(Logger, request.CorrelationId); _useCaseExecuted.Record(1); - activity?.SetTag("correlationId", request.CorrelationId); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 84d9902e..4acfbcff 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -39,7 +39,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = Activities.StartActivity($"{ClassName}.{HandleMethodName}")!; Logs.StartingOperation(Logger, request.CorrelationId); @@ -60,8 +60,6 @@ CancellationToken cancellationToken Logs.FinishedOperation(Logger, request.CorrelationId); _useCaseExecuted.Record(1); - - activity?.SetTag("correlationId", request.CorrelationId); } public abstract Task HandleInternalAsync(TRequest request, CancellationToken cancellationToken); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index b9b6ea8c..f93b58c1 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -30,7 +30,7 @@ protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvide public async Task HandleAsync(CancellationToken cancellationToken) { - using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = Activities.StartActivity($"{ClassName}.{HandleMethodName}")!; var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, correlationId); @@ -41,8 +41,6 @@ public async Task HandleAsync(CancellationToken cancellationToken _useCaseExecuted.Record(1); - activity?.SetTag("correlationId", correlationId); - return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index 9d704b3b..09dc8992 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,5 +1,3 @@ -using System.Diagnostics; -using Application.Common.Constants; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -10,7 +8,6 @@ public abstract class BaseUseCase protected IServiceProvider ServiceProvider { get; } protected ILogger Logger { get; } protected string ClassName { get; set; } - protected ActivitySource ActivitySource { get; } protected BaseUseCase(IServiceProvider serviceProvider) { @@ -20,7 +17,5 @@ protected BaseUseCase(IServiceProvider serviceProvider) ServiceProvider = serviceProvider; Logger = serviceProvider.GetRequiredService().CreateLogger(classType); - - ActivitySource = DefaultConfigurations.ActivitySource; } } \ No newline at end of file diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 1f2e0e02..655b1970 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Services; @@ -12,8 +11,6 @@ internal sealed class HybridCacheService(HybridCache cache, ILogger _logger = logger; private readonly string _className = nameof(HybridCacheService); - private readonly ActivitySource _activitySource = DefaultConfigurations.ActivitySource; - public async ValueTask GetOrCreateAsync( Guid correlationId, string key, @@ -21,35 +18,41 @@ public async ValueTask GetOrCreateAsync( CancellationToken cancellationToken ) { - using var activity = _activitySource.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); + using var activity = Activities.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); Logs.DebugStartingOperation(_logger, correlationId, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); Logs.DebugFinishedOperation(_logger, correlationId, $"Cache hit: {result != null} for key: {key}"); + activity?.SetTag("key", key); + return result; } public async ValueTask CreateAsync(Guid correlationId, string key, TResult value, CancellationToken cancellationToken) { - using var activity = _activitySource.StartActivity($"{_className}.{nameof(CreateAsync)}"); + using var activity = Activities.StartActivity($"{_className}.{nameof(CreateAsync)}"); Logs.DebugStartingOperation(_logger, correlationId, key); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); Logs.DebugFinishedOperation(_logger, correlationId, $"Cached hit: {value != null} for key: {key}"); + + activity?.SetTag("key", key); } public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) { - using var activity = _activitySource.StartActivity($"{_className}.{nameof(DeleteAsync)}"); + using var activity = Activities.StartActivity($"{_className}.{nameof(DeleteAsync)}"); Logs.DebugStartingOperation(_logger, correlationId, key); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); Logs.DebugFinishedOperation(_logger, correlationId, $"Cache entry removed for key: {key}"); + + activity?.SetTag("key", key); } } diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 9f48fb3d..a36ca2d5 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -1,6 +1,5 @@ using System.Linq.Expressions; using System.Runtime.CompilerServices; -using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Repositories; using Domain.Common; @@ -26,7 +25,7 @@ private async Task HandleBaseQueryAsync( string methodName = null! ) where TEntity : DomainEntity { - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{methodName}")!; + using var activity = Activities.StartActivity($"{_className}.{methodName}"); Logs.DebugStartingOperation(logger, correlationId); @@ -51,7 +50,7 @@ public IQueryable GetQueryable( string methodName = null! ) where TEntity : DomainEntity { - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{_className}.{nameof(GetQueryable)}")!; + using var activity = Activities.StartActivity($"{_className}.{nameof(GetQueryable)}"); Logs.DebugStartingOperation(logger, correlationId); diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index 4a227f2b..bffd2210 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -1,6 +1,5 @@ -using System.Diagnostics; using System.Text.Json; -using Application.Common.Constants; +using Application.Common.Helpers; using Application.Common.Messages; using Application.Common.Services; using Infrastructure.Common; @@ -22,8 +21,6 @@ internal abstract class BaseConsumer : BaseBackgroundServic private readonly IDictionary _arguments; private readonly ConnectionFactory _factory; protected IProduceService producerService = null!; - private readonly ActivitySource _activitySource = DefaultConfigurations.ActivitySource; - public BaseConsumer( ILogger> logger, @@ -96,7 +93,7 @@ private async Task HandleRabbitMqAsync( CancellationToken cancellationToken ) { - using var activity = _activitySource.StartActivity($"{_consumerName}.{nameof(HandleRabbitMqAsync)}"); + using var activity = Activities.StartActivity($"{_consumerName}.{nameof(HandleRabbitMqAsync)}"); var connection = await _factory.CreateConnectionAsync(cancellationToken); var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); @@ -169,6 +166,9 @@ await channel.BasicConsumeAsync( consumer: consumer, cancellationToken: cancellationToken ); + + activity?.SetTag("queueName", _queueName); + activity?.SetTag("consumerName", _consumerName); } protected abstract Task HandleUseCaseAsync(IServiceProvider serviceProvider, TMessage message, CancellationToken cancellationToken); diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 234c9bcc..0b933288 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -5,7 +5,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using RabbitMQ.Client; -using Application.Common.Constants; namespace Infrastructure.Messaging.Producers; @@ -37,7 +36,7 @@ public async Task HandleAsync( { await Task.Yield(); - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}")!; + using var activity = Activities.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}"); using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); @@ -52,8 +51,6 @@ await channel.BasicPublishAsync( ); Logs.DebugFinishedOperation(_logger, message.CorrelationId, typeof(TMessage).Name + " published."); - - activity?.SetTag("correlationId", message.CorrelationId); } public async Task HandleAsync( @@ -65,7 +62,7 @@ public async Task HandleAsync( { await Task.Yield(); - using var activity = DefaultConfigurations.ActivitySource.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}")!; + using var activity = Activities.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}"); Logs.Debug(_logger, messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); @@ -84,11 +81,8 @@ await channel.BasicPublishAsync( ); Logs.DebugFinishedOperation(_logger, message.CorrelationId, typeof(TMessage).Name + " batch published."); - activity?.SetTag("correlationId", message.CorrelationId); } Logs.Debug(_logger, messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing finished."); - - activity?.SetTag("correlationId", messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty); } } From 74809e89fad898c3ffda8d760273d96410aabc0a Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 06:55:22 -0300 Subject: [PATCH 107/161] 53 refactor: change ActivitySource to private in Activities helper --- templates/Full/src/Application/Common/Helpers/Activities.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Full/src/Application/Common/Helpers/Activities.cs b/templates/Full/src/Application/Common/Helpers/Activities.cs index d6f66cca..4e7e73b0 100644 --- a/templates/Full/src/Application/Common/Helpers/Activities.cs +++ b/templates/Full/src/Application/Common/Helpers/Activities.cs @@ -5,6 +5,6 @@ namespace Application.Common.Helpers; public static class Activities { - public static ActivitySource ActivitySource => new(DefaultConfigurations.ApplicationName, DefaultConfigurations.Version); - public static Activity StartActivity(string name) => ActivitySource.StartActivity(name) ?? new Activity(name); + private static ActivitySource _activitySource => new(DefaultConfigurations.ApplicationName, DefaultConfigurations.Version); + public static Activity StartActivity(string name) => _activitySource.StartActivity(name) ?? new Activity(name); } \ No newline at end of file From 8aa098f6c2906b28e825b044695bc5606800b876 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 06:59:12 -0300 Subject: [PATCH 108/161] 53 refactor: use anchor for postgres image in docker-compose files --- templates/Full/docker-compose-load-tests.yml | 35 ++++++++++++++++++++ templates/Full/docker-compose.yml | 4 +-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 91c349ac..c6ae1b9a 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -84,6 +84,41 @@ services: file: docker-compose.yml service: rabbitmq + alloy: + extends: + file: docker-compose-grafana.yml + service: alloy + + prometheus: + extends: + file: docker-compose-grafana.yml + service: prometheus + + loki: + extends: + file: docker-compose-grafana.yml + service: loki + + grafana: + extends: + file: docker-compose-grafana.yml + service: grafana + + tempo-init: + extends: + file: docker-compose-grafana.yml + service: tempo-init + + memcached: + extends: + file: docker-compose-grafana.yml + service: memcached + + tempo: + extends: + file: docker-compose-grafana.yml + service: tempo + networks: hexagonal_solution_template_full_network: driver: bridge diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 4dcd1a65..da5d467f 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -2,7 +2,7 @@ name: hexagonal-solution-template-full services: postgres: - image: postgres:17-alpine + image: &postgresImage postgres:17-alpine container_name: postgres environment: POSTGRES_PASSWORD: "cY5VvZkkh4AzES" @@ -20,7 +20,7 @@ services: - "5432:5432" postgres-init: - image: postgres:17-alpine + image: *postgresImage container_name: postgres-init depends_on: postgres: From 3d02e26cb3a6956ef564b896b31eb82411d27836 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 07:18:55 -0300 Subject: [PATCH 109/161] 53 refactor: update dependencies for alloy and adjust otel endpoints --- templates/Full/docker-compose-grafana.yml | 2 ++ templates/Full/docker-compose-load-tests.yml | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index 31cd474e..bf3a3393 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -12,6 +12,8 @@ services: command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy depends_on: - loki + - prometheus + - tempo prometheus: image: prom/prometheus:v3.1.0 diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index c6ae1b9a..6f5c02e3 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -48,10 +48,10 @@ services: - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning - ENABLE_SENSITIVE_DATA_LOGGING=false - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp - - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://alloy:4317 - - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://alloy:4317 - - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://alloy:4317 - - OTEL_EXPORTER_OTLP_PROTOCOL=grpc + - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://alloy:4318 + - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://alloy:4318 + - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://alloy:4318 + - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - ConnectionStrings__OrderDb=Host=postgres;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES - ConnectionStrings__Redis=redis:6379 From 73ae77b3904c4f853f3598e121561f9c60afe9bc Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 07:32:23 -0300 Subject: [PATCH 110/161] 53 refactor: simplify OpenTelemetry exporter configuration --- ...ructureOpenTelemetryDependencyInjection.cs | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 9ebd4459..444f4d75 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using OpenTelemetry.Exporter; using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -17,26 +16,13 @@ internal static class InfrastructureOpenTelemetryDependencyInjection public WebApplicationBuilder AddOpenTelemetry() { var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "grpc" - ? OtlpExportProtocol.Grpc - : OtlpExportProtocol.HttpProtobuf; - var exporterMetricsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"); - var exporterTracesEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"); - var exporterLogsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"); - - if ( - string.Equals(environment, "IntegrationTests", StringComparison.OrdinalIgnoreCase) || - string.IsNullOrWhiteSpace(exporterLogsEndpoint) || - string.IsNullOrWhiteSpace(exporterMetricsEndpoint) || - string.IsNullOrWhiteSpace(exporterTracesEndpoint) - ) - { + if (string.Equals(environment, "IntegrationTests", StringComparison.OrdinalIgnoreCase)) return builder; - } var serviceName = DefaultConfigurations.ApplicationName; var serviceVersion = DefaultConfigurations.Version; - var resourceBuilder = ResourceBuilder.CreateDefault() + var resourceBuilder = ResourceBuilder + .CreateDefault() .AddService(serviceName, serviceVersion: serviceVersion); builder.Services.AddOpenTelemetry() @@ -54,11 +40,7 @@ public WebApplicationBuilder AddOpenTelemetry() .AddRuntimeInstrumentation() .AddProcessInstrumentation() .AddPrometheusExporter() - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterMetricsEndpoint); - }) + .AddOtlpExporter() ) .WithTracing(tracing => tracing .AddSource(serviceName) @@ -69,11 +51,7 @@ public WebApplicationBuilder AddOpenTelemetry() .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddEntityFrameworkCoreInstrumentation() - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterTracesEndpoint); - }) + .AddOtlpExporter() ); builder.Logging.AddOpenTelemetry(options => @@ -84,11 +62,7 @@ public WebApplicationBuilder AddOpenTelemetry() options .SetResourceBuilder(resourceBuilder) .AttachLogsToActivityEvent() - .AddOtlpExporter(exporterOptions => - { - exporterOptions.Protocol = exporterProtocol; - exporterOptions.Endpoint = new Uri(exporterLogsEndpoint!); - }); + .AddOtlpExporter(); }); return builder; From 043a4783497a86bd08533b846fb7cbfb9edbc375 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 07:35:01 -0300 Subject: [PATCH 111/161] 53 refactor: update grafana provisioning paths and remove unused file --- templates/Full/docker-compose-grafana.yml | 4 ++-- templates/Full/grafana/datasources/datasources.yaml | 0 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 templates/Full/grafana/datasources/datasources.yaml diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index bf3a3393..cf56bf72 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -41,7 +41,7 @@ services: ports: - 3000:3000/tcp volumes: - - ./grafana:/etc/grafana/provisioning + - ./scripts/grafana:/etc/grafana/provisioning - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml # Tempo @@ -55,7 +55,7 @@ services: - "10001:10001" - "/var/tempo" volumes: - - ./tempo-data:/var/tempo + - ./scripts/grafana/tempo-data:/var/tempo memcached: image: memcached:1.6.29 diff --git a/templates/Full/grafana/datasources/datasources.yaml b/templates/Full/grafana/datasources/datasources.yaml deleted file mode 100644 index e69de29b..00000000 From 7387b6b99116537be051ac240a71592b3f9ddcfe Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 07:35:42 -0300 Subject: [PATCH 112/161] 53 refactor: remove tempo initialization comments from docker-compose --- templates/Full/docker-compose-grafana.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index cf56bf72..7539e303 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -44,7 +44,6 @@ services: - ./scripts/grafana:/etc/grafana/provisioning - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml -# Tempo # Tempo runs as user 10001, and docker compose creates the volume as root. # As such, we need to chown the volume in order for Tempo to start correctly. tempo-init: From cc06a7861203eaab3636f2f59a9219dcecb7aebe Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 17:06:16 -0300 Subject: [PATCH 113/161] 53 refactor: replace Activities with ActivitySource for tracing --- .../Common/Constants/DefaultConfigurations.cs | 2 ++ .../Full/src/Application/Common/Helpers/Activities.cs | 10 ---------- .../Application/Common/UseCases/BaseInOutUseCase.cs | 2 +- .../src/Application/Common/UseCases/BaseInUseCase.cs | 2 +- .../src/Application/Common/UseCases/BaseOutUseCase.cs | 2 +- .../src/Application/Common/UseCases/BaseUseCase.cs | 3 +++ .../Cache/Services/HybridCacheService.cs | 8 +++++--- .../src/Infrastructure/Data/Common/BaseRepository.cs | 11 ++++++----- .../Messaging/Consumers/BaseConsumer.cs | 6 ++++-- .../Messaging/Producers/ProducerService.cs | 7 +++++-- 10 files changed, 28 insertions(+), 25 deletions(-) delete mode 100644 templates/Full/src/Application/Common/Helpers/Activities.cs diff --git a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs b/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs index e5085778..64a96b43 100644 --- a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs +++ b/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Diagnostics.Metrics; namespace Application.Common.Constants; @@ -7,4 +8,5 @@ public static class DefaultConfigurations public static string ApplicationName => "Hexagonal.Solution.Template"; public static string Version => typeof(DefaultConfigurations).Assembly.GetName().Version!.ToString(); public static readonly Meter Meter = new("Application"); + public static readonly ActivitySource ActivitySource = new(ApplicationName, Version); } diff --git a/templates/Full/src/Application/Common/Helpers/Activities.cs b/templates/Full/src/Application/Common/Helpers/Activities.cs deleted file mode 100644 index 4e7e73b0..00000000 --- a/templates/Full/src/Application/Common/Helpers/Activities.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Diagnostics; -using Application.Common.Constants; - -namespace Application.Common.Helpers; - -public static class Activities -{ - private static ActivitySource _activitySource => new(DefaultConfigurations.ApplicationName, DefaultConfigurations.Version); - public static Activity StartActivity(string name) => _activitySource.StartActivity(name) ?? new Activity(name); -} \ No newline at end of file diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index f7b70935..116f09e0 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -43,7 +43,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = Activities.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = ActivitySource.StartActivity(); Logs.StartingOperation(Logger, request.CorrelationId); TResponseData response; diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 4acfbcff..b18d8dd1 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -39,7 +39,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = Activities.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = ActivitySource.StartActivity(); Logs.StartingOperation(Logger, request.CorrelationId); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index f93b58c1..ae9406d1 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -30,7 +30,7 @@ protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvide public async Task HandleAsync(CancellationToken cancellationToken) { - using var activity = Activities.StartActivity($"{ClassName}.{HandleMethodName}")!; + using var activity = ActivitySource.StartActivity(); var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, correlationId); diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index 09dc8992..7d22b1f5 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; +using Application.Common.Constants; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -8,6 +10,7 @@ public abstract class BaseUseCase protected IServiceProvider ServiceProvider { get; } protected ILogger Logger { get; } protected string ClassName { get; set; } + protected ActivitySource ActivitySource { get; set; } = DefaultConfigurations.ActivitySource; protected BaseUseCase(IServiceProvider serviceProvider) { diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index 655b1970..af5114e9 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Services; @@ -11,6 +12,7 @@ internal sealed class HybridCacheService(HybridCache cache, ILogger _logger = logger; private readonly string _className = nameof(HybridCacheService); + private readonly ActivitySource _activities = DefaultConfigurations.ActivitySource; public async ValueTask GetOrCreateAsync( Guid correlationId, string key, @@ -18,7 +20,7 @@ public async ValueTask GetOrCreateAsync( CancellationToken cancellationToken ) { - using var activity = Activities.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); + using var activity = _activities.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); Logs.DebugStartingOperation(_logger, correlationId, key); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); @@ -32,7 +34,7 @@ CancellationToken cancellationToken public async ValueTask CreateAsync(Guid correlationId, string key, TResult value, CancellationToken cancellationToken) { - using var activity = Activities.StartActivity($"{_className}.{nameof(CreateAsync)}"); + using var activity = _activities.StartActivity($"{_className}.{nameof(CreateAsync)}"); Logs.DebugStartingOperation(_logger, correlationId, key); @@ -45,7 +47,7 @@ public async ValueTask CreateAsync(Guid correlationId, string key, TRes public async ValueTask DeleteAsync(Guid correlationId, string key, CancellationToken cancellationToken) { - using var activity = Activities.StartActivity($"{_className}.{nameof(DeleteAsync)}"); + using var activity = _activities.StartActivity($"{_className}.{nameof(DeleteAsync)}"); Logs.DebugStartingOperation(_logger, correlationId, key); diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index a36ca2d5..22b9b55e 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -1,5 +1,7 @@ -using System.Linq.Expressions; +using System.Diagnostics; +using System.Linq.Expressions; using System.Runtime.CompilerServices; +using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Repositories; using Domain.Common; @@ -16,6 +18,7 @@ IDbContextFactory dbContextFactory private readonly IDbContextFactory _dbContextFactory = dbContextFactory; private readonly MyDbContext _dbContext = dbContextFactory.CreateDbContext(); private readonly string _className = nameof(BaseRepository); + private readonly ActivitySource _activities = DefaultConfigurations.ActivitySource; private async Task HandleBaseQueryAsync( Func, Task> query, @@ -25,7 +28,7 @@ private async Task HandleBaseQueryAsync( string methodName = null! ) where TEntity : DomainEntity { - using var activity = Activities.StartActivity($"{_className}.{methodName}"); + using var activity = _activities.StartActivity($"{_className}.{methodName}.{typeof(TEntity).Name}"); Logs.DebugStartingOperation(logger, correlationId); @@ -50,7 +53,7 @@ public IQueryable GetQueryable( string methodName = null! ) where TEntity : DomainEntity { - using var activity = Activities.StartActivity($"{_className}.{nameof(GetQueryable)}"); + using var activity = _activities.StartActivity($"{_className}.{nameof(GetQueryable)}"); Logs.DebugStartingOperation(logger, correlationId); @@ -60,8 +63,6 @@ public IQueryable GetQueryable( Logs.DebugFinishedOperation(logger, correlationId); - activity?.SetTag("correlationId", correlationId); - return dbSet; } diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index bffd2210..ee1604e4 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -1,5 +1,6 @@ +using System.Diagnostics; using System.Text.Json; -using Application.Common.Helpers; +using Application.Common.Constants; using Application.Common.Messages; using Application.Common.Services; using Infrastructure.Common; @@ -21,6 +22,7 @@ internal abstract class BaseConsumer : BaseBackgroundServic private readonly IDictionary _arguments; private readonly ConnectionFactory _factory; protected IProduceService producerService = null!; + private readonly ActivitySource _activities = DefaultConfigurations.ActivitySource; public BaseConsumer( ILogger> logger, @@ -93,7 +95,7 @@ private async Task HandleRabbitMqAsync( CancellationToken cancellationToken ) { - using var activity = Activities.StartActivity($"{_consumerName}.{nameof(HandleRabbitMqAsync)}"); + using var activity = _activities.StartActivity($"{_consumerName}.{nameof(HandleRabbitMqAsync)}"); var connection = await _factory.CreateConnectionAsync(cancellationToken); var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 0b933288..896088ac 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -5,6 +5,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using RabbitMQ.Client; +using Application.Common.Constants; +using System.Diagnostics; namespace Infrastructure.Messaging.Producers; @@ -12,6 +14,7 @@ public sealed class ProducerService : IProduceService { private readonly ILogger _logger; private readonly ConnectionFactory _factory; + private readonly ActivitySource _activities = DefaultConfigurations.ActivitySource; public ProducerService(ILogger logger, IConfiguration configuration) { @@ -36,7 +39,7 @@ public async Task HandleAsync( { await Task.Yield(); - using var activity = Activities.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}"); + using var activity = _activities.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}.{typeof(TMessage).Name}"); using var connection = await _factory.CreateConnectionAsync(cancellationToken); using var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); @@ -62,7 +65,7 @@ public async Task HandleAsync( { await Task.Yield(); - using var activity = Activities.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}"); + using var activity = _activities.StartActivity($"{nameof(ProducerService)}.{nameof(HandleAsync)}.{typeof(TMessage).Name}.Batch"); Logs.Debug(_logger, messages.FirstOrDefault()?.CorrelationId ?? Guid.Empty, typeof(TMessage).Name + " batch publishing started."); From 12bbd5cfe5b3f64bd8a3cebf4c8bcb2fd59e83e5 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 17:15:05 -0300 Subject: [PATCH 114/161] 53 refactor: simplify service provider usage in use cases --- .../Common/UseCases/BaseInOutUseCase.cs | 30 +++++-------------- .../Common/UseCases/BaseInUseCase.cs | 26 +++++----------- .../Common/UseCases/BaseOutUseCase.cs | 22 ++++---------- .../Common/UseCases/BaseUseCase.cs | 13 ++++++-- .../Application/Orders/CreateOrderUseCase.cs | 6 +++- 5 files changed, 36 insertions(+), 61 deletions(-) diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 116f09e0..4fc43cb4 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -1,6 +1,4 @@ -using System.Diagnostics.Metrics; -using Application.Common.Constants; -using Application.Common.Helpers; +using Application.Common.Helpers; using Application.Common.Repositories; using Application.Common.Requests; using Application.Common.Services; @@ -16,28 +14,16 @@ public interface IBaseInOutUseCase Task HandleAsync(TRequest request, CancellationToken cancellationToken); } -public abstract class BaseInOutUseCase : BaseUseCase, IBaseInOutUseCase +public abstract class BaseInOutUseCase(IServiceProvider serviceProvider) : BaseUseCase(serviceProvider), IBaseInOutUseCase where TRequest : BaseRequest where TResponseData : BaseResponse { - protected IHybridCacheService Cache { get; } - protected IProduceService ProduceService { get; } - protected IBaseRepository Repository { get; } - private readonly IValidator _validator; - private readonly Histogram _useCaseExecuted; + protected IHybridCacheService Cache { get; } = serviceProvider.GetRequiredService(); + protected IProduceService ProduceService { get; } = serviceProvider.GetRequiredService(); + protected IBaseRepository Repository { get; } = serviceProvider.GetRequiredService(); + private readonly IValidator _validator = serviceProvider.GetRequiredService>(); protected const string HandleMethodName = nameof(HandleAsync); - protected BaseInOutUseCase(IServiceProvider serviceProvider) : base(serviceProvider) - { - Cache = serviceProvider.GetRequiredService(); - ProduceService = serviceProvider.GetRequiredService(); - Repository = serviceProvider.GetRequiredService(); - _validator = serviceProvider.GetRequiredService>(); - - _useCaseExecuted = DefaultConfigurations.Meter - .CreateHistogram($"{ClassName}.Executed", "total", "Number of times the use case was executed"); - } - public async Task HandleAsync( TRequest request, CancellationToken cancellationToken @@ -62,7 +48,7 @@ CancellationToken cancellationToken Success = false, Message = errors }; - + UseCaseFailedMetric.Add(1); return response!; } } @@ -71,7 +57,7 @@ CancellationToken cancellationToken Logs.FinishedOperation(Logger, request.CorrelationId); - _useCaseExecuted.Record(1); + UseCaseExecutedMetric.Add(1); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index b18d8dd1..8ff223d7 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -14,26 +14,14 @@ public interface IBaseInUseCase where TRequest : BaseRequest Task HandleAsync(TRequest request, CancellationToken cancellationToken); } -public abstract class BaseInUseCase : BaseUseCase, IBaseInUseCase where TRequest : BaseRequest +public abstract class BaseInUseCase(IServiceProvider serviceProvider) : BaseUseCase(serviceProvider), IBaseInUseCase where TRequest : BaseRequest { - protected IHybridCacheService Cache { get; } - protected IProduceService ProduceService { get; } - protected IBaseRepository Repository { get; } - private readonly IValidator _validator; - private readonly Histogram _useCaseExecuted; + protected IHybridCacheService Cache { get; } = serviceProvider.GetRequiredService(); + protected IProduceService ProduceService { get; } = serviceProvider.GetRequiredService(); + protected IBaseRepository Repository { get; } = serviceProvider.GetRequiredService(); + private readonly IValidator _validator = serviceProvider.GetRequiredService>(); protected const string HandleMethodName = nameof(HandleAsync); - protected BaseInUseCase(IServiceProvider serviceProvider) : base(serviceProvider) - { - Cache = serviceProvider.GetRequiredService(); - ProduceService = serviceProvider.GetRequiredService(); - Repository = serviceProvider.GetRequiredService(); - _validator = serviceProvider.GetRequiredService>(); - - _useCaseExecuted = DefaultConfigurations.Meter - .CreateHistogram($"{ClassName}.Executed", "total", "Number of times the use case was executed"); - } - public async Task HandleAsync( TRequest request, CancellationToken cancellationToken @@ -50,7 +38,7 @@ CancellationToken cancellationToken { var errors = string.Join(", ", validationResult.Errors); Logs.ValidationErrors(Logger, request.CorrelationId, errors); - + UseCaseFailedMetric.Add(1); return; } } @@ -59,7 +47,7 @@ CancellationToken cancellationToken Logs.FinishedOperation(Logger, request.CorrelationId); - _useCaseExecuted.Record(1); + UseCaseExecutedMetric.Add(1); } public abstract Task HandleInternalAsync(TRequest request, CancellationToken cancellationToken); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index ae9406d1..f40b3f7f 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -1,6 +1,4 @@ -using System.Diagnostics.Metrics; -using Application.Common.Constants; -using Application.Common.Requests; +using Application.Common.Requests; using Application.Common.Services; using Microsoft.Extensions.DependencyInjection; using Application.Common.Helpers; @@ -12,22 +10,12 @@ public interface IBaseOutUseCase where TResponseData : BaseRespon Task HandleAsync(CancellationToken cancellationToken); } -public abstract class BaseOutUseCase : BaseUseCase, IBaseOutUseCase where TResponseData : BaseResponse +public abstract class BaseOutUseCase(IServiceProvider serviceProvider) : BaseUseCase(serviceProvider), IBaseOutUseCase where TResponseData : BaseResponse { - protected IHybridCacheService Cache { get; } - protected IProduceService ProduceService { get; } - private readonly Histogram _useCaseExecuted; + protected IHybridCacheService Cache { get; } = serviceProvider.GetRequiredService(); + protected IProduceService ProduceService { get; } = serviceProvider.GetRequiredService(); protected const string HandleMethodName = nameof(HandleAsync); - protected BaseOutUseCase(IServiceProvider serviceProvider) : base(serviceProvider) - { - Cache = serviceProvider.GetRequiredService(); - ProduceService = serviceProvider.GetRequiredService(); - - _useCaseExecuted = DefaultConfigurations.Meter - .CreateHistogram($"{ClassName}.Executed", "total", "Number of times the use case was executed"); - } - public async Task HandleAsync(CancellationToken cancellationToken) { using var activity = ActivitySource.StartActivity(); @@ -39,7 +27,7 @@ public async Task HandleAsync(CancellationToken cancellationToken Logs.FinishedOperation(Logger, correlationId); - _useCaseExecuted.Record(1); + UseCaseExecutedMetric.Add(1); return response; } diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index 7d22b1f5..c1deb482 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Diagnostics.Metrics; using Application.Common.Constants; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -9,8 +10,10 @@ public abstract class BaseUseCase { protected IServiceProvider ServiceProvider { get; } protected ILogger Logger { get; } - protected string ClassName { get; set; } - protected ActivitySource ActivitySource { get; set; } = DefaultConfigurations.ActivitySource; + protected string ClassName { get; } + protected ActivitySource ActivitySource { get; } = DefaultConfigurations.ActivitySource; + protected Counter UseCaseExecutedMetric { get; } + protected Counter UseCaseFailedMetric { get; } protected BaseUseCase(IServiceProvider serviceProvider) { @@ -20,5 +23,11 @@ protected BaseUseCase(IServiceProvider serviceProvider) ServiceProvider = serviceProvider; Logger = serviceProvider.GetRequiredService().CreateLogger(classType); + + UseCaseExecutedMetric = DefaultConfigurations.Meter + .CreateCounter($"{ClassName}.Executed", "total", "Number of times the use case was executed"); + + UseCaseFailedMetric = DefaultConfigurations.Meter + .CreateCounter($"{ClassName}.Failed", "total", "Number of times the use case execution failed"); } } \ No newline at end of file diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 21c54b54..0392324f 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -46,7 +46,7 @@ public override async Task> HandleInternalAsync( CancellationToken cancellationToken ) { - Guid correlationId = request.CorrelationId; + var correlationId = request.CorrelationId; BaseResponse response; var items = request.Items @@ -67,6 +67,8 @@ CancellationToken cancellationToken CreateNotification(correlationId, "Failed", response); + UseCaseFailedMetric.Add(1); + return response; } @@ -79,6 +81,8 @@ CancellationToken cancellationToken CreateNotification(correlationId, "Failed", response); + UseCaseFailedMetric.Add(1); + return response; } From 1c347980dd6f9d0529c1d3c2303b32ad07dc52a7 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 12 Mar 2026 17:43:25 -0300 Subject: [PATCH 115/161] 53 refactor: enhance activity tracing with method names --- .../Full/src/Application/Common/UseCases/BaseInOutUseCase.cs | 2 +- .../Full/src/Application/Common/UseCases/BaseInUseCase.cs | 4 +--- .../Full/src/Application/Common/UseCases/BaseOutUseCase.cs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 4fc43cb4..c6c01c47 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -29,7 +29,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = ActivitySource.StartActivity(); + using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}"); Logs.StartingOperation(Logger, request.CorrelationId); TResponseData response; diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 8ff223d7..81aa29cf 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -2,8 +2,6 @@ using FluentValidation; using Microsoft.Extensions.DependencyInjection; using Application.Common.Services; -using Application.Common.Constants; -using System.Diagnostics.Metrics; using Application.Common.Repositories; using Application.Common.Helpers; @@ -27,7 +25,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = ActivitySource.StartActivity(); + using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}"); Logs.StartingOperation(Logger, request.CorrelationId); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index f40b3f7f..9f446bcf 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -18,7 +18,7 @@ public abstract class BaseOutUseCase(IServiceProvider serviceProv public async Task HandleAsync(CancellationToken cancellationToken) { - using var activity = ActivitySource.StartActivity(); + using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}"); var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, correlationId); From 5e482c4893e8d22a420f8aeecebf1f43c071662c Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Mar 2026 06:57:33 -0300 Subject: [PATCH 116/161] 53 refactor: remove unused DefaultApplicationMessages and update imports --- .../Full/scripts/grafana/datasources/datasources.yaml | 0 .../Common/Constants/DefaultApplicationMessages.cs | 8 -------- .../src/Application/Common/UseCases/BaseUseCase.cs | 2 +- .../Common}/DefaultConfigurations.cs | 2 +- templates/Full/src/Domain/Common/DomainEntity.cs | 11 +++++++++-- templates/Full/src/Domain/Orders/Order.cs | 4 ++++ .../Cache/Services/HybridCacheService.cs | 2 +- .../src/Infrastructure/Data/Common/BaseRepository.cs | 1 - .../Messaging/Consumers/BaseConsumer.cs | 2 +- .../Messaging/Producers/ProducerService.cs | 2 +- .../InfrastructureOpenTelemetryDependencyInjection.cs | 2 +- 11 files changed, 19 insertions(+), 17 deletions(-) create mode 100644 templates/Full/scripts/grafana/datasources/datasources.yaml delete mode 100644 templates/Full/src/Application/Common/Constants/DefaultApplicationMessages.cs rename templates/Full/src/{Application/Common/Constants => Domain/Common}/DefaultConfigurations.cs (91%) diff --git a/templates/Full/scripts/grafana/datasources/datasources.yaml b/templates/Full/scripts/grafana/datasources/datasources.yaml new file mode 100644 index 00000000..e69de29b diff --git a/templates/Full/src/Application/Common/Constants/DefaultApplicationMessages.cs b/templates/Full/src/Application/Common/Constants/DefaultApplicationMessages.cs deleted file mode 100644 index 8c72be68..00000000 --- a/templates/Full/src/Application/Common/Constants/DefaultApplicationMessages.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Application.Common.Constants; - -public static class DefaultApplicationMessages -{ - public const string StartToExecuteUseCase = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Start to execute use case"; - public const string FinishedExecutingUseCase = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Elapsed time: {ElapsedMilliseconds} ms | Finished executing use case"; - public const string ValidationErrors = "[{ClassName}] | [{MethodName}] | [{CorrelationId}] | Validation errors: [{errors}]"; -} diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index c1deb482..9642f99a 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,6 +1,6 @@ using System.Diagnostics; using System.Diagnostics.Metrics; -using Application.Common.Constants; +using Domain.Common; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs b/templates/Full/src/Domain/Common/DefaultConfigurations.cs similarity index 91% rename from templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs rename to templates/Full/src/Domain/Common/DefaultConfigurations.cs index 64a96b43..ff9aaac1 100644 --- a/templates/Full/src/Application/Common/Constants/DefaultConfigurations.cs +++ b/templates/Full/src/Domain/Common/DefaultConfigurations.cs @@ -1,7 +1,7 @@ using System.Diagnostics; using System.Diagnostics.Metrics; -namespace Application.Common.Constants; +namespace Domain.Common; public static class DefaultConfigurations { diff --git a/templates/Full/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs index 4c3f55e2..675be8ff 100644 --- a/templates/Full/src/Domain/Common/DomainEntity.cs +++ b/templates/Full/src/Domain/Common/DomainEntity.cs @@ -1,4 +1,6 @@ -namespace Domain.Common; +using System.Diagnostics; + +namespace Domain.Common; public abstract class DomainEntity { @@ -12,7 +14,7 @@ protected DomainEntity(string user, string? timezoneId = null) UpdatedBy = CreatedBy; UpdatedByTimezoneId = CreatedByTimezoneId; } - + protected virtual ActivitySource ActivitySource { get; } = DefaultConfigurations.ActivitySource; public int Id { get; init; } public DateTime CreatedAt { get; init; } public string? CreatedBy { get; init; } @@ -23,8 +25,13 @@ protected DomainEntity(string user, string? timezoneId = null) public virtual void Update(string? user = null, string? timezoneId = null) { + using var activity = ActivitySource.StartActivity($"{GetType().Name}.{nameof(Update)}"); + UpdatedAt = DateTime.UtcNow; UpdatedBy = user ?? "System"; UpdatedByTimezoneId = TimeZoneInfo.FindSystemTimeZoneById(string.IsNullOrWhiteSpace(timezoneId) ? TimeZoneInfo.Utc.Id : timezoneId).Id; + + activity?.SetTag(nameof(UpdatedBy), UpdatedBy); + activity?.SetTag(nameof(UpdatedByTimezoneId), UpdatedByTimezoneId); } } diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index c1dea298..d2b52624 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -23,12 +23,16 @@ public Order( public Result SetTotal(string user = "System", string? timezoneId = null) { + using var activity = ActivitySource.StartActivity($"{GetType().Name}.{nameof(SetTotal)}"); + if (Items == null || Items.Count == 0) return Result.Fail("Order must have at least one item."); Total = Items.Sum(item => item.Value); Update(user, timezoneId); + activity?.SetTag(nameof(Total), Total); + return Result.Ok(); } } diff --git a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs index af5114e9..9bfa2ce9 100644 --- a/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs +++ b/templates/Full/src/Infrastructure/Cache/Services/HybridCacheService.cs @@ -1,7 +1,7 @@ using System.Diagnostics; -using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Services; +using Domain.Common; using Microsoft.Extensions.Caching.Hybrid; using Microsoft.Extensions.Logging; diff --git a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs index 22b9b55e..32802ae5 100644 --- a/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs +++ b/templates/Full/src/Infrastructure/Data/Common/BaseRepository.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Linq.Expressions; using System.Runtime.CompilerServices; -using Application.Common.Constants; using Application.Common.Helpers; using Application.Common.Repositories; using Domain.Common; diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index ee1604e4..0f0f4007 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -1,8 +1,8 @@ using System.Diagnostics; using System.Text.Json; -using Application.Common.Constants; using Application.Common.Messages; using Application.Common.Services; +using Domain.Common; using Infrastructure.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; diff --git a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs index 896088ac..dca50bb8 100644 --- a/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs +++ b/templates/Full/src/Infrastructure/Messaging/Producers/ProducerService.cs @@ -5,8 +5,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using RabbitMQ.Client; -using Application.Common.Constants; using System.Diagnostics; +using Domain.Common; namespace Infrastructure.Messaging.Producers; diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 444f4d75..5323095c 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -1,4 +1,4 @@ -using Application.Common.Constants; +using Domain.Common; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; From 53b0085586af95c156df70404b07dde370f378e0 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Mar 2026 07:02:53 -0300 Subject: [PATCH 117/161] 53 fix: correct total amount assertion in GetOrderGrpcTest --- .../IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs index cb90bec8..363e4920 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Grpc/Orders/GetOrderGrpcTest.cs @@ -45,7 +45,7 @@ public async Task GivenAValidRequestThenPass() Assert.Equal(1, response.Data.Id); Assert.NotNull(response.Data.Items); Assert.NotEmpty(response.Data.Items); - Assert.Equal(2000.0, response.Data.Total); + Assert.Equal(1000.0, response.Data.Total); } [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] From 9a8bb9b22f832cede95ae5263b5b49a7092b9640 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Mar 2026 07:19:48 -0300 Subject: [PATCH 118/161] 53 refactor: enhance logging messages and activity tracing --- .../Application/Notifications/CreateNotificationUseCase.cs | 2 +- templates/Full/src/Application/Orders/CreateOrderUseCase.cs | 2 +- templates/Full/src/Domain/Common/DefaultConfigurations.cs | 2 +- templates/Full/src/Domain/Notifications/Notification.cs | 5 +++++ templates/Full/src/Domain/Orders/Order.cs | 5 +++++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index efa09477..8f509cd1 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -41,6 +41,6 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(notification, request.CorrelationId, cancellationToken); if (addResult == 0) - Logs.FailedOperation(Logger, request.CorrelationId, "Failed to create notification"); + Logs.FailedOperation(Logger, request.CorrelationId, "Failed to create notification. No rows affected."); } } diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 0392324f..1db826da 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -75,7 +75,7 @@ CancellationToken cancellationToken var addResult = await Repository.AddAsync(newOrder, correlationId, cancellationToken); if (addResult == 0) { - Logs.FailedOperation(Logger, correlationId, "Failed to create order."); + Logs.FailedOperation(Logger, correlationId, "Failed to create order. No rows affected."); response = new(false, null, "Failed to create order."); diff --git a/templates/Full/src/Domain/Common/DefaultConfigurations.cs b/templates/Full/src/Domain/Common/DefaultConfigurations.cs index ff9aaac1..a20fa200 100644 --- a/templates/Full/src/Domain/Common/DefaultConfigurations.cs +++ b/templates/Full/src/Domain/Common/DefaultConfigurations.cs @@ -7,6 +7,6 @@ public static class DefaultConfigurations { public static string ApplicationName => "Hexagonal.Solution.Template"; public static string Version => typeof(DefaultConfigurations).Assembly.GetName().Version!.ToString(); - public static readonly Meter Meter = new("Application"); + public static readonly Meter Meter = new(ApplicationName, Version); public static readonly ActivitySource ActivitySource = new(ApplicationName, Version); } diff --git a/templates/Full/src/Domain/Notifications/Notification.cs b/templates/Full/src/Domain/Notifications/Notification.cs index ffbecbcf..43fccaf3 100644 --- a/templates/Full/src/Domain/Notifications/Notification.cs +++ b/templates/Full/src/Domain/Notifications/Notification.cs @@ -15,9 +15,14 @@ public Notification( string? timezoneId = null ) : base(createdBy ?? "System", timezoneId) { + using var activity = ActivitySource.StartActivity($"{GetType().Name}.Constructor"); + NotificationType = notificationType; NotificationStatus = notificationStatus; Message = message != null ? JsonSerializer.Serialize(message) : string.Empty; + + activity?.SetTag(nameof(NotificationType), NotificationType); + activity?.SetTag(nameof(NotificationStatus), NotificationStatus); } public string NotificationType { get; init; } public string NotificationStatus { get; init; } diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index d2b52624..1503ec0d 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -13,8 +13,13 @@ public Order( string? timezoneId = null ) : base(createdBy ?? "System", timezoneId) { + using var activity = ActivitySource.StartActivity($"{GetType().Name}.Constructor"); + Description = description; Items = items; + + activity?.SetTag(nameof(Description), Description); + activity?.SetTag(nameof(Items), Items.Count); } public string Description { get; private set; } From 4b19bf52b8a1d1be4db361faa7f8b043ff8fe3dc Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 13 Mar 2026 07:33:01 -0300 Subject: [PATCH 119/161] 53 fix: update otel exporter endpoints and protocol in load tests --- templates/Full/docker-compose-grafana.yml | 14 ++++++++++++++ templates/Full/docker-compose-load-tests.yml | 11 ++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index 7539e303..1e4b5bcc 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -14,6 +14,8 @@ services: - loki - prometheus - tempo + networks: + - hexagonal_solution_template_full_network prometheus: image: prom/prometheus:v3.1.0 @@ -22,6 +24,8 @@ services: volumes: - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage + networks: + - hexagonal_solution_template_full_network loki: image: grafana/loki:3.4.2 @@ -30,6 +34,8 @@ services: volumes: - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml command: -config.file=/etc/loki/local-config.yaml + networks: + - hexagonal_solution_template_full_network grafana: image: grafana/grafana:11.5.2 @@ -43,6 +49,8 @@ services: volumes: - ./scripts/grafana:/etc/grafana/provisioning - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + networks: + - hexagonal_solution_template_full_network # Tempo runs as user 10001, and docker compose creates the volume as root. # As such, we need to chown the volume in order for Tempo to start correctly. @@ -55,6 +63,8 @@ services: - "/var/tempo" volumes: - ./scripts/grafana/tempo-data:/var/tempo + networks: + - hexagonal_solution_template_full_network memcached: image: memcached:1.6.29 @@ -64,6 +74,8 @@ services: environment: - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage - MEMCACHED_THREADS=4 # Number of threads to use + networks: + - hexagonal_solution_template_full_network tempo: image: *tempoImage @@ -75,6 +87,8 @@ services: depends_on: - tempo-init - memcached + networks: + - hexagonal_solution_template_full_network networks: hexagonal_solution_template_full_network: diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 6f5c02e3..9d4e7df4 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -41,17 +41,14 @@ services: start_period: 15s environment: - ASPNETCORE_ENVIRONMENT=Development - - LOGGING__LOGLEVEL__DEFAULT=Warning - - LOGGING__LOGLEVEL__MICROSOFT=Warning - - LOGGING__LOGLEVEL__MICROSOFT_HOSTING_LIFETIME=Warning - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE=Warning - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning - ENABLE_SENSITIVE_DATA_LOGGING=false - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp - - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://alloy:4318 - - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://alloy:4318 - - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://alloy:4318 - - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf + - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - ConnectionStrings__OrderDb=Host=postgres;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES - ConnectionStrings__Redis=redis:6379 From 62b5949b3a9d191fa23db76db4804caba0412da0 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 14 Mar 2026 16:25:25 -0300 Subject: [PATCH 120/161] 53 feat: update docker-compose files and configurations for exporters --- templates/Full/docker-compose-grafana.yml | 47 +++++++++++++------ templates/Full/docker-compose-load-tests.yml | 34 +++++++++++--- templates/Full/docker-compose.yml | 18 ++----- templates/Full/scripts/grafana/config.alloy | 22 +++++++++ templates/Full/scripts/grafana/prometheus.yml | 26 ++++++++-- .../src/WebApp/Properties/launchSettings.json | 4 +- 6 files changed, 109 insertions(+), 42 deletions(-) diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index 1e4b5bcc..023ecfa9 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -9,13 +9,16 @@ services: - 4317:4317 volumes: - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + networks: + - default + - hexagonal-solution-template-full_default depends_on: - loki - prometheus - tempo - networks: - - hexagonal_solution_template_full_network prometheus: image: prom/prometheus:v3.1.0 @@ -24,8 +27,6 @@ services: volumes: - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage - networks: - - hexagonal_solution_template_full_network loki: image: grafana/loki:3.4.2 @@ -34,8 +35,6 @@ services: volumes: - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml command: -config.file=/etc/loki/local-config.yaml - networks: - - hexagonal_solution_template_full_network grafana: image: grafana/grafana:11.5.2 @@ -49,8 +48,6 @@ services: volumes: - ./scripts/grafana:/etc/grafana/provisioning - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml - networks: - - hexagonal_solution_template_full_network # Tempo runs as user 10001, and docker compose creates the volume as root. # As such, we need to chown the volume in order for Tempo to start correctly. @@ -63,8 +60,6 @@ services: - "/var/tempo" volumes: - ./scripts/grafana/tempo-data:/var/tempo - networks: - - hexagonal_solution_template_full_network memcached: image: memcached:1.6.29 @@ -74,8 +69,6 @@ services: environment: - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage - MEMCACHED_THREADS=4 # Number of threads to use - networks: - - hexagonal_solution_template_full_network tempo: image: *tempoImage @@ -87,9 +80,33 @@ services: depends_on: - tempo-init - memcached + + postgres-exporter: + image: prometheuscommunity/postgres-exporter:v0.16.0 + container_name: postgres-exporter + environment: + DATA_SOURCE_NAME: "postgresql://postgres:cY5VvZkkh4AzES@postgres:5432/OrderDb?sslmode=disable" + ports: + - "9187:9187" networks: - - hexagonal_solution_template_full_network + - default + - hexagonal-solution-template-full_default + depends_on: + - prometheus + + redis-exporter: + image: oliver006/redis_exporter:v1.66.0 + container_name: redis-exporter + environment: + REDIS_ADDR: "redis:6379" + ports: + - "9121:9121" + networks: + - default + - hexagonal-solution-template-full_default + depends_on: + - prometheus networks: - hexagonal_solution_template_full_network: - driver: bridge \ No newline at end of file + hexagonal-solution-template-full_default: + external: true \ No newline at end of file diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 9d4e7df4..3d835e2b 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -36,19 +36,17 @@ services: healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000/health" ] interval: 30s - timeout: 10s + timeout: 15s retries: 5 - start_period: 15s + start_period: 30s environment: - ASPNETCORE_ENVIRONMENT=Development - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE=Warning - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning - ENABLE_SENSITIVE_DATA_LOGGING=false - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp - - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://alloy:4317 - - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://alloy:4317 - - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://alloy:4317 - - OTEL_EXPORTER_OTLP_PROTOCOL=grpc + - OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4318 + - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - ConnectionStrings__OrderDb=Host=postgres;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES - ConnectionStrings__Redis=redis:6379 @@ -60,61 +58,85 @@ services: extends: file: docker-compose.yml service: postgres + networks: + - hexagonal_solution_template_full_network postgres-init: extends: file: docker-compose.yml service: postgres-init + networks: + - hexagonal_solution_template_full_network pgadmin: extends: file: docker-compose.yml service: pgadmin + networks: + - hexagonal_solution_template_full_network redis: extends: file: docker-compose.yml service: redis + networks: + - hexagonal_solution_template_full_network rabbitmq: extends: file: docker-compose.yml service: rabbitmq + networks: + - hexagonal_solution_template_full_network alloy: extends: file: docker-compose-grafana.yml service: alloy + networks: + - hexagonal_solution_template_full_network prometheus: extends: file: docker-compose-grafana.yml service: prometheus + networks: + - hexagonal_solution_template_full_network loki: extends: file: docker-compose-grafana.yml service: loki + networks: + - hexagonal_solution_template_full_network grafana: extends: file: docker-compose-grafana.yml service: grafana + networks: + - hexagonal_solution_template_full_network tempo-init: extends: file: docker-compose-grafana.yml service: tempo-init + networks: + - hexagonal_solution_template_full_network memcached: extends: file: docker-compose-grafana.yml service: memcached + networks: + - hexagonal_solution_template_full_network tempo: extends: file: docker-compose-grafana.yml service: tempo + networks: + - hexagonal_solution_template_full_network networks: hexagonal_solution_template_full_network: diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index da5d467f..a7901e52 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -8,8 +8,6 @@ services: POSTGRES_PASSWORD: "cY5VvZkkh4AzES" POSTGRES_USER: "postgres" POSTGRES_DB: "OrderDb" - networks: - - hexagonal_solution_template_full_network healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 3s @@ -25,8 +23,6 @@ services: depends_on: postgres: condition: service_healthy - networks: - - hexagonal_solution_template_full_network volumes: - ./scripts/sql/migrations.sql:/tmp/migrations.sql - ./scripts/sql/seeds.sql:/tmp/seeds.sql @@ -50,8 +46,6 @@ services: condition: service_healthy ports: - "5050:80" - networks: - - hexagonal_solution_template_full_network redis: image: redis:8 @@ -64,8 +58,6 @@ services: timeout: 10s retries: 5 start_period: 15s - networks: - - hexagonal_solution_template_full_network rabbitmq: image: rabbitmq:management @@ -73,18 +65,16 @@ services: ports: - "5672:5672" - "15672:15672" + - "15692:15692" environment: RABBITMQ_DEFAULT_USER: guest RABBITMQ_DEFAULT_PASS: guest + command: > + bash -c "rabbitmq-plugins enable rabbitmq_prometheus && + docker-entrypoint.sh rabbitmq-server" healthcheck: test: ["CMD", "rabbitmq-diagnostics", "ping"] interval: 3s timeout: 10s retries: 5 start_period: 15s - networks: - - hexagonal_solution_template_full_network - -networks: - hexagonal_solution_template_full_network: - driver: bridge diff --git a/templates/Full/scripts/grafana/config.alloy b/templates/Full/scripts/grafana/config.alloy index 6e7d7f58..5d796c59 100644 --- a/templates/Full/scripts/grafana/config.alloy +++ b/templates/Full/scripts/grafana/config.alloy @@ -33,6 +33,28 @@ otelcol.exporter.otlphttp "traces" { } } +// Docker log collection +discovery.docker "containers" { + host = "unix:///var/run/docker.sock" + filter { + name = "name" + values = ["postgres", "rabbitmq", "redis"] + } + refresh_interval = "5s" +} + +loki.source.docker "docker_logs" { + host = "unix:///var/run/docker.sock" + targets = discovery.docker.containers.targets + forward_to = [loki.write.default.receiver] +} + +loki.write "default" { + endpoint { + url = "http://loki:3100/loki/api/v1/push" + } +} + livedebugging { enabled = true } \ No newline at end of file diff --git a/templates/Full/scripts/grafana/prometheus.yml b/templates/Full/scripts/grafana/prometheus.yml index 45fad92e..495a910d 100644 --- a/templates/Full/scripts/grafana/prometheus.yml +++ b/templates/Full/scripts/grafana/prometheus.yml @@ -7,9 +7,27 @@ storage: out_of_order_time_window: 30m scrape_configs: - - job_name: apicontagem-http - metrics_path: /metrics - scheme: http + - job_name: postgres static_configs: - - targets: ['localhost:5101'] + - targets: ['postgres-exporter:9187'] + relabel_configs: + - source_labels: [__address__] + target_label: instance + replacement: postgres + + - job_name: redis + static_configs: + - targets: ['redis-exporter:9121'] + relabel_configs: + - source_labels: [__address__] + target_label: instance + replacement: redis + + - job_name: rabbitmq + static_configs: + - targets: ['rabbitmq:15692'] + relabel_configs: + - source_labels: [__address__] + target_label: instance + replacement: rabbitmq diff --git a/templates/Full/src/WebApp/Properties/launchSettings.json b/templates/Full/src/WebApp/Properties/launchSettings.json index 459ebd12..6b70726a 100644 --- a/templates/Full/src/WebApp/Properties/launchSettings.json +++ b/templates/Full/src/WebApp/Properties/launchSettings.json @@ -10,9 +10,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:4317", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:4317", - "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", "ENABLE_SENSITIVE_DATA_LOGGING": "true" From ce96118bdec711d55d614ac5a1968f8d7012da06 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sat, 14 Mar 2026 17:36:24 -0300 Subject: [PATCH 121/161] 53 refactor: remove unused docker services and configurations --- templates/Full/docker-compose-grafana.yml | 35 +------------------ templates/Full/scripts/grafana/config.alloy | 22 ------------ templates/Full/scripts/grafana/prometheus.yml | 26 -------------- 3 files changed, 1 insertion(+), 82 deletions(-) diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index 023ecfa9..c6a6a12c 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -12,9 +12,6 @@ services: - /var/lib/docker/containers:/var/lib/docker/containers:ro - /var/run/docker.sock:/var/run/docker.sock command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy - networks: - - default - - hexagonal-solution-template-full_default depends_on: - loki - prometheus @@ -79,34 +76,4 @@ services: - "3200:3200" # tempo depends_on: - tempo-init - - memcached - - postgres-exporter: - image: prometheuscommunity/postgres-exporter:v0.16.0 - container_name: postgres-exporter - environment: - DATA_SOURCE_NAME: "postgresql://postgres:cY5VvZkkh4AzES@postgres:5432/OrderDb?sslmode=disable" - ports: - - "9187:9187" - networks: - - default - - hexagonal-solution-template-full_default - depends_on: - - prometheus - - redis-exporter: - image: oliver006/redis_exporter:v1.66.0 - container_name: redis-exporter - environment: - REDIS_ADDR: "redis:6379" - ports: - - "9121:9121" - networks: - - default - - hexagonal-solution-template-full_default - depends_on: - - prometheus - -networks: - hexagonal-solution-template-full_default: - external: true \ No newline at end of file + - memcached \ No newline at end of file diff --git a/templates/Full/scripts/grafana/config.alloy b/templates/Full/scripts/grafana/config.alloy index 5d796c59..6e7d7f58 100644 --- a/templates/Full/scripts/grafana/config.alloy +++ b/templates/Full/scripts/grafana/config.alloy @@ -33,28 +33,6 @@ otelcol.exporter.otlphttp "traces" { } } -// Docker log collection -discovery.docker "containers" { - host = "unix:///var/run/docker.sock" - filter { - name = "name" - values = ["postgres", "rabbitmq", "redis"] - } - refresh_interval = "5s" -} - -loki.source.docker "docker_logs" { - host = "unix:///var/run/docker.sock" - targets = discovery.docker.containers.targets - forward_to = [loki.write.default.receiver] -} - -loki.write "default" { - endpoint { - url = "http://loki:3100/loki/api/v1/push" - } -} - livedebugging { enabled = true } \ No newline at end of file diff --git a/templates/Full/scripts/grafana/prometheus.yml b/templates/Full/scripts/grafana/prometheus.yml index 495a910d..e71c1eb3 100644 --- a/templates/Full/scripts/grafana/prometheus.yml +++ b/templates/Full/scripts/grafana/prometheus.yml @@ -5,29 +5,3 @@ global: storage: tsdb: out_of_order_time_window: 30m - -scrape_configs: - - job_name: postgres - static_configs: - - targets: ['postgres-exporter:9187'] - relabel_configs: - - source_labels: [__address__] - target_label: instance - replacement: postgres - - - job_name: redis - static_configs: - - targets: ['redis-exporter:9121'] - relabel_configs: - - source_labels: [__address__] - target_label: instance - replacement: redis - - - job_name: rabbitmq - static_configs: - - targets: ['rabbitmq:15692'] - relabel_configs: - - source_labels: [__address__] - target_label: instance - replacement: rabbitmq - From 1355155c529d7189257370f8e12964a84e04a3f2 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 15 Mar 2026 19:07:46 -0300 Subject: [PATCH 122/161] 53 feat: add metrics for consumer errors and duplicated messages --- .../Messaging/Consumers/BaseConsumer.cs | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index 0f0f4007..e69a4040 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Diagnostics.Metrics; using System.Text.Json; using Application.Common.Messages; using Application.Common.Services; @@ -23,6 +24,8 @@ internal abstract class BaseConsumer : BaseBackgroundServic private readonly ConnectionFactory _factory; protected IProduceService producerService = null!; private readonly ActivitySource _activities = DefaultConfigurations.ActivitySource; + protected Counter ConsumerErrorMetric { get; } + protected Counter ConsumerDuplicatedMessageMetric { get; } public BaseConsumer( ILogger> logger, @@ -42,19 +45,30 @@ public BaseConsumer( _queueName = queueName; _arguments = arguments; _factory = new() { Uri = new(connectionString) }; + + ConsumerErrorMetric = DefaultConfigurations.Meter + .CreateCounter($"{_consumerName}.Error", "total", "Number of times the consumer encountered an error"); + + ConsumerDuplicatedMessageMetric = DefaultConfigurations.Meter + .CreateCounter($"{_consumerName}.DuplicatedMessage", "total", "Number of times the consumer received a duplicated message"); } protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) => await HandleRabbitMqAsync( async (message, cancellationToken) => { + var messageType = nameof(TMessage); + using var activity = _activities.StartActivity($"{_consumerName}.{messageType}", ActivityKind.Consumer); + activity?.SetTag("correlationId", message.CorrelationId); + activity?.SetTag("consumerName", _consumerName); + activity?.SetTag("queueName", _queueName); + producerService = serviceProvider.GetRequiredService(); try { var hybridCacheService = serviceProvider.GetRequiredService(); - - Logs.Debug(logger, message.CorrelationId, typeof(TMessage).Name + " received. Checking if it has already been processed."); + Logs.Debug(logger, message.CorrelationId, messageType + " received. Checking if it has already been processed."); var isExecutedKey = _consumerName + "-" + message.CorrelationId; var isExecuted = await hybridCacheService.GetOrCreateAsync( @@ -66,17 +80,18 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi if (isExecuted) { - Logs.Warning(logger, message.CorrelationId, typeof(TMessage).Name + " has already been processed. Skipping."); + Logs.Warning(logger, message.CorrelationId, messageType + " has already been processed. Skipping."); + ConsumerDuplicatedMessageMetric.Add(1); return; } - Logs.DebugStartingOperation(logger, message.CorrelationId, typeof(TMessage).Name + " processing started."); + Logs.DebugStartingOperation(logger, message.CorrelationId, messageType + " processing started."); await HandleUseCaseAsync(serviceProvider, message, cancellationToken); await hybridCacheService.CreateAsync(message.CorrelationId, isExecutedKey, true, cancellationToken); - Logs.DebugFinishedOperation(logger, message.CorrelationId, typeof(TMessage).Name + " processing finished."); + Logs.DebugFinishedOperation(logger, message.CorrelationId, messageType + " processing finished."); } catch (Exception ex) { @@ -84,6 +99,8 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi _ = producerService.HandleAsync(message!, CancellationToken.None, _queueName + "_deadLetter"); + ConsumerErrorMetric.Add(1); + throw; } }, @@ -95,8 +112,6 @@ private async Task HandleRabbitMqAsync( CancellationToken cancellationToken ) { - using var activity = _activities.StartActivity($"{_consumerName}.{nameof(HandleRabbitMqAsync)}"); - var connection = await _factory.CreateConnectionAsync(cancellationToken); var channel = await connection.CreateChannelAsync(cancellationToken: cancellationToken); @@ -168,9 +183,6 @@ await channel.BasicConsumeAsync( consumer: consumer, cancellationToken: cancellationToken ); - - activity?.SetTag("queueName", _queueName); - activity?.SetTag("consumerName", _consumerName); } protected abstract Task HandleUseCaseAsync(IServiceProvider serviceProvider, TMessage message, CancellationToken cancellationToken); From 54f1f2adeba279414e4c7c158e4ed3baeda1be32 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 15 Mar 2026 19:09:45 -0300 Subject: [PATCH 123/161] 53 fix: handle message in dead letter queue on error --- .../src/Infrastructure/Messaging/Consumers/BaseConsumer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index e69a4040..b77a8667 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -97,10 +97,10 @@ protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvi { Logs.Error(logger, message.CorrelationId, ex.Message); - _ = producerService.HandleAsync(message!, CancellationToken.None, _queueName + "_deadLetter"); - ConsumerErrorMetric.Add(1); + _ = producerService.HandleAsync(message!, CancellationToken.None, _queueName + "_deadLetter"); + throw; } }, From 7ef52357970d9cc36883fd951c7eb860a019e6db Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 15 Mar 2026 19:23:24 -0300 Subject: [PATCH 124/161] 53 feat: add container names to docker-compose files for clarity --- templates/Full/docker-compose-grafana.yml | 8 +++++++- templates/Full/docker-compose-load-tests.yml | 2 ++ .../src/Application/Common/UseCases/BaseInOutUseCase.cs | 2 +- .../Full/src/Application/Common/UseCases/BaseInUseCase.cs | 2 +- .../src/Application/Common/UseCases/BaseOutUseCase.cs | 2 +- .../Infrastructure/Messaging/Consumers/BaseConsumer.cs | 5 ++--- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml index c6a6a12c..66065470 100644 --- a/templates/Full/docker-compose-grafana.yml +++ b/templates/Full/docker-compose-grafana.yml @@ -2,6 +2,7 @@ name: hexagonal-solution-template-grafana-full services: alloy: + container_name: alloy image: grafana/alloy:v1.7.5 ports: - 12345:12345 @@ -18,6 +19,7 @@ services: - tempo prometheus: + container_name: prometheus image: prom/prometheus:v3.1.0 ports: - 9090:9090 @@ -26,6 +28,7 @@ services: command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage loki: + container_name: loki image: grafana/loki:3.4.2 ports: - "3100:3100" @@ -34,6 +37,7 @@ services: command: -config.file=/etc/loki/local-config.yaml grafana: + container_name: grafana image: grafana/grafana:11.5.2 environment: - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin @@ -49,6 +53,7 @@ services: # Tempo runs as user 10001, and docker compose creates the volume as root. # As such, we need to chown the volume in order for Tempo to start correctly. tempo-init: + container_name: tempo-init image: &tempoImage grafana/tempo:2.7.1 user: root entrypoint: @@ -59,8 +64,8 @@ services: - ./scripts/grafana/tempo-data:/var/tempo memcached: + container_name: tempo-memcached image: memcached:1.6.29 - container_name: memcached ports: - "11211:11211" environment: @@ -68,6 +73,7 @@ services: - MEMCACHED_THREADS=4 # Number of threads to use tempo: + container_name: tempo image: *tempoImage command: [ "-config.file=/etc/tempo.yaml" ] volumes: diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 3d835e2b..0807f043 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -2,6 +2,7 @@ name: hexagonal-solution-template-full-load-tests services: k6: + container_name: k6 image: grafana/k6:1.4.2 depends_on: webapp: @@ -21,6 +22,7 @@ services: - hexagonal_solution_template_full_network webapp: + container_name: webapp build: context: . dockerfile: ./Dockerfile diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index c6c01c47..2db387db 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -29,7 +29,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}"); + using var activity = ActivitySource.StartActivity($"{ClassName}"); Logs.StartingOperation(Logger, request.CorrelationId); TResponseData response; diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index 81aa29cf..be3cdf85 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -25,7 +25,7 @@ public async Task HandleAsync( CancellationToken cancellationToken ) { - using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}"); + using var activity = ActivitySource.StartActivity($"{ClassName}"); Logs.StartingOperation(Logger, request.CorrelationId); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 9f446bcf..3787c9e4 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -18,7 +18,7 @@ public abstract class BaseOutUseCase(IServiceProvider serviceProv public async Task HandleAsync(CancellationToken cancellationToken) { - using var activity = ActivitySource.StartActivity($"{ClassName}.{HandleMethodName}"); + using var activity = ActivitySource.StartActivity($"{ClassName}"); var correlationId = Guid.NewGuid(); Logs.StartingOperation(Logger, correlationId); diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index b77a8667..e5a76516 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -56,10 +56,9 @@ public BaseConsumer( protected override async Task ExecuteInternalAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) => await HandleRabbitMqAsync( async (message, cancellationToken) => { - var messageType = nameof(TMessage); - using var activity = _activities.StartActivity($"{_consumerName}.{messageType}", ActivityKind.Consumer); + var messageType = typeof(TMessage).Name; + using var activity = _activities.StartActivity($"{_consumerName}", ActivityKind.Consumer); activity?.SetTag("correlationId", message.CorrelationId); - activity?.SetTag("consumerName", _consumerName); activity?.SetTag("queueName", _queueName); producerService = serviceProvider.GetRequiredService(); From 6690ff03842e3d90e16e608fe9b3d5878077d19b Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Sun, 15 Mar 2026 19:40:28 -0300 Subject: [PATCH 125/161] 53 fix: update otel exporter endpoint and protocol for load tests --- templates/Full/docker-compose-load-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 0807f043..4ed8b9b1 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -47,8 +47,8 @@ services: - LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND=Warning - ENABLE_SENSITIVE_DATA_LOGGING=false - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.WebApp - - OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4318 - - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf + - OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - ConnectionStrings__OrderDb=Host=postgres;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES - ConnectionStrings__Redis=redis:6379 From 306de22b342879179213b0603243022f0364db76 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 17 Mar 2026 06:36:07 -0300 Subject: [PATCH 126/161] chore(53): remove prometheus exporter from dependencies and configs --- templates/Full/Directory.Packages.props | 1 - .../Full/src/Infrastructure/Infrastructure.csproj | 1 - .../InfrastructureOpenTelemetryDependencyInjection.cs | 1 - templates/Full/src/Infrastructure/packages.lock.json | 9 --------- templates/Full/src/WebApp/packages.lock.json | 10 ---------- .../Full/tests/IntegrationTests/packages.lock.json | 10 ---------- 6 files changed, 32 deletions(-) diff --git a/templates/Full/Directory.Packages.props b/templates/Full/Directory.Packages.props index 9847919c..fded11fc 100644 --- a/templates/Full/Directory.Packages.props +++ b/templates/Full/Directory.Packages.props @@ -35,7 +35,6 @@ - diff --git a/templates/Full/src/Infrastructure/Infrastructure.csproj b/templates/Full/src/Infrastructure/Infrastructure.csproj index d6e2467c..a802f246 100644 --- a/templates/Full/src/Infrastructure/Infrastructure.csproj +++ b/templates/Full/src/Infrastructure/Infrastructure.csproj @@ -14,7 +14,6 @@ - diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 5323095c..8945d6d7 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -39,7 +39,6 @@ public WebApplicationBuilder AddOpenTelemetry() .AddHttpClientInstrumentation() .AddRuntimeInstrumentation() .AddProcessInstrumentation() - .AddPrometheusExporter() .AddOtlpExporter() ) .WithTracing(tracing => tracing diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json index e402a72a..fce12d85 100644 --- a/templates/Full/src/Infrastructure/packages.lock.json +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -75,15 +75,6 @@ "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Exporter.Prometheus.AspNetCore": { - "type": "Direct", - "requested": "[1.15.0-beta.1, )", - "resolved": "1.15.0-beta.1", - "contentHash": "015RkS1PLohpIRXQA9mhi+KLUSIz4kdZ683ur1IIJ1YuCT1sf0maXTKZ3zNy6JtBzBXCIUmzt8ug1lRK8qLQ0A==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, "OpenTelemetry.Extensions": { "type": "Direct", "requested": "[1.14.0-beta.1, )", diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json index 334b8df9..eb027aa7 100644 --- a/templates/Full/src/WebApp/packages.lock.json +++ b/templates/Full/src/WebApp/packages.lock.json @@ -180,7 +180,6 @@ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", - "OpenTelemetry.Exporter.Prometheus.AspNetCore": "[1.15.0-beta.1, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", @@ -272,15 +271,6 @@ "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Exporter.Prometheus.AspNetCore": { - "type": "CentralTransitive", - "requested": "[1.15.0-beta.1, )", - "resolved": "1.15.0-beta.1", - "contentHash": "015RkS1PLohpIRXQA9mhi+KLUSIz4kdZ683ur1IIJ1YuCT1sf0maXTKZ3zNy6JtBzBXCIUmzt8ug1lRK8qLQ0A==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, "OpenTelemetry.Extensions": { "type": "CentralTransitive", "requested": "[1.14.0-beta.1, )", diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json index e006a40c..5c723b91 100644 --- a/templates/Full/tests/IntegrationTests/packages.lock.json +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -604,7 +604,6 @@ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", - "OpenTelemetry.Exporter.Prometheus.AspNetCore": "[1.15.0-beta.1, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", @@ -807,15 +806,6 @@ "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Exporter.Prometheus.AspNetCore": { - "type": "CentralTransitive", - "requested": "[1.15.0-beta.1, )", - "resolved": "1.15.0-beta.1", - "contentHash": "015RkS1PLohpIRXQA9mhi+KLUSIz4kdZ683ur1IIJ1YuCT1sf0maXTKZ3zNy6JtBzBXCIUmzt8ug1lRK8qLQ0A==", - "dependencies": { - "OpenTelemetry": "[1.15.0, 2.0.0)" - } - }, "OpenTelemetry.Extensions": { "type": "CentralTransitive", "requested": "[1.14.0-beta.1, )", From 004aafadbe98d900f48cfb686cf673925586ff49 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 17 Mar 2026 06:57:12 -0300 Subject: [PATCH 127/161] chore: remove health check extensions and integrate OpenTelemetry --- templates/Bff/Directory.Packages.props | 17 ++- templates/Bff/docker-compose-grafana.yml | 85 +++++++++++ templates/Bff/docker-compose-load-tests.yml | 65 +++++++-- templates/Bff/docker-compose.yml | 21 +-- .../Common/DefaultConfigurations.cs | 6 + .../src/Infrastructure/Infrastructure.csproj | 9 +- .../InfrastructureDependencyInjection.cs | 99 ++++++------- .../Bff/src/Infrastructure/packages.lock.json | 114 +++++++++------ .../Extensions/HealthCheckExtensions.cs | 35 ----- templates/Bff/src/MockApi/MockApi.csproj | 9 ++ templates/Bff/src/MockApi/Program.cs | 63 ++++++++- .../MockApi/Properties/launchSettings.json | 6 +- templates/Bff/src/MockApi/packages.lock.json | 102 ++++++++++++++ .../src/WebApp/Properties/launchSettings.json | 10 +- .../src/WebApp/appsettings.Development.json | 7 + templates/Bff/src/WebApp/appsettings.json | 7 + templates/Bff/src/WebApp/packages.lock.json | 125 +++++++++------- .../tests/IntegrationTests/packages.lock.json | 133 +++++++++++------- 18 files changed, 622 insertions(+), 291 deletions(-) create mode 100644 templates/Bff/docker-compose-grafana.yml delete mode 100644 templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs diff --git a/templates/Bff/Directory.Packages.props b/templates/Bff/Directory.Packages.props index 1bc13279..265a0b3d 100644 --- a/templates/Bff/Directory.Packages.props +++ b/templates/Bff/Directory.Packages.props @@ -26,13 +26,16 @@ - - - - - - - + + + + + + + + + + diff --git a/templates/Bff/docker-compose-grafana.yml b/templates/Bff/docker-compose-grafana.yml new file mode 100644 index 00000000..662d6dc2 --- /dev/null +++ b/templates/Bff/docker-compose-grafana.yml @@ -0,0 +1,85 @@ +# Sample from: https://github.com/grafana/adventure/blob/main/docker-compose.yml +name: hexagonal-solution-template-grafana-bff +services: + alloy: + container_name: alloy + image: grafana/alloy:v1.7.5 + ports: + - 12345:12345 + - 4318:4318 + - 4317:4317 + volumes: + - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + depends_on: + - loki + - prometheus + - tempo + + prometheus: + container_name: prometheus + image: prom/prometheus:v3.1.0 + ports: + - 9090:9090 + volumes: + - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml + command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage + + loki: + container_name: loki + image: grafana/loki:3.4.2 + ports: + - "3100:3100" + volumes: + - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml + command: -config.file=/etc/loki/local-config.yaml + + grafana: + container_name: grafana + image: grafana/grafana:11.5.2 + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall + ports: + - 3000:3000/tcp + volumes: + - ./scripts/grafana:/etc/grafana/provisioning + - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + + # Tempo runs as user 10001, and docker compose creates the volume as root. + # As such, we need to chown the volume in order for Tempo to start correctly. + tempo-init: + container_name: tempo-init + image: &tempoImage grafana/tempo:2.7.1 + user: root + entrypoint: + - "chown" + - "10001:10001" + - "/var/tempo" + volumes: + - ./scripts/grafana/tempo-data:/var/tempo + + memcached: + container_name: tempo-memcached + image: memcached:1.6.29 + ports: + - "11211:11211" + environment: + - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage + - MEMCACHED_THREADS=4 # Number of threads to use + + tempo: + container_name: tempo + image: *tempoImage + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml + ports: + - "3200:3200" # tempo + depends_on: + - tempo-init + - memcached \ No newline at end of file diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml index 0eaf5988..f54e45a0 100644 --- a/templates/Bff/docker-compose-load-tests.yml +++ b/templates/Bff/docker-compose-load-tests.yml @@ -34,13 +34,8 @@ services: start_period: 15s environment: - ASPNETCORE_ENVIRONMENT=Development - - LOGGING__LOGLEVEL__DEFAULT=Warning - - LOGGING__LOGLEVEL__MICROSOFT=Warning - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.Bff - - OTEL_EXPORTER_OTLP_ENDPOINT=http://aspire-dashboard:18889 - - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://aspire-dashboard:18889 - - OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://aspire-dashboard:18889 - - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://aspire-dashboard:18889 + - OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4317 - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests networks: @@ -49,18 +44,62 @@ services: mock-api: extends: file: docker-compose.yml - service: mockApi - - aspire-dashboard: - extends: - file: docker-compose.yml - service: aspire-dashboard + service: mock-api redis: extends: file: docker-compose.yml service: redis + alloy: + extends: + file: docker-compose-grafana.yml + service: alloy + networks: + - hexagonal_solution_template_bff_network + + prometheus: + extends: + file: docker-compose-grafana.yml + service: prometheus + networks: + - hexagonal_solution_template_bff_network + + loki: + extends: + file: docker-compose-grafana.yml + service: loki + networks: + - hexagonal_solution_template_bff_network + + grafana: + extends: + file: docker-compose-grafana.yml + service: grafana + networks: + - hexagonal_solution_template_bff_network + + tempo-init: + extends: + file: docker-compose-grafana.yml + service: tempo-init + networks: + - hexagonal_solution_template_bff_network + + memcached: + extends: + file: docker-compose-grafana.yml + service: memcached + networks: + - hexagonal_solution_template_bff_network + + tempo: + extends: + file: docker-compose-grafana.yml + service: tempo + networks: + - hexagonal_solution_template_bff_network + networks: - hexagonal_solution_template_network: + hexagonal_solution_template_bff_network: driver: bridge diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index 69607e62..c4a5c586 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -1,14 +1,6 @@ name: hexagonal-solution-template-bff services: - aspire-dashboard: - image: mcr.microsoft.com/dotnet/aspire-dashboard:13 - ports: - - "18888:18888" - - "18889:18889" - environment: - - DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS=true - mock-api: build: context: . @@ -21,8 +13,11 @@ services: timeout: 10s retries: 5 start_period: 15s - networks: - - hexagonal_solution_template_network + environment: + - ASPNETCORE_ENVIRONMENT=Development + - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.Bff.MockApi + - OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_PROTOCOL=grpc redis: image: redis:8 @@ -34,9 +29,3 @@ services: timeout: 10s retries: 5 start_period: 15s - networks: - - hexagonal_solution_template_network - -networks: - hexagonal_solution_template_network: - driver: bridge diff --git a/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs b/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs index 4dee5d30..6cef19f9 100644 --- a/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs +++ b/templates/Bff/src/Infrastructure/Common/DefaultConfigurations.cs @@ -1,6 +1,12 @@ +using System.Diagnostics; +using System.Diagnostics.Metrics; + namespace Infrastructure.Common; public static class DefaultConfigurations { public static string ApplicationName => "Hexagonal.Solution.Template.Bff"; + public static string Version => typeof(DefaultConfigurations).Assembly.GetName().Version!.ToString(); + public static readonly Meter Meter = new(ApplicationName, Version); + public static readonly ActivitySource ActivitySource = new(ApplicationName, Version); } diff --git a/templates/Bff/src/Infrastructure/Infrastructure.csproj b/templates/Bff/src/Infrastructure/Infrastructure.csproj index 0c798058..4a4db540 100644 --- a/templates/Bff/src/Infrastructure/Infrastructure.csproj +++ b/templates/Bff/src/Infrastructure/Infrastructure.csproj @@ -3,13 +3,16 @@ + + - + + + + - - diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index 686e42c5..c37b9e08 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Exporter; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; @@ -11,7 +10,6 @@ using Infrastructure.Http; using Polly; using Polly.Extensions.Http; -using System.Globalization; using Infrastructure.Grpc; using Infrastructure.Common; using GrpcPayment; @@ -44,68 +42,51 @@ public WebApplicationBuilder AddInfrastructure() internal WebApplicationBuilder AddOpenTelemetry() { var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - var exporterProtocol = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL")?.ToLower(CultureInfo.CurrentCulture) == "grpc" - ? OtlpExportProtocol.Grpc - : OtlpExportProtocol.HttpProtobuf; - var exporterMetricsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"); - var exporterTracesEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"); - var exporterLogsEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"); - - if ( - string.Equals(environment, "IntegrationTests", StringComparison.OrdinalIgnoreCase) || - string.IsNullOrWhiteSpace(exporterLogsEndpoint) || - string.IsNullOrWhiteSpace(exporterMetricsEndpoint) || - string.IsNullOrWhiteSpace(exporterTracesEndpoint) - ) - { + if (string.Equals(environment, "IntegrationTests", StringComparison.OrdinalIgnoreCase)) return builder; - } + + var serviceName = DefaultConfigurations.ApplicationName; + var serviceVersion = DefaultConfigurations.Version; + var resourceBuilder = ResourceBuilder + .CreateDefault() + .AddService(serviceName, serviceVersion: serviceVersion); builder.Services.AddOpenTelemetry() - .ConfigureResource(resource => resource.AddEnvironmentVariableDetector()) - .WithMetrics(metrics => metrics - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterMetricsEndpoint); - }) - ) - .WithTracing(tracing => tracing - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation(options => - { - options.RecordException = true; - }) - .AddEntityFrameworkCoreInstrumentation( - options => - { - options.SetDbStatementForText = true; - options.SetDbStatementForStoredProcedure = true; - } + .WithMetrics(metrics => metrics + .AddMeter( + DefaultConfigurations.Meter.Name, + "System.Diagnostics.Metrics", + "Microsoft.AspNetCore.Hosting", + "Microsoft.AspNetCore.Server.Kestrel", + "System.Net.Http" + ) + .SetResourceBuilder(resourceBuilder) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddProcessInstrumentation() + .AddOtlpExporter() ) - .AddRedisInstrumentation() - .AddGrpcClientInstrumentation() - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterTracesEndpoint!); - }) - ) - .WithLogging(logging => logging - .AddOtlpExporter(options => - { - options.Protocol = exporterProtocol; - options.Endpoint = new Uri(exporterLogsEndpoint!); - }) - ); - - builder.Services.AddLogging(logging => logging.AddOpenTelemetry(openTelemetryLoggerOptions => + .WithTracing(tracing => tracing + .AddSource(serviceName) + .SetResourceBuilder(resourceBuilder) + .AddRedisInstrumentation() + .AddGrpcClientInstrumentation() + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddOtlpExporter() + ); + + builder.Logging.AddOpenTelemetry(options => { - openTelemetryLoggerOptions.IncludeScopes = true; - openTelemetryLoggerOptions.IncludeFormattedMessage = true; - })); + options.IncludeFormattedMessage = true; + options.IncludeScopes = true; + options.ParseStateValues = true; + options + .SetResourceBuilder(resourceBuilder) + .AttachLogsToActivityEvent() + .AddOtlpExporter(); + }); return builder; } diff --git a/templates/Bff/src/Infrastructure/packages.lock.json b/templates/Bff/src/Infrastructure/packages.lock.json index 11a45ea5..26abfedf 100644 --- a/templates/Bff/src/Infrastructure/packages.lock.json +++ b/templates/Bff/src/Infrastructure/packages.lock.json @@ -37,74 +37,100 @@ "Polly.Extensions.Http": "3.0.0" } }, + "OpenTelemetry.Exporter.Console": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "Jweov3Q70xmy5U8bwab8xd+xAuaFBI4695q/IpH4/dcAwKytyB+WhV5HufmKfXiKZhRbSEo8piG+i1ENEmdFXw==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Extensions.Hosting": { + "OpenTelemetry.Extensions": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "requested": "[1.14.0-beta.1, )", + "resolved": "1.14.0-beta.1", + "contentHash": "4DxYvBgz3OaCD8DE2lutiMiaEZh/PvWW05ewhAeqS6lKAETultAzfCveDXxMcIkaVwc93PR/Z22/P5zZ2rmHZA==", "dependencies": { - "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "[1.14.0, 2.0.0)" } }, - "OpenTelemetry.Instrumentation.AspNetCore": { + "OpenTelemetry.Extensions.Hosting": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "RixjKyB1pbYGhWdvPto4KJs+exdQknJsnjUO9WszdLles5Vcd0EYzxPNJdwmLjYfP+Jfbr4B5nktM4ZgeHSWtg==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", + "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "OpenTelemetry.Instrumentation.AspNetCore": { "type": "Direct", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.GrpcNetClient": { "type": "Direct", - "requested": "[1.12.0-beta.1, )", - "resolved": "1.12.0-beta.1", - "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "SBas5+C4kGUqoy8OPpQis+QIgJ7/aaJl4H3oLzHCJnZLCb8TXZmQL2/r753RXXJUH8oIeLIzdW+EXgujSy+cpQ==", "dependencies": { - "OpenTelemetry": "[1.12.0, 2.0.0)" + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.Http": { "type": "Direct", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "uToc7bUp8IEdb0ny9mKsL6FrrYelINPzxxiSShJgOf4XmQc4Azww6S5RjRj24YhsOn2a1MABOrxfVTZXtDk4Eg==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.0", "Microsoft.Extensions.Options": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Process": { + "type": "Direct", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "99zSvpwsMpKextd3RkHGY8iRsmw1qg3PjqkjC5hZI0fZG6m+wPsssrX6z9RhwDGcZ8sdGNjoKyLpNOmR154zQg==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Runtime": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "OOvpqR/j2Pb6+tWhHNODIbSJ53Or/MDtTiXEyrsWI02K2lLAgvBFcxUOrHggS/8015cYR3AdSaXv6NZrkz5yQA==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.StackExchangeRedis": { "type": "Direct", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "Igg/3MlBZZ9lZCTzMcvoFKav263+zOcKx9s4LVIdq96YmBHCuPmDiyygAIPdeIVzwN08VwD3RG1nXHDuRF1Ssg==", "dependencies": { - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", - "StackExchange.Redis": "[2.6.122, 3.0.0)" + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)", + "StackExchange.Redis": "2.6.122" } }, "Google.Protobuf": { @@ -319,26 +345,26 @@ }, "OpenTelemetry": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "resolved": "1.15.0", + "contentHash": "7mS/oZFF8S6xyqGQfMU1btp0nXJQUPWV535Vp/XMLYwRAUv36xQN+U4vufWBF1+z4HnRTOwuFHtUSGnHbyN6FQ==", "dependencies": { "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", "Microsoft.Extensions.Logging.Configuration": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.0" } }, "OpenTelemetry.Api": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + "resolved": "1.15.0", + "contentHash": "vk5OGdf6K9kQScCWo3bRjhDWCv6Pqw92IpX4dlARZ8B1WL7/2NGTDtCkkw42eQf7UdwyoHKzVvMH/PtL8d6z7w==" }, "OpenTelemetry.Api.ProviderBuilderExtensions": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "resolved": "1.15.0", + "contentHash": "OnuSUlRpGvowkOzGFQfy+KZFu0cITfKfh2IYJJiZskxVJiOuexwOOuvfDAgpJdmTzVWAHjYdz2shcHZaJ06UjQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", - "OpenTelemetry.Api": "1.14.0" + "OpenTelemetry.Api": "1.15.0" } }, "Pipelines.Sockets.Unofficial": { diff --git a/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs b/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs deleted file mode 100644 index fc9ecd97..00000000 --- a/templates/Bff/src/MockApi/Extensions/HealthCheckExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace MockApi.Extensions; - -internal static class HealthCheckExtensions -{ - public static IServiceCollection AddCustomHealthChecks(this IServiceCollection services) - { - services - .AddHealthChecks() - .AddCheck("self", () => HealthCheckResult.Healthy()); - - return services; - } - - public static IApplicationBuilder UseCustomHealthChecks( - this IApplicationBuilder app - ) - { - app.UseHealthChecks("/health", new HealthCheckOptions - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - - app.UseHealthChecks("/live", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); - - return app; - } -} \ No newline at end of file diff --git a/templates/Bff/src/MockApi/MockApi.csproj b/templates/Bff/src/MockApi/MockApi.csproj index ded60fc5..95f8b3af 100644 --- a/templates/Bff/src/MockApi/MockApi.csproj +++ b/templates/Bff/src/MockApi/MockApi.csproj @@ -2,6 +2,15 @@ + + + + + + + + + diff --git a/templates/Bff/src/MockApi/Program.cs b/templates/Bff/src/MockApi/Program.cs index 21da2232..a27f368f 100644 --- a/templates/Bff/src/MockApi/Program.cs +++ b/templates/Bff/src/MockApi/Program.cs @@ -1,8 +1,14 @@ -using Microsoft.AspNetCore.ResponseCompression; +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.Diagnostics.HealthChecks; using MockApi.Endpoints; -using MockApi.Extensions; using MockApi.GrpcServices; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; var builder = WebApplication.CreateBuilder(args); @@ -15,19 +21,66 @@ options.Providers.Add(); } ); -builder.Services.AddCustomHealthChecks(); +builder.Services + .AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy()); builder.WebHost.ConfigureKestrel(options => options .ConfigureEndpointDefaults(listenOptions => listenOptions.Protocols = HttpProtocols.Http2 )); +var serviceName = "Hexagonal.Solution.Template.Bff.MockApi"; +var serviceVersion = typeof(Program).Assembly.GetName().Version!.ToString(); +var resourceBuilder = ResourceBuilder + .CreateDefault() + .AddService(serviceName, serviceVersion: serviceVersion); + +builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => metrics + .SetResourceBuilder(resourceBuilder) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddProcessInstrumentation() + .AddOtlpExporter() + ) + .WithTracing(tracing => tracing + .AddSource(serviceName) + .SetResourceBuilder(resourceBuilder) + .AddGrpcClientInstrumentation() + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddOtlpExporter() + ); + +builder.Logging.AddOpenTelemetry(options => +{ + options.IncludeFormattedMessage = true; + options.IncludeScopes = true; + options.ParseStateValues = true; + options + .SetResourceBuilder(resourceBuilder) + .AttachLogsToActivityEvent() + .AddOtlpExporter(); +}); + var app = builder.Build(); app.UseHttpsRedirection(); app.MapEndpoints() .MapGrpcServices() - .UseResponseCompression() - .UseCustomHealthChecks(); + .UseResponseCompression(); + +app.UseHealthChecks("/health", new HealthCheckOptions +{ + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse +}); + +app.UseHealthChecks("/live", new HealthCheckOptions +{ + Predicate = r => r.Name.Contains("self") +}); await app.RunAsync(); diff --git a/templates/Bff/src/MockApi/Properties/launchSettings.json b/templates/Bff/src/MockApi/Properties/launchSettings.json index a4821660..483c40c3 100644 --- a/templates/Bff/src/MockApi/Properties/launchSettings.json +++ b/templates/Bff/src/MockApi/Properties/launchSettings.json @@ -7,7 +7,11 @@ "launchBrowser": false, "applicationUrl": "https://*:7177;http://*:5012", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "Development", + "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.Bff.MockApi", + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", + "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", + "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", } } } diff --git a/templates/Bff/src/MockApi/packages.lock.json b/templates/Bff/src/MockApi/packages.lock.json index 6885a8ae..c7a021de 100644 --- a/templates/Bff/src/MockApi/packages.lock.json +++ b/templates/Bff/src/MockApi/packages.lock.json @@ -20,6 +20,87 @@ "Fare": "[2.1.1, 3.0.0)" } }, + "OpenTelemetry.Exporter.Console": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "Jweov3Q70xmy5U8bwab8xd+xAuaFBI4695q/IpH4/dcAwKytyB+WhV5HufmKfXiKZhRbSEo8piG+i1ENEmdFXw==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Exporter.OpenTelemetryProtocol": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Extensions": { + "type": "Direct", + "requested": "[1.14.0-beta.1, )", + "resolved": "1.14.0-beta.1", + "contentHash": "4DxYvBgz3OaCD8DE2lutiMiaEZh/PvWW05ewhAeqS6lKAETultAzfCveDXxMcIkaVwc93PR/Z22/P5zZ2rmHZA==", + "dependencies": { + "OpenTelemetry": "[1.14.0, 2.0.0)" + } + }, + "OpenTelemetry.Extensions.Hosting": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "RixjKyB1pbYGhWdvPto4KJs+exdQknJsnjUO9WszdLles5Vcd0EYzxPNJdwmLjYfP+Jfbr4B5nktM4ZgeHSWtg==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, + "OpenTelemetry.Instrumentation.AspNetCore": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.GrpcNetClient": { + "type": "Direct", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "SBas5+C4kGUqoy8OPpQis+QIgJ7/aaJl4H3oLzHCJnZLCb8TXZmQL2/r753RXXJUH8oIeLIzdW+EXgujSy+cpQ==", + "dependencies": { + "OpenTelemetry": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Http": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "uToc7bUp8IEdb0ny9mKsL6FrrYelINPzxxiSShJgOf4XmQc4Azww6S5RjRj24YhsOn2a1MABOrxfVTZXtDk4Eg==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Process": { + "type": "Direct", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "99zSvpwsMpKextd3RkHGY8iRsmw1qg3PjqkjC5hZI0fZG6m+wPsssrX6z9RhwDGcZ8sdGNjoKyLpNOmR154zQg==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Runtime": { + "type": "Direct", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "OOvpqR/j2Pb6+tWhHNODIbSJ53Or/MDtTiXEyrsWI02K2lLAgvBFcxUOrHggS/8015cYR3AdSaXv6NZrkz5yQA==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", @@ -102,6 +183,27 @@ "Microsoft.NETCore.Platforms": "1.1.0" } }, + "OpenTelemetry": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "7mS/oZFF8S6xyqGQfMU1btp0nXJQUPWV535Vp/XMLYwRAUv36xQN+U4vufWBF1+z4HnRTOwuFHtUSGnHbyN6FQ==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.0" + } + }, + "OpenTelemetry.Api": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "vk5OGdf6K9kQScCWo3bRjhDWCv6Pqw92IpX4dlARZ8B1WL7/2NGTDtCkkw42eQf7UdwyoHKzVvMH/PtL8d6z7w==" + }, + "OpenTelemetry.Api.ProviderBuilderExtensions": { + "type": "Transitive", + "resolved": "1.15.0", + "contentHash": "OnuSUlRpGvowkOzGFQfy+KZFu0cITfKfh2IYJJiZskxVJiOuexwOOuvfDAgpJdmTzVWAHjYdz2shcHZaJ06UjQ==", + "dependencies": { + "OpenTelemetry.Api": "1.15.0" + } + }, "contracts": { "type": "Project", "dependencies": { diff --git a/templates/Bff/src/WebApp/Properties/launchSettings.json b/templates/Bff/src/WebApp/Properties/launchSettings.json index c4535c50..57889df9 100644 --- a/templates/Bff/src/WebApp/Properties/launchSettings.json +++ b/templates/Bff/src/WebApp/Properties/launchSettings.json @@ -9,17 +9,9 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "RATE_LIMITING_ENABLED": "true", - "LOGGING__LOGLEVEL__DEFAULT": "Debug", - "LOGGING__LOGLEVEL__MICROSOFT": "Information", - "LOGGING__LOGLEVEL__MICROSOFT_HOSTING_LIFETIME": "Information", - "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE": "Information", - "LOGGING__LOGLEVEL__MICROSOFT_ENTITY_FRAMEWORK_CORE_DATABASE_COMMAND": "Information", "ENABLE_SENSITIVE_DATA_LOGGING": "true", "OTEL_SERVICE_NAME": "Hexagonal.Solution.Template.WebApp.Bff", - "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:18889", - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "http://localhost:18889", - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "http://localhost:18889", - "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT": "http://localhost:18889", + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317", "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc", "OTEL_RESOURCE_ATTRIBUTES": "service.namespace=development", "ConnectionStrings__Redis": "127.0.0.1:6379" diff --git a/templates/Bff/src/WebApp/appsettings.Development.json b/templates/Bff/src/WebApp/appsettings.Development.json index 9a50acb2..f7738e78 100644 --- a/templates/Bff/src/WebApp/appsettings.Development.json +++ b/templates/Bff/src/WebApp/appsettings.Development.json @@ -2,6 +2,13 @@ "ConnectionStrings": { "Redis": "localhost:6379" }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, "Services": [ { "Name": "Orders", diff --git a/templates/Bff/src/WebApp/appsettings.json b/templates/Bff/src/WebApp/appsettings.json index cfc0c760..c28e77bb 100644 --- a/templates/Bff/src/WebApp/appsettings.json +++ b/templates/Bff/src/WebApp/appsettings.json @@ -2,6 +2,13 @@ "ConnectionStrings": { "Redis": "redis:6379" }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, "Services": [ { "Name": "Orders", diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json index 9aa1724d..ffaa4634 100644 --- a/templates/Bff/src/WebApp/packages.lock.json +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -109,23 +109,23 @@ }, "OpenTelemetry": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "resolved": "1.15.0", + "contentHash": "7mS/oZFF8S6xyqGQfMU1btp0nXJQUPWV535Vp/XMLYwRAUv36xQN+U4vufWBF1+z4HnRTOwuFHtUSGnHbyN6FQ==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.0" } }, "OpenTelemetry.Api": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + "resolved": "1.15.0", + "contentHash": "vk5OGdf6K9kQScCWo3bRjhDWCv6Pqw92IpX4dlARZ8B1WL7/2NGTDtCkkw42eQf7UdwyoHKzVvMH/PtL8d6z7w==" }, "OpenTelemetry.Api.ProviderBuilderExtensions": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "resolved": "1.15.0", + "contentHash": "OnuSUlRpGvowkOzGFQfy+KZFu0cITfKfh2IYJJiZskxVJiOuexwOOuvfDAgpJdmTzVWAHjYdz2shcHZaJ06UjQ==", "dependencies": { - "OpenTelemetry.Api": "1.14.0" + "OpenTelemetry.Api": "1.15.0" } }, "Pipelines.Sockets.Unofficial": { @@ -167,13 +167,17 @@ "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", "Microsoft.Extensions.Http.Polly": "[10.0.1, )", - "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", - "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", - "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", - "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", - "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )" + "OpenTelemetry.Exporter.Console": "[1.15.0, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", + "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", + "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "(, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", + "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Runtime": "[1.15.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.15.0-beta.1, )" } }, "Grpc.AspNetCore": { @@ -212,68 +216,95 @@ "Polly.Extensions.Http": "3.0.0" } }, + "OpenTelemetry.Exporter.Console": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "Jweov3Q70xmy5U8bwab8xd+xAuaFBI4695q/IpH4/dcAwKytyB+WhV5HufmKfXiKZhRbSEo8piG+i1ENEmdFXw==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Extensions.Hosting": { + "OpenTelemetry.Extensions": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "requested": "[1.14.0-beta.1, )", + "resolved": "1.14.0-beta.1", + "contentHash": "4DxYvBgz3OaCD8DE2lutiMiaEZh/PvWW05ewhAeqS6lKAETultAzfCveDXxMcIkaVwc93PR/Z22/P5zZ2rmHZA==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "[1.14.0, 2.0.0)" } }, - "OpenTelemetry.Instrumentation.AspNetCore": { + "OpenTelemetry.Extensions.Hosting": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "RixjKyB1pbYGhWdvPto4KJs+exdQknJsnjUO9WszdLles5Vcd0EYzxPNJdwmLjYfP+Jfbr4B5nktM4ZgeHSWtg==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.GrpcNetClient": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.1, )", - "resolved": "1.12.0-beta.1", - "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "SBas5+C4kGUqoy8OPpQis+QIgJ7/aaJl4H3oLzHCJnZLCb8TXZmQL2/r753RXXJUH8oIeLIzdW+EXgujSy+cpQ==", "dependencies": { - "OpenTelemetry": "[1.12.0, 2.0.0)" + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.Http": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "uToc7bUp8IEdb0ny9mKsL6FrrYelINPzxxiSShJgOf4XmQc4Azww6S5RjRj24YhsOn2a1MABOrxfVTZXtDk4Eg==", + "dependencies": { + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Process": { + "type": "CentralTransitive", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "99zSvpwsMpKextd3RkHGY8iRsmw1qg3PjqkjC5hZI0fZG6m+wPsssrX6z9RhwDGcZ8sdGNjoKyLpNOmR154zQg==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Runtime": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "OOvpqR/j2Pb6+tWhHNODIbSJ53Or/MDtTiXEyrsWI02K2lLAgvBFcxUOrHggS/8015cYR3AdSaXv6NZrkz5yQA==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "Igg/3MlBZZ9lZCTzMcvoFKav263+zOcKx9s4LVIdq96YmBHCuPmDiyygAIPdeIVzwN08VwD3RG1nXHDuRF1Ssg==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", - "StackExchange.Redis": "[2.6.122, 3.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)", + "StackExchange.Redis": "2.6.122" } } } diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json index 310dd11a..453d9d90 100644 --- a/templates/Bff/tests/IntegrationTests/packages.lock.json +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -459,26 +459,26 @@ }, "OpenTelemetry": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "aiPBAr1+0dPDItH++MQQr5UgMf4xiybruzNlAoYYMYN3UUk+mGRcoKuZy4Z4rhhWUZIpK2Xhe7wUUXSTM32duQ==", + "resolved": "1.15.0", + "contentHash": "7mS/oZFF8S6xyqGQfMU1btp0nXJQUPWV535Vp/XMLYwRAUv36xQN+U4vufWBF1+z4HnRTOwuFHtUSGnHbyN6FQ==", "dependencies": { "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.0", "Microsoft.Extensions.Logging.Configuration": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "1.14.0" + "OpenTelemetry.Api.ProviderBuilderExtensions": "1.15.0" } }, "OpenTelemetry.Api": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "foHci6viUw1f3gUB8qzz3Rk02xZIWMo299X0rxK0MoOWok/3dUVru+KKdY7WIoSHwRGpxGKkmAz9jIk2RFNbsQ==" + "resolved": "1.15.0", + "contentHash": "vk5OGdf6K9kQScCWo3bRjhDWCv6Pqw92IpX4dlARZ8B1WL7/2NGTDtCkkw42eQf7UdwyoHKzVvMH/PtL8d6z7w==" }, "OpenTelemetry.Api.ProviderBuilderExtensions": { "type": "Transitive", - "resolved": "1.14.0", - "contentHash": "i/lxOM92v+zU5I0rGl5tXAGz6EJtxk2MvzZ0VN6F6L5pMqT6s6RCXnGWXg6fW+vtZJsllBlQaf/VLPTzgefJpg==", + "resolved": "1.15.0", + "contentHash": "OnuSUlRpGvowkOzGFQfy+KZFu0cITfKfh2IYJJiZskxVJiOuexwOOuvfDAgpJdmTzVWAHjYdz2shcHZaJ06UjQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", - "OpenTelemetry.Api": "1.14.0" + "OpenTelemetry.Api": "1.15.0" } }, "Pipelines.Sockets.Unofficial": { @@ -558,13 +558,17 @@ "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", "Microsoft.Extensions.Http.Polly": "[10.0.1, )", - "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.14.0, )", - "OpenTelemetry.Extensions.Hosting": "[1.14.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.14.0, )", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.12.0-beta.2, )", - "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.12.0-beta.1, )", - "OpenTelemetry.Instrumentation.Http": "[1.14.0, )", - "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.12.0-beta.2, )" + "OpenTelemetry.Exporter.Console": "[1.15.0, )", + "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", + "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", + "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", + "OpenTelemetry.Instrumentation.EntityFrameworkCore": "(, )", + "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", + "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", + "OpenTelemetry.Instrumentation.Runtime": "[1.15.0, )", + "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.15.0-beta.1, )" } }, "webapp": { @@ -573,7 +577,6 @@ "AspNetCore.HealthChecks.Redis": "[9.0.0, )", "AspNetCore.HealthChecks.UI.Client": "[9.0.0, )", "AspNetCore.HealthChecks.Uris": "[9.0.0, )", - "Grpc.AspNetCore": "[2.76.0, )", "Infrastructure": "[1.0.0, )", "Microsoft.AspNetCore.OpenApi": "[10.0.1, )", "Scalar.AspNetCore": "[2.12.4, )" @@ -689,74 +692,100 @@ "resolved": "13.0.4", "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" }, + "OpenTelemetry.Exporter.Console": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "Jweov3Q70xmy5U8bwab8xd+xAuaFBI4695q/IpH4/dcAwKytyB+WhV5HufmKfXiKZhRbSEo8piG+i1ENEmdFXw==", + "dependencies": { + "OpenTelemetry": "1.15.0" + } + }, "OpenTelemetry.Exporter.OpenTelemetryProtocol": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "7ELExeje+T/KOywHuHwZBGQNtYlepUaYRFXWgoEaT1iKpFJVwOlE1Y2+uqHI2QQmah0Ue+XgRmDy924vWHfJ6Q==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "VH8ANc/js9IRvfYt0Q2UaAxNCOWm+IU+vWrtoH7pfx4oWPVdISUt+9uWfBCFMWZg5WzQip5dhslyDjeyZXXfSQ==", "dependencies": { - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Extensions.Hosting": { + "OpenTelemetry.Extensions": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "ZAxkCIa3Q3YWZ1sGrolXfkhPqn2PFSz2Cel74em/fATZgY5ixlw6MQp2icmqKCz4C7M1W2G0b92K3rX8mOtFRg==", + "requested": "[1.14.0-beta.1, )", + "resolved": "1.14.0-beta.1", + "contentHash": "4DxYvBgz3OaCD8DE2lutiMiaEZh/PvWW05ewhAeqS6lKAETultAzfCveDXxMcIkaVwc93PR/Z22/P5zZ2rmHZA==", "dependencies": { - "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", - "OpenTelemetry": "1.14.0" + "OpenTelemetry": "[1.14.0, 2.0.0)" } }, - "OpenTelemetry.Instrumentation.AspNetCore": { + "OpenTelemetry.Extensions.Hosting": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "NQAQpFa3a4ofPUYwxcwtNPGpuRNwwx1HM7MnLEESYjYkhfhER+PqqGywW65rWd7bJEc1/IaL+xbmHH99pYDE0A==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "RixjKyB1pbYGhWdvPto4KJs+exdQknJsnjUO9WszdLles5Vcd0EYzxPNJdwmLjYfP+Jfbr4B5nktM4ZgeHSWtg==", "dependencies": { - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "Microsoft.Extensions.Hosting.Abstractions": "10.0.0", + "OpenTelemetry": "1.15.0" } }, - "OpenTelemetry.Instrumentation.EntityFrameworkCore": { + "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "4D2PLiJWbBbQbauojkIflT11WGVXoRU+xgox1mvOkpfm7YXIfwTtROOlcdscS51sMh5fgwjGKJtLWpLKppe7dw==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "9.0.0", - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.GrpcNetClient": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.1, )", - "resolved": "1.12.0-beta.1", - "contentHash": "mi3Njei+Y5bn7oJYwIy/XWON+TK/sDUZy1m8UPw4ihtaCVjBTUdPWCjOW1sv3mZoctnJAT6PjNXD9222rqO65w==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "SBas5+C4kGUqoy8OPpQis+QIgJ7/aaJl4H3oLzHCJnZLCb8TXZmQL2/r753RXXJUH8oIeLIzdW+EXgujSy+cpQ==", "dependencies": { - "OpenTelemetry": "[1.12.0, 2.0.0)" + "OpenTelemetry": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.Http": { "type": "CentralTransitive", - "requested": "[1.14.0, )", - "resolved": "1.14.0", - "contentHash": "uH8X1fYnywrgaUrSbemKvFiFkBwY7ZbBU7Wh4A/ORQmdpF3G/5STidY4PlK4xYuIv9KkdMXH/vkpvzQcayW70g==", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "uToc7bUp8IEdb0ny9mKsL6FrrYelINPzxxiSShJgOf4XmQc4Azww6S5RjRj24YhsOn2a1MABOrxfVTZXtDk4Eg==", "dependencies": { "Microsoft.Extensions.Configuration": "10.0.0", "Microsoft.Extensions.Options": "10.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.14.0, 2.0.0)" + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Process": { + "type": "CentralTransitive", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "99zSvpwsMpKextd3RkHGY8iRsmw1qg3PjqkjC5hZI0fZG6m+wPsssrX6z9RhwDGcZ8sdGNjoKyLpNOmR154zQg==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" + } + }, + "OpenTelemetry.Instrumentation.Runtime": { + "type": "CentralTransitive", + "requested": "[1.15.0, )", + "resolved": "1.15.0", + "contentHash": "OOvpqR/j2Pb6+tWhHNODIbSJ53Or/MDtTiXEyrsWI02K2lLAgvBFcxUOrHggS/8015cYR3AdSaXv6NZrkz5yQA==", + "dependencies": { + "OpenTelemetry.Api": "[1.15.0, 2.0.0)" } }, "OpenTelemetry.Instrumentation.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[1.12.0-beta.2, )", - "resolved": "1.12.0-beta.2", - "contentHash": "0gfZKwJ5gUHBRL4eD+3VGqumzvS+GPpMmQea8j4TlseBrqi2FDXFfh4YAnanj8ogex90QAwwpOTsMaTGZ9aEzg==", + "requested": "[1.15.0-beta.1, )", + "resolved": "1.15.0-beta.1", + "contentHash": "Igg/3MlBZZ9lZCTzMcvoFKav263+zOcKx9s4LVIdq96YmBHCuPmDiyygAIPdeIVzwN08VwD3RG1nXHDuRF1Ssg==", "dependencies": { - "Microsoft.Extensions.Options": "9.0.0", - "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.12.0, 2.0.0)", - "StackExchange.Redis": "[2.6.122, 3.0.0)" + "Microsoft.Extensions.Configuration": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)", + "StackExchange.Redis": "2.6.122" } }, "Scalar.AspNetCore": { From 0659280522f4e5f2d0a2813a7ce76c0682155fb9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 17 Mar 2026 07:01:41 -0300 Subject: [PATCH 128/161] 53 feat: add grafana configuration files for observability tools --- templates/Bff/scripts/grafana/config.alloy | 38 ++++++++ .../grafana/datasources/datasources.yaml | 0 .../scripts/grafana/grafana-datasources.yaml | 55 ++++++++++++ .../Bff/scripts/grafana/loki-config.yaml | 40 +++++++++ templates/Bff/scripts/grafana/prometheus.yml | 7 ++ templates/Bff/scripts/grafana/tempo.yaml | 90 +++++++++++++++++++ 6 files changed, 230 insertions(+) create mode 100644 templates/Bff/scripts/grafana/config.alloy create mode 100644 templates/Bff/scripts/grafana/datasources/datasources.yaml create mode 100644 templates/Bff/scripts/grafana/grafana-datasources.yaml create mode 100644 templates/Bff/scripts/grafana/loki-config.yaml create mode 100644 templates/Bff/scripts/grafana/prometheus.yml create mode 100644 templates/Bff/scripts/grafana/tempo.yaml diff --git a/templates/Bff/scripts/grafana/config.alloy b/templates/Bff/scripts/grafana/config.alloy new file mode 100644 index 00000000..6e7d7f58 --- /dev/null +++ b/templates/Bff/scripts/grafana/config.alloy @@ -0,0 +1,38 @@ +otelcol.receiver.otlp "default" { + http { + endpoint = "0.0.0.0:4318" + + } + grpc { + endpoint = "0.0.0.0:4317" + } + + output { + metrics = [otelcol.exporter.otlphttp.metrics.input] + logs = [otelcol.exporter.otlphttp.logs.input] + traces = [otelcol.exporter.otlphttp.traces.input] + } +} + +otelcol.exporter.otlphttp "logs" { + client { + endpoint = "http://loki:3100/otlp" + } + +} + +otelcol.exporter.otlphttp "metrics" { + client { + endpoint = "http://prometheus:9090/api/v1/otlp" + } +} + +otelcol.exporter.otlphttp "traces" { + client { + endpoint = "http://tempo:4318" + } +} + +livedebugging { + enabled = true +} \ No newline at end of file diff --git a/templates/Bff/scripts/grafana/datasources/datasources.yaml b/templates/Bff/scripts/grafana/datasources/datasources.yaml new file mode 100644 index 00000000..e69de29b diff --git a/templates/Bff/scripts/grafana/grafana-datasources.yaml b/templates/Bff/scripts/grafana/grafana-datasources.yaml new file mode 100644 index 00000000..b6e00255 --- /dev/null +++ b/templates/Bff/scripts/grafana/grafana-datasources.yaml @@ -0,0 +1,55 @@ +apiVersion: 1 + +datasources: +- name: Tempo + type: tempo + access: proxy + orgId: 1 + url: http://tempo:3200 + basicAuth: false + isDefault: true + version: 1 + editable: false + apiVersion: 1 + uid: tempo + jsonData: + httpMethod: GET + serviceMap: + datasourceUid: prometheus + tracesToLogsV2: + # Field with an internal link pointing to a logs data source in Grafana. + # datasourceUid value must match the uid value of the logs data source. + datasourceUid: 'Loki' + spanStartTimeShift: '-1h' + spanEndTimeShift: '1h' + filterByTraceID: true + filterBySpanID: true + customQuery: false + query: 'method="$${__span.tags.method}"' +- name: Prometheus + type: prometheus + uid: prometheus + access: proxy + orgId: 1 + url: http://prometheus:9090 + basicAuth: false + isDefault: false + version: 1 + editable: false + jsonData: + httpMethod: GET +- name: Loki + type: loki + access: proxy + orgId: 1 + url: http://loki:3100 + basicAuth: false + isDefault: false + version: 1 + editable: false + jsonData: + derivedFields: + - datasourceUid: tempo + matcherRegex: tid=(\w+) + name: TraceId + url: $${__value.raw} diff --git a/templates/Bff/scripts/grafana/loki-config.yaml b/templates/Bff/scripts/grafana/loki-config.yaml new file mode 100644 index 00000000..a9eca0ee --- /dev/null +++ b/templates/Bff/scripts/grafana/loki-config.yaml @@ -0,0 +1,40 @@ + +# This is a complete configuration to deploy Loki backed by the filesystem. +# The index will be shipped to the storage via tsdb-shipper. + +auth_enabled: false + +limits_config: + allow_structured_metadata: true + volume_enabled: true + +server: + http_listen_port: 3100 + +common: + ring: + instance_addr: 0.0.0.0 + kvstore: + store: inmemory + replication_factor: 1 + path_prefix: /tmp/loki + +schema_config: + configs: + - from: 2020-05-15 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +storage_config: + tsdb_shipper: + active_index_directory: /tmp/loki/index + cache_location: /tmp/loki/index_cache + filesystem: + directory: /tmp/loki/chunks + +pattern_ingester: + enabled: true \ No newline at end of file diff --git a/templates/Bff/scripts/grafana/prometheus.yml b/templates/Bff/scripts/grafana/prometheus.yml new file mode 100644 index 00000000..e71c1eb3 --- /dev/null +++ b/templates/Bff/scripts/grafana/prometheus.yml @@ -0,0 +1,7 @@ +global: + scrape_interval: 3s + evaluation_interval: 3s + +storage: + tsdb: + out_of_order_time_window: 30m diff --git a/templates/Bff/scripts/grafana/tempo.yaml b/templates/Bff/scripts/grafana/tempo.yaml new file mode 100644 index 00000000..735326c2 --- /dev/null +++ b/templates/Bff/scripts/grafana/tempo.yaml @@ -0,0 +1,90 @@ +stream_over_http_enabled: true +server: + http_listen_port: 3200 + log_level: info + + +cache: + background: + writeback_goroutines: 5 + caches: + - roles: + - frontend-search + memcached: + host: memcached:11211 + +query_frontend: + search: + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + metadata_slo: + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + trace_by_id: + duration_slo: 100ms + metrics: + max_duration: 120h # maximum duration of a metrics query, increase for local setups + query_backend_after: 5m + duration_slo: 5s + throughput_bytes_slo: 1.073741824e+09 + +distributor: + receivers: # this configuration will listen on all ports and protocols that tempo is capable of. + jaeger: # the receives all come from the OpenTelemetry collector. more configuration information can + protocols: # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver + thrift_http: # + endpoint: "tempo:14268" # for a production deployment you should only enable the receivers you need! + grpc: + endpoint: "tempo:14250" + thrift_binary: + endpoint: "tempo:6832" + thrift_compact: + endpoint: "tempo:6831" + zipkin: + endpoint: "tempo:9411" + otlp: + protocols: + grpc: + endpoint: "tempo:4317" + http: + endpoint: "tempo:4318" + opencensus: + endpoint: "tempo:55678" + +ingester: + max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally + +compactor: + compaction: + block_retention: 24h # overall Tempo trace retention. set for demo purposes + +metrics_generator: + registry: + external_labels: + source: tempo + cluster: docker-compose + storage: + path: /var/tempo/generator/wal + remote_write: + - url: http://prometheus:9090/api/v1/write + send_exemplars: true + traces_storage: + path: /var/tempo/generator/traces + processor: + local_blocks: + filter_server_spans: false + flush_to_storage: true + +storage: + trace: + backend: local # backend configuration to use + wal: + path: /var/tempo/wal # where to store the wal locally + local: + path: /var/tempo/blocks + +overrides: + defaults: + metrics_generator: + processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator + generate_native_histograms: both \ No newline at end of file From bc2aa5480f6631a3309dc3f7c09a3e5b730bc103 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Tue, 17 Mar 2026 07:05:07 -0300 Subject: [PATCH 129/161] 53 style: fix indentation for alloy service in docker-compose --- templates/Bff/docker-compose-load-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml index f54e45a0..4e0b3370 100644 --- a/templates/Bff/docker-compose-load-tests.yml +++ b/templates/Bff/docker-compose-load-tests.yml @@ -51,7 +51,7 @@ services: file: docker-compose.yml service: redis - alloy: + alloy: extends: file: docker-compose-grafana.yml service: alloy From d6b1f78c4dd467c9fb7229c9a03216dc3cf33ef4 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 18 Mar 2026 07:06:52 -0300 Subject: [PATCH 130/161] 53 refactor: simplify logging in cache and HTTP services --- .../Cache/HybridCacheService.cs | 68 +++------ .../Bff/src/Infrastructure/Common/Logs.cs | 140 +++++++++--------- .../Infrastructure/Grpc/BaseGrpcService.cs | 15 +- .../Infrastructure/Http/BaseHttpService.cs | 18 +-- .../ExceptionHandlingMiddleware.cs | 3 +- 5 files changed, 101 insertions(+), 143 deletions(-) diff --git a/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs b/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs index af013a1d..2c038eed 100644 --- a/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs +++ b/templates/Bff/src/Infrastructure/Cache/HybridCacheService.cs @@ -1,82 +1,58 @@ +using System.Diagnostics; using Infrastructure.Common; using Microsoft.Extensions.Caching.Hybrid; using Microsoft.Extensions.Logging; namespace Infrastructure.Cache; -public sealed partial class HybridCacheService(HybridCache cache, ILogger logger) +public sealed class HybridCacheService(HybridCache cache, ILogger logger) { private readonly HybridCache _cache = cache; private readonly ILogger _logger = logger; - + private readonly string _className = nameof(HybridCacheService); + private readonly ActivitySource _activities = DefaultConfigurations.ActivitySource; public async ValueTask GetOrCreateAsync( string key, Func> factory, CancellationToken cancellationToken ) { - RetrievingCacheEntry(_logger, key); + using var activity = _activities.StartActivity($"{_className}.{nameof(GetOrCreateAsync)}"); + + Logs.DebugStartingOperation(_logger, $"Key: {key}"); var result = await _cache.GetOrCreateAsync($"{DefaultConfigurations.ApplicationName}:{key}", factory, cancellationToken: cancellationToken); - CacheEntryRetrieved(_logger, key); + Logs.DebugFinishedOperation(_logger, $"Cache hit: {result != null} for key: {key}"); + + activity?.SetTag("key", key); return result; } public async ValueTask CreateAsync(string key, TResult value, CancellationToken cancellationToken) { - CreatingCacheEntry(_logger, key); + using var activity = _activities.StartActivity($"{_className}.{nameof(CreateAsync)}"); + + Logs.DebugStartingOperation(_logger, $"Key: {key}"); await _cache.SetAsync($"{DefaultConfigurations.ApplicationName}:{key}", value, cancellationToken: cancellationToken); - CacheEntryCreated(_logger, key); + Logs.DebugFinishedOperation(_logger, $"Cached hit: {value != null} for key: {key}"); + + activity?.SetTag("key", key); } public async ValueTask DeleteAsync(string key, CancellationToken cancellationToken) { - DeletingCacheEntry(_logger, key); + using var activity = _activities.StartActivity($"{_className}.{nameof(DeleteAsync)}"); + + Logs.DebugStartingOperation(_logger, $"Key: {key}"); await _cache.RemoveAsync($"{DefaultConfigurations.ApplicationName}:{key}", cancellationToken); - CacheEntryDeleted(_logger, key); - } + Logs.DebugFinishedOperation(_logger, $"Cache entry removed for key: {key}"); - [LoggerMessage( - EventId = 1, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Retrieving cache entry" - )] - private static partial void RetrievingCacheEntry(ILogger logger, string key); - - [LoggerMessage( - EventId = 2, - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [GetOrCreateAsync] | [{Key}] | Cache entry retrieved" - )] - private static partial void CacheEntryRetrieved(ILogger logger, string key); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Creating cache entry" - )] - private static partial void CreatingCacheEntry(ILogger logger, string key); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [CreateAsync] | [{Key}] | Cache entry created" - )] - private static partial void CacheEntryCreated(ILogger logger, string key); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Deleting cache entry" - )] - private static partial void DeletingCacheEntry(ILogger logger, string key); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "[HybridCacheService] | [DeleteAsync] | [{Key}] | Cache entry deleted" - )] - private static partial void CacheEntryDeleted(ILogger logger, string key); + activity?.SetTag("key", key); + } } diff --git a/templates/Bff/src/Infrastructure/Common/Logs.cs b/templates/Bff/src/Infrastructure/Common/Logs.cs index 9031b073..116185d9 100644 --- a/templates/Bff/src/Infrastructure/Common/Logs.cs +++ b/templates/Bff/src/Infrastructure/Common/Logs.cs @@ -1,126 +1,122 @@ -using System.Net; +using System.Runtime.CompilerServices; using Microsoft.Extensions.Logging; namespace Infrastructure.Common; -public static partial class Logs +public partial class Logs { /// - /// Logs the sending of a request + /// Logs a generic debug message with a custom message. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service + /// The logger instance to use for logging. + /// The debug message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 1, - Level = LogLevel.Information, - Message = "[{ClassName}] | [{Method}] | Sending request" + Level = LogLevel.Debug, + Message = "[{MethodName}] | {Message}" )] - public static partial void SendingRequest(ILogger logger, string className, string method); + public static partial void Debug(ILogger logger, string message, [CallerMemberName] string methodName = null!); /// - /// Logs the sending of a request + /// Logs a generic information message with a custom message. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service - /// HTTP method of the request - /// URI of the request + /// The logger instance to use for logging. + /// The information message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 2, Level = LogLevel.Information, - Message = "[{ClassName}] | [{Method}] | [{HttpMethod}] | [{RequestUri}] | Sending request" + Message = "[{MethodName}] | {Message}" )] - public static partial void SendingRequest(ILogger logger, string className, string method, HttpMethod httpMethod, string requestUri); + public static partial void Information(ILogger logger, string message, [CallerMemberName] string methodName = null!); /// - /// Logs a completed request with elapsed time + /// Logs a generic warning message with a custom message. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service + /// The logger instance to use for logging. + /// The warning message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 3, - Level = LogLevel.Information, - Message = "[{ClassName}] | [{Method}] | Request completed" + Level = LogLevel.Warning, + Message = "[{MethodName}] | {Message}" )] - public static partial void RequestCompleted(ILogger logger, string className, string method); - + public static partial void Warning(ILogger logger, string message, [CallerMemberName] string methodName = null!); /// - /// Logs a completed request with elapsed time + /// Logs a generic operation failure with a custom message. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service - /// elapsed time in milliseconds + /// The logger instance to use for logging. + /// The failure message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 4, - Level = LogLevel.Information, - Message = "[{ClassName}] | [{Method}] | Request completed in {ElapsedMilliseconds} ms" + Level = LogLevel.Error, + Message = "[{MethodName}] | Error: {Message}" )] - public static partial void RequestCompletedWithElapsed(ILogger logger, string className, string method, long elapsedMilliseconds); - + public static partial void Error(ILogger logger, string message, [CallerMemberName] string methodName = null!); + /// - /// Logs a completed request with elapsed time + /// Logs the start of the execution of an operation, including the method name and correlation ID. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service - /// HTTP method of the request - /// URI of the request - /// elapsed time in milliseconds + /// The logger instance to use for logging. + /// The name of the method where the operation is executed (auto-captured). [LoggerMessage( EventId = 5, Level = LogLevel.Information, - Message = "[{ClassName}] | [{Method}] | [{HttpMethod}] | [{RequestUri}] | Request completed in {ElapsedMilliseconds} ms" + Message = "[{MethodName}] | Starting operation" )] - public static partial void RequestCompletedWithElapsed(ILogger logger, string className, string method, HttpMethod httpMethod, string requestUri, long elapsedMilliseconds); + public static partial void StartingOperation(ILogger logger, [CallerMemberName] string methodName = null!); /// - /// Logs a failed request + /// Logs the completion of the execution of an operation, including the method name and correlation ID. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service - /// message associated with the request + /// The logger instance to use for logging. + /// The name of the method where the operation is executed (auto-captured). [LoggerMessage( EventId = 6, - Level = LogLevel.Error, - Message = "[{ClassName}] | [{Method}] | Request failed | Message: {Message}" + Level = LogLevel.Information, + Message = "[{MethodName}] | Finished operation" )] - public static partial void RequestFailed(ILogger logger, string className, string method, string message); + public static partial void FinishedOperation(ILogger logger, [CallerMemberName] string methodName = null!); /// - /// Logs a failed request with elapsed time + /// Logs a generic operation failure with a custom message. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service - /// elapsed time in milliseconds - /// exception that occurred + /// The logger instance to use for logging. + /// The failure message. + /// The name of the method (auto-captured). [LoggerMessage( EventId = 7, - Level = LogLevel.Error, - Message = "[{ClassName}] | [{Method}] | Failed in {ElapsedMilliseconds} ms" + Level = LogLevel.Warning, + Message = "[{MethodName}] | Failed operation: {Message}" )] - public static partial void RequestFailedWithElapsed(ILogger logger, string className, string method, long elapsedMilliseconds, Exception exception); + public static partial void FailedOperation(ILogger logger, string message, [CallerMemberName] string methodName = null!); /// - /// Logs a failed request with elapsed time + /// Logs the start of an operation. /// - /// logger instance for logging - /// class name of the calling service - /// method name of the calling service - /// HTTP method of the request - /// URI of the request - /// message associated with the request - /// HTTP status code of the response - /// elapsed time in milliseconds + /// The logger instance to use for logging. + /// Optional details about the operation. + /// The method name (auto-captured). [LoggerMessage( EventId = 8, - Level = LogLevel.Error, - Message = "[{ClassName}] | [{Method}] | [{HttpMethod}] | [{RequestUri}] | [{Message}] | {StatusCode} | Request failed with status in {ElapsedMilliseconds} ms" + Level = LogLevel.Debug, + Message = "[{Method}] | Starting operation. | {Details}" + )] + public static partial void DebugStartingOperation(ILogger logger, string details = "", [CallerMemberName] string method = null!); + + /// + /// Logs the completion of an operation. + /// + /// The logger instance to use for logging. + /// Optional details about the operation. + /// The method name (auto-captured). + [LoggerMessage( + EventId = 9, + Level = LogLevel.Debug, + Message = "[{Method}] | Finished operation. | {Details}" )] - public static partial void RequestFailedWithElapsed(ILogger logger, string className, string method, HttpMethod httpMethod, string requestUri, string? message, HttpStatusCode statusCode, long elapsedMilliseconds); + public static partial void DebugFinishedOperation(ILogger logger, string details = "", [CallerMemberName] string method = null!); } \ No newline at end of file diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index 883baa53..d3b5efef 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Runtime.CompilerServices; using Grpc.Core; using Grpc.Net.ClientFactory; @@ -18,10 +17,6 @@ public partial class BaseGrpcService where TGrpcService : ClientBa /// public ILogger Logger { get; } /// - /// Stopwatch instance for measuring request duration - /// - public Stopwatch Stopwatch { get; } = new(); - /// /// Class name of the derived gRPC service /// public string ClassName { get; } @@ -51,26 +46,24 @@ protected async Task ExecuteHandlerAsync( [CallerMemberName] string? methodName = null ) where TResponse : class { - Stopwatch.Restart(); - try { - Logs.SendingRequest(Logger, ClassName, methodName!); + Logs.Information(Logger, "Sending request"); var response = handler.Invoke(); - Logs.RequestCompletedWithElapsed(Logger, ClassName, methodName!, Stopwatch.ElapsedMilliseconds); + Logs.Information(Logger, "Request completed"); return await response.ResponseAsync; } catch (RpcException rpcEx) { - Logs.RequestFailedWithElapsed(Logger, ClassName, methodName!, Stopwatch.ElapsedMilliseconds, rpcEx); + Logs.Error(Logger, $"Request failed: {rpcEx.Message}"); throw; } catch (Exception ex) { - Logs.RequestFailedWithElapsed(Logger, ClassName, methodName!, Stopwatch.ElapsedMilliseconds, ex); + Logs.Error(Logger, $"Request failed: {ex.Message}"); throw; } diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index f9fa852a..6f6dfc7b 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Net; using System.Text.Json; using Infrastructure.Common; @@ -12,9 +11,6 @@ public class BaseHttpService(HttpClient httpClient, ILogger log public ILogger Logger { get; } = logger; public int HttpProtocolVersion { get; } = httpProtocolVersion; public JsonSerializerOptions JsonSerializerOptions { get; } = new(JsonSerializerDefaults.Web); - public Stopwatch Stopwatch { get; } = new(); - private readonly string _className = nameof(BaseHttpService); - private readonly string _method = nameof(SendAsync); private static Version GetHttpVersion(int httpVersion) => httpVersion switch { @@ -32,8 +28,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log CancellationToken cancellationToken = default ) where TRequest : class where TResponse : class { - Stopwatch.Start(); - Logs.SendingRequest(Logger, _className, _method, httpMethod, requestUri); + Logs.StartingOperation(Logger); HttpRequestMessage requestMessage = new(httpMethod, requestUri) { @@ -56,7 +51,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log if (!response.IsSuccessStatusCode) { - Logs.RequestFailedWithElapsed(Logger, _className, _method, httpMethod, requestUri, response.ReasonPhrase, response.StatusCode, Stopwatch.ElapsedMilliseconds); + Logs.FailedOperation(Logger, $"{httpMethod} {requestUri} failed: {response.StatusCode} - {response.ReasonPhrase}"); return null; } @@ -64,7 +59,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log var result = await JsonSerializer.DeserializeAsync(content, JsonSerializerOptions, cancellationToken); - Logs.RequestCompletedWithElapsed(Logger, _className, _method, httpMethod, requestUri, Stopwatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger); return result; } @@ -76,8 +71,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log Dictionary? headers = null ) where TResponse : class { - Stopwatch.Start(); - Logs.SendingRequest(Logger, _className, _method, httpMethod, requestUri); + Logs.StartingOperation(Logger); var requestMessage = new HttpRequestMessage(httpMethod, requestUri) { @@ -92,7 +86,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log if (!response.IsSuccessStatusCode) { - Logs.RequestFailedWithElapsed(Logger, _className, _method, httpMethod, requestUri, response.ReasonPhrase, response.StatusCode, Stopwatch.ElapsedMilliseconds); + Logs.FailedOperation(Logger, $"{httpMethod} {requestUri} failed: {response.StatusCode} - {response.ReasonPhrase}"); return null; } @@ -100,7 +94,7 @@ public class BaseHttpService(HttpClient httpClient, ILogger log var result = await JsonSerializer.DeserializeAsync(content, JsonSerializerOptions, cancellationToken); - Logs.RequestCompletedWithElapsed(Logger, _className, _method, httpMethod, requestUri, Stopwatch.ElapsedMilliseconds); + Logs.FinishedOperation(Logger); return result; } diff --git a/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs index 523ea071..4970e848 100644 --- a/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs +++ b/templates/Bff/src/WebApp/Middlewares/ExceptionHandlingMiddleware.cs @@ -7,7 +7,6 @@ internal sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger< { private readonly RequestDelegate _next = next; private readonly ILogger _logger = logger; - private readonly string _className = nameof(ExceptionHandlingMiddleware); public async Task InvokeAsync(HttpContext context) { @@ -26,7 +25,7 @@ private async Task HandleExceptionAsync(HttpContext context, Exception exception context.Response.ContentType = "application/json"; context.Response.StatusCode = (int) HttpStatusCode.BadRequest; - Logs.RequestFailed(_logger, _className, nameof(HandleExceptionAsync), exception.Message); + Logs.Error(_logger, exception.Message); } } From a8f710b14a4e2d05e7136649397ee05f6053acb3 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 18 Mar 2026 16:53:36 -0300 Subject: [PATCH 131/161] 53 chore: remove grafana docker-compose file and update services --- templates/Bff/docker-compose-grafana.yml | 85 ------------------- templates/Bff/docker-compose-load-tests.yml | 34 ++------ templates/Bff/docker-compose.yml | 85 ++++++++++++++++++- templates/Bff/scripts/grafana/config.alloy | 13 ++- .../Infrastructure/Grpc/BaseGrpcService.cs | 2 + .../Infrastructure/Http/BaseHttpService.cs | 4 + 6 files changed, 109 insertions(+), 114 deletions(-) delete mode 100644 templates/Bff/docker-compose-grafana.yml diff --git a/templates/Bff/docker-compose-grafana.yml b/templates/Bff/docker-compose-grafana.yml deleted file mode 100644 index 662d6dc2..00000000 --- a/templates/Bff/docker-compose-grafana.yml +++ /dev/null @@ -1,85 +0,0 @@ -# Sample from: https://github.com/grafana/adventure/blob/main/docker-compose.yml -name: hexagonal-solution-template-grafana-bff -services: - alloy: - container_name: alloy - image: grafana/alloy:v1.7.5 - ports: - - 12345:12345 - - 4318:4318 - - 4317:4317 - volumes: - - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy - - /var/lib/docker/containers:/var/lib/docker/containers:ro - - /var/run/docker.sock:/var/run/docker.sock - command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy - depends_on: - - loki - - prometheus - - tempo - - prometheus: - container_name: prometheus - image: prom/prometheus:v3.1.0 - ports: - - 9090:9090 - volumes: - - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml - command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage - - loki: - container_name: loki - image: grafana/loki:3.4.2 - ports: - - "3100:3100" - volumes: - - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml - command: -config.file=/etc/loki/local-config.yaml - - grafana: - container_name: grafana - image: grafana/grafana:11.5.2 - environment: - - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin - - GF_AUTH_ANONYMOUS_ENABLED=true - - GF_AUTH_BASIC_ENABLED=false - - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall - ports: - - 3000:3000/tcp - volumes: - - ./scripts/grafana:/etc/grafana/provisioning - - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml - - # Tempo runs as user 10001, and docker compose creates the volume as root. - # As such, we need to chown the volume in order for Tempo to start correctly. - tempo-init: - container_name: tempo-init - image: &tempoImage grafana/tempo:2.7.1 - user: root - entrypoint: - - "chown" - - "10001:10001" - - "/var/tempo" - volumes: - - ./scripts/grafana/tempo-data:/var/tempo - - memcached: - container_name: tempo-memcached - image: memcached:1.6.29 - ports: - - "11211:11211" - environment: - - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage - - MEMCACHED_THREADS=4 # Number of threads to use - - tempo: - container_name: tempo - image: *tempoImage - command: [ "-config.file=/etc/tempo.yaml" ] - volumes: - - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml - ports: - - "3200:3200" # tempo - depends_on: - - tempo-init - - memcached \ No newline at end of file diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml index 4e0b3370..95e8de4d 100644 --- a/templates/Bff/docker-compose-load-tests.yml +++ b/templates/Bff/docker-compose-load-tests.yml @@ -2,6 +2,7 @@ name: hexagonal-solution-template-bff-load-tests services: k6: + container_name: k6 image: grafana/k6:1.4.2 depends_on: webapp: @@ -18,6 +19,7 @@ services: - hexagonal_solution_template_network bff: + container_name: bff build: context: . dockerfile: ./Dockerfile @@ -53,53 +55,35 @@ services: alloy: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: alloy - networks: - - hexagonal_solution_template_bff_network prometheus: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: prometheus - networks: - - hexagonal_solution_template_bff_network loki: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: loki - networks: - - hexagonal_solution_template_bff_network grafana: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: grafana - networks: - - hexagonal_solution_template_bff_network tempo-init: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: tempo-init - networks: - - hexagonal_solution_template_bff_network memcached: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: memcached - networks: - - hexagonal_solution_template_bff_network tempo: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: tempo - networks: - - hexagonal_solution_template_bff_network - -networks: - hexagonal_solution_template_bff_network: - driver: bridge diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index c4a5c586..f3264707 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -2,13 +2,14 @@ name: hexagonal-solution-template-bff services: mock-api: + container_name: mock-api build: context: . dockerfile: Dockerfile.MockApi ports: - "5012:5012" healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5012/health"] + test: ["CMD", "curl", "--http2-prior-knowledge", "-v", "http://localhost:5012/health"] interval: 3s timeout: 10s retries: 5 @@ -21,6 +22,7 @@ services: redis: image: redis:8 + container_name: redis ports: - "6379:6379" healthcheck: @@ -29,3 +31,84 @@ services: timeout: 10s retries: 5 start_period: 15s + + alloy: + container_name: alloy + image: grafana/alloy:v1.7.5 + ports: + - 12345:12345 + - 4318:4318 + - 4317:4317 + volumes: + - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + depends_on: + - loki + - prometheus + - tempo + + prometheus: + container_name: prometheus + image: prom/prometheus:v3.1.0 + ports: + - 9090:9090 + volumes: + - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml + command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage + + loki: + container_name: loki + image: grafana/loki:3.4.2 + ports: + - "3100:3100" + volumes: + - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml + command: -config.file=/etc/loki/local-config.yaml + + grafana: + container_name: grafana + image: grafana/grafana:11.5.2 + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall + ports: + - 3000:3000/tcp + volumes: + - ./scripts/grafana:/etc/grafana/provisioning + - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + + tempo-init: + container_name: tempo-init + image: &tempoImage grafana/tempo:2.7.1 + user: root + entrypoint: + - "chown" + - "10001:10001" + - "/var/tempo" + volumes: + - ./scripts/grafana/tempo-data:/var/tempo + + memcached: + container_name: tempo-memcached + image: memcached:1.6.29 + ports: + - "11211:11211" + environment: + - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage + - MEMCACHED_THREADS=4 # Number of threads to use + + tempo: + container_name: tempo + image: *tempoImage + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml + ports: + - "3200:3200" # tempo + depends_on: + - tempo-init + - memcached diff --git a/templates/Bff/scripts/grafana/config.alloy b/templates/Bff/scripts/grafana/config.alloy index 6e7d7f58..34d692e5 100644 --- a/templates/Bff/scripts/grafana/config.alloy +++ b/templates/Bff/scripts/grafana/config.alloy @@ -10,7 +10,15 @@ otelcol.receiver.otlp "default" { output { metrics = [otelcol.exporter.otlphttp.metrics.input] logs = [otelcol.exporter.otlphttp.logs.input] - traces = [otelcol.exporter.otlphttp.traces.input] + traces = [otelcol.exporter.otlphttp.traces.input,otelcol.connector.servicegraph.default.input] + } +} + +otelcol.connector.servicegraph "default" { + dimensions = ["http.method"] + + output { + metrics = [otelcol.exporter.otlphttp.metrics.input] } } @@ -18,7 +26,6 @@ otelcol.exporter.otlphttp "logs" { client { endpoint = "http://loki:3100/otlp" } - } otelcol.exporter.otlphttp "metrics" { @@ -29,7 +36,7 @@ otelcol.exporter.otlphttp "metrics" { otelcol.exporter.otlphttp "traces" { client { - endpoint = "http://tempo:4318" + endpoint = "http://tempo:4318" } } diff --git a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs index d3b5efef..bee26efa 100644 --- a/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs +++ b/templates/Bff/src/Infrastructure/Grpc/BaseGrpcService.cs @@ -46,6 +46,8 @@ protected async Task ExecuteHandlerAsync( [CallerMemberName] string? methodName = null ) where TResponse : class { + DefaultConfigurations.ActivitySource.StartActivity($"{ClassName}.{methodName}"); + try { Logs.Information(Logger, "Sending request"); diff --git a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs index 6f6dfc7b..79588ee2 100644 --- a/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs +++ b/templates/Bff/src/Infrastructure/Http/BaseHttpService.cs @@ -28,6 +28,8 @@ public class BaseHttpService(HttpClient httpClient, ILogger log CancellationToken cancellationToken = default ) where TRequest : class where TResponse : class { + DefaultConfigurations.ActivitySource.StartActivity($"{nameof(BaseHttpService)}.{nameof(SendAsync)}"); + Logs.StartingOperation(Logger); HttpRequestMessage requestMessage = new(httpMethod, requestUri) @@ -71,6 +73,8 @@ public class BaseHttpService(HttpClient httpClient, ILogger log Dictionary? headers = null ) where TResponse : class { + DefaultConfigurations.ActivitySource.StartActivity($"{nameof(BaseHttpService)}.{nameof(SendAsync)}"); + Logs.StartingOperation(Logger); var requestMessage = new HttpRequestMessage(httpMethod, requestUri) From 931e5f18fc2c19dd2ee6c4665c2e0f2eef100d04 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 18 Mar 2026 17:41:50 -0300 Subject: [PATCH 132/161] 53 feat: update load test configurations and remove memcached --- templates/Bff/docker-compose-load-tests.yml | 22 +++++++++------------ templates/Bff/docker-compose.yml | 11 +---------- templates/Bff/scripts/grafana/tempo.yaml | 5 ++--- templates/Bff/src/WebApp/Program.cs | 3 ++- templates/Bff/tests/LoadTests/scriptGrpc.js | 6 +++--- templates/Bff/tests/LoadTests/scriptHttp.js | 6 +++--- 6 files changed, 20 insertions(+), 33 deletions(-) diff --git a/templates/Bff/docker-compose-load-tests.yml b/templates/Bff/docker-compose-load-tests.yml index 95e8de4d..fdb74a85 100644 --- a/templates/Bff/docker-compose-load-tests.yml +++ b/templates/Bff/docker-compose-load-tests.yml @@ -5,18 +5,21 @@ services: container_name: k6 image: grafana/k6:1.4.2 depends_on: - webapp: + bff: condition: service_healthy ports: - "6565:6565" environment: - - WEBAPP_URL=http://webapp:5000 + - WEBAPP_URL=http://bff:5011 - K6_SUMMARY_MODE=full + - K6_WEB_DASHBOARD=true + - K6_WEB_DASHBOARD_EXPORT=html-report.html + - VUS=50 + - DURATION=180s + - GRACEFUL_STOP=10s command: ["run", "/LoadTests/scriptHttp.js", ] volumes: - ./tests/LoadTests:/LoadTests - networks: - - hexagonal_solution_template_network bff: container_name: bff @@ -24,12 +27,12 @@ services: context: . dockerfile: ./Dockerfile ports: - - "5000:5000" + - "5011:5011" depends_on: redis: condition: service_healthy healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5000/health" ] + test: ["CMD", "curl", "-f", "http://localhost:5011/health" ] interval: 30s timeout: 10s retries: 5 @@ -40,8 +43,6 @@ services: - OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4317 - OTEL_EXPORTER_OTLP_PROTOCOL=grpc - OTEL_RESOURCE_ATTRIBUTES=service.namespace=load-tests - networks: - - hexagonal_solution_template_network mock-api: extends: @@ -78,11 +79,6 @@ services: file: docker-compose.yml service: tempo-init - memcached: - extends: - file: docker-compose.yml - service: memcached - tempo: extends: file: docker-compose.yml diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index f3264707..f104c1a7 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -92,15 +92,6 @@ services: volumes: - ./scripts/grafana/tempo-data:/var/tempo - memcached: - container_name: tempo-memcached - image: memcached:1.6.29 - ports: - - "11211:11211" - environment: - - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage - - MEMCACHED_THREADS=4 # Number of threads to use - tempo: container_name: tempo image: *tempoImage @@ -111,4 +102,4 @@ services: - "3200:3200" # tempo depends_on: - tempo-init - - memcached + - redis diff --git a/templates/Bff/scripts/grafana/tempo.yaml b/templates/Bff/scripts/grafana/tempo.yaml index 735326c2..94b3d42f 100644 --- a/templates/Bff/scripts/grafana/tempo.yaml +++ b/templates/Bff/scripts/grafana/tempo.yaml @@ -3,15 +3,14 @@ server: http_listen_port: 3200 log_level: info - cache: background: writeback_goroutines: 5 caches: - roles: - frontend-search - memcached: - host: memcached:11211 + redis: + endpoint: redis:6379 query_frontend: search: diff --git a/templates/Bff/src/WebApp/Program.cs b/templates/Bff/src/WebApp/Program.cs index 783fce29..177e8209 100644 --- a/templates/Bff/src/WebApp/Program.cs +++ b/templates/Bff/src/WebApp/Program.cs @@ -53,7 +53,8 @@ private static async Task Main(string[] args) app.UseHttpsRedirection(); - app.UseRateLimiter(); + if (app.Configuration.GetValue("RATE_LIMITING_ENABLED")) + app.UseRateLimiter(); app.MapEndpoints() .UseCustomHealthChecks() diff --git a/templates/Bff/tests/LoadTests/scriptGrpc.js b/templates/Bff/tests/LoadTests/scriptGrpc.js index 457d6bcf..1bef07a9 100644 --- a/templates/Bff/tests/LoadTests/scriptGrpc.js +++ b/templates/Bff/tests/LoadTests/scriptGrpc.js @@ -7,9 +7,9 @@ export const options = { get_order: { exec: 'getOrder', executor: 'constant-vus', - vus: 10, - duration: '60s', - gracefulStop: '10s' + vus: __ENV.VUS ? parseInt(__ENV.VUS) : 10, + duration: __ENV.DURATION ? __ENV.DURATION : '60s', + gracefulStop: __ENV.GRACEFUL_STOP ? __ENV.GRACEFUL_STOP : '10s' } }, thresholds: { diff --git a/templates/Bff/tests/LoadTests/scriptHttp.js b/templates/Bff/tests/LoadTests/scriptHttp.js index f36a1164..5967d804 100644 --- a/templates/Bff/tests/LoadTests/scriptHttp.js +++ b/templates/Bff/tests/LoadTests/scriptHttp.js @@ -7,9 +7,9 @@ export const options = { get_order: { exec: 'getOrder', executor: 'constant-vus', - vus: 10, - duration: '60s', - gracefulStop: '10s' + vus: __ENV.VUS ? parseInt(__ENV.VUS) : 10, + duration: __ENV.DURATION ? __ENV.DURATION : '60s', + gracefulStop: __ENV.GRACEFUL_STOP ? __ENV.GRACEFUL_STOP : '10s' } }, thresholds: { From 6461e9920b56b8a924d6a8dcf46f37b554373168 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 06:44:21 -0300 Subject: [PATCH 133/161] 53 feat: update docker-compose files and remove memcached service --- templates/Full/docker-compose-grafana.yml | 85 ------------------- templates/Full/docker-compose-load-tests.yml | 47 ++-------- templates/Full/docker-compose.yml | 72 ++++++++++++++++ templates/Full/scripts/grafana/config.alloy | 13 ++- .../Full/scripts/grafana/loki-config.yaml | 1 - templates/Full/scripts/grafana/tempo.yaml | 5 +- 6 files changed, 90 insertions(+), 133 deletions(-) delete mode 100644 templates/Full/docker-compose-grafana.yml diff --git a/templates/Full/docker-compose-grafana.yml b/templates/Full/docker-compose-grafana.yml deleted file mode 100644 index 66065470..00000000 --- a/templates/Full/docker-compose-grafana.yml +++ /dev/null @@ -1,85 +0,0 @@ -# Sample from: https://github.com/grafana/adventure/blob/main/docker-compose.yml -name: hexagonal-solution-template-grafana-full -services: - alloy: - container_name: alloy - image: grafana/alloy:v1.7.5 - ports: - - 12345:12345 - - 4318:4318 - - 4317:4317 - volumes: - - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy - - /var/lib/docker/containers:/var/lib/docker/containers:ro - - /var/run/docker.sock:/var/run/docker.sock - command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy - depends_on: - - loki - - prometheus - - tempo - - prometheus: - container_name: prometheus - image: prom/prometheus:v3.1.0 - ports: - - 9090:9090 - volumes: - - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml - command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage - - loki: - container_name: loki - image: grafana/loki:3.4.2 - ports: - - "3100:3100" - volumes: - - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml - command: -config.file=/etc/loki/local-config.yaml - - grafana: - container_name: grafana - image: grafana/grafana:11.5.2 - environment: - - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin - - GF_AUTH_ANONYMOUS_ENABLED=true - - GF_AUTH_BASIC_ENABLED=false - - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall - ports: - - 3000:3000/tcp - volumes: - - ./scripts/grafana:/etc/grafana/provisioning - - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml - - # Tempo runs as user 10001, and docker compose creates the volume as root. - # As such, we need to chown the volume in order for Tempo to start correctly. - tempo-init: - container_name: tempo-init - image: &tempoImage grafana/tempo:2.7.1 - user: root - entrypoint: - - "chown" - - "10001:10001" - - "/var/tempo" - volumes: - - ./scripts/grafana/tempo-data:/var/tempo - - memcached: - container_name: tempo-memcached - image: memcached:1.6.29 - ports: - - "11211:11211" - environment: - - MEMCACHED_MAX_MEMORY=64m # Set the maximum memory usage - - MEMCACHED_THREADS=4 # Number of threads to use - - tempo: - container_name: tempo - image: *tempoImage - command: [ "-config.file=/etc/tempo.yaml" ] - volumes: - - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml - ports: - - "3200:3200" # tempo - depends_on: - - tempo-init - - memcached \ No newline at end of file diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index 4ed8b9b1..fd0599fa 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -18,8 +18,6 @@ services: command: ["run", "/LoadTests/scriptHttp.js", ] volumes: - ./tests/LoadTests:/LoadTests - networks: - - hexagonal_solution_template_full_network webapp: container_name: webapp @@ -53,93 +51,60 @@ services: - ConnectionStrings__OrderDb=Host=postgres;Port=5432;Database=OrderDb;Username=postgres;Password=cY5VvZkkh4AzES - ConnectionStrings__Redis=redis:6379 - ConnectionStrings__RabbitMQ=amqp://guest:guest@rabbitmq:5672/ - networks: - - hexagonal_solution_template_full_network postgres: extends: file: docker-compose.yml service: postgres - networks: - - hexagonal_solution_template_full_network postgres-init: extends: file: docker-compose.yml service: postgres-init - networks: - - hexagonal_solution_template_full_network pgadmin: extends: file: docker-compose.yml service: pgadmin - networks: - - hexagonal_solution_template_full_network redis: extends: file: docker-compose.yml service: redis - networks: - - hexagonal_solution_template_full_network rabbitmq: extends: file: docker-compose.yml service: rabbitmq - networks: - - hexagonal_solution_template_full_network alloy: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: alloy - networks: - - hexagonal_solution_template_full_network prometheus: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: prometheus - networks: - - hexagonal_solution_template_full_network loki: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: loki - networks: - - hexagonal_solution_template_full_network grafana: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: grafana - networks: - - hexagonal_solution_template_full_network tempo-init: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: tempo-init - networks: - - hexagonal_solution_template_full_network - - memcached: - extends: - file: docker-compose-grafana.yml - service: memcached - networks: - - hexagonal_solution_template_full_network tempo: extends: - file: docker-compose-grafana.yml + file: docker-compose.yml service: tempo networks: - hexagonal_solution_template_full_network - -networks: - hexagonal_solution_template_full_network: - driver: bridge diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index a7901e52..543cc563 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -78,3 +78,75 @@ services: timeout: 10s retries: 5 start_period: 15s + + alloy: + container_name: alloy + image: grafana/alloy:v1.7.5 + ports: + - 12345:12345 + - 4318:4318 + - 4317:4317 + volumes: + - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + depends_on: + - loki + - prometheus + - tempo + + prometheus: + container_name: prometheus + image: prom/prometheus:v3.1.0 + ports: + - 9090:9090 + volumes: + - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml + command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage + + loki: + container_name: loki + image: grafana/loki:3.4.2 + ports: + - "3100:3100" + volumes: + - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml + command: -config.file=/etc/loki/local-config.yaml + + grafana: + container_name: grafana + image: grafana/grafana:11.5.2 + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall + ports: + - 3000:3000/tcp + volumes: + - ./scripts/grafana:/etc/grafana/provisioning + - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + + tempo-init: + container_name: tempo-init + image: &tempoImage grafana/tempo:2.7.1 + user: root + entrypoint: + - "chown" + - "10001:10001" + - "/var/tempo" + volumes: + - ./scripts/grafana/tempo-data:/var/tempo + + tempo: + container_name: tempo + image: *tempoImage + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml + ports: + - "3200:3200" # tempo + depends_on: + - tempo-init + - redis \ No newline at end of file diff --git a/templates/Full/scripts/grafana/config.alloy b/templates/Full/scripts/grafana/config.alloy index 6e7d7f58..34d692e5 100644 --- a/templates/Full/scripts/grafana/config.alloy +++ b/templates/Full/scripts/grafana/config.alloy @@ -10,7 +10,15 @@ otelcol.receiver.otlp "default" { output { metrics = [otelcol.exporter.otlphttp.metrics.input] logs = [otelcol.exporter.otlphttp.logs.input] - traces = [otelcol.exporter.otlphttp.traces.input] + traces = [otelcol.exporter.otlphttp.traces.input,otelcol.connector.servicegraph.default.input] + } +} + +otelcol.connector.servicegraph "default" { + dimensions = ["http.method"] + + output { + metrics = [otelcol.exporter.otlphttp.metrics.input] } } @@ -18,7 +26,6 @@ otelcol.exporter.otlphttp "logs" { client { endpoint = "http://loki:3100/otlp" } - } otelcol.exporter.otlphttp "metrics" { @@ -29,7 +36,7 @@ otelcol.exporter.otlphttp "metrics" { otelcol.exporter.otlphttp "traces" { client { - endpoint = "http://tempo:4318" + endpoint = "http://tempo:4318" } } diff --git a/templates/Full/scripts/grafana/loki-config.yaml b/templates/Full/scripts/grafana/loki-config.yaml index a9eca0ee..0efbec82 100644 --- a/templates/Full/scripts/grafana/loki-config.yaml +++ b/templates/Full/scripts/grafana/loki-config.yaml @@ -1,4 +1,3 @@ - # This is a complete configuration to deploy Loki backed by the filesystem. # The index will be shipped to the storage via tsdb-shipper. diff --git a/templates/Full/scripts/grafana/tempo.yaml b/templates/Full/scripts/grafana/tempo.yaml index 735326c2..94b3d42f 100644 --- a/templates/Full/scripts/grafana/tempo.yaml +++ b/templates/Full/scripts/grafana/tempo.yaml @@ -3,15 +3,14 @@ server: http_listen_port: 3200 log_level: info - cache: background: writeback_goroutines: 5 caches: - roles: - frontend-search - memcached: - host: memcached:11211 + redis: + endpoint: redis:6379 query_frontend: search: From 62ff9434de6b97f178acc42d9a895f6073cb19f8 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 06:56:08 -0300 Subject: [PATCH 134/161] 53 feat: update load test configurations for dynamic parameters --- templates/Full/docker-compose-load-tests.yml | 5 +++-- templates/Full/tests/LoadTests/scriptGrpc.js | 6 +++--- templates/Full/tests/LoadTests/scriptHttp.js | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/templates/Full/docker-compose-load-tests.yml b/templates/Full/docker-compose-load-tests.yml index fd0599fa..b9aa3db5 100644 --- a/templates/Full/docker-compose-load-tests.yml +++ b/templates/Full/docker-compose-load-tests.yml @@ -15,6 +15,9 @@ services: - K6_SUMMARY_MODE=full - K6_WEB_DASHBOARD=true - K6_WEB_DASHBOARD_EXPORT=html-report.html + - VUS=50 + - DURATION=180s + - GRACEFUL_STOP=10s command: ["run", "/LoadTests/scriptHttp.js", ] volumes: - ./tests/LoadTests:/LoadTests @@ -106,5 +109,3 @@ services: extends: file: docker-compose.yml service: tempo - networks: - - hexagonal_solution_template_full_network diff --git a/templates/Full/tests/LoadTests/scriptGrpc.js b/templates/Full/tests/LoadTests/scriptGrpc.js index 457d6bcf..1bef07a9 100644 --- a/templates/Full/tests/LoadTests/scriptGrpc.js +++ b/templates/Full/tests/LoadTests/scriptGrpc.js @@ -7,9 +7,9 @@ export const options = { get_order: { exec: 'getOrder', executor: 'constant-vus', - vus: 10, - duration: '60s', - gracefulStop: '10s' + vus: __ENV.VUS ? parseInt(__ENV.VUS) : 10, + duration: __ENV.DURATION ? __ENV.DURATION : '60s', + gracefulStop: __ENV.GRACEFUL_STOP ? __ENV.GRACEFUL_STOP : '10s' } }, thresholds: { diff --git a/templates/Full/tests/LoadTests/scriptHttp.js b/templates/Full/tests/LoadTests/scriptHttp.js index f36a1164..5967d804 100644 --- a/templates/Full/tests/LoadTests/scriptHttp.js +++ b/templates/Full/tests/LoadTests/scriptHttp.js @@ -7,9 +7,9 @@ export const options = { get_order: { exec: 'getOrder', executor: 'constant-vus', - vus: 10, - duration: '60s', - gracefulStop: '10s' + vus: __ENV.VUS ? parseInt(__ENV.VUS) : 10, + duration: __ENV.DURATION ? __ENV.DURATION : '60s', + gracefulStop: __ENV.GRACEFUL_STOP ? __ENV.GRACEFUL_STOP : '10s' } }, thresholds: { From 532d083f4ac32e1d9d93764cb06332db1269127f Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 06:59:28 -0300 Subject: [PATCH 135/161] 53 fix: update start_period for healthchecks in docker-compose --- templates/Full/docker-compose.yml | 6 +++--- templates/Full/tests/LoadTests/scriptHttp.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 543cc563..45572f2f 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -13,7 +13,7 @@ services: interval: 3s timeout: 10s retries: 10 - start_period: 15s + start_period: 10s ports: - "5432:5432" @@ -57,7 +57,7 @@ services: interval: 3s timeout: 10s retries: 5 - start_period: 15s + start_period: 10s rabbitmq: image: rabbitmq:management @@ -77,7 +77,7 @@ services: interval: 3s timeout: 10s retries: 5 - start_period: 15s + start_period: 10s alloy: container_name: alloy diff --git a/templates/Full/tests/LoadTests/scriptHttp.js b/templates/Full/tests/LoadTests/scriptHttp.js index 5967d804..c870acb4 100644 --- a/templates/Full/tests/LoadTests/scriptHttp.js +++ b/templates/Full/tests/LoadTests/scriptHttp.js @@ -1,4 +1,4 @@ -import http, { get } from 'k6/http'; +import http from 'k6/http'; import { check, sleep } from 'k6'; import { Counter, Trend, Rate } from 'k6/metrics'; From ca11d514cb25cd8dd80e7432130ea9c6d9e0f89b Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 10:20:45 -0300 Subject: [PATCH 136/161] update package versions in packages.lock.json --- templates/Full/Directory.Packages.props | 30 +- .../Full/src/Application/packages.lock.json | 86 +-- templates/Full/src/Domain/packages.lock.json | 12 +- .../src/Infrastructure/packages.lock.json | 184 +++---- templates/Full/src/WebApp/packages.lock.json | 78 +-- .../tests/IntegrationTests/packages.lock.json | 488 +++++++++--------- .../Full/tests/UnitTests/packages.lock.json | 162 +++--- 7 files changed, 520 insertions(+), 520 deletions(-) diff --git a/templates/Full/Directory.Packages.props b/templates/Full/Directory.Packages.props index fded11fc..e621105c 100644 --- a/templates/Full/Directory.Packages.props +++ b/templates/Full/Directory.Packages.props @@ -11,23 +11,23 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + @@ -35,14 +35,14 @@ - + - + diff --git a/templates/Full/src/Application/packages.lock.json b/templates/Full/src/Application/packages.lock.json index f30a00bd..57833687 100644 --- a/templates/Full/src/Application/packages.lock.json +++ b/templates/Full/src/Application/packages.lock.json @@ -20,97 +20,97 @@ }, "Microsoft.EntityFrameworkCore": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5" } }, "Microsoft.Extensions.Logging": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2" + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" + "resolved": "10.0.5", + "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" + "resolved": "10.0.5", + "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", + "resolved": "10.0.5", + "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", + "resolved": "10.0.5", + "contentHash": "jUEXmkBUPdOS/MP9areK/sbKhdklq9+tEhvwfxGalZVnmyLUO5rrheNNutUBtvbZ7J8ECkG7/r2KXi/IFC06cA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.5, )" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } } } diff --git a/templates/Full/src/Domain/packages.lock.json b/templates/Full/src/Domain/packages.lock.json index 003574ab..34de2c6c 100644 --- a/templates/Full/src/Domain/packages.lock.json +++ b/templates/Full/src/Domain/packages.lock.json @@ -4,17 +4,17 @@ "net10.0": { "Microsoft.Extensions.DependencyInjection": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" } } } diff --git a/templates/Full/src/Infrastructure/packages.lock.json b/templates/Full/src/Infrastructure/packages.lock.json index fce12d85..eb62228c 100644 --- a/templates/Full/src/Infrastructure/packages.lock.json +++ b/templates/Full/src/Infrastructure/packages.lock.json @@ -4,57 +4,57 @@ "net10.0": { "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "tBwpyZ75SqcRjmZyhLYJVGKyfz1Z1dmsYtvJGZ2QLSI1tOHgMuLPTovunHA1JF9a0EWJs+WB6tVWmqo3DTL/MQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "gm6f0cC2w/2tcd4GeZJqEMruTercpIJfO5sSAFLtqTqblDBHgAFk70xwshUIUVX4I6sZwdEUSd1YxoKFk1AL0w==", "dependencies": { "Humanizer.Core": "2.14.1", "Microsoft.Build.Framework": "18.0.2", "Microsoft.CodeAnalysis.CSharp": "5.0.0", "Microsoft.CodeAnalysis.CSharp.Workspaces": "5.0.0", "Microsoft.CodeAnalysis.Workspaces.MSBuild": "5.0.0", - "Microsoft.EntityFrameworkCore.Relational": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.DependencyModel": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.EntityFrameworkCore.Relational": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyModel": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", "Mono.TextTemplating": "3.0.0", "Newtonsoft.Json": "13.0.3" } }, "Microsoft.Extensions.Caching.Hybrid": { "type": "Direct", - "requested": "[10.1.0, )", - "resolved": "10.1.0", - "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "requested": "[10.4.0, )", + "resolved": "10.4.0", + "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.4", + "Microsoft.Extensions.Caching.Memory": "10.0.4", + "Microsoft.Extensions.Logging.Abstractions": "10.0.4", + "Microsoft.Extensions.Options": "10.0.4" } }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "WEx0VM6KVv4Bf6lwe4WQTd4EixIfw38ZU3u/7zMe+uC5fOyiANu8Os/qyiqv2iEsIJb296tbd2E2BTaWIha3Vg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", "StackExchange.Redis": "2.7.27" } }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "Direct", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "E2+uSWxSB8LdsUVwPaqRWOcGOP92biry2JEwc0KJMdLJF+aZdczeIdEXVwEyv4nSVMQJH0o8tLhyAMiR6VF0lw==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "P6EwH0Q4xkaA264iNZDqCPhWt8pscfUGxXazDQg4noBfqjoOlk4hKWfvBjF9ZX3R/9JybRmmJfmxr2iBMj0EpA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "[10.0.0, 11.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[10.0.0, 11.0.0)", - "Npgsql": "10.0.0" + "Microsoft.EntityFrameworkCore": "[10.0.4, 11.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[10.0.4, 11.0.0)", + "Npgsql": "10.0.2" } }, "OpenTelemetry.Exporter.Console": { @@ -96,9 +96,9 @@ }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "requested": "[1.15.1, )", + "resolved": "1.15.1", + "contentHash": "wXaZTu6LHY8xcbRd6ClcrtjHqGVoGYCcArXEZA3iUjUcYSVYwDGyPU0PdkwTfylxv8JeCCVDQhVb0fT7xBJjGA==", "dependencies": { "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } @@ -166,9 +166,9 @@ }, "RabbitMQ.Client": { "type": "Direct", - "requested": "[7.2.0, )", - "resolved": "7.2.0", - "contentHash": "PPQ7cF7lwbhqC4up6en1bTUZlz06YqQwJecOJzsguTtyhNA7oL5uNDZIx/h6ZfcyPZV4V3DYKSCxfm4RUFLcbA==", + "requested": "[7.2.1, )", + "resolved": "7.2.1", + "contentHash": "YKXEfg9fVQiTKgZlvIhAfPSFaamEgi8DsQmisCH0IAsU4FYLrtoguDrDj6JtJVGUt40QPnBLRH6fTQcAC4qsOg==", "dependencies": { "System.Threading.RateLimiting": "8.0.0" } @@ -260,32 +260,32 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" + "resolved": "10.0.5", + "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" + "resolved": "10.0.5", + "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", + "resolved": "10.0.5", + "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", + "resolved": "10.0.5", + "contentHash": "jUEXmkBUPdOS/MP9areK/sbKhdklq9+tEhvwfxGalZVnmyLUO5rrheNNutUBtvbZ7J8ECkG7/r2KXi/IFC06cA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration": { @@ -299,10 +299,10 @@ }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", + "resolved": "10.0.5", + "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Binder": { @@ -316,13 +316,13 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "DHXzj/FKmUDmhzw7AHbMXbNNcgO8Cb9PDbYOUDICjFAdIALy2WbC6pm+dFKiAUNtcYMucZu2iMVw1ef/sdcZFw==" + "resolved": "10.0.5", + "contentHash": "xA4kkL+QS6KCAOKz/O0oquHs44Ob8J7zpBCNt3wjkBWDg5aCqfwG8rWWLsg5V86AM0sB849g9JjPjIdksTCIKg==" }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", @@ -355,10 +355,10 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.Configuration": { @@ -378,11 +378,11 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -399,8 +399,8 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "Microsoft.VisualStudio.SolutionPersistence": { "type": "Transitive", @@ -417,8 +417,8 @@ }, "Npgsql": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "xZAYhPOU2rUIFpV48xsqhCx9vXs6Y+0jX2LCoSEfDFYMw9jtAOUk3iQsCnDLrFIv9NT3JGMihn7nnuZsPKqJmA==", + "resolved": "10.0.2", + "contentHash": "q5RfBI+wywJSFUNDE1L4ZbHEHCFTblo8Uf6A6oe4feOUFYiUQXyAf9GBh5qEZpvJaHiEbpBPkQumjEhXCJxdrg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "10.0.0" } @@ -525,14 +525,14 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.2, )", - "Microsoft.Extensions.Logging": "[10.0.2, )" + "Microsoft.EntityFrameworkCore": "[10.0.5, )", + "Microsoft.Extensions.Logging": "[10.0.5, )" } }, "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.5, )" } }, "FluentValidation": { @@ -553,46 +553,46 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5" } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "uxmFjZEAB/KbsgWFSS4lLqkEHCfXxB2x0UcbiO4e5fCRpFFeTMSx/me6009nYJLu5IKlDwO1POh++P6RilFTDw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" + "Microsoft.EntityFrameworkCore": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2" + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Newtonsoft.Json": { diff --git a/templates/Full/src/WebApp/packages.lock.json b/templates/Full/src/WebApp/packages.lock.json index eb027aa7..1950c02f 100644 --- a/templates/Full/src/WebApp/packages.lock.json +++ b/templates/Full/src/WebApp/packages.lock.json @@ -112,18 +112,18 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" + "resolved": "10.0.5", + "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" + "resolved": "10.0.5", + "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg==" }, "Npgsql": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "xZAYhPOU2rUIFpV48xsqhCx9vXs6Y+0jX2LCoSEfDFYMw9jtAOUk3iQsCnDLrFIv9NT3JGMihn7nnuZsPKqJmA==" + "resolved": "10.0.2", + "contentHash": "q5RfBI+wywJSFUNDE1L4ZbHEHCFTblo8Uf6A6oe4feOUFYiUQXyAf9GBh5qEZpvJaHiEbpBPkQumjEhXCJxdrg==" }, "OpenTelemetry": { "type": "Transitive", @@ -165,7 +165,7 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.2, )" + "Microsoft.EntityFrameworkCore": "[10.0.5, )" } }, "domain": { @@ -175,21 +175,21 @@ "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", - "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", + "Microsoft.Extensions.Caching.Hybrid": "[10.4.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.5, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.1, )", "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.Runtime": "[1.15.0, )", "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.15.0-beta.1, )", - "RabbitMQ.Client": "[7.2.0, )", + "RabbitMQ.Client": "[7.2.1, )", "RabbitMQ.Client.OpenTelemetry": "[1.0.0-rc.2, )" } }, @@ -210,47 +210,47 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5" } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "uxmFjZEAB/KbsgWFSS4lLqkEHCfXxB2x0UcbiO4e5fCRpFFeTMSx/me6009nYJLu5IKlDwO1POh++P6RilFTDw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2" + "Microsoft.EntityFrameworkCore": "10.0.5" } }, "Microsoft.Extensions.Caching.Hybrid": { "type": "CentralTransitive", - "requested": "[10.1.0, )", - "resolved": "10.1.0", - "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==" + "requested": "[10.4.0, )", + "resolved": "10.4.0", + "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==" }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "WEx0VM6KVv4Bf6lwe4WQTd4EixIfw38ZU3u/7zMe+uC5fOyiANu8Os/qyiqv2iEsIJb296tbd2E2BTaWIha3Vg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==", "dependencies": { "StackExchange.Redis": "2.7.27" } }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "CentralTransitive", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "E2+uSWxSB8LdsUVwPaqRWOcGOP92biry2JEwc0KJMdLJF+aZdczeIdEXVwEyv4nSVMQJH0o8tLhyAMiR6VF0lw==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "P6EwH0Q4xkaA264iNZDqCPhWt8pscfUGxXazDQg4noBfqjoOlk4hKWfvBjF9ZX3R/9JybRmmJfmxr2iBMj0EpA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "[10.0.0, 11.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[10.0.0, 11.0.0)", - "Npgsql": "10.0.0" + "Microsoft.EntityFrameworkCore": "[10.0.4, 11.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[10.0.4, 11.0.0)", + "Npgsql": "10.0.2" } }, "OpenTelemetry.Exporter.Console": { @@ -291,9 +291,9 @@ }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "requested": "[1.15.1, )", + "resolved": "1.15.1", + "contentHash": "wXaZTu6LHY8xcbRd6ClcrtjHqGVoGYCcArXEZA3iUjUcYSVYwDGyPU0PdkwTfylxv8JeCCVDQhVb0fT7xBJjGA==", "dependencies": { "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } @@ -355,9 +355,9 @@ }, "RabbitMQ.Client": { "type": "CentralTransitive", - "requested": "[7.2.0, )", - "resolved": "7.2.0", - "contentHash": "PPQ7cF7lwbhqC4up6en1bTUZlz06YqQwJecOJzsguTtyhNA7oL5uNDZIx/h6ZfcyPZV4V3DYKSCxfm4RUFLcbA==" + "requested": "[7.2.1, )", + "resolved": "7.2.1", + "contentHash": "YKXEfg9fVQiTKgZlvIhAfPSFaamEgi8DsQmisCH0IAsU4FYLrtoguDrDj6JtJVGUt40QPnBLRH6fTQcAC4qsOg==" }, "RabbitMQ.Client.OpenTelemetry": { "type": "CentralTransitive", diff --git a/templates/Full/tests/IntegrationTests/packages.lock.json b/templates/Full/tests/IntegrationTests/packages.lock.json index 5c723b91..3a5f57ad 100644 --- a/templates/Full/tests/IntegrationTests/packages.lock.json +++ b/templates/Full/tests/IntegrationTests/packages.lock.json @@ -4,35 +4,35 @@ "net10.0": { "coverlet.collector": { "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "heVQl5tKYnnIDYlR1QMVGueYH6iriZTcZB6AjDczQNwZzxkjDIt9C84Pt4cCiZYrbo7jkZOYGWbs6Lo9wAtVLg==" }, "Microsoft.AspNetCore.Mvc.Testing": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "wXZUGX1hphnKUKEoi7nBuMjT6UOWJmkw4n424O44WSS20QPFYI9D/UiTkgog6S1N6sKFevGF2JmlcSGvKJH2ng==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "MfacYQ7jNzj6073YobyoFfXpNmGqrV1UCywTM339DOcYpfalcM4K4heFjV5k3dDkKkWOGWO/DV3hdmVRqFkIxA==", "dependencies": { - "Microsoft.AspNetCore.TestHost": "10.0.2", - "Microsoft.Extensions.DependencyModel": "10.0.2", - "Microsoft.Extensions.Hosting": "10.0.2" + "Microsoft.AspNetCore.TestHost": "10.0.5", + "Microsoft.Extensions.DependencyModel": "10.0.5", + "Microsoft.Extensions.Hosting": "10.0.5" } }, "Microsoft.AspNetCore.TestHost": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "LhgZh1EiJ8crslosaxgWUK4pA0xwwGsptYmmxFtZixNpTvkby4pjR1jVBt8vqGKkDeI91SLs051xowFg96+cgw==" + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "PJEdrZnnhvxIEXzDdvdZ38GvpdaiUfKkZ99kudS8riJwhowFb/Qh26Wjk9smrCWcYdMFQmpN5epGiL4o1s8LYA==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[18.0.1, )", - "resolved": "18.0.1", - "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", + "requested": "[18.3.0, )", + "resolved": "18.3.0", + "contentHash": "xW3kXuWRQtgoxJp4J+gdhHSQyK+6Wb/AZDSd7lMvuMRYlZ1tnpkojyfZlWilB5G4dmZ0Y0ZxU/M23TlubndNkw==", "dependencies": { - "Microsoft.CodeCoverage": "18.0.1", - "Microsoft.TestPlatform.TestHost": "18.0.1" + "Microsoft.CodeCoverage": "18.3.0", + "Microsoft.TestPlatform.TestHost": "18.3.0" } }, "xunit": { @@ -128,144 +128,144 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + "resolved": "18.3.0", + "contentHash": "23BNy/vziREC20Wwhb50K7+kZe0m07KlLWDQv4qjJ9tt3QjpDpDIqJFrhYHmMEo9xDkuSp55U/8h4bMF7MiB+g==" }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" + "resolved": "10.0.5", + "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" + "resolved": "10.0.5", + "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", + "resolved": "10.0.5", + "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", + "resolved": "10.0.5", + "contentHash": "jUEXmkBUPdOS/MP9areK/sbKhdklq9+tEhvwfxGalZVnmyLUO5rrheNNutUBtvbZ7J8ECkG7/r2KXi/IFC06cA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "Lws+o4DFw6p5NquRoYA3d5QVvi49ugNw7TxbW4QGLsL8F1LCCyJqWFy0+RMQ/hzUuS9aKV5NJ/XGAF5N9/RQcQ==", + "resolved": "10.0.5", + "contentHash": "8Rx5sqg04FttxrumyG6bmoRuFRgYzK6IVwF1i0/o0cXfKBdDeVpJejKHtJCMjyg9E/DNMVqpqOGe/tCT5gYvVA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", + "resolved": "10.0.5", + "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "/SdW50prUuenglSy7MXU3eVQkOk4/J4fjc+GIhv4NkTmaZOQyTqpVAYi8nRjNtOKHzCy7g5cSlOSgkbT7clLwQ==", + "resolved": "10.0.5", + "contentHash": "99Z4rjyXopb1MIazDSPcvwYCUdYNO01Cf1GUs2WUjIFAbkGmwzj2vPa2k+3pheJRV+YgNd2QqRKHAri0oBAU4Q==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.CommandLine": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "KhqLwsao0b40VNgWPdg/UdaW4mxHhrthTngL0G8wSb2YkNSGTrs2RrBzkouiw1eTeQfhYcy0tpGy5ETCO+xjfg==", + "resolved": "10.0.5", + "contentHash": "or9fOLopMUTJOQVJ3bou4aD6PwvsiKf4kZC4EE5sRRKSkmh+wfk/LekJXRjAX88X+1JA9zHjDo+5fiQ7z3MY/A==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "Y+wv67KiW7q0+PxAg3osjohrddgxl/7mMriRZPBcYUgOzOq/O2rTvAXbuy1vINIarT2YmENfePaDcu2KgvIDXw==", + "resolved": "10.0.5", + "contentHash": "tchMGQ+zVTO40np/Zzg2Li/TIR8bksQgg4UVXZa0OzeFCKWnIYtxE2FVs+eSmjPGCjMS2voZbwN/mUcYfpSTuA==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "6vStNVa/7hcT6VrYvVMGCWUl/QIKwNeQaSGnKw1E4RPpZbQbOjDsATCbrQUa0sFUs7LW8T9aZ2NBKttMz1+WuA==", + "resolved": "10.0.5", + "contentHash": "OhTr0O79dP49734lLTqVveivVX9sDXxbI/8vjELAZTHXqoN90mdpgTAgwicJED42iaHMCcZcK6Bj+8wNyBikaw==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", - "Microsoft.Extensions.FileProviders.Physical": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Physical": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "ovjOVr+rNxOT249iezwihlPNMaIJdBC6PMGeMnzhkm5EoKJWFjp3mmvtndfHY6A88X4wulXlidMhmjX8v6V/aw==", + "resolved": "10.0.5", + "contentHash": "brBM/WP0YAUYh2+QqSYVdK8eQHYQTtTEUJXJ+84Zkdo2buGLja9VSrMIhgoeBUU7JBmcskAib8Lb/N83bvxgYQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Configuration.FileExtensions": "10.0.2", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "4Doiw8ILZz60D3WuoKRPoUjcNO6a5tP+D1T3L4SdVm/AC8VcyEYgUVbpzu+XPCzstM2oPefLvOGXMde8C8UAKg==", + "resolved": "10.0.5", + "contentHash": "fhdG6UV9lIp70QhNkVyaHciUVq25IPFkczheVJL9bIFvmnJ+Zghaie6dWkDbbVmxZlHl9gj3zTDxMxJs5zNhIA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Configuration.Json": "10.0.2", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", - "Microsoft.Extensions.FileProviders.Physical": "10.0.2" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Json": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Physical": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "DHXzj/FKmUDmhzw7AHbMXbNNcgO8Cb9PDbYOUDICjFAdIALy2WbC6pm+dFKiAUNtcYMucZu2iMVw1ef/sdcZFw==" + "resolved": "10.0.5", + "contentHash": "xA4kkL+QS6KCAOKz/O0oquHs44Ob8J7zpBCNt3wjkBWDg5aCqfwG8rWWLsg5V86AM0sB849g9JjPjIdksTCIKg==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "+2lv/hi6VGnaJt4877BFkkySiMiHrKCeX7K5ElIshIKpF65o63zeXMnUR4U6EQ/eM75Hx7T8s3RcqnjdcnVB1A==", + "resolved": "10.0.5", + "contentHash": "vAJHd4yOpmKoK+jBuYV7a3y+Ab9U4ARCc29b6qvMy276RgJFw9LFs0DdsPqOL3ahwzyrX7tM+i4cCxU/RX0qAg==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.2", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.2" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.5" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "GaiaeKUuLuUbRPkUokndDuzonhO6dk4lcfGflHsCeXiJ5JrZxcyks1KuG6eB9pON16x/+9uWfa4w9g3oP8AYvQ==", + "resolved": "10.0.5", + "contentHash": "/nYGrpa9/0BZofrVpBbbj+Ns8ZesiPE0V/KxsuHgDgHQopIzN54nRaQGSuvPw16/kI9sW1Zox5yyAPqvf0Jz6A==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { @@ -286,66 +286,66 @@ }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "+r/eJ+slW/EmwWmH3En4gzRg1k6+yTqexoHBrMuz5fxsIKJA8MDiSGepjw/ko3XyNqg+w3dxQe+huoVXs5XDJw==", + "resolved": "10.0.5", + "contentHash": "nCBmCx0Xemlu65ZiWMcXbvfvtznKxf4/YYKF9R28QkqdI9lTikedGqzJ28/xmdGGsxUnsP5/3TQGpiPwVjK0dA==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "4+ypApaugtHIz5Q2Z3oC4+erDbOgy0HrMFYS3Nm3qmTXyqK7sU7LJWY9gci99Wcx6j7ivgk8kdCkgmvsA4t0Ow==", + "resolved": "10.0.5", + "contentHash": "dMu5kUPSfol1Rqhmr6nWPSmbFjDe9w6bkoKithG17bWTZA0UyKirTatM5mqYUN3mGpNA0MorlusIoVTh6J7o5g==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", - "Microsoft.Extensions.FileSystemGlobbing": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "XozoMaWcFIv1tv0LDF+YMeZYjiNiNIewpNdZ3TEoVGf8ROrp0hVoEdUyUBsI8oYGM5U3Z5hiNEv0j2Z5COnMgg==" + "resolved": "10.0.5", + "contentHash": "mOE3ARusNQR0a5x8YOcnUbfyyXGqoAWQtEc7qFOfNJgruDWQLo39Re+3/Lzj5pLPFuFYj8hN4dgKzaSQDKiOCw==" }, "Microsoft.Extensions.Hosting": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "qUU5XAayGD0wySbu9ntSLCjM3gudmDLzuWVBbnMKlIbdgqhr0dC1bfULpFnPCVSQsU5TaSblU1qShFyTRwSjrQ==", - "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Configuration.Binder": "10.0.2", - "Microsoft.Extensions.Configuration.CommandLine": "10.0.2", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.2", - "Microsoft.Extensions.Configuration.FileExtensions": "10.0.2", - "Microsoft.Extensions.Configuration.Json": "10.0.2", - "Microsoft.Extensions.Configuration.UserSecrets": "10.0.2", - "Microsoft.Extensions.DependencyInjection": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Diagnostics": "10.0.2", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", - "Microsoft.Extensions.FileProviders.Physical": "10.0.2", - "Microsoft.Extensions.Hosting.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Configuration": "10.0.2", - "Microsoft.Extensions.Logging.Console": "10.0.2", - "Microsoft.Extensions.Logging.Debug": "10.0.2", - "Microsoft.Extensions.Logging.EventLog": "10.0.2", - "Microsoft.Extensions.Logging.EventSource": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2" + "resolved": "10.0.5", + "contentHash": "8i7e5IBdiKLNqt/+ciWrS8U95Rv5DClaaj7ulkZbimnCi4uREWd+lXzkp3joofFuIPOlAzV4AckxLTIELv2jdg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Binder": "10.0.5", + "Microsoft.Extensions.Configuration.CommandLine": "10.0.5", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.5", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.5", + "Microsoft.Extensions.Configuration.Json": "10.0.5", + "Microsoft.Extensions.Configuration.UserSecrets": "10.0.5", + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Diagnostics": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Physical": "10.0.5", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Configuration": "10.0.5", + "Microsoft.Extensions.Logging.Console": "10.0.5", + "Microsoft.Extensions.Logging.Debug": "10.0.5", + "Microsoft.Extensions.Logging.EventLog": "10.0.5", + "Microsoft.Extensions.Logging.EventSource": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "CeAAPVOtI/wtBcHOwq6Pw3VPdGi+pNaGHZj6vfXX/5zr8beO9SyL7IOCSQ70BauFTAFS0QF7f6zu2A6hC8D6nw==", + "resolved": "10.0.5", + "contentHash": "+Wb7KAMVZTomwJkQrjuPTe5KBzGod7N8XeG+ScxRlkPOB4sZLG4ccVwjV4Phk5BCJt7uIMnGHVoN6ZMVploX+g==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.2", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Http": { @@ -363,98 +363,98 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "XVtNJfLZVTDmQS5RCUjIr7QEAgGhJ3yQ0L3PduN7rE4aijmqYl0pIF09ZSU8jgnxml91Mw59ze220g8S7anaOg==", + "resolved": "10.0.5", + "contentHash": "cSgxsDgfP0+gmVRPVoNHI/KIDavIZxh+CxE6tSLPlYTogqccDnjBFI9CgEsiNuMP6+fiuXUwhhlTz36uUEpwbQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Configuration.Binder": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.2" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Binder": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.5" } }, "Microsoft.Extensions.Logging.Console": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "Z6gBfpHqJsz2hGH+eUQUQI+DSHsDNhTKt8toHAtDhYFRlxUN1FKPKzNmTgSrAz1gtDTOBjDU5lQZ50463Ehw7A==", + "resolved": "10.0.5", + "contentHash": "PMs2gha2v24hvH5o5KQem5aNK4mN0BhhCWlMqsg9tzifWKzjeQi2tyPOP/RaWMVvalOhVLcrmoMYPqbnia/epg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Configuration": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Configuration": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Logging.Debug": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "LohXg8Uc+cGLzPhUTgwOR9Z/lqMFpDT+NylPFwT6yXZfJAm/QE+Gv14HNir9tfposwGl5IuR+3yfLOuc6PNHVg==", + "resolved": "10.0.5", + "contentHash": "/VacEkBQ02A8PBXSa6YpbIXCuisYy6JJr62/+ANJDZE+RMBfZMcXJXLfr/LpyLE6pgdp17Wxlt7e7R9zvkwZ3Q==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.EventLog": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "gRQ3bpTZRCr2Z3Qx4RNP6G23XnJMXmRCsIgWg542B/cp/Rz8Jv3nfINVNmeOCy4aGQ65VfHtoi6Rd8RzoG5a5g==", + "resolved": "10.0.5", + "contentHash": "0ezhWYJS4/6KrqQel9JL+Tr4n+4EX2TF5EYiaysBWNNEM2c3Gtj1moD39esfgk8OHblSX+UFjtZ3z0c4i9tRvw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "System.Diagnostics.EventLog": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "System.Diagnostics.EventLog": "10.0.5" } }, "Microsoft.Extensions.Logging.EventSource": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "PSn5a9GF1TmdlvsSnBsddcl1Qcrk/mfJbyXEV+RNJTV5GC/Tkm8DI344C/3LXy/YG1r+aIyjPB6/FlWoCmvEdA==", + "resolved": "10.0.5", + "contentHash": "vN+aq1hBFXyYvY5Ow9WyeR66drKQxRZmas4lAjh6QWfryPkjTn1uLtX5AFIxyDaZj78v5TG2sELUyvrXpAPQQw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "8njGDg0OdDBM4Zox0ybuUOJZkQ8HcH49F+POZBlG+nsfzEyqOCHyHEkWeRVI62qsssiugUVEKqUttT1ZbV0aJQ==", + "resolved": "10.0.5", + "contentHash": "BB9uUW3+6Rxu1R97OB1H/13lUF8P2+H1+eDhpZlK30kDh/6E4EKHBUqTp+ilXQmZLzsRErxON8aBSR6WpUKJdg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Configuration.Binder": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Binder": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -463,15 +463,15 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==" + "resolved": "18.3.0", + "contentHash": "AEIEX2aWdPO9XbtR96eBaJxmXRD9vaI9uQ1T/JbPEKlTAZwYx0ZrMzKyULMdh/HH9Sg03kXCoN7LszQ90o6nPQ==" }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", + "resolved": "18.3.0", + "contentHash": "twmsoelXnp1uWMU3VGip9f0Jr1mZ0PZqgJdF35CIrdYgYrkHIJMV1m8uKyhcdjLdsQDESHAgkR7KhS9i1qpJag==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "18.0.1", + "Microsoft.TestPlatform.ObjectModel": "18.3.0", "Newtonsoft.Json": "13.0.3" } }, @@ -485,8 +485,8 @@ }, "Npgsql": { "type": "Transitive", - "resolved": "10.0.0", - "contentHash": "xZAYhPOU2rUIFpV48xsqhCx9vXs6Y+0jX2LCoSEfDFYMw9jtAOUk3iQsCnDLrFIv9NT3JGMihn7nnuZsPKqJmA==", + "resolved": "10.0.2", + "contentHash": "q5RfBI+wywJSFUNDE1L4ZbHEHCFTblo8Uf6A6oe4feOUFYiUQXyAf9GBh5qEZpvJaHiEbpBPkQumjEhXCJxdrg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "10.0.0" } @@ -531,8 +531,8 @@ }, "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "TJPpTLF5MFPobq09c9BQ5X8QuviQfsKvH0Jbm7MkGylGvfIdRqJQLZDPC5sMRFkk9aZhmgir1NJKekip2NxfaA==" + "resolved": "10.0.5", + "contentHash": "wugvy+pBVzjQEnRs9wMTWwoaeNFX3hsaHeVHFDIvJSWXp7wfmNWu3mxAwBIE6pyW+g6+rHa1Of5fTzb0QVqUTA==" }, "System.Threading.RateLimiting": { "type": "Transitive", @@ -577,8 +577,8 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.2, )", - "Microsoft.Extensions.Logging": "[10.0.2, )" + "Microsoft.EntityFrameworkCore": "[10.0.5, )", + "Microsoft.Extensions.Logging": "[10.0.5, )" } }, "commontests": { @@ -592,28 +592,28 @@ "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.5, )" } }, "infrastructure": { "type": "Project", "dependencies": { "Application": "[1.0.0, )", - "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", - "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.2, )", - "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.0, )", + "Microsoft.Extensions.Caching.Hybrid": "[10.4.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.5, )", + "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.1, )", "OpenTelemetry.Instrumentation.EntityFrameworkCore": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.Runtime": "[1.15.0, )", "OpenTelemetry.Instrumentation.StackExchangeRedis": "[1.15.0-beta.1, )", - "RabbitMQ.Client": "[7.2.0, )", + "RabbitMQ.Client": "[7.2.1, )", "RabbitMQ.Client.OpenTelemetry": "[1.0.0-rc.2, )" } }, @@ -705,70 +705,70 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5" } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "uxmFjZEAB/KbsgWFSS4lLqkEHCfXxB2x0UcbiO4e5fCRpFFeTMSx/me6009nYJLu5IKlDwO1POh++P6RilFTDw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" + "Microsoft.EntityFrameworkCore": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5" } }, "Microsoft.Extensions.Caching.Hybrid": { "type": "CentralTransitive", - "requested": "[10.1.0, )", - "resolved": "10.1.0", - "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "requested": "[10.4.0, )", + "resolved": "10.4.0", + "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.4", + "Microsoft.Extensions.Caching.Memory": "10.0.4", + "Microsoft.Extensions.Logging.Abstractions": "10.0.4", + "Microsoft.Extensions.Options": "10.0.4" } }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "WEx0VM6KVv4Bf6lwe4WQTd4EixIfw38ZU3u/7zMe+uC5fOyiANu8Os/qyiqv2iEsIJb296tbd2E2BTaWIha3Vg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", "StackExchange.Redis": "2.7.27" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2" + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Newtonsoft.Json": { @@ -779,13 +779,13 @@ }, "Npgsql.EntityFrameworkCore.PostgreSQL": { "type": "CentralTransitive", - "requested": "[10.0.0, )", - "resolved": "10.0.0", - "contentHash": "E2+uSWxSB8LdsUVwPaqRWOcGOP92biry2JEwc0KJMdLJF+aZdczeIdEXVwEyv4nSVMQJH0o8tLhyAMiR6VF0lw==", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "P6EwH0Q4xkaA264iNZDqCPhWt8pscfUGxXazDQg4noBfqjoOlk4hKWfvBjF9ZX3R/9JybRmmJfmxr2iBMj0EpA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "[10.0.0, 11.0.0)", - "Microsoft.EntityFrameworkCore.Relational": "[10.0.0, 11.0.0)", - "Npgsql": "10.0.0" + "Microsoft.EntityFrameworkCore": "[10.0.4, 11.0.0)", + "Microsoft.EntityFrameworkCore.Relational": "[10.0.4, 11.0.0)", + "Npgsql": "10.0.2" } }, "OpenTelemetry.Exporter.Console": { @@ -827,9 +827,9 @@ }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "requested": "[1.15.1, )", + "resolved": "1.15.1", + "contentHash": "wXaZTu6LHY8xcbRd6ClcrtjHqGVoGYCcArXEZA3iUjUcYSVYwDGyPU0PdkwTfylxv8JeCCVDQhVb0fT7xBJjGA==", "dependencies": { "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } @@ -897,9 +897,9 @@ }, "RabbitMQ.Client": { "type": "CentralTransitive", - "requested": "[7.2.0, )", - "resolved": "7.2.0", - "contentHash": "PPQ7cF7lwbhqC4up6en1bTUZlz06YqQwJecOJzsguTtyhNA7oL5uNDZIx/h6ZfcyPZV4V3DYKSCxfm4RUFLcbA==", + "requested": "[7.2.1, )", + "resolved": "7.2.1", + "contentHash": "YKXEfg9fVQiTKgZlvIhAfPSFaamEgi8DsQmisCH0IAsU4FYLrtoguDrDj6JtJVGUt40QPnBLRH6fTQcAC4qsOg==", "dependencies": { "System.Threading.RateLimiting": "8.0.0" } diff --git a/templates/Full/tests/UnitTests/packages.lock.json b/templates/Full/tests/UnitTests/packages.lock.json index dcc6b5d9..98f491d9 100644 --- a/templates/Full/tests/UnitTests/packages.lock.json +++ b/templates/Full/tests/UnitTests/packages.lock.json @@ -4,9 +4,9 @@ "net10.0": { "coverlet.collector": { "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "heVQl5tKYnnIDYlR1QMVGueYH6iriZTcZB6AjDczQNwZzxkjDIt9C84Pt4cCiZYrbo7jkZOYGWbs6Lo9wAtVLg==" }, "ILogger.Moq": { "type": "Direct", @@ -20,21 +20,21 @@ }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[18.0.1, )", - "resolved": "18.0.1", - "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", + "requested": "[18.3.0, )", + "resolved": "18.3.0", + "contentHash": "xW3kXuWRQtgoxJp4J+gdhHSQyK+6Wb/AZDSd7lMvuMRYlZ1tnpkojyfZlWilB5G4dmZ0Y0ZxU/M23TlubndNkw==", "dependencies": { - "Microsoft.CodeCoverage": "18.0.1", - "Microsoft.TestPlatform.TestHost": "18.0.1" + "Microsoft.CodeCoverage": "18.3.0", + "Microsoft.TestPlatform.TestHost": "18.3.0" } }, "MockQueryable.Moq": { "type": "Direct", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "3qrQC5f4fYab1ttnwxfQ52bdc8bSRyRdKq6sqLJK/WnTw7+KY3DiyT7iIgUX054YiKIHeCxXvqR9OoH976NI6A==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "D6d2NvV0kShKih2OKINKnW7joRzfL+bdBjFNj0rw11EPp/3Mgqm5svwsvQ2UHDqqwgwavjQGfGL6zKB1iDJA3g==", "dependencies": { - "MockQueryable.EntityFrameworkCore": "10.0.2", + "MockQueryable.EntityFrameworkCore": "10.0.5", "Moq": "4.20.72" } }, @@ -91,73 +91,73 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + "resolved": "18.3.0", + "contentHash": "23BNy/vziREC20Wwhb50K7+kZe0m07KlLWDQv4qjJ9tt3QjpDpDIqJFrhYHmMEo9xDkuSp55U/8h4bMF7MiB+g==" }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "BzAwIU5mYeOmnKbEXrkwx7feW2V+zUTrK/kRonSib94tjvc0/iRj2a4N6YGXRhTNjaFP3tvCMIDaX1vIFF6dkg==" + "resolved": "10.0.5", + "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "5C8aKN2HFhXfLhkrVvQ6yH990nw35pS7HNnwdBc628zIREaKJ4/rkdCXzxOdZo/SnCT3Kg7a/CqnhlzuJhlD4Q==" + "resolved": "10.0.5", + "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "WIRPDa/qoKHmJhTAPCO/zLu9kRLQ2Fd6HD5tzgdXJ3xGEVXDHP6FvakKJjynwKrVDld8H4G4tcbW53wuC/wxMQ==", + "resolved": "10.0.5", + "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "MkdPYdtsu0Ta4m9Di4XnWVdO9u+wi1LtvisoR1EteIxsXWO/+3iyAPH6RZbw2lBlWZu9lastbl2YsHVIaL9j+g==", + "resolved": "10.0.5", + "contentHash": "jUEXmkBUPdOS/MP9areK/sbKhdklq9+tEhvwfxGalZVnmyLUO5rrheNNutUBtvbZ7J8ECkG7/r2KXi/IFC06cA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", + "resolved": "10.0.5", + "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", - "Microsoft.Extensions.Primitives": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", @@ -166,31 +166,31 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==" + "resolved": "18.3.0", + "contentHash": "AEIEX2aWdPO9XbtR96eBaJxmXRD9vaI9uQ1T/JbPEKlTAZwYx0ZrMzKyULMdh/HH9Sg03kXCoN7LszQ90o6nPQ==" }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", + "resolved": "18.3.0", + "contentHash": "twmsoelXnp1uWMU3VGip9f0Jr1mZ0PZqgJdF35CIrdYgYrkHIJMV1m8uKyhcdjLdsQDESHAgkR7KhS9i1qpJag==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "18.0.1", + "Microsoft.TestPlatform.ObjectModel": "18.3.0", "Newtonsoft.Json": "13.0.3" } }, "MockQueryable.Core": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "cIxlylfWTobgbj43B8GJnjjzlOq7aJDCiUtOlDBSB9oBzlaElihmq3qO7o/LP5YxPz6HeMZEKY7uMwlJe4CR/g==" + "resolved": "10.0.5", + "contentHash": "8F5qLtCaXx9L2cBiq1ryF2McglfMDoDZbWFz4L+WaS7trf2i7KON57VMZAqWR7jmF7Nqoq69qrKKIVqUUHLdbw==" }, "MockQueryable.EntityFrameworkCore": { "type": "Transitive", - "resolved": "10.0.2", - "contentHash": "CuVbsrABnV9J91NbHWXu13d0jE4L1sn3dgwTOHLoy1JKnKHohSV0ssHBuGibmWEHBdAStlxORIImxY+qmRm0uQ==", + "resolved": "10.0.5", + "contentHash": "zQe2B4JQoX6/tRh+G3mkhvZZ+Hr4rrNU3HdwEYpgHGp4yJ6vtuvxIDJ/55DoCMXDE6WX2NmUpc4hkMggRg+69g==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2", - "Microsoft.EntityFrameworkCore.Relational": "10.0.2", - "MockQueryable.Core": "10.0.2" + "Microsoft.EntityFrameworkCore": "10.0.5", + "Microsoft.EntityFrameworkCore.Relational": "10.0.5", + "MockQueryable.Core": "10.0.5" } }, "Mono.Cecil": { @@ -249,8 +249,8 @@ "Domain": "[1.0.0, )", "FluentValidation": "[12.1.1, )", "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )", - "Microsoft.EntityFrameworkCore": "[10.0.2, )", - "Microsoft.Extensions.Logging": "[10.0.2, )" + "Microsoft.EntityFrameworkCore": "[10.0.5, )", + "Microsoft.Extensions.Logging": "[10.0.5, )" } }, "commontests": { @@ -264,7 +264,7 @@ "domain": { "type": "Project", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "[10.0.2, )" + "Microsoft.Extensions.DependencyInjection": "[10.0.5, )" } }, "AutoFixture": { @@ -294,46 +294,46 @@ }, "Microsoft.EntityFrameworkCore": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "d3+XKbLSHPCu3vwpXECoXcFbvGKmAhEeUmc1xy2czmuPnEF7rZN2HP5ZGMwCMbAKk4B01+nS4HixSMo2Vf/Y9g==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "10.0.2", - "Microsoft.EntityFrameworkCore.Analyzers": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5" } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "1fUeyNmqDNfMogJ2ut7OKO57/WGjjkHMYeX51SpA3PwP7ftbx8g/Z3fbErD+1q14DILrqJfsszYsYhGssBRfDg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "uxmFjZEAB/KbsgWFSS4lLqkEHCfXxB2x0UcbiO4e5fCRpFFeTMSx/me6009nYJLu5IKlDwO1POh++P6RilFTDw==", "dependencies": { - "Microsoft.EntityFrameworkCore": "10.0.2", - "Microsoft.Extensions.Caching.Memory": "10.0.2", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", - "Microsoft.Extensions.Logging": "10.0.2" + "Microsoft.EntityFrameworkCore": "10.0.5", + "Microsoft.Extensions.Caching.Memory": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.2, )", - "resolved": "10.0.2", - "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.2", - "Microsoft.Extensions.Logging.Abstractions": "10.0.2", - "Microsoft.Extensions.Options": "10.0.2" + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Newtonsoft.Json": { From bc42032e226e015f0dff87754985b0666e28721d Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 10:24:23 -0300 Subject: [PATCH 137/161] chore: update packages --- templates/Bff/Directory.Packages.props | 24 +- .../Bff/src/Contracts/packages.lock.json | 42 +- .../Bff/src/Infrastructure/packages.lock.json | 172 +++---- templates/Bff/src/MockApi/packages.lock.json | 6 +- templates/Bff/src/WebApp/packages.lock.json | 45 +- .../tests/IntegrationTests/packages.lock.json | 463 +++++++++--------- 6 files changed, 375 insertions(+), 377 deletions(-) diff --git a/templates/Bff/Directory.Packages.props b/templates/Bff/Directory.Packages.props index 265a0b3d..7a7040ba 100644 --- a/templates/Bff/Directory.Packages.props +++ b/templates/Bff/Directory.Packages.props @@ -10,19 +10,19 @@ - + - - - - - - - - - + + + + + + + + + @@ -30,13 +30,13 @@ - + - + diff --git a/templates/Bff/src/Contracts/packages.lock.json b/templates/Bff/src/Contracts/packages.lock.json index f7f2344e..2a36cb88 100644 --- a/templates/Bff/src/Contracts/packages.lock.json +++ b/templates/Bff/src/Contracts/packages.lock.json @@ -98,8 +98,8 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", @@ -135,19 +135,19 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { @@ -164,27 +164,27 @@ }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } } } diff --git a/templates/Bff/src/Infrastructure/packages.lock.json b/templates/Bff/src/Infrastructure/packages.lock.json index 26abfedf..4d967e3b 100644 --- a/templates/Bff/src/Infrastructure/packages.lock.json +++ b/templates/Bff/src/Infrastructure/packages.lock.json @@ -4,35 +4,35 @@ "net10.0": { "Microsoft.Extensions.Caching.Hybrid": { "type": "Direct", - "requested": "[10.1.0, )", - "resolved": "10.1.0", - "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "requested": "[10.4.0, )", + "resolved": "10.4.0", + "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.4", + "Microsoft.Extensions.Caching.Memory": "10.0.4", + "Microsoft.Extensions.Logging.Abstractions": "10.0.4", + "Microsoft.Extensions.Options": "10.0.4" } }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", "StackExchange.Redis": "2.7.27" } }, "Microsoft.Extensions.Http.Polly": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "J7xjjT+r6qxDcID37DJE6n45sReV43hOE/d87AnNJX5UPTv2qxKWGAyoKG3cjO1p0UCPhOVf6zlPIriDyJmNrQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "4VcH+2eVBQw3MtRpo02nrhv/nU54tL/pkcRF0fSSwD+8MoxgE1EjylPKbSIqHEK7iiB17I0iB37Ao8y+q1sV8g==", "dependencies": { - "Microsoft.Extensions.Http": "10.0.1", + "Microsoft.Extensions.Http": "10.0.5", "Polly": "7.2.4", "Polly.Extensions.Http": "3.0.0" } @@ -76,9 +76,9 @@ }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "requested": "[1.15.1, )", + "resolved": "1.15.1", + "contentHash": "wXaZTu6LHY8xcbRd6ClcrtjHqGVoGYCcArXEZA3iUjUcYSVYwDGyPU0PdkwTfylxv8JeCCVDQhVb0fT7xBJjGA==", "dependencies": { "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } @@ -193,72 +193,72 @@ }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "resolved": "10.0.5", + "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "resolved": "10.0.4", + "contentHash": "CLLussNUMdSbyJOu4VBF7sqskHGB/5N1EcFzrqG/HsPATN8fCRUcfp0qns1VwkxKHwxrtYCh5FKe+kM81Q1PHA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.4", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.4", + "Microsoft.Extensions.Logging.Abstractions": "10.0.4", + "Microsoft.Extensions.Options": "10.0.4", + "Microsoft.Extensions.Primitives": "10.0.4" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "resolved": "10.0.5", + "contentHash": "8Rx5sqg04FttxrumyG6bmoRuFRgYzK6IVwF1i0/o0cXfKBdDeVpJejKHtJCMjyg9E/DNMVqpqOGe/tCT5gYvVA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "resolved": "10.0.5", + "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "resolved": "10.0.5", + "contentHash": "99Z4rjyXopb1MIazDSPcvwYCUdYNO01Cf1GUs2WUjIFAbkGmwzj2vPa2k+3pheJRV+YgNd2QqRKHAri0oBAU4Q==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", + "resolved": "10.0.5", + "contentHash": "vAJHd4yOpmKoK+jBuYV7a3y+Ab9U4ARCc29b6qvMy276RgJFw9LFs0DdsPqOL3ahwzyrX7tM+i4cCxU/RX0qAg==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.5" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", + "resolved": "10.0.5", + "contentHash": "/nYGrpa9/0BZofrVpBbbj+Ns8ZesiPE0V/KxsuHgDgHQopIzN54nRaQGSuvPw16/kI9sW1Zox5yyAPqvf0Jz6A==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.FileProviders.Abstractions": { @@ -283,23 +283,23 @@ }, "Microsoft.Extensions.Http": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "ZXJup9ReE1Ot3M8jqcw1b/lnc8USxyYS3cyLsssU39u04TES9JNGviWUGIvP3K7mMU3TF7kQl2aS0SmVwegflw==", + "resolved": "10.0.5", + "contentHash": "AiFvHYM8nP0wPC7bGPI3NHQlSYSLqjjT7DMJUuuxhd+7pz3O89iu2gdQfgACy5DxsXENiok5i1bMacJL7KR8jA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Diagnostics": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Diagnostics": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.Configuration": { @@ -319,29 +319,29 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "resolved": "10.0.5", + "contentHash": "BB9uUW3+6Rxu1R97OB1H/13lUF8P2+H1+eDhpZlK30kDh/6E4EKHBUqTp+ilXQmZLzsRErxON8aBSR6WpUKJdg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Binder": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "OpenTelemetry": { "type": "Transitive", @@ -413,22 +413,22 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } } } diff --git a/templates/Bff/src/MockApi/packages.lock.json b/templates/Bff/src/MockApi/packages.lock.json index c7a021de..f336c9ba 100644 --- a/templates/Bff/src/MockApi/packages.lock.json +++ b/templates/Bff/src/MockApi/packages.lock.json @@ -58,9 +58,9 @@ }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "requested": "[1.15.1, )", + "resolved": "1.15.1", + "contentHash": "wXaZTu6LHY8xcbRd6ClcrtjHqGVoGYCcArXEZA3iUjUcYSVYwDGyPU0PdkwTfylxv8JeCCVDQhVb0fT7xBJjGA==", "dependencies": { "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } diff --git a/templates/Bff/src/WebApp/packages.lock.json b/templates/Bff/src/WebApp/packages.lock.json index ffaa4634..6d7e1f26 100644 --- a/templates/Bff/src/WebApp/packages.lock.json +++ b/templates/Bff/src/WebApp/packages.lock.json @@ -28,18 +28,18 @@ }, "Microsoft.AspNetCore.OpenApi": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "vTcxIfOPyfFbYk1g8YcXJfkMnlEWVkSnnjxcZLy60zgwiHMRf2SnZR+9E4HlpwKxgE3yfKMOti8J6WfKuKsw6w==", "dependencies": { "Microsoft.OpenApi": "2.0.0" } }, "Scalar.AspNetCore": { "type": "Direct", - "requested": "[2.12.4, )", - "resolved": "2.12.4", - "contentHash": "z2AsZCSuota9x0o/E1pptMD0hH65cJFaAIiuVmm34hntZy2zR9LusRQkUvWIStf+jwBSHzzFcli88kDlrcNJ3w==" + "requested": "[2.13.11, )", + "resolved": "2.13.11", + "contentHash": "bH99KIEEaYhC+mMM9011OJtou0y/9O2NXo6h9/k104sAniMzFSGKNaiIX6NRkxc487MJD8vYu3I3nJtW/nU/3g==" }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", @@ -164,15 +164,14 @@ "type": "Project", "dependencies": { "Contracts": "[1.0.0, )", - "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", - "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", - "Microsoft.Extensions.Http.Polly": "[10.0.1, )", + "Microsoft.Extensions.Caching.Hybrid": "[10.4.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.5, )", + "Microsoft.Extensions.Http.Polly": "[10.0.5, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "(, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.1, )", "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", @@ -193,24 +192,24 @@ }, "Microsoft.Extensions.Caching.Hybrid": { "type": "CentralTransitive", - "requested": "[10.1.0, )", - "resolved": "10.1.0", - "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==" + "requested": "[10.4.0, )", + "resolved": "10.4.0", + "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==" }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==", "dependencies": { "StackExchange.Redis": "2.7.27" } }, "Microsoft.Extensions.Http.Polly": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "J7xjjT+r6qxDcID37DJE6n45sReV43hOE/d87AnNJX5UPTv2qxKWGAyoKG3cjO1p0UCPhOVf6zlPIriDyJmNrQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "4VcH+2eVBQw3MtRpo02nrhv/nU54tL/pkcRF0fSSwD+8MoxgE1EjylPKbSIqHEK7iiB17I0iB37Ao8y+q1sV8g==", "dependencies": { "Polly": "7.2.4", "Polly.Extensions.Http": "3.0.0" @@ -254,9 +253,9 @@ }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "requested": "[1.15.1, )", + "resolved": "1.15.1", + "contentHash": "wXaZTu6LHY8xcbRd6ClcrtjHqGVoGYCcArXEZA3iUjUcYSVYwDGyPU0PdkwTfylxv8JeCCVDQhVb0fT7xBJjGA==", "dependencies": { "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } diff --git a/templates/Bff/tests/IntegrationTests/packages.lock.json b/templates/Bff/tests/IntegrationTests/packages.lock.json index 453d9d90..59692e6f 100644 --- a/templates/Bff/tests/IntegrationTests/packages.lock.json +++ b/templates/Bff/tests/IntegrationTests/packages.lock.json @@ -4,35 +4,35 @@ "net10.0": { "coverlet.collector": { "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "heVQl5tKYnnIDYlR1QMVGueYH6iriZTcZB6AjDczQNwZzxkjDIt9C84Pt4cCiZYrbo7jkZOYGWbs6Lo9wAtVLg==" }, "Microsoft.AspNetCore.Mvc.Testing": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "84aSUoB++qrL9mlkAT1ybV9KQ5bv7sbpx2B5uo9se0ryYjNPIeiuknVy7r0FOwRk8T58PYybhIBa7WOkdMgOZQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "MfacYQ7jNzj6073YobyoFfXpNmGqrV1UCywTM339DOcYpfalcM4K4heFjV5k3dDkKkWOGWO/DV3hdmVRqFkIxA==", "dependencies": { - "Microsoft.AspNetCore.TestHost": "10.0.1", - "Microsoft.Extensions.DependencyModel": "10.0.1", - "Microsoft.Extensions.Hosting": "10.0.1" + "Microsoft.AspNetCore.TestHost": "10.0.5", + "Microsoft.Extensions.DependencyModel": "10.0.5", + "Microsoft.Extensions.Hosting": "10.0.5" } }, "Microsoft.AspNetCore.TestHost": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "Vvos4CyBam5dCsH3eD1c9MQI4ESWwzNSJsToFz4i6NmfPsaySzNSiv0QYRmSAAIBXb8GXxPmuy42TkIrw2xCzQ==" + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "PJEdrZnnhvxIEXzDdvdZ38GvpdaiUfKkZ99kudS8riJwhowFb/Qh26Wjk9smrCWcYdMFQmpN5epGiL4o1s8LYA==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[18.0.1, )", - "resolved": "18.0.1", - "contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==", + "requested": "[18.3.0, )", + "resolved": "18.3.0", + "contentHash": "xW3kXuWRQtgoxJp4J+gdhHSQyK+6Wb/AZDSd7lMvuMRYlZ1tnpkojyfZlWilB5G4dmZ0Y0ZxU/M23TlubndNkw==", "dependencies": { - "Microsoft.CodeCoverage": "18.0.1", - "Microsoft.TestPlatform.TestHost": "18.0.1" + "Microsoft.CodeCoverage": "18.3.0", + "Microsoft.TestPlatform.TestHost": "18.3.0" } }, "xunit": { @@ -120,134 +120,134 @@ }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA==" + "resolved": "18.3.0", + "contentHash": "23BNy/vziREC20Wwhb50K7+kZe0m07KlLWDQv4qjJ9tt3QjpDpDIqJFrhYHmMEo9xDkuSp55U/8h4bMF7MiB+g==" }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Vb1vVAQDxHpXVdL9fpOX2BzeV7bbhzG4pAcIKRauRl0/VfkE8mq0f+fYC+gWICh3dlzTZInJ/cTeBS2MgU/XvQ==", + "resolved": "10.0.5", + "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "NxqSP0Ky4dZ5ybszdZCqs1X2C70s+dXflqhYBUh/vhcQVTIooNCXIYnLVbafoAFGZMs51d9+rHxveXs0ZC3SQQ==", + "resolved": "10.0.4", + "contentHash": "CLLussNUMdSbyJOu4VBF7sqskHGB/5N1EcFzrqG/HsPATN8fCRUcfp0qns1VwkxKHwxrtYCh5FKe+kM81Q1PHA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.4", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.4", + "Microsoft.Extensions.Logging.Abstractions": "10.0.4", + "Microsoft.Extensions.Options": "10.0.4", + "Microsoft.Extensions.Primitives": "10.0.4" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "resolved": "10.0.5", + "contentHash": "8Rx5sqg04FttxrumyG6bmoRuFRgYzK6IVwF1i0/o0cXfKBdDeVpJejKHtJCMjyg9E/DNMVqpqOGe/tCT5gYvVA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "resolved": "10.0.5", + "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "resolved": "10.0.5", + "contentHash": "99Z4rjyXopb1MIazDSPcvwYCUdYNO01Cf1GUs2WUjIFAbkGmwzj2vPa2k+3pheJRV+YgNd2QqRKHAri0oBAU4Q==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.CommandLine": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "s5cxcdtIig66YT3J+7iHflMuorznK8kXuwBBPHMp4KImx5ZGE3FRa1Nj9fI/xMwFV+KzUMjqZ2MhOedPH8LiBQ==", + "resolved": "10.0.5", + "contentHash": "or9fOLopMUTJOQVJ3bou4aD6PwvsiKf4kZC4EE5sRRKSkmh+wfk/LekJXRjAX88X+1JA9zHjDo+5fiQ7z3MY/A==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "csD8Eps3HQ3yc0x6NhgTV+aIFKSs3qVlVCtFnMHz/JOjnv7eEj/qSXKXiK9LzBzB1qSfAVqFnB5iaX2nUmagIQ==", + "resolved": "10.0.5", + "contentHash": "tchMGQ+zVTO40np/Zzg2Li/TIR8bksQgg4UVXZa0OzeFCKWnIYtxE2FVs+eSmjPGCjMS2voZbwN/mUcYfpSTuA==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "N/6GiwiZFCBFZDk3vg1PhHW3zMqqu5WWpmeZAA9VTXv7Q8pr8NZR/EQsH0DjzqydDksJtY6EQBsu81d5okQOlA==", + "resolved": "10.0.5", + "contentHash": "OhTr0O79dP49734lLTqVveivVX9sDXxbI/8vjELAZTHXqoN90mdpgTAgwicJED42iaHMCcZcK6Bj+8wNyBikaw==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Physical": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Physical": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "0zW3eYBJlRctmgqk5s0kFIi5o5y2g80mvGCD8bkYxREPQlKUnr0ndU/Sop+UDIhyWN0fIi4RW63vo7BKTi7ncA==", + "resolved": "10.0.5", + "contentHash": "brBM/WP0YAUYh2+QqSYVdK8eQHYQTtTEUJXJ+84Zkdo2buGLja9VSrMIhgoeBUU7JBmcskAib8Lb/N83bvxgYQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "ULEJ0nkaW90JYJGkFujPcJtADXcJpXiSOLbokPcWJZ8iDbtDINifEYAUVqZVr81IDNTrRFul6O8RolOKOsgFPg==", + "resolved": "10.0.5", + "contentHash": "fhdG6UV9lIp70QhNkVyaHciUVq25IPFkczheVJL9bIFvmnJ+Zghaie6dWkDbbVmxZlHl9gj3zTDxMxJs5zNhIA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Json": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Physical": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Json": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Physical": "10.0.5" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "resolved": "10.0.5", + "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "IiWPd4j8JLNjSkyXl5hvJwX2ZENDVQVPDHYgZmYdw8+YkY2xp9iQt0vjdnAQZLpo/ipeW1xgOqfSBEnivKWPYQ==" + "resolved": "10.0.5", + "contentHash": "xA4kkL+QS6KCAOKz/O0oquHs44Ob8J7zpBCNt3wjkBWDg5aCqfwG8rWWLsg5V86AM0sB849g9JjPjIdksTCIKg==" }, "Microsoft.Extensions.Diagnostics": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", + "resolved": "10.0.5", + "contentHash": "vAJHd4yOpmKoK+jBuYV7a3y+Ab9U4ARCc29b6qvMy276RgJFw9LFs0DdsPqOL3ahwzyrX7tM+i4cCxU/RX0qAg==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.5" } }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", + "resolved": "10.0.5", + "contentHash": "/nYGrpa9/0BZofrVpBbbj+Ns8ZesiPE0V/KxsuHgDgHQopIzN54nRaQGSuvPw16/kI9sW1Zox5yyAPqvf0Jz6A==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { @@ -268,175 +268,175 @@ }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", + "resolved": "10.0.5", + "contentHash": "nCBmCx0Xemlu65ZiWMcXbvfvtznKxf4/YYKF9R28QkqdI9lTikedGqzJ28/xmdGGsxUnsP5/3TQGpiPwVjK0dA==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "4bxzGXIzZnz0Bf7czQ72jGvpOqJsRW/44PS7YLFXTTnu6cNcPvmSREDvBoH0ZVP2hAbMfL4sUoCUn54k70jPWw==", + "resolved": "10.0.5", + "contentHash": "dMu5kUPSfol1Rqhmr6nWPSmbFjDe9w6bkoKithG17bWTZA0UyKirTatM5mqYUN3mGpNA0MorlusIoVTh6J7o5g==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileSystemGlobbing": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "49dFvGJjLSwGn76eHnP1gBvCJkL8HRYpCrG0DCvsP6wRpEQRLN2Fq8rTxbP+6jS7jmYKCnSVO5C65v4mT3rzeA==" + "resolved": "10.0.5", + "contentHash": "mOE3ARusNQR0a5x8YOcnUbfyyXGqoAWQtEc7qFOfNJgruDWQLo39Re+3/Lzj5pLPFuFYj8hN4dgKzaSQDKiOCw==" }, "Microsoft.Extensions.Hosting": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "0jjfjQSOFZlHhwOoIQw0WyzxtkDMYdnPY3iFrOLasxYq/5J4FDt1HWT8TktBclOVjFY1FOOkoOc99X7AhbqSIw==", - "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.Configuration.CommandLine": "10.0.1", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", - "Microsoft.Extensions.Configuration.Json": "10.0.1", - "Microsoft.Extensions.Configuration.UserSecrets": "10.0.1", - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Diagnostics": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Physical": "10.0.1", - "Microsoft.Extensions.Hosting.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Configuration": "10.0.1", - "Microsoft.Extensions.Logging.Console": "10.0.1", - "Microsoft.Extensions.Logging.Debug": "10.0.1", - "Microsoft.Extensions.Logging.EventLog": "10.0.1", - "Microsoft.Extensions.Logging.EventSource": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "resolved": "10.0.5", + "contentHash": "8i7e5IBdiKLNqt/+ciWrS8U95Rv5DClaaj7ulkZbimnCi4uREWd+lXzkp3joofFuIPOlAzV4AckxLTIELv2jdg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Binder": "10.0.5", + "Microsoft.Extensions.Configuration.CommandLine": "10.0.5", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.5", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.5", + "Microsoft.Extensions.Configuration.Json": "10.0.5", + "Microsoft.Extensions.Configuration.UserSecrets": "10.0.5", + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Diagnostics": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Physical": "10.0.5", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Configuration": "10.0.5", + "Microsoft.Extensions.Logging.Console": "10.0.5", + "Microsoft.Extensions.Logging.Debug": "10.0.5", + "Microsoft.Extensions.Logging.EventLog": "10.0.5", + "Microsoft.Extensions.Logging.EventSource": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", + "resolved": "10.0.5", + "contentHash": "+Wb7KAMVZTomwJkQrjuPTe5KBzGod7N8XeG+ScxRlkPOB4sZLG4ccVwjV4Phk5BCJt7uIMnGHVoN6ZMVploX+g==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", - "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Http": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "ZXJup9ReE1Ot3M8jqcw1b/lnc8USxyYS3cyLsssU39u04TES9JNGviWUGIvP3K7mMU3TF7kQl2aS0SmVwegflw==", + "resolved": "10.0.5", + "contentHash": "AiFvHYM8nP0wPC7bGPI3NHQlSYSLqjjT7DMJUuuxhd+7pz3O89iu2gdQfgACy5DxsXENiok5i1bMacJL7KR8jA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Diagnostics": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Diagnostics": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "resolved": "10.0.5", + "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", + "resolved": "10.0.5", + "contentHash": "cSgxsDgfP0+gmVRPVoNHI/KIDavIZxh+CxE6tSLPlYTogqccDnjBFI9CgEsiNuMP6+fiuXUwhhlTz36uUEpwbQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.5", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Binder": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.5" } }, "Microsoft.Extensions.Logging.Console": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", + "resolved": "10.0.5", + "contentHash": "PMs2gha2v24hvH5o5KQem5aNK4mN0BhhCWlMqsg9tzifWKzjeQi2tyPOP/RaWMVvalOhVLcrmoMYPqbnia/epg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Configuration": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Configuration": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Microsoft.Extensions.Logging.Debug": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "VqfTvbX9C6BA0VeIlpzPlljnNsXxiI5CdUHb9ksWERH94WQ6ft3oLGUAa4xKcDGu4xF+rIZ8wj7IOAd6/q7vGw==", + "resolved": "10.0.5", + "contentHash": "/VacEkBQ02A8PBXSa6YpbIXCuisYy6JJr62/+ANJDZE+RMBfZMcXJXLfr/LpyLE6pgdp17Wxlt7e7R9zvkwZ3Q==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Logging.EventLog": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Zp9MM+jFCa7oktIug62V9eNygpkf+6kFVatF+UC/ODeUwIr5givYKy8fYSSI9sWdxqDqv63y1x0mm2VjOl8GOw==", + "resolved": "10.0.5", + "contentHash": "0ezhWYJS4/6KrqQel9JL+Tr4n+4EX2TF5EYiaysBWNNEM2c3Gtj1moD39esfgk8OHblSX+UFjtZ3z0c4i9tRvw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "System.Diagnostics.EventLog": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "System.Diagnostics.EventLog": "10.0.5" } }, "Microsoft.Extensions.Logging.EventSource": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "WnFvZP+Y+lfeNFKPK/+mBpaCC7EeBDlobrQOqnP7rrw/+vE7yu8Rjczum1xbC0F/8cAHafog84DMp9200akMNQ==", + "resolved": "10.0.5", + "contentHash": "vN+aq1hBFXyYvY5Ow9WyeR66drKQxRZmas4lAjh6QWfryPkjTn1uLtX5AFIxyDaZj78v5TG2sELUyvrXpAPQQw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.5", + "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "resolved": "10.0.5", + "contentHash": "BB9uUW3+6Rxu1R97OB1H/13lUF8P2+H1+eDhpZlK30kDh/6E4EKHBUqTp+ilXQmZLzsRErxON8aBSR6WpUKJdg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.5", + "Microsoft.Extensions.Configuration.Binder": "10.0.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", + "Microsoft.Extensions.Primitives": "10.0.5" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.5", + "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g==" }, "Microsoft.OpenApi": { "type": "Transitive", @@ -445,15 +445,15 @@ }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==" + "resolved": "18.3.0", + "contentHash": "AEIEX2aWdPO9XbtR96eBaJxmXRD9vaI9uQ1T/JbPEKlTAZwYx0ZrMzKyULMdh/HH9Sg03kXCoN7LszQ90o6nPQ==" }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "18.0.1", - "contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==", + "resolved": "18.3.0", + "contentHash": "twmsoelXnp1uWMU3VGip9f0Jr1mZ0PZqgJdF35CIrdYgYrkHIJMV1m8uKyhcdjLdsQDESHAgkR7KhS9i1qpJag==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "18.0.1", + "Microsoft.TestPlatform.ObjectModel": "18.3.0", "Newtonsoft.Json": "13.0.3" } }, @@ -510,8 +510,8 @@ }, "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "xfaHEHVDkMOOZR5S6ZGezD0+vekdH1Nx/9Ih8/rOqOGSOk1fxiN3u94bYkBW/wigj0Uw2Wt3vvRj9mtYdgwEjw==" + "resolved": "10.0.5", + "contentHash": "wugvy+pBVzjQEnRs9wMTWwoaeNFX3hsaHeVHFDIvJSWXp7wfmNWu3mxAwBIE6pyW+g6+rHa1Of5fTzb0QVqUTA==" }, "xunit.abstractions": { "type": "Transitive", @@ -555,15 +555,14 @@ "type": "Project", "dependencies": { "Contracts": "[1.0.0, )", - "Microsoft.Extensions.Caching.Hybrid": "[10.1.0, )", - "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.1, )", - "Microsoft.Extensions.Http.Polly": "[10.0.1, )", + "Microsoft.Extensions.Caching.Hybrid": "[10.4.0, )", + "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.5, )", + "Microsoft.Extensions.Http.Polly": "[10.0.5, )", "OpenTelemetry.Exporter.Console": "[1.15.0, )", "OpenTelemetry.Exporter.OpenTelemetryProtocol": "[1.15.0, )", "OpenTelemetry.Extensions": "[1.14.0-beta.1, )", "OpenTelemetry.Extensions.Hosting": "[1.15.0, )", - "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.0, )", - "OpenTelemetry.Instrumentation.EntityFrameworkCore": "(, )", + "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.1, )", "OpenTelemetry.Instrumentation.GrpcNetClient": "[1.15.0-beta.1, )", "OpenTelemetry.Instrumentation.Http": "[1.15.0, )", "OpenTelemetry.Instrumentation.Process": "[1.15.0-beta.1, )", @@ -578,8 +577,8 @@ "AspNetCore.HealthChecks.UI.Client": "[9.0.0, )", "AspNetCore.HealthChecks.Uris": "[9.0.0, )", "Infrastructure": "[1.0.0, )", - "Microsoft.AspNetCore.OpenApi": "[10.0.1, )", - "Scalar.AspNetCore": "[2.12.4, )" + "Microsoft.AspNetCore.OpenApi": "[10.0.5, )", + "Scalar.AspNetCore": "[2.13.11, )" } }, "AspNetCore.HealthChecks.Redis": { @@ -624,66 +623,66 @@ }, "Microsoft.AspNetCore.OpenApi": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "vTcxIfOPyfFbYk1g8YcXJfkMnlEWVkSnnjxcZLy60zgwiHMRf2SnZR+9E4HlpwKxgE3yfKMOti8J6WfKuKsw6w==", "dependencies": { "Microsoft.OpenApi": "2.0.0" } }, "Microsoft.Extensions.Caching.Hybrid": { "type": "CentralTransitive", - "requested": "[10.1.0, )", - "resolved": "10.1.0", - "contentHash": "mcqlFN2TidtsD/ZUs+m5Xbj9oyNFtlHrawSp57DS8Pq6/Gf316sdSLdoo8i4LfQX5MFPQRdTMKddAQtfZ1uXxQ==", + "requested": "[10.4.0, )", + "resolved": "10.4.0", + "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Caching.Memory": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.Caching.Abstractions": "10.0.4", + "Microsoft.Extensions.Caching.Memory": "10.0.4", + "Microsoft.Extensions.Logging.Abstractions": "10.0.4", + "Microsoft.Extensions.Options": "10.0.4" } }, "Microsoft.Extensions.Caching.StackExchangeRedis": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "M7+349/EtaszydCxz/vtj4fUgbwE6NfDAfh98+oeWHPdBthgWKDCdnFV92p9UtyFN8Ln0e0w1ZzJvvbNzpMtaQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Caching.Abstractions": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5", "StackExchange.Redis": "2.7.27" } }, "Microsoft.Extensions.DependencyInjection": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5" } }, "Microsoft.Extensions.Http.Polly": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "J7xjjT+r6qxDcID37DJE6n45sReV43hOE/d87AnNJX5UPTv2qxKWGAyoKG3cjO1p0UCPhOVf6zlPIriDyJmNrQ==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "4VcH+2eVBQw3MtRpo02nrhv/nU54tL/pkcRF0fSSwD+8MoxgE1EjylPKbSIqHEK7iiB17I0iB37Ao8y+q1sV8g==", "dependencies": { - "Microsoft.Extensions.Http": "10.0.1", + "Microsoft.Extensions.Http": "10.0.5", "Polly": "7.2.4", "Polly.Extensions.Http": "3.0.0" } }, "Microsoft.Extensions.Logging": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.5, )", + "resolved": "10.0.5", + "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.5", + "Microsoft.Extensions.Logging.Abstractions": "10.0.5", + "Microsoft.Extensions.Options": "10.0.5" } }, "Newtonsoft.Json": { @@ -731,9 +730,9 @@ }, "OpenTelemetry.Instrumentation.AspNetCore": { "type": "CentralTransitive", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "mte1nRYefxjed2syXgVWq3UCfMKO7MkebvTZmf0O1aLgVgCktLsVjQ6mftyjIbWGBBCHN0wg+Glxj8BSFS70pQ==", + "requested": "[1.15.1, )", + "resolved": "1.15.1", + "contentHash": "wXaZTu6LHY8xcbRd6ClcrtjHqGVoGYCcArXEZA3iUjUcYSVYwDGyPU0PdkwTfylxv8JeCCVDQhVb0fT7xBJjGA==", "dependencies": { "OpenTelemetry.Api.ProviderBuilderExtensions": "[1.15.0, 2.0.0)" } @@ -790,9 +789,9 @@ }, "Scalar.AspNetCore": { "type": "CentralTransitive", - "requested": "[2.12.4, )", - "resolved": "2.12.4", - "contentHash": "z2AsZCSuota9x0o/E1pptMD0hH65cJFaAIiuVmm34hntZy2zR9LusRQkUvWIStf+jwBSHzzFcli88kDlrcNJ3w==" + "requested": "[2.13.11, )", + "resolved": "2.13.11", + "contentHash": "bH99KIEEaYhC+mMM9011OJtou0y/9O2NXo6h9/k104sAniMzFSGKNaiIX6NRkxc487MJD8vYu3I3nJtW/nU/3g==" }, "xunit.extensibility.core": { "type": "CentralTransitive", From 654c34666924ee4f9231aaecf326a6d359581673 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 10:31:17 -0300 Subject: [PATCH 138/161] 53 feat: update grafana datasource configurations and remove old file --- templates/Bff/docker-compose.yml | 5 +---- .../grafana/{grafana-datasources.yaml => datasources.yaml} | 0 templates/Bff/scripts/grafana/datasources/datasources.yaml | 0 templates/Full/docker-compose.yml | 5 +---- .../grafana/{grafana-datasources.yaml => datasources.yaml} | 0 templates/Full/scripts/grafana/datasources/datasources.yaml | 0 6 files changed, 2 insertions(+), 8 deletions(-) rename templates/Bff/scripts/grafana/{grafana-datasources.yaml => datasources.yaml} (100%) delete mode 100644 templates/Bff/scripts/grafana/datasources/datasources.yaml rename templates/Full/scripts/grafana/{grafana-datasources.yaml => datasources.yaml} (100%) delete mode 100644 templates/Full/scripts/grafana/datasources/datasources.yaml diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index f104c1a7..dec4aaf4 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -78,8 +78,7 @@ services: ports: - 3000:3000/tcp volumes: - - ./scripts/grafana:/etc/grafana/provisioning - - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + - ./scripts/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml tempo-init: container_name: tempo-init @@ -89,8 +88,6 @@ services: - "chown" - "10001:10001" - "/var/tempo" - volumes: - - ./scripts/grafana/tempo-data:/var/tempo tempo: container_name: tempo diff --git a/templates/Bff/scripts/grafana/grafana-datasources.yaml b/templates/Bff/scripts/grafana/datasources.yaml similarity index 100% rename from templates/Bff/scripts/grafana/grafana-datasources.yaml rename to templates/Bff/scripts/grafana/datasources.yaml diff --git a/templates/Bff/scripts/grafana/datasources/datasources.yaml b/templates/Bff/scripts/grafana/datasources/datasources.yaml deleted file mode 100644 index e69de29b..00000000 diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index 45572f2f..a9b94299 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -125,8 +125,7 @@ services: ports: - 3000:3000/tcp volumes: - - ./scripts/grafana:/etc/grafana/provisioning - - ./scripts/grafana/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + - ./scripts/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml tempo-init: container_name: tempo-init @@ -136,8 +135,6 @@ services: - "chown" - "10001:10001" - "/var/tempo" - volumes: - - ./scripts/grafana/tempo-data:/var/tempo tempo: container_name: tempo diff --git a/templates/Full/scripts/grafana/grafana-datasources.yaml b/templates/Full/scripts/grafana/datasources.yaml similarity index 100% rename from templates/Full/scripts/grafana/grafana-datasources.yaml rename to templates/Full/scripts/grafana/datasources.yaml diff --git a/templates/Full/scripts/grafana/datasources/datasources.yaml b/templates/Full/scripts/grafana/datasources/datasources.yaml deleted file mode 100644 index e69de29b..00000000 From 8bb5d84253c36c7cbffdf7ee02caddfc4440a20d Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 10:33:11 -0300 Subject: [PATCH 139/161] 53 refactor: remove unused meter configurations from OpenTelemetry --- .../Infrastructure/InfrastructureDependencyInjection.cs | 7 ------- .../InfrastructureOpenTelemetryDependencyInjection.cs | 7 ------- 2 files changed, 14 deletions(-) diff --git a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs index c37b9e08..2e838a6c 100644 --- a/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs +++ b/templates/Bff/src/Infrastructure/InfrastructureDependencyInjection.cs @@ -53,13 +53,6 @@ internal WebApplicationBuilder AddOpenTelemetry() builder.Services.AddOpenTelemetry() .WithMetrics(metrics => metrics - .AddMeter( - DefaultConfigurations.Meter.Name, - "System.Diagnostics.Metrics", - "Microsoft.AspNetCore.Hosting", - "Microsoft.AspNetCore.Server.Kestrel", - "System.Net.Http" - ) .SetResourceBuilder(resourceBuilder) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() diff --git a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs index 8945d6d7..047d5dc1 100644 --- a/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs +++ b/templates/Full/src/Infrastructure/OpenTelemetry/InfrastructureOpenTelemetryDependencyInjection.cs @@ -27,13 +27,6 @@ public WebApplicationBuilder AddOpenTelemetry() builder.Services.AddOpenTelemetry() .WithMetrics(metrics => metrics - .AddMeter( - DefaultConfigurations.Meter.Name, - "System.Diagnostics.Metrics", - "Microsoft.AspNetCore.Hosting", - "Microsoft.AspNetCore.Server.Kestrel", - "System.Net.Http" - ) .SetResourceBuilder(resourceBuilder) .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() From 8c793b53a22cf854cac8856682cd4ef7f36f1a77 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 16:57:48 -0300 Subject: [PATCH 140/161] 53 refactor: use EntityName for activity names in DomainEntity --- templates/Full/src/Domain/Common/DomainEntity.cs | 9 +++++++-- templates/Full/src/Domain/Orders/Order.cs | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/templates/Full/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs index 675be8ff..69448095 100644 --- a/templates/Full/src/Domain/Common/DomainEntity.cs +++ b/templates/Full/src/Domain/Common/DomainEntity.cs @@ -4,9 +4,14 @@ namespace Domain.Common; public abstract class DomainEntity { - protected DomainEntity() {} + protected virtual string EntityName { get; } + protected DomainEntity() + { + EntityName = GetType().Name; + } protected DomainEntity(string user, string? timezoneId = null) { + EntityName = GetType().Name; CreatedAt = DateTime.UtcNow; CreatedBy = user; CreatedByTimezoneId = TimeZoneInfo.FindSystemTimeZoneById(string.IsNullOrWhiteSpace(timezoneId) ? TimeZoneInfo.Utc.Id : timezoneId).Id; @@ -25,7 +30,7 @@ protected DomainEntity(string user, string? timezoneId = null) public virtual void Update(string? user = null, string? timezoneId = null) { - using var activity = ActivitySource.StartActivity($"{GetType().Name}.{nameof(Update)}"); + using var activity = ActivitySource.StartActivity($"{EntityName}.{nameof(Update)}"); UpdatedAt = DateTime.UtcNow; UpdatedBy = user ?? "System"; diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index 1503ec0d..d140aac4 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -13,7 +13,7 @@ public Order( string? timezoneId = null ) : base(createdBy ?? "System", timezoneId) { - using var activity = ActivitySource.StartActivity($"{GetType().Name}.Constructor"); + using var activity = ActivitySource.StartActivity($"{EntityName}.Constructor"); Description = description; Items = items; @@ -28,7 +28,7 @@ public Order( public Result SetTotal(string user = "System", string? timezoneId = null) { - using var activity = ActivitySource.StartActivity($"{GetType().Name}.{nameof(SetTotal)}"); + using var activity = ActivitySource.StartActivity($"{EntityName}.{nameof(SetTotal)}"); if (Items == null || Items.Count == 0) return Result.Fail("Order must have at least one item."); From 39544d9e28af786f85198ff00bca22023f4ce7f0 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Thu, 19 Mar 2026 17:07:28 -0300 Subject: [PATCH 141/161] 53 feat: add DomainException for item value validation --- .../Full/src/Domain/Common/DomainException.cs | 3 +++ templates/Full/src/Domain/Orders/Item.cs | 4 ++++ .../Full/tests/UnitTests/Domain/OrderTests.cs | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 templates/Full/src/Domain/Common/DomainException.cs diff --git a/templates/Full/src/Domain/Common/DomainException.cs b/templates/Full/src/Domain/Common/DomainException.cs new file mode 100644 index 00000000..d4094e14 --- /dev/null +++ b/templates/Full/src/Domain/Common/DomainException.cs @@ -0,0 +1,3 @@ +namespace Domain.Common; + +public sealed class DomainException(string message) : Exception(message); \ No newline at end of file diff --git a/templates/Full/src/Domain/Orders/Item.cs b/templates/Full/src/Domain/Orders/Item.cs index f9d20406..c8e6c76e 100644 --- a/templates/Full/src/Domain/Orders/Item.cs +++ b/templates/Full/src/Domain/Orders/Item.cs @@ -15,6 +15,10 @@ public Item( { Name = name; Description = description; + + if (value <= 0) + throw new DomainException("Item value cannot be zero or negative."); + Value = value; } diff --git a/templates/Full/tests/UnitTests/Domain/OrderTests.cs b/templates/Full/tests/UnitTests/Domain/OrderTests.cs index f263b5e8..92ca515e 100644 --- a/templates/Full/tests/UnitTests/Domain/OrderTests.cs +++ b/templates/Full/tests/UnitTests/Domain/OrderTests.cs @@ -1,4 +1,5 @@ -using Domain.Orders; +using Domain.Common; +using Domain.Orders; namespace UnitTests.Domain; @@ -62,6 +63,19 @@ public void GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldSe Assert.Equal("UTC", order.UpdatedByTimezoneId); } + [Fact(DisplayName = nameof(GivenANewItemWithValueZeroThenShouldBeFailure))] + public void GivenANewItemWithValueZeroThenShouldBeFailure() + { + // Arrange, Act + var exception = Assert.Throws(() => new Item("Mouse", "Razer", 0)); + + // Assert + Assert.NotNull(exception); + Assert.Equal("Item value cannot be zero or negative.", exception.Message); + } + + + [Fact(DisplayName = nameof(GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure))] public void GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure() { From 70d48fdbcf4394c96e8d51d3aaa5871c92d60116 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 20 Mar 2026 07:10:20 -0300 Subject: [PATCH 142/161] 53 feat: update workflows and project files for BFF architecture --- .github/workflows/publish.yml | 22 +++++++++---------- .github/workflows/validate.yml | 16 +++++++------- GPreviatti.Template.Hexagonal.Solution.csproj | 12 ++++++---- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b8735b2c..04771664 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,32 +9,32 @@ jobs: with: project: "hexagonal-solution-template-full" organization: "gpreviatti" - unit_test_project_path: "./Full/templates/tests/UnitTests/" + unit_test_project_path: "./templates/Full/tests/UnitTests/" unit_test_verbosity: 'd' dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" - domain_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-domain.json" - application_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-application.json" - integration_test_project_path: "./Full/templates/tests/IntegrationTests" - docker_compose_file_path: "./Full/templates/docker-compose.yml" + domain_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-domain.json" + application_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-application.json" + integration_test_project_path: "./templates/Full/tests/IntegrationTests" + docker_compose_file_path: "./templates/Full/docker-compose.yml" secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} - validate-minimal-template: + validate-bff-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: - project: "hexagonal-solution-template-minimal" + project: "hexagonal-solution-template-bff" organization: "gpreviatti" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" - integration_test_project_path: "./Minimal/templates/tests/IntegrationTests" - docker_compose_file_path: "./Minimal/templates/docker-compose.yml" + integration_test_project_path: "./templates/Bff/tests/IntegrationTests" + docker_compose_file_path: "./templates/Bff/docker-compose.yml" secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} pack-and-publish: - needs: [validate-full-template, validate-minimal-template] + needs: [validate-full-template, validate-bff-template] uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-pack.yml@v1 with: dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" - package_version: "10.0.3" + package_version: "10.1.0" secrets: nuget_api_key: ${{ secrets.NUGET_API_KEY }} diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 44940c7a..e8491da8 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -13,20 +13,20 @@ jobs: unit_test_project_path: "./Full/templates/tests/UnitTests/" unit_test_verbosity: 'd' dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" - domain_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-domain.json" - application_stryker_config_path: "./Full/templates/tests/UnitTests/stryker-config-application.json" - integration_test_project_path: "./Full/templates/tests/IntegrationTests" - docker_compose_file_path: "./Full/templates/docker-compose.yml" + domain_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-domain.json" + application_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-application.json" + integration_test_project_path: "./templates/Full/tests/IntegrationTests" + docker_compose_file_path: "./templates/Full/docker-compose.yml" secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} - validate-minimal-template: + validate-bff-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: - project: "hexagonal-solution-template-minimal" + project: "hexagonal-solution-template-bff" organization: "gpreviatti" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" - integration_test_project_path: "./Minimal/templates/tests/IntegrationTests" - docker_compose_file_path: "./Minimal/templates/docker-compose.yml" + integration_test_project_path: "./templates/Bff/tests/IntegrationTests" + docker_compose_file_path: "./templates/Bff/docker-compose.yml" secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/GPreviatti.Template.Hexagonal.Solution.csproj b/GPreviatti.Template.Hexagonal.Solution.csproj index af673d66..844afca5 100644 --- a/GPreviatti.Template.Hexagonal.Solution.csproj +++ b/GPreviatti.Template.Hexagonal.Solution.csproj @@ -18,10 +18,14 @@ git://github.com/gpreviatti/hexagonal-solution-template true - - Add docker support - - Add docker-compose file for load tests - - Add automatic assembly mapping for entities - - Simplify load tests methods definitions + - Add new new template with BFF architecture + - Remove Repository pattern since it's not needed with modern EF Core and adds unnecessary complexity + - Update dependencies to latest versions + - Update GitHub Actions workflows to test and analyze both templates + - Improve OpenTelemetry configuration and add more metrics and traces + - Remove Aspire Dashboard + - Add Grafana, Prometheus, Tempo and Loki configuration with Docker Compose for easier local development and testing of observability features + - Change DB to PostgreSQL since it's more widely used in production than SQL Server and works better with Docker From a5ed78b87d3a5e4efc3c860daf90cf3793948cdb Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 20 Mar 2026 07:13:29 -0300 Subject: [PATCH 143/161] 53 feat: add pull request template for better contribution guidelines --- .github/pull_request_template.md | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..cb5219f2 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,43 @@ +# Pull Request + +## 📌 Summary + + + +## 🎯 Type of change + +- [ ] Feature +- [ ] Bug fix +- [ ] Refactor +- [ ] Performance +- [ ] Documentation +- [ ] Chore / Build / CI + +## 🔗 Related work + + + +## 🧪 How was this tested? + + + +- [ ] Unit tests +- [ ] Integration tests +- [ ] Manual tests +- [ ] Not applicable + +## ✅ Checklist + +- [ ] I have reviewed my own changes +- [ ] I have updated documentation/comments when needed +- [ ] I have added or updated tests when needed +- [ ] I have verified no sensitive data/secrets were introduced +- [ ] This PR is ready for review + +## 📝 Additional notes (optional) + + + +## 🧾 Release notes (optional) + + \ No newline at end of file From 76ac176966d5ac919b72ab28bfd993ccf0d346f2 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 20 Mar 2026 10:55:42 -0300 Subject: [PATCH 144/161] 53 fix: correct unit test project path in validate workflow --- .github/workflows/validate.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index e8491da8..9b8073d6 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -10,7 +10,7 @@ jobs: with: project: "hexagonal-solution-template-full" organization: "gpreviatti" - unit_test_project_path: "./Full/templates/tests/UnitTests/" + unit_test_project_path: "./templates/Full/tests/UnitTests/" unit_test_verbosity: 'd' dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" domain_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-domain.json" @@ -24,9 +24,6 @@ jobs: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: project: "hexagonal-solution-template-bff" - organization: "gpreviatti" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" integration_test_project_path: "./templates/Bff/tests/IntegrationTests" - docker_compose_file_path: "./templates/Bff/docker-compose.yml" - secrets: - sonar_token: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file + docker_compose_file_path: "./templates/Bff/docker-compose.yml" \ No newline at end of file From 5f072e02bc2ad863836048d271e1a424ea209e59 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 20 Mar 2026 11:04:35 -0300 Subject: [PATCH 145/161] 53 feat: add build_path to validate workflows for templates --- .github/workflows/publish.yml | 3 ++- .github/workflows/validate.yml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 04771664..e7591ab2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,6 +7,7 @@ jobs: validate-full-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: + build_path: "./templates/Full" project: "hexagonal-solution-template-full" organization: "gpreviatti" unit_test_project_path: "./templates/Full/tests/UnitTests/" @@ -22,8 +23,8 @@ jobs: validate-bff-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: + build_path: "./templates/Bff" project: "hexagonal-solution-template-bff" - organization: "gpreviatti" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" integration_test_project_path: "./templates/Bff/tests/IntegrationTests" docker_compose_file_path: "./templates/Bff/docker-compose.yml" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 9b8073d6..ff978de1 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -8,6 +8,7 @@ jobs: validate-full-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: + build_path: "./templates/Full" project: "hexagonal-solution-template-full" organization: "gpreviatti" unit_test_project_path: "./templates/Full/tests/UnitTests/" @@ -23,6 +24,7 @@ jobs: validate-bff-template: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: + build_path: "./templates/Bff" project: "hexagonal-solution-template-bff" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" integration_test_project_path: "./templates/Bff/tests/IntegrationTests" From aeee20bf9a0935f6a99f249a32034146bb5ea30e Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 20 Mar 2026 11:29:05 -0300 Subject: [PATCH 146/161] 53 chore: remove unit test verbosity from workflow configurations --- .github/workflows/publish.yml | 1 - .github/workflows/validate.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e7591ab2..c1e16515 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,7 +11,6 @@ jobs: project: "hexagonal-solution-template-full" organization: "gpreviatti" unit_test_project_path: "./templates/Full/tests/UnitTests/" - unit_test_verbosity: 'd' dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" domain_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-domain.json" application_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-application.json" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index ff978de1..47a45cca 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -12,7 +12,6 @@ jobs: project: "hexagonal-solution-template-full" organization: "gpreviatti" unit_test_project_path: "./templates/Full/tests/UnitTests/" - unit_test_verbosity: 'd' dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" domain_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-domain.json" application_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-application.json" From b37a70b73e0143d6a11b1a2897f418ed02e8ea7c Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 20 Mar 2026 13:07:19 -0300 Subject: [PATCH 147/161] 53 refactor: rename validation job identifiers for consistency --- .github/workflows/publish.yml | 6 +++--- .github/workflows/validate.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c1e16515..1b262129 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: jobs: - validate-full-template: + validate-template-full: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Full" @@ -19,7 +19,7 @@ jobs: secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} - validate-bff-template: + validate-template-bff: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Bff" @@ -31,7 +31,7 @@ jobs: sonar_token: ${{ secrets.SONAR_TOKEN }} pack-and-publish: - needs: [validate-full-template, validate-bff-template] + needs: [validate-template-full, validate-template-bff] uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-pack.yml@v1 with: dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 47a45cca..30495b13 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -5,7 +5,7 @@ on: workflow_dispatch: jobs: - validate-full-template: + validate-template-full: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Full" @@ -20,7 +20,7 @@ jobs: secrets: sonar_token: ${{ secrets.SONAR_TOKEN }} - validate-bff-template: + validate-template-bff: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Bff" From bbbd89fb5e977b65521360d6ef148805a5980626 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 20 Mar 2026 13:27:40 -0300 Subject: [PATCH 148/161] 53 feat: add docker-compose files for full and local environments --- templates/Bff/docker-compose-local.yml | 102 ++++++++++++++++ templates/Bff/docker-compose.yml | 69 ----------- templates/Full/docker-compose-local.yml | 149 ++++++++++++++++++++++++ templates/Full/docker-compose.yml | 83 +------------ 4 files changed, 252 insertions(+), 151 deletions(-) create mode 100644 templates/Bff/docker-compose-local.yml create mode 100644 templates/Full/docker-compose-local.yml diff --git a/templates/Bff/docker-compose-local.yml b/templates/Bff/docker-compose-local.yml new file mode 100644 index 00000000..dec4aaf4 --- /dev/null +++ b/templates/Bff/docker-compose-local.yml @@ -0,0 +1,102 @@ +name: hexagonal-solution-template-bff + +services: + mock-api: + container_name: mock-api + build: + context: . + dockerfile: Dockerfile.MockApi + ports: + - "5012:5012" + healthcheck: + test: ["CMD", "curl", "--http2-prior-knowledge", "-v", "http://localhost:5012/health"] + interval: 3s + timeout: 10s + retries: 5 + start_period: 15s + environment: + - ASPNETCORE_ENVIRONMENT=Development + - OTEL_SERVICE_NAME=Hexagonal.Solution.Template.Bff.MockApi + - OTEL_EXPORTER_OTLP_ENDPOINT=http://alloy:4317 + - OTEL_EXPORTER_OTLP_PROTOCOL=grpc + + redis: + image: redis:8 + container_name: redis + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 3s + timeout: 10s + retries: 5 + start_period: 15s + + alloy: + container_name: alloy + image: grafana/alloy:v1.7.5 + ports: + - 12345:12345 + - 4318:4318 + - 4317:4317 + volumes: + - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + depends_on: + - loki + - prometheus + - tempo + + prometheus: + container_name: prometheus + image: prom/prometheus:v3.1.0 + ports: + - 9090:9090 + volumes: + - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml + command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage + + loki: + container_name: loki + image: grafana/loki:3.4.2 + ports: + - "3100:3100" + volumes: + - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml + command: -config.file=/etc/loki/local-config.yaml + + grafana: + container_name: grafana + image: grafana/grafana:11.5.2 + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall + ports: + - 3000:3000/tcp + volumes: + - ./scripts/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + + tempo-init: + container_name: tempo-init + image: &tempoImage grafana/tempo:2.7.1 + user: root + entrypoint: + - "chown" + - "10001:10001" + - "/var/tempo" + + tempo: + container_name: tempo + image: *tempoImage + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml + ports: + - "3200:3200" # tempo + depends_on: + - tempo-init + - redis diff --git a/templates/Bff/docker-compose.yml b/templates/Bff/docker-compose.yml index dec4aaf4..be73b356 100644 --- a/templates/Bff/docker-compose.yml +++ b/templates/Bff/docker-compose.yml @@ -31,72 +31,3 @@ services: timeout: 10s retries: 5 start_period: 15s - - alloy: - container_name: alloy - image: grafana/alloy:v1.7.5 - ports: - - 12345:12345 - - 4318:4318 - - 4317:4317 - volumes: - - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy - - /var/lib/docker/containers:/var/lib/docker/containers:ro - - /var/run/docker.sock:/var/run/docker.sock - command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy - depends_on: - - loki - - prometheus - - tempo - - prometheus: - container_name: prometheus - image: prom/prometheus:v3.1.0 - ports: - - 9090:9090 - volumes: - - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml - command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage - - loki: - container_name: loki - image: grafana/loki:3.4.2 - ports: - - "3100:3100" - volumes: - - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml - command: -config.file=/etc/loki/local-config.yaml - - grafana: - container_name: grafana - image: grafana/grafana:11.5.2 - environment: - - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin - - GF_AUTH_ANONYMOUS_ENABLED=true - - GF_AUTH_BASIC_ENABLED=false - - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall - ports: - - 3000:3000/tcp - volumes: - - ./scripts/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml - - tempo-init: - container_name: tempo-init - image: &tempoImage grafana/tempo:2.7.1 - user: root - entrypoint: - - "chown" - - "10001:10001" - - "/var/tempo" - - tempo: - container_name: tempo - image: *tempoImage - command: [ "-config.file=/etc/tempo.yaml" ] - volumes: - - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml - ports: - - "3200:3200" # tempo - depends_on: - - tempo-init - - redis diff --git a/templates/Full/docker-compose-local.yml b/templates/Full/docker-compose-local.yml new file mode 100644 index 00000000..a9b94299 --- /dev/null +++ b/templates/Full/docker-compose-local.yml @@ -0,0 +1,149 @@ +name: hexagonal-solution-template-full + +services: + postgres: + image: &postgresImage postgres:17-alpine + container_name: postgres + environment: + POSTGRES_PASSWORD: "cY5VvZkkh4AzES" + POSTGRES_USER: "postgres" + POSTGRES_DB: "OrderDb" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 3s + timeout: 10s + retries: 10 + start_period: 10s + ports: + - "5432:5432" + + postgres-init: + image: *postgresImage + container_name: postgres-init + depends_on: + postgres: + condition: service_healthy + volumes: + - ./scripts/sql/migrations.sql:/tmp/migrations.sql + - ./scripts/sql/seeds.sql:/tmp/seeds.sql + command: + - /bin/sh + - -c + - | + psql -h postgres -U postgres -d OrderDb -f /tmp/migrations.sql && + psql -h postgres -U postgres -d OrderDb -f /tmp/seeds.sql + environment: + PGPASSWORD: "cY5VvZkkh4AzES" + + pgadmin: + image: dpage/pgadmin4:9.12.0 + container_name: pgadmin + environment: + PGADMIN_DEFAULT_EMAIL: "admin@admin.com" + PGADMIN_DEFAULT_PASSWORD: "admin" + depends_on: + postgres: + condition: service_healthy + ports: + - "5050:80" + + redis: + image: redis:8 + container_name: redis + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 3s + timeout: 10s + retries: 5 + start_period: 10s + + rabbitmq: + image: rabbitmq:management + container_name: rabbitmq + ports: + - "5672:5672" + - "15672:15672" + - "15692:15692" + environment: + RABBITMQ_DEFAULT_USER: guest + RABBITMQ_DEFAULT_PASS: guest + command: > + bash -c "rabbitmq-plugins enable rabbitmq_prometheus && + docker-entrypoint.sh rabbitmq-server" + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "ping"] + interval: 3s + timeout: 10s + retries: 5 + start_period: 10s + + alloy: + container_name: alloy + image: grafana/alloy:v1.7.5 + ports: + - 12345:12345 + - 4318:4318 + - 4317:4317 + volumes: + - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /var/run/docker.sock:/var/run/docker.sock + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + depends_on: + - loki + - prometheus + - tempo + + prometheus: + container_name: prometheus + image: prom/prometheus:v3.1.0 + ports: + - 9090:9090 + volumes: + - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml + command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage + + loki: + container_name: loki + image: grafana/loki:3.4.2 + ports: + - "3100:3100" + volumes: + - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml + command: -config.file=/etc/loki/local-config.yaml + + grafana: + container_name: grafana + image: grafana/grafana:11.5.2 + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall + ports: + - 3000:3000/tcp + volumes: + - ./scripts/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + + tempo-init: + container_name: tempo-init + image: &tempoImage grafana/tempo:2.7.1 + user: root + entrypoint: + - "chown" + - "10001:10001" + - "/var/tempo" + + tempo: + container_name: tempo + image: *tempoImage + command: [ "-config.file=/etc/tempo.yaml" ] + volumes: + - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml + ports: + - "3200:3200" # tempo + depends_on: + - tempo-init + - redis \ No newline at end of file diff --git a/templates/Full/docker-compose.yml b/templates/Full/docker-compose.yml index a9b94299..f421cf90 100644 --- a/templates/Full/docker-compose.yml +++ b/templates/Full/docker-compose.yml @@ -35,18 +35,6 @@ services: environment: PGPASSWORD: "cY5VvZkkh4AzES" - pgadmin: - image: dpage/pgadmin4:9.12.0 - container_name: pgadmin - environment: - PGADMIN_DEFAULT_EMAIL: "admin@admin.com" - PGADMIN_DEFAULT_PASSWORD: "admin" - depends_on: - postgres: - condition: service_healthy - ports: - - "5050:80" - redis: image: redis:8 container_name: redis @@ -77,73 +65,4 @@ services: interval: 3s timeout: 10s retries: 5 - start_period: 10s - - alloy: - container_name: alloy - image: grafana/alloy:v1.7.5 - ports: - - 12345:12345 - - 4318:4318 - - 4317:4317 - volumes: - - ./scripts/grafana/config.alloy:/etc/alloy/config.alloy - - /var/lib/docker/containers:/var/lib/docker/containers:ro - - /var/run/docker.sock:/var/run/docker.sock - command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy - depends_on: - - loki - - prometheus - - tempo - - prometheus: - container_name: prometheus - image: prom/prometheus:v3.1.0 - ports: - - 9090:9090 - volumes: - - ./scripts/grafana/prometheus.yml:/etc/prometheus/prometheus.yml - command: --config.file=/etc/prometheus/prometheus.yml --web.enable-otlp-receiver --enable-feature=exemplar-storage - - loki: - container_name: loki - image: grafana/loki:3.4.2 - ports: - - "3100:3100" - volumes: - - ./scripts/grafana/loki-config.yaml:/etc/loki/local-config.yaml - command: -config.file=/etc/loki/local-config.yaml - - grafana: - container_name: grafana - image: grafana/grafana:11.5.2 - environment: - - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin - - GF_AUTH_ANONYMOUS_ENABLED=true - - GF_AUTH_BASIC_ENABLED=false - - GF_FEATURE_TOGGLES_ENABLE=accessControlOnCall - ports: - - 3000:3000/tcp - volumes: - - ./scripts/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml - - tempo-init: - container_name: tempo-init - image: &tempoImage grafana/tempo:2.7.1 - user: root - entrypoint: - - "chown" - - "10001:10001" - - "/var/tempo" - - tempo: - container_name: tempo - image: *tempoImage - command: [ "-config.file=/etc/tempo.yaml" ] - volumes: - - ./scripts/grafana/tempo.yaml:/etc/tempo.yaml - ports: - - "3200:3200" # tempo - depends_on: - - tempo-init - - redis \ No newline at end of file + start_period: 10s \ No newline at end of file From d4d46ebc7ecc4808af10084a5b527404d5f0c208 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Mon, 23 Mar 2026 06:53:37 -0300 Subject: [PATCH 149/161] feat(53): add test_no_build parameter to validate workflow --- .github/workflows/validate.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 30495b13..4c8531b5 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -11,6 +11,7 @@ jobs: build_path: "./templates/Full" project: "hexagonal-solution-template-full" organization: "gpreviatti" + test_no_build: false unit_test_project_path: "./templates/Full/tests/UnitTests/" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" domain_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-domain.json" From a5000156a8bacd03d50beb2198d8102075c9f5e9 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 1 Apr 2026 06:54:22 -0300 Subject: [PATCH 150/161] 53 feat: add launch and task configurations for development --- .vscode/launch.json | 47 +++++++++++++++++++ .vscode/tasks.json | 41 ++++++++++++++++ .../Common/Constants/NotificationType.cs | 6 --- .../Common/Enums/NotificationType.cs | 6 +++ .../Messages/CreateNotificationMessage.cs | 4 +- .../Common/UseCases/BaseInOutUseCase.cs | 4 +- .../Common/UseCases/BaseUseCase.cs | 24 ++++++++++ .../Application/Orders/CreateOrderUseCase.cs | 32 ++++--------- .../Full/src/Domain/Common/DomainEntity.cs | 2 +- templates/Full/src/Domain/Orders/Order.cs | 23 +++++++-- 10 files changed, 152 insertions(+), 37 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json delete mode 100644 templates/Full/src/Application/Common/Constants/NotificationType.cs create mode 100644 templates/Full/src/Application/Common/Enums/NotificationType.cs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..a0fc763c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Full [Development]", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/templates/Full/src/WebApp/bin/Debug/net10.0/WebApp.dll", + "args": [], + "cwd": "${workspaceFolder}/templates/Full/src/WebApp", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": "Bff [Development]", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/templates/Bff/src/WebApp/bin/Debug/net10.0/WebApp.dll", + "args": [], + "cwd": "${workspaceFolder}/templates/Bff/src/WebApp", + "stopAtEntry": false, + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..caabffa5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/templates/Bff/src/WebApp/WebApp.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/templates/Bff/src/WebApp/WebApp.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/templates/Bff/src/WebApp/WebApp.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/templates/Full/src/Application/Common/Constants/NotificationType.cs b/templates/Full/src/Application/Common/Constants/NotificationType.cs deleted file mode 100644 index d7b8c80b..00000000 --- a/templates/Full/src/Application/Common/Constants/NotificationType.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Application.Common.Constants; - -public static class NotificationType -{ - public const string OrderCreated = nameof(OrderCreated); -} diff --git a/templates/Full/src/Application/Common/Enums/NotificationType.cs b/templates/Full/src/Application/Common/Enums/NotificationType.cs new file mode 100644 index 00000000..b39a8166 --- /dev/null +++ b/templates/Full/src/Application/Common/Enums/NotificationType.cs @@ -0,0 +1,6 @@ +namespace Application.Common.Enums; + +public enum NotificationType +{ + OrderCreated +} diff --git a/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs b/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs index ac8c5716..3085150b 100644 --- a/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs +++ b/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs @@ -1,8 +1,10 @@ +using Application.Common.Enums; + namespace Application.Common.Messages; public sealed record CreateNotificationMessage( Guid CorrelationId, - string NotificationType, + NotificationType NotificationType, string NotificationStatus, string? CreatedBy = null, object? Message = null diff --git a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs index 2db387db..2c9e54ac 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInOutUseCase.cs @@ -19,7 +19,6 @@ public abstract class BaseInOutUseCase(IServiceProvider where TResponseData : BaseResponse { protected IHybridCacheService Cache { get; } = serviceProvider.GetRequiredService(); - protected IProduceService ProduceService { get; } = serviceProvider.GetRequiredService(); protected IBaseRepository Repository { get; } = serviceProvider.GetRequiredService(); private readonly IValidator _validator = serviceProvider.GetRequiredService>(); protected const string HandleMethodName = nameof(HandleAsync); @@ -40,6 +39,7 @@ CancellationToken cancellationToken if (!validationResult.IsValid) { string errors = string.Join(", ", validationResult.Errors); + Logs.ValidationErrors(Logger, request.CorrelationId, errors); response = Activator.CreateInstance(); @@ -48,7 +48,9 @@ CancellationToken cancellationToken Success = false, Message = errors }; + UseCaseFailedMetric.Add(1); + return response!; } } diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index 9642f99a..f4938a20 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,5 +1,8 @@ using System.Diagnostics; using System.Diagnostics.Metrics; +using Application.Common.Enums; +using Application.Common.Messages; +using Application.Common.Services; using Domain.Common; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -12,6 +15,7 @@ public abstract class BaseUseCase protected ILogger Logger { get; } protected string ClassName { get; } protected ActivitySource ActivitySource { get; } = DefaultConfigurations.ActivitySource; + protected IProduceService ProduceService { get; } protected Counter UseCaseExecutedMetric { get; } protected Counter UseCaseFailedMetric { get; } @@ -24,10 +28,30 @@ protected BaseUseCase(IServiceProvider serviceProvider) Logger = serviceProvider.GetRequiredService().CreateLogger(classType); + ProduceService = serviceProvider.GetRequiredService(); + UseCaseExecutedMetric = DefaultConfigurations.Meter .CreateCounter($"{ClassName}.Executed", "total", "Number of times the use case was executed"); UseCaseFailedMetric = DefaultConfigurations.Meter .CreateCounter($"{ClassName}.Failed", "total", "Number of times the use case execution failed"); } + + protected void CreateNotification( + Guid correlationId, + string notificationStatus, + string createdBy, + NotificationType notificationType, + object message + ) => _ = ProduceService.HandleAsync( + new CreateNotificationMessage( + correlationId, + notificationType, + notificationStatus, + createdBy, + message + ), + CancellationToken.None, + queue: notificationType.ToString() + ); } \ No newline at end of file diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 1db826da..5dbba4d6 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -1,8 +1,7 @@ -using Application.Common.Constants; -using Application.Common.Messages; +using Application.Common.Enums; +using Application.Common.Helpers; using Application.Common.Requests; using Application.Common.UseCases; -using Application.Common.Helpers; using Domain.Orders; using FluentValidation; @@ -41,6 +40,7 @@ public CreateOrderRequestValidator() public sealed class CreateOrderUseCase(IServiceProvider serviceProvider) : BaseInOutUseCase>(serviceProvider) { + private readonly NotificationType _notificationType = NotificationType.OrderCreated; public override async Task> HandleInternalAsync( CreateOrderRequest request, CancellationToken cancellationToken @@ -53,33 +53,31 @@ CancellationToken cancellationToken .Select(i => new Item(i.Name, i.Description, i.Value)) .ToList(); - var newOrder = new Order( + var createResult = Order.Create( request.Description, items, request.CreatedBy, request.TimezoneId ); - - var createResult = newOrder.SetTotal(); if (createResult.IsFailure) { Logs.FailedOperation(Logger, correlationId, createResult.Message); response = new(false, null, createResult.Message); - CreateNotification(correlationId, "Failed", response); + CreateNotification(correlationId, "Failed", request.CreatedBy, _notificationType, response); UseCaseFailedMetric.Add(1); return response; } - var addResult = await Repository.AddAsync(newOrder, correlationId, cancellationToken); - if (addResult == 0) + var newOrder = createResult.Value; + if (await Repository.AddAsync(newOrder, correlationId, cancellationToken) == 0) { Logs.FailedOperation(Logger, correlationId, "Failed to create order. No rows affected."); response = new(false, null, "Failed to create order."); - CreateNotification(correlationId, "Failed", response); + CreateNotification(correlationId, "Failed", request.CreatedBy, _notificationType, response); UseCaseFailedMetric.Add(1); @@ -99,20 +97,8 @@ CancellationToken cancellationToken })] }); - CreateNotification(correlationId, "Success", response); + CreateNotification(correlationId, "Success", request.CreatedBy, _notificationType, response); return response; } - - private void CreateNotification(Guid correlationId, string notificationStatus, object message) => _ = ProduceService.HandleAsync( - new CreateNotificationMessage( - correlationId, - NotificationType.OrderCreated, - notificationStatus, - "System", - message - ), - CancellationToken.None, - NotificationType.OrderCreated - ); } diff --git a/templates/Full/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs index 69448095..c1b1aa07 100644 --- a/templates/Full/src/Domain/Common/DomainEntity.cs +++ b/templates/Full/src/Domain/Common/DomainEntity.cs @@ -19,7 +19,7 @@ protected DomainEntity(string user, string? timezoneId = null) UpdatedBy = CreatedBy; UpdatedByTimezoneId = CreatedByTimezoneId; } - protected virtual ActivitySource ActivitySource { get; } = DefaultConfigurations.ActivitySource; + protected static ActivitySource ActivitySource { get; } = DefaultConfigurations.ActivitySource; public int Id { get; init; } public DateTime CreatedAt { get; init; } public string? CreatedBy { get; init; } diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index d140aac4..4e5fb489 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -13,19 +13,32 @@ public Order( string? timezoneId = null ) : base(createdBy ?? "System", timezoneId) { - using var activity = ActivitySource.StartActivity($"{EntityName}.Constructor"); - Description = description; Items = items; - - activity?.SetTag(nameof(Description), Description); - activity?.SetTag(nameof(Items), Items.Count); } public string Description { get; private set; } public decimal Total { get; private set; } public ICollection Items { get; private set; } + public static Result Create( + string description, + ICollection items, + string user = "System", + string? timezoneId = null + ) + { + using var activity = ActivitySource.StartActivity($"{nameof(Order)}.{nameof(Create)}"); + + Order order = new (description, items, user, timezoneId); + + order.SetTotal(user, timezoneId); + + activity?.SetTag(nameof(description), description); + + return Result.Ok(order); + } + public Result SetTotal(string user = "System", string? timezoneId = null) { using var activity = ActivitySource.StartActivity($"{EntityName}.{nameof(SetTotal)}"); From ca2de8b3b8862d6a898f10ab309b7315987955e0 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 1 Apr 2026 07:07:55 -0300 Subject: [PATCH 151/161] 53 refactor: remove unused ProduceService from use case classes --- .../Common/UseCases/BaseInUseCase.cs | 1 - .../Common/UseCases/BaseOutUseCase.cs | 1 - templates/Full/src/Domain/Orders/Order.cs | 6 ++- .../Orders/CreateOrderUseCaseTests.cs | 7 +--- .../Orders/GetOrderUseCaseTests.cs | 17 +++------ .../Full/tests/UnitTests/Domain/OrderTests.cs | 38 +++++++++++-------- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index be3cdf85..a80c0eaf 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -15,7 +15,6 @@ public interface IBaseInUseCase where TRequest : BaseRequest public abstract class BaseInUseCase(IServiceProvider serviceProvider) : BaseUseCase(serviceProvider), IBaseInUseCase where TRequest : BaseRequest { protected IHybridCacheService Cache { get; } = serviceProvider.GetRequiredService(); - protected IProduceService ProduceService { get; } = serviceProvider.GetRequiredService(); protected IBaseRepository Repository { get; } = serviceProvider.GetRequiredService(); private readonly IValidator _validator = serviceProvider.GetRequiredService>(); protected const string HandleMethodName = nameof(HandleAsync); diff --git a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs index 3787c9e4..636094c7 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseOutUseCase.cs @@ -13,7 +13,6 @@ public interface IBaseOutUseCase where TResponseData : BaseRespon public abstract class BaseOutUseCase(IServiceProvider serviceProvider) : BaseUseCase(serviceProvider), IBaseOutUseCase where TResponseData : BaseResponse { protected IHybridCacheService Cache { get; } = serviceProvider.GetRequiredService(); - protected IProduceService ProduceService { get; } = serviceProvider.GetRequiredService(); protected const string HandleMethodName = nameof(HandleAsync); public async Task HandleAsync(CancellationToken cancellationToken) diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index 4e5fb489..23fc62ef 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -6,7 +6,7 @@ public sealed class Order : DomainEntity { public Order() { } - public Order( + private Order( string description, ICollection items, string? createdBy = null, @@ -32,7 +32,9 @@ public static Result Create( Order order = new (description, items, user, timezoneId); - order.SetTotal(user, timezoneId); + var setTotalResult = order.SetTotal(user, timezoneId); + if(setTotalResult.IsFailure) + return Result.Fail(setTotalResult.Message); activity?.SetTag(nameof(description), description); diff --git a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs index 15ab43be..09f39b8e 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/CreateOrderUseCaseTests.cs @@ -56,17 +56,14 @@ public async Task GivenAnInvalidRequestThenFails() public sealed class CreateOrderUseCaseFixture : BaseApplicationFixture { - public CreateOrderUseCaseFixture() - { - UseCase = new(MockServiceProvider.Object); - } + public CreateOrderUseCaseFixture() => UseCase = new(MockServiceProvider.Object); public CreateOrderRequest SetValidRequest() { var items = AutoFixture .CreateMany(1); - return new CreateOrderRequest(Guid.NewGuid(), "AwesomeComputer", [.. items]); + return new(Guid.NewGuid(), "AwesomeComputer", [.. items]); } public static CreateOrderRequest SetInvalidRequestWithNoItems() => diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index 1189c39b..21ff7ffb 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -26,17 +26,14 @@ public async Task GivenAValidRequestThenPass() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - Order expectedOrder = new( + var resultCreateOrder = Order.Create( "Test Order", [ new("Item 1", "Description 1", 500m), new("Item 2", "Description 2", 500m) ] - ) - { - Id = request.Id - }; - expectedOrder.SetTotal(); + ); + var expectedOrder = resultCreateOrder.Value; _fixture.MockRepository.SetupQueryable(request.CorrelationId, null, [expectedOrder]); // Act @@ -65,13 +62,11 @@ public async Task GivenAValidRequestWithoutItemsThenPass() // Arrange var request = _fixture.SetValidRequest(); _fixture.SetSuccessfulValidator(request); - Order expectedOrder = new( + var resultCreateOrder = Order.Create( "Test Order", [] - ) - { - Id = request.Id - }; + ); + var expectedOrder = resultCreateOrder.Value; _fixture.MockRepository.SetupQueryable(request.CorrelationId, null, [expectedOrder]); diff --git a/templates/Full/tests/UnitTests/Domain/OrderTests.cs b/templates/Full/tests/UnitTests/Domain/OrderTests.cs index 92ca515e..f89845d7 100644 --- a/templates/Full/tests/UnitTests/Domain/OrderTests.cs +++ b/templates/Full/tests/UnitTests/Domain/OrderTests.cs @@ -5,8 +5,8 @@ namespace UnitTests.Domain; public sealed class OrderTests { - [Fact(DisplayName = nameof(GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess))] - public void GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess() + [Fact(DisplayName = nameof(GivenANewOrderWhenItemsAreProvidedThenShouldCreatedWithSuccess))] + public void GivenANewOrderWhenItemsAreProvidedThenShouldCreatedWithSuccess() { /// Arrange var items = new List() @@ -15,13 +15,18 @@ public void GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess() new("Mouse", "Razer", 100), new("Headphone", "Logitech", 100), }; - Order order = new("Amazing Computer", items, "John Doe", "America/New_York"); - var initialUpdatedAt = order.UpdatedAt; /// Act - var result = order.SetTotal(); + var result = Order.Create("Amazing Computer", items, "John Doe", "America/New_York"); + var order = result.Value; + var initialUpdatedAt = order.UpdatedAt; // Assert + Assert.NotNull(result); + Assert.True(result.Success); + Assert.Empty(result.Message); + Assert.IsType(result.Value); + Assert.NotNull(order); Assert.NotNull(result); Assert.True(result.Success); @@ -33,8 +38,8 @@ public void GivenANewOrderWhenItemsAreProvidedThenShouldSetTotalWithSuccess() Assert.Equal(items.Sum(i => i.Value), order.Total); } - [Fact(DisplayName = nameof(GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldSetTotalWithSuccess))] - public void GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldSetTotalWithSuccess() + [Fact(DisplayName = nameof(GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldCreateWithSuccess))] + public void GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldCreateWithSuccess() { /// Arrange var items = new List() @@ -43,13 +48,18 @@ public void GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldSe new("Mouse", "Razer", 100), new("Headphone", "Logitech", 100), }; - Order order = new("Amazing Computer", items); - var initialUpdatedAt = order.UpdatedAt; /// Act - var result = order.SetTotal(); + var result = Order.Create("Amazing Computer", items); + var order = result.Value; + var initialUpdatedAt = order.UpdatedAt; // Assert + Assert.NotNull(result); + Assert.True(result.Success); + Assert.Empty(result.Message); + Assert.IsType(result.Value); + Assert.NotNull(order); Assert.NotNull(result); Assert.True(result.Success); @@ -79,14 +89,10 @@ public void GivenANewItemWithValueZeroThenShouldBeFailure() [Fact(DisplayName = nameof(GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure))] public void GivenANewOrderWhenItemsIsEmptyThenShouldReturnFailure() { - /// Arrange - Order order = new("Amazing Computer", Array.Empty()); - - /// Act - var result = order.SetTotal("John Doe", "America/New_York"); + /// Arrange, Act + var result = Order.Create("Amazing Computer", Array.Empty()); // Assert - Assert.NotNull(order); Assert.NotNull(result); Assert.True(result.IsFailure); Assert.Equal("Order must have at least one item.", result.Message); From b8dc26f4d626d257b397efdc1ab65a531ac43fb0 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Wed, 1 Apr 2026 07:10:06 -0300 Subject: [PATCH 152/161] 53 refactor: remove redundant updatedAt assertions in order tests --- templates/Full/tests/UnitTests/Domain/OrderTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/Full/tests/UnitTests/Domain/OrderTests.cs b/templates/Full/tests/UnitTests/Domain/OrderTests.cs index f89845d7..cba44ee0 100644 --- a/templates/Full/tests/UnitTests/Domain/OrderTests.cs +++ b/templates/Full/tests/UnitTests/Domain/OrderTests.cs @@ -34,7 +34,6 @@ public void GivenANewOrderWhenItemsAreProvidedThenShouldCreatedWithSuccess() Assert.NotEqual(0, order.Total); Assert.Equal("John Doe", order.CreatedBy); Assert.Equal("America/New_York", order.CreatedByTimezoneId); - Assert.NotEqual(initialUpdatedAt, order.UpdatedAt); Assert.Equal(items.Sum(i => i.Value), order.Total); } @@ -65,7 +64,6 @@ public void GivenANewOrderWithoutUserAndTimezoneWhenItemsAreProvidedThenShouldCr Assert.True(result.Success); Assert.Empty(result.Message); Assert.NotEqual(0, order.Total); - Assert.NotEqual(initialUpdatedAt, order.UpdatedAt); Assert.Equal(items.Sum(i => i.Value), order.Total); Assert.Equal("System", order.CreatedBy); Assert.Equal("UTC", order.CreatedByTimezoneId); From 7d77c210d056a64c1020bb421841ed510d6ab54e Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 07:46:59 -0300 Subject: [PATCH 153/161] 53 feat: introduce NotificationType enum and update related code --- .../Common/Messages/CreateNotificationMessage.cs | 2 +- .../Full/src/Application/Common/UseCases/BaseUseCase.cs | 2 +- .../Notifications/CreateNotificationUseCase.cs | 3 ++- .../Full/src/Application/Notifications/NotificationDto.cs | 4 +++- .../Full/src/Application/Orders/CreateOrderUseCase.cs | 4 ++-- .../Common/Enums/NotificationType.cs | 2 +- templates/Full/src/Domain/Notifications/Notification.cs | 5 +++-- .../Infrastructure/Messaging/Consumers/BaseConsumer.cs | 5 +++-- .../Messaging/Consumers/CreateNotificationConsumer.cs | 2 +- .../Messaging/Notifications/CreateNotificationTest.cs | 8 ++++---- .../Notifications/CreateNotificationUseCaseTests.cs | 7 ++++--- .../Full/tests/UnitTests/Domain/NotificationTests.cs | 5 +++-- 12 files changed, 28 insertions(+), 21 deletions(-) rename templates/Full/src/{Application => Domain}/Common/Enums/NotificationType.cs (58%) diff --git a/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs b/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs index 3085150b..2a07f9a0 100644 --- a/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs +++ b/templates/Full/src/Application/Common/Messages/CreateNotificationMessage.cs @@ -1,4 +1,4 @@ -using Application.Common.Enums; +using Domain.Common.Enums; namespace Application.Common.Messages; diff --git a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs index f4938a20..626ed4a5 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseUseCase.cs @@ -1,9 +1,9 @@ using System.Diagnostics; using System.Diagnostics.Metrics; -using Application.Common.Enums; using Application.Common.Messages; using Application.Common.Services; using Domain.Common; +using Domain.Common.Enums; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 8f509cd1..4044ba48 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -3,12 +3,13 @@ using Application.Common.Helpers; using Domain.Notifications; using FluentValidation; +using Domain.Common.Enums; namespace Application.Notifications; public sealed record CreateNotificationRequest( Guid CorrelationId, - string NotificationType, + NotificationType NotificationType, string NotificationStatus, string? CreatedBy = null, object? Message = null diff --git a/templates/Full/src/Application/Notifications/NotificationDto.cs b/templates/Full/src/Application/Notifications/NotificationDto.cs index 46dc9967..f87f8696 100644 --- a/templates/Full/src/Application/Notifications/NotificationDto.cs +++ b/templates/Full/src/Application/Notifications/NotificationDto.cs @@ -1,9 +1,11 @@ +using Domain.Common.Enums; + namespace Application.Notifications; public sealed record NotificationDto { public int Id { get; set; } - public string NotificationType { get; set; } + public NotificationType NotificationType { get; set; } public string NotificationStatus { get; set; } public string Message { get; set; } }; diff --git a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs index 5dbba4d6..f13f3dbd 100644 --- a/templates/Full/src/Application/Orders/CreateOrderUseCase.cs +++ b/templates/Full/src/Application/Orders/CreateOrderUseCase.cs @@ -1,7 +1,7 @@ -using Application.Common.Enums; -using Application.Common.Helpers; +using Application.Common.Helpers; using Application.Common.Requests; using Application.Common.UseCases; +using Domain.Common.Enums; using Domain.Orders; using FluentValidation; diff --git a/templates/Full/src/Application/Common/Enums/NotificationType.cs b/templates/Full/src/Domain/Common/Enums/NotificationType.cs similarity index 58% rename from templates/Full/src/Application/Common/Enums/NotificationType.cs rename to templates/Full/src/Domain/Common/Enums/NotificationType.cs index b39a8166..0b7dc50d 100644 --- a/templates/Full/src/Application/Common/Enums/NotificationType.cs +++ b/templates/Full/src/Domain/Common/Enums/NotificationType.cs @@ -1,4 +1,4 @@ -namespace Application.Common.Enums; +namespace Domain.Common.Enums; public enum NotificationType { diff --git a/templates/Full/src/Domain/Notifications/Notification.cs b/templates/Full/src/Domain/Notifications/Notification.cs index 43fccaf3..e62e56be 100644 --- a/templates/Full/src/Domain/Notifications/Notification.cs +++ b/templates/Full/src/Domain/Notifications/Notification.cs @@ -1,5 +1,6 @@ using System.Text.Json; using Domain.Common; +using Domain.Common.Enums; namespace Domain.Notifications; @@ -8,7 +9,7 @@ public sealed class Notification : DomainEntity public Notification() {} public Notification( - string notificationType, + NotificationType notificationType, string notificationStatus, object? message = null, string? createdBy = null, @@ -24,7 +25,7 @@ public Notification( activity?.SetTag(nameof(NotificationType), NotificationType); activity?.SetTag(nameof(NotificationStatus), NotificationStatus); } - public string NotificationType { get; init; } + public NotificationType NotificationType { get; init; } public string NotificationStatus { get; init; } public string Message { get; init; } } diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs index e5a76516..72de2699 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/BaseConsumer.cs @@ -4,6 +4,7 @@ using Application.Common.Messages; using Application.Common.Services; using Domain.Common; +using Domain.Common.Enums; using Infrastructure.Common; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -31,7 +32,7 @@ public BaseConsumer( ILogger> logger, IServiceScopeFactory serviceScopeFactory, IConfiguration configuration, - string queueName, + NotificationType queueName, IDictionary arguments = null! ) : base(logger, serviceScopeFactory, configuration) { @@ -42,7 +43,7 @@ public BaseConsumer( throw new ArgumentException("Invalid RabbitMQ connection string."); } - _queueName = queueName; + _queueName = queueName.ToString(); _arguments = arguments; _factory = new() { Uri = new(connectionString) }; diff --git a/templates/Full/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs b/templates/Full/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs index 776808bb..7745f8b8 100644 --- a/templates/Full/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs +++ b/templates/Full/src/Infrastructure/Messaging/Consumers/CreateNotificationConsumer.cs @@ -1,7 +1,7 @@ -using Application.Common.Constants; using Application.Common.Messages; using Application.Common.UseCases; using Application.Notifications; +using Domain.Common.Enums; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs index 404f9666..c7584d08 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs @@ -1,4 +1,3 @@ -using Application.Common.Constants; using Application.Common.Messages; using Domain.Notifications; using IntegrationTests.Common; @@ -6,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using WebApp; using Microsoft.EntityFrameworkCore; +using Domain.Common.Enums; namespace IntegrationTests.WebApp.Messaging.Notifications; @@ -38,7 +38,7 @@ public async Task GivenAValidMessageThenPass() var message = _fixture.SetValidMessage(); // Act - await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); + await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated.ToString()); var notification = await _fixture.Repository.GetQueryable(Guid.NewGuid()) .Where(n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus) @@ -56,8 +56,8 @@ public async Task GivenADuplicateMessageThenShouldNotCreateDuplicatedMessage() var message = _fixture.SetValidMessage(); // Act - await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); - await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated); + await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated.ToString()); + await _fixture.HandleProducerAsync(message, NotificationType.OrderCreated.ToString()); var notifications = await _fixture.Repository.GetQueryable(Guid.NewGuid()) .Where(n => n.NotificationType == message.NotificationType && n.NotificationStatus == message.NotificationStatus) diff --git a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs index d4136aeb..d6bb7bf0 100644 --- a/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Notifications/CreateNotificationUseCaseTests.cs @@ -1,4 +1,5 @@ using Application.Notifications; +using Domain.Common.Enums; using Domain.Notifications; using FluentValidation; using FluentValidation.TestHelper; @@ -11,7 +12,7 @@ public sealed class CreateNotificationRequestValidationFixture public IValidator Validator { get; } = new CreateNotificationRequestValidator(); public static CreateNotificationRequest GetValidRequest() => - new(Guid.NewGuid(), "TestNotification", "Success", "System", new { Test = "Message" }); + new(Guid.NewGuid(), NotificationType.OrderCreated, "Success", "System", new { Test = "Message" }); } public sealed class CreateNotificationRequestValidationTests(CreateNotificationRequestValidationFixture fixture) : IClassFixture @@ -38,7 +39,7 @@ public async Task GivenAnInvalidRequestThenFails() var request = CreateNotificationRequestValidationFixture.GetValidRequest() with { CorrelationId = Guid.Empty, - NotificationType = string.Empty + NotificationType = (NotificationType)(-1) }; // Act @@ -58,7 +59,7 @@ public CreateNotificationUseCaseFixture() } public static CreateNotificationRequest SetValidRequest() => - new(Guid.NewGuid(), "TestNotification", "Success", "System", new { Test = "Message" }); + new(Guid.NewGuid(), NotificationType.OrderCreated, "Success", "System", new { Test = "Message" }); } public sealed class CreateNotificationUseCaseTests : IClassFixture diff --git a/templates/Full/tests/UnitTests/Domain/NotificationTests.cs b/templates/Full/tests/UnitTests/Domain/NotificationTests.cs index aa9453fd..ae60a658 100644 --- a/templates/Full/tests/UnitTests/Domain/NotificationTests.cs +++ b/templates/Full/tests/UnitTests/Domain/NotificationTests.cs @@ -1,3 +1,4 @@ +using Domain.Common.Enums; using Domain.Notifications; namespace UnitTests.Domain; @@ -8,7 +9,7 @@ public sealed class NotificationTests public void GivenANewNotificationWhenPropertiesAreProvidedThenShouldCreateNotificationWithSuccess() { /// Arrange - var notificationType = "TestNotification"; + var notificationType = NotificationType.OrderCreated; var notificationStatus = "Success"; var createdBy = "System"; var timezoneId = "America/New_York"; @@ -31,7 +32,7 @@ public void GivenANewNotificationWhenPropertiesAreProvidedThenShouldCreateNotifi public void GivenANewNotificationWhenMessageIsNullThenShouldCreateNotificationWithEmptyMessage() { /// Arrange - var notificationType = "TestNotification"; + var notificationType = NotificationType.OrderCreated; var notificationStatus = "Success"; var createdBy = "System"; var timezoneId = "America/New_York"; From fa5b9485be7d45312f0094d8f7552486990c3d32 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 08:07:18 -0300 Subject: [PATCH 154/161] 53 refactor: update notification type validation and clean tests --- .../CreateNotificationUseCase.cs | 2 +- .../Data/Mapping/NotificationDbMapping.cs | 1 - .../Orders/GetOrderUseCaseTests.cs | 40 ++----------------- 3 files changed, 5 insertions(+), 38 deletions(-) diff --git a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs index 4044ba48..a0c389eb 100644 --- a/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs +++ b/templates/Full/src/Application/Notifications/CreateNotificationUseCase.cs @@ -20,7 +20,7 @@ public sealed class CreateNotificationRequestValidator : AbstractValidator r.CorrelationId).NotEmpty(); - RuleFor(r => r.NotificationType).NotEmpty(); + RuleFor(r => r.NotificationType).IsInEnum(); } } diff --git a/templates/Full/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs b/templates/Full/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs index 2d6dbc04..075dee99 100644 --- a/templates/Full/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs +++ b/templates/Full/src/Infrastructure/Data/Mapping/NotificationDbMapping.cs @@ -9,7 +9,6 @@ internal sealed class NotificationDbMapping : BaseDbMapping public override void ConfigureDomainEntity(EntityTypeBuilder builder) { builder.Property(p => p.NotificationType) - .HasMaxLength(100) .IsRequired(); builder.Property(p => p.NotificationStatus) diff --git a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs index 21ff7ffb..0fc61235 100644 --- a/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs +++ b/templates/Full/tests/UnitTests/Application/Orders/GetOrderUseCaseTests.cs @@ -7,7 +7,7 @@ namespace UnitTests.Application.Orders; public sealed class GetOrderUseCaseFixture : BaseApplicationFixture { public GetOrderUseCaseFixture() => UseCase = new(MockServiceProvider.Object); - public GetOrderRequest SetValidRequest() => new(Guid.NewGuid(), AutoFixture.Create()); + public GetOrderRequest SetValidRequest(int? id = null) => new(Guid.NewGuid(), id ?? AutoFixture.Create()); } public sealed class GetOrderUseCaseTest : IClassFixture @@ -24,8 +24,6 @@ public GetOrderUseCaseTest(GetOrderUseCaseFixture fixture) public async Task GivenAValidRequestThenPass() { // Arrange - var request = _fixture.SetValidRequest(); - _fixture.SetSuccessfulValidator(request); var resultCreateOrder = Order.Create( "Test Order", [ @@ -34,6 +32,9 @@ public async Task GivenAValidRequestThenPass() ] ); var expectedOrder = resultCreateOrder.Value; + var request = _fixture.SetValidRequest(resultCreateOrder.Value.Id); + request = request with { Id = expectedOrder.Id }; + _fixture.SetSuccessfulValidator(request); _fixture.MockRepository.SetupQueryable(request.CorrelationId, null, [expectedOrder]); // Act @@ -56,39 +57,6 @@ public async Task GivenAValidRequestThenPass() _fixture.MockLogger.VerifyFinishOperation(); } - [Fact(DisplayName = nameof(GivenAValidRequestWithoutItemsThenPass))] - public async Task GivenAValidRequestWithoutItemsThenPass() - { - // Arrange - var request = _fixture.SetValidRequest(); - _fixture.SetSuccessfulValidator(request); - var resultCreateOrder = Order.Create( - "Test Order", - [] - ); - var expectedOrder = resultCreateOrder.Value; - - _fixture.MockRepository.SetupQueryable(request.CorrelationId, null, [expectedOrder]); - - // Act - var result = await _fixture.UseCase.HandleAsync(request, _fixture.CancellationToken); - - // Assert - Assert.NotNull(result); - Assert.True(result.Success); - Assert.Null(result.Message); - Assert.NotNull(result.Data); - Assert.Equal(expectedOrder.Id, result.Data.Id); - Assert.Equal(expectedOrder.Description, result.Data.Description); - Assert.Equal(expectedOrder.Total, result.Data.Total); - Assert.Equal(0, result.Data.Items?.Count); - - _fixture.MockRepository.VerifyQueryable(); - _fixture.MockLogger.VerifyStartOperation(); - _fixture.MockLogger.VerifyNotFound(0); - _fixture.MockLogger.VerifyFinishOperation(); - } - [Fact(DisplayName = nameof(GivenAInvalidRequestThenFails))] public async Task GivenAInvalidRequestThenFails() { From 9bde54dd4410998da4a357a928c6b0da69b9edb3 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 08:07:30 -0300 Subject: [PATCH 155/161] 53 feat: add launch and task configurations for development --- templates/Full/.vscode/launch.json | 18 ++++++++++++++++++ templates/Full/.vscode/tasks.json | 29 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 templates/Full/.vscode/launch.json create mode 100644 templates/Full/.vscode/tasks.json diff --git a/templates/Full/.vscode/launch.json b/templates/Full/.vscode/launch.json new file mode 100644 index 00000000..1243368b --- /dev/null +++ b/templates/Full/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "WebApp [Development]", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/src/WebApp/bin/Debug/net10.0/WebApp.dll", + "args": [], + "cwd": "${workspaceFolder}/src/WebApp", + "stopAtEntry": false, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + ] +} \ No newline at end of file diff --git a/templates/Full/.vscode/tasks.json b/templates/Full/.vscode/tasks.json new file mode 100644 index 00000000..6407faab --- /dev/null +++ b/templates/Full/.vscode/tasks.json @@ -0,0 +1,29 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/src/WebApp/WebApp.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary;ForceNoAlign" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/src/WebApp/WebApp.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file From a668b8f58c83a2c34d39349e3edcb0da73579549 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 08:15:56 -0300 Subject: [PATCH 156/161] 53 feat: update notification type to integer and adjust migrations --- templates/Full/Readme.md | 2 +- templates/Full/scripts/sql/migrations.sql | 14 +-- templates/Full/scripts/sql/seeds.sql | 2 +- .../Migrations/20260227203704_CreateTables.cs | 100 ----------------- ...> 20260403110832_CreateTables.Designer.cs} | 10 +- .../Migrations/20260403110832_CreateTables.cs | 102 ++++++++++++++++++ .../Migrations/MyDbContextModelSnapshot.cs | 8 +- .../Notifications/CreateNotificationTest.cs | 4 +- 8 files changed, 121 insertions(+), 121 deletions(-) delete mode 100644 templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.cs rename templates/Full/src/Infrastructure/Data/Migrations/{20260227203704_CreateTables.Designer.cs => 20260403110832_CreateTables.Designer.cs} (95%) create mode 100644 templates/Full/src/Infrastructure/Data/Migrations/20260403110832_CreateTables.cs diff --git a/templates/Full/Readme.md b/templates/Full/Readme.md index 65728a31..c636c4c5 100644 --- a/templates/Full/Readme.md +++ b/templates/Full/Readme.md @@ -33,7 +33,7 @@ dotnet ef migrations add --project src/Infrastructure --startup- ### Generate SQL script for migrations with idempotent option ```bash -dotnet ef migrations script --idempotent --project src/Infrastructure --startup-project src/Infrastructure --output scripts/migrations.sql +dotnet ef migrations script --idempotent --project src/Infrastructure --startup-project src/Infrastructure --output scripts/sql/migrations.sql ``` ### Run the application diff --git a/templates/Full/scripts/sql/migrations.sql b/templates/Full/scripts/sql/migrations.sql index 8dc0bb54..efbb37b3 100644 --- a/templates/Full/scripts/sql/migrations.sql +++ b/templates/Full/scripts/sql/migrations.sql @@ -8,10 +8,10 @@ START TRANSACTION; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260403110832_CreateTables') THEN CREATE TABLE "Notification" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, - "NotificationType" text NOT NULL, + "NotificationType" integer NOT NULL, "NotificationStatus" text NOT NULL, "Message" text, "CreatedAt" timestamp with time zone NOT NULL, @@ -27,7 +27,7 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260403110832_CreateTables') THEN CREATE TABLE "Order" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "Description" text NOT NULL, @@ -45,7 +45,7 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260403110832_CreateTables') THEN CREATE TABLE "Item" ( "Id" integer GENERATED BY DEFAULT AS IDENTITY, "Name" text NOT NULL, @@ -66,16 +66,16 @@ END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260403110832_CreateTables') THEN CREATE INDEX "IX_Item_OrderId" ON "Item" ("OrderId"); END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260227202813_CreateTables') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260403110832_CreateTables') THEN INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") - VALUES ('20260227202813_CreateTables', '10.0.2'); + VALUES ('20260403110832_CreateTables', '10.0.5'); END IF; END $EF$; COMMIT; diff --git a/templates/Full/scripts/sql/seeds.sql b/templates/Full/scripts/sql/seeds.sql index e4571e40..753a3098 100644 --- a/templates/Full/scripts/sql/seeds.sql +++ b/templates/Full/scripts/sql/seeds.sql @@ -23,7 +23,7 @@ DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM "Notification" WHERE "Id" = 1 LIMIT 1) THEN INSERT INTO "Notification" ("Id", "NotificationType", "NotificationStatus", "CreatedAt", "UpdatedAt", "CreatedBy", "CreatedByTimezoneId") - VALUES (1, 'OrderCreated', 'Created', NOW(), NOW(), 'System', 'UTC'); + VALUES (1, 1, 'Created', NOW(), NOW(), 'System', 'UTC'); -- Reset the sequence to the maximum ID + 1 PERFORM setval(pg_get_serial_sequence('"Notification"', 'Id'), COALESCE(MAX("Id"), 1), true) FROM "Notification"; diff --git a/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.cs b/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.cs deleted file mode 100644 index 77b824a3..00000000 --- a/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Infrastructure.Data.Migrations; - -/// -public partial class CreateTables : Migration -{ - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Notification", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - NotificationType = table.Column(type: "text", maxLength: 100, nullable: false), - NotificationStatus = table.Column(type: "text", maxLength: 100, nullable: false), - Message = table.Column(type: "text", maxLength: 4000, nullable: true), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), - CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), - UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), - UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Notification", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Order", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Description = table.Column(type: "text", maxLength: 255, nullable: false), - Total = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), - CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), - UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), - UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Order", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Item", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", maxLength: 200, nullable: false), - Description = table.Column(type: "text", maxLength: 255, nullable: false), - Value = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), - OrderId = table.Column(type: "integer", nullable: true), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), - CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), - UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), - UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Item", x => x.Id); - table.ForeignKey( - name: "FK_Item_Order_OrderId", - column: x => x.OrderId, - principalTable: "Order", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_Item_OrderId", - table: "Item", - column: "OrderId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Item"); - - migrationBuilder.DropTable( - name: "Notification"); - - migrationBuilder.DropTable( - name: "Order"); - } -} diff --git a/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.Designer.cs b/templates/Full/src/Infrastructure/Data/Migrations/20260403110832_CreateTables.Designer.cs similarity index 95% rename from templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.Designer.cs rename to templates/Full/src/Infrastructure/Data/Migrations/20260403110832_CreateTables.Designer.cs index e0130465..0b964959 100644 --- a/templates/Full/src/Infrastructure/Data/Migrations/20260227203704_CreateTables.Designer.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/20260403110832_CreateTables.Designer.cs @@ -12,7 +12,7 @@ namespace Infrastructure.Data.Migrations { [DbContext(typeof(MyDbContext))] - [Migration("20260227203704_CreateTables")] + [Migration("20260403110832_CreateTables")] partial class CreateTables { /// @@ -20,7 +20,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("ProductVersion", "10.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -54,10 +54,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasMaxLength(100) .HasColumnType("text"); - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("text"); + b.Property("NotificationType") + .HasColumnType("integer"); b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); diff --git a/templates/Full/src/Infrastructure/Data/Migrations/20260403110832_CreateTables.cs b/templates/Full/src/Infrastructure/Data/Migrations/20260403110832_CreateTables.cs new file mode 100644 index 00000000..e94e045a --- /dev/null +++ b/templates/Full/src/Infrastructure/Data/Migrations/20260403110832_CreateTables.cs @@ -0,0 +1,102 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Data.Migrations +{ + /// + public partial class CreateTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Notification", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + NotificationType = table.Column(type: "integer", nullable: false), + NotificationStatus = table.Column(type: "text", maxLength: 100, nullable: false), + Message = table.Column(type: "text", maxLength: 4000, nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Notification", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Description = table.Column(type: "text", maxLength: 255, nullable: false), + Total = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Item", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", maxLength: 200, nullable: false), + Description = table.Column(type: "text", maxLength: 255, nullable: false), + Value = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + OrderId = table.Column(type: "integer", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + CreatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedBy = table.Column(type: "text", maxLength: 100, nullable: true), + UpdatedByTimezoneId = table.Column(type: "text", maxLength: 100, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Item", x => x.Id); + table.ForeignKey( + name: "FK_Item_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Item_OrderId", + table: "Item", + column: "OrderId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Item"); + + migrationBuilder.DropTable( + name: "Notification"); + + migrationBuilder.DropTable( + name: "Order"); + } + } +} diff --git a/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs index 60b63967..c5412c4c 100644 --- a/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs +++ b/templates/Full/src/Infrastructure/Data/Migrations/MyDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("ProductVersion", "10.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -51,10 +51,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(100) .HasColumnType("text"); - b.Property("NotificationType") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("text"); + b.Property("NotificationType") + .HasColumnType("integer"); b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); diff --git a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs index c7584d08..9894aed0 100644 --- a/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs +++ b/templates/Full/tests/IntegrationTests/WebApp/Messaging/Notifications/CreateNotificationTest.cs @@ -17,7 +17,9 @@ public class CreateNotificationTestFixture : BaseMessagingFixture SetServices(scope); } - public CreateNotificationMessage SetValidMessage() => AutoFixture.Build().Create(); + public CreateNotificationMessage SetValidMessage() => AutoFixture.Build() + .With(m => m.NotificationType, NotificationType.OrderCreated) + .Create(); } [Collection("WebApplicationFactoryCollectionDefinition")] From 8e7b8e622df869e99bed938d5a72df94c198cc44 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 08:32:03 -0300 Subject: [PATCH 157/161] 53 refactor: streamline update handling in DomainEntity and Order --- .../Full/src/Domain/Common/DomainEntity.cs | 29 ++++++++++++++++--- templates/Full/src/Domain/Orders/Order.cs | 23 +++++++-------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/templates/Full/src/Domain/Common/DomainEntity.cs b/templates/Full/src/Domain/Common/DomainEntity.cs index c1b1aa07..183a9b33 100644 --- a/templates/Full/src/Domain/Common/DomainEntity.cs +++ b/templates/Full/src/Domain/Common/DomainEntity.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System.Diagnostics; +using System.Runtime.CompilerServices; namespace Domain.Common; @@ -28,15 +29,35 @@ protected DomainEntity(string user, string? timezoneId = null) public string? UpdatedBy { get; private set; } public string? UpdatedByTimezoneId { get; private set; } - public virtual void Update(string? user = null, string? timezoneId = null) + protected static Result Handle( + Func> factory, + [CallerMemberName] string callerName = null! + ) where TEntity : DomainEntity { - using var activity = ActivitySource.StartActivity($"{EntityName}.{nameof(Update)}"); + using var activity = ActivitySource.StartActivity($"{typeof(TEntity).Name}.{callerName}"); + return factory(activity); + } + + protected static Result Handle( + Func factory, + [CallerMemberName] string callerName = null! + ) + { + using var activity = ActivitySource.StartActivity($"{typeof(DomainEntity).Name}.{callerName}"); + + return factory(activity); + } + + public Result Update(string? user = null, string? timezoneId = null) => Handle(activity => + { UpdatedAt = DateTime.UtcNow; UpdatedBy = user ?? "System"; UpdatedByTimezoneId = TimeZoneInfo.FindSystemTimeZoneById(string.IsNullOrWhiteSpace(timezoneId) ? TimeZoneInfo.Utc.Id : timezoneId).Id; activity?.SetTag(nameof(UpdatedBy), UpdatedBy); activity?.SetTag(nameof(UpdatedByTimezoneId), UpdatedByTimezoneId); - } + + return Result.Ok(); + }); } diff --git a/templates/Full/src/Domain/Orders/Order.cs b/templates/Full/src/Domain/Orders/Order.cs index 23fc62ef..10ef0d51 100644 --- a/templates/Full/src/Domain/Orders/Order.cs +++ b/templates/Full/src/Domain/Orders/Order.cs @@ -1,4 +1,4 @@ -using Domain.Common; +using Domain.Common; namespace Domain.Orders; @@ -26,33 +26,32 @@ public static Result Create( ICollection items, string user = "System", string? timezoneId = null - ) + ) => Handle(activity => { - using var activity = ActivitySource.StartActivity($"{nameof(Order)}.{nameof(Create)}"); - - Order order = new (description, items, user, timezoneId); + Order order = new(description, items, user, timezoneId); var setTotalResult = order.SetTotal(user, timezoneId); - if(setTotalResult.IsFailure) + if (setTotalResult.IsFailure) return Result.Fail(setTotalResult.Message); activity?.SetTag(nameof(description), description); return Result.Ok(order); - } + }); - public Result SetTotal(string user = "System", string? timezoneId = null) + public Result SetTotal(string user = "System", string? timezoneId = null) => Handle(activity => { - using var activity = ActivitySource.StartActivity($"{EntityName}.{nameof(SetTotal)}"); - if (Items == null || Items.Count == 0) return Result.Fail("Order must have at least one item."); Total = Items.Sum(item => item.Value); - Update(user, timezoneId); + + var result = Update(user, timezoneId); + if (result.IsFailure) + return Result.Fail(result.Message); activity?.SetTag(nameof(Total), Total); return Result.Ok(); - } + }); } From f9f0ac397104ee6d059b5f8d22abc5a0e038a92b Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 08:42:46 -0300 Subject: [PATCH 158/161] 53 chore: add names to validation jobs in workflow files --- .github/workflows/publish.yml | 3 +++ .github/workflows/validate.yml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1b262129..1cb65309 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,7 @@ on: jobs: validate-template-full: + name: Validate Full Template uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Full" @@ -20,6 +21,7 @@ jobs: sonar_token: ${{ secrets.SONAR_TOKEN }} validate-template-bff: + name: Validate BFF Template uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Bff" @@ -31,6 +33,7 @@ jobs: sonar_token: ${{ secrets.SONAR_TOKEN }} pack-and-publish: + name: Pack and Publish Template needs: [validate-template-full, validate-template-bff] uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-pack.yml@v1 with: diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 4c8531b5..33d122e9 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -6,6 +6,7 @@ on: jobs: validate-template-full: + name: Validate Full Template uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Full" @@ -22,6 +23,7 @@ jobs: sonar_token: ${{ secrets.SONAR_TOKEN }} validate-template-bff: + name: Validate BFF Template uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Bff" From b4f53d39911e872275c0c282299054da7d44e657 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 09:14:48 -0300 Subject: [PATCH 159/161] refactor(53): update project and organization keys in workflows --- .github/workflows/publish.yml | 1 - .github/workflows/validate.yml | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1cb65309..9344bfbf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -25,7 +25,6 @@ jobs: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Bff" - project: "hexagonal-solution-template-bff" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" integration_test_project_path: "./templates/Bff/tests/IntegrationTests" docker_compose_file_path: "./templates/Bff/docker-compose.yml" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 33d122e9..778f8537 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -10,8 +10,8 @@ jobs: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Full" - project: "hexagonal-solution-template-full" - organization: "gpreviatti" + sonar_project: "hexagonal-solution-template-full" + sonar_organization: "gpreviatti" test_no_build: false unit_test_project_path: "./templates/Full/tests/UnitTests/" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" @@ -27,7 +27,6 @@ jobs: uses: gpreviatti/github-actions-templates/.github/workflows/dotnet-validate.yml@v1 with: build_path: "./templates/Bff" - project: "hexagonal-solution-template-bff" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" integration_test_project_path: "./templates/Bff/tests/IntegrationTests" docker_compose_file_path: "./templates/Bff/docker-compose.yml" \ No newline at end of file From 09c4acfa1d0b5da7bd2ceebb960885507445d9b6 Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 09:28:20 -0300 Subject: [PATCH 160/161] chore(53): remove sonar organization from validation workflow --- .github/workflows/validate.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 778f8537..5b0d7898 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -11,7 +11,6 @@ jobs: with: build_path: "./templates/Full" sonar_project: "hexagonal-solution-template-full" - sonar_organization: "gpreviatti" test_no_build: false unit_test_project_path: "./templates/Full/tests/UnitTests/" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" From 28077b6e88bfc0ee8c11898830dc717e4674a99f Mon Sep 17 00:00:00 2001 From: Giovanni Previatti Date: Fri, 3 Apr 2026 09:40:50 -0300 Subject: [PATCH 161/161] refactor(53): change interface parameter to use 'in' modifier --- .github/workflows/publish.yml | 1 - templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9344bfbf..4f082bbf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,7 +10,6 @@ jobs: with: build_path: "./templates/Full" project: "hexagonal-solution-template-full" - organization: "gpreviatti" unit_test_project_path: "./templates/Full/tests/UnitTests/" dotnet_version: "${{ vars.PROJECT_DOTNET_VERSION }}" domain_stryker_config_path: "./templates/Full/tests/UnitTests/stryker-config-domain.json" diff --git a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs index a80c0eaf..c9efcbe7 100644 --- a/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs +++ b/templates/Full/src/Application/Common/UseCases/BaseInUseCase.cs @@ -7,7 +7,7 @@ namespace Application.Common.UseCases; -public interface IBaseInUseCase where TRequest : BaseRequest +public interface IBaseInUseCase where TRequest : BaseRequest { Task HandleAsync(TRequest request, CancellationToken cancellationToken); }