From c37320b3e58002533a4d98424eb1713b955bfab2 Mon Sep 17 00:00:00 2001 From: Sumit Ghosh Date: Wed, 20 Oct 2021 18:53:30 +0530 Subject: [PATCH] Updates all the services to .NET 6.0 (#1770) * Created global using file for catalog.api * Moved individual usings statements to globalusing * Updated catalog.api project * Fixed local run bug for catalog.api * Included globalusing for payment.api * Refactored namespace statement for payment.api * Moved namespaces to ordering.domain project * Included globalusing for ordering.domain project * Included globalusings for ordering.infrastructure project * Refactored namespaces for ordering.infrastructure project * Updated relevant packages in ordering.infrastructure project * Included globalusings for ordering.signalrHub project * Moved all the namespace to globalusings * Updated packages in ordering.signalrHub csproj file * Refactored namespace statements in catalog.api project * Fixed namespace name in ordering.domain * Included global usings for ordering.api project * Moved all usings to globalusing file * Updated ordering.api csproj project * Fixed bug in statup.cs * Updated ordering.unittests.csproj file * Included globalusings in webhooks.api project * Moved using statements to globalusing file in webhooks.api * Included globalusing for web.bff.shoppping aggregator project * Moved namespaces to globalusing shopping aggregator * Included globalusing mobile.bff.shoppping project * Moved namespaces to globalusing file * Included globalusing for eventbus project * Moved namespaces to global usings for eventbus * Included globalusing for EventBusRabbitMQ project * Moved using statements to EventBusRabbitMQ project * Included global using in EventBusServiceBus project * Moved using statements to globalusing for EventBusServiceBus * Included globalusing file for IntegrationEventLogEF project * Move using statements to globalusing file * Updated packages of IntegrationEventLogEF project * Included globalusing to Devspaces.Support project * Moved using statements to globalusing Devspaces * Updated dependent packages for Devspaces.Support.csproj * Fixed bug in Basket API * Fixed bug in catalog.api * Fixed bug Identity.API * Included globalusing to Basket.UnitTest project * Moved namespaces to Basket.UnitTest project * Updated packages of Basket.UnitTest csproj * Included globalusing for Basket.FunctionalTests project * Included file-scoped namespaces Basket.FunctionalTests * Updated packages of Basket.FunctionalTests.csproj file * Updated catalog unit test project to Net 6.0 * Included global usings for Catalog.FunctionalTests * Included file-scope namespace catalog.functionaltests * Updated packages of catalog.functionaltest csproj * Included MigrateDbContext method in HostExtensions * Included globalusing for ordering.UnitTests project * Included file-scope statement for Ordering.UnitTest project * Included globalusing for Ordering.FunctionalTests * Included file-scope namespace statement for using * Updated packages in Ordering.FunctionalTests.csproj * Apply suggestions from code review Co-authored-by: David Pine * Apply suggestions from code review Co-authored-by: David Pine * Update src/Services/Ordering/Ordering.API/Startup.cs Co-authored-by: David Pine * Update src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs Co-authored-by: David Pine * Update src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs Co-authored-by: David Pine * Update src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs Co-authored-by: David Pine * Apply suggestions from code review Co-authored-by: David Pine * Apply suggestions from code review Co-authored-by: David Pine Co-authored-by: David Pine --- .../aggregator/Config/UrlsConfig.cs | 45 +- .../Controllers/BasketController.cs | 240 +++---- .../aggregator/Controllers/HomeController.cs | 15 +- .../aggregator/Controllers/OrderController.cs | 60 +- .../Filters/AuthorizeCheckOperationFilter.cs | 9 +- .../aggregator/GlobalUsings.cs | 41 ++ .../GrpcExceptionInterceptor.cs | 54 +- ...ttpClientAuthorizationDelegatingHandler.cs | 74 +- .../aggregator/Models/AddBasketItemRequest.cs | 19 +- .../aggregator/Models/BasketData.cs | 25 +- .../aggregator/Models/BasketDataItem.cs | 23 +- .../aggregator/Models/CatalogItem.cs | 15 +- .../aggregator/Models/OrderData.cs | 48 +- .../aggregator/Models/OrderItemData.cs | 21 +- .../aggregator/Models/UpdateBasketItemData.cs | 19 +- .../Models/UpdateBasketItemsRequest.cs | 19 +- .../aggregator/Models/UpdateBasketRequest.cs | 15 +- .../Models/UpdateBasketRequestItemData.cs | 15 +- .../Mobile.Bff.Shopping/aggregator/Program.cs | 8 +- .../aggregator/Services/BasketService.cs | 131 ++-- .../aggregator/Services/CatalogService.cs | 61 +- .../aggregator/Services/IBasketService.cs | 12 +- .../aggregator/Services/ICatalogService.cs | 13 +- .../aggregator/Services/IOrderApiClient.cs | 10 +- .../aggregator/Services/IOrderingService.cs | 12 +- .../aggregator/Services/OrderApiClient.cs | 53 +- .../aggregator/Services/OrderingService.cs | 115 ++- .../Mobile.Bff.Shopping/aggregator/Startup.cs | 326 ++++----- .../aggregator/Config/UrlsConfig.cs | 54 +- .../Controllers/BasketController.cs | 252 ++++--- .../aggregator/Controllers/HomeController.cs | 15 +- .../aggregator/Controllers/OrderController.cs | 61 +- .../Filters/AuthorizeCheckOperationFilter.cs | 23 +- .../aggregator/GlobalUsings.cs | 41 ++ .../GrpcExceptionInterceptor.cs | 54 +- ...ttpClientAuthorizationDelegatingHandler.cs | 65 +- .../aggregator/Models/AddBasketItemRequest.cs | 22 +- .../aggregator/Models/BasketData.cs | 26 +- .../aggregator/Models/BasketDataItem.cs | 23 +- .../aggregator/Models/CatalogItem.cs | 17 +- .../aggregator/Models/OrderData.cs | 49 +- .../aggregator/Models/OrderItemData.cs | 21 +- .../aggregator/Models/UpdateBasketItemData.cs | 17 +- .../Models/UpdateBasketItemsRequest.cs | 19 +- .../aggregator/Models/UpdateBasketRequest.cs | 13 +- .../Models/UpdateBasketRequestItemData.cs | 13 +- .../Web.Bff.Shopping/aggregator/Program.cs | 7 +- .../aggregator/Services/BasketService.cs | 149 ++-- .../aggregator/Services/CatalogService.cs | 74 +- .../aggregator/Services/IBasketService.cs | 12 +- .../aggregator/Services/ICatalogService.cs | 13 +- .../aggregator/Services/IOrderApiClient.cs | 10 +- .../aggregator/Services/IOrderingService.cs | 12 +- .../aggregator/Services/OrderApiClient.cs | 53 +- .../aggregator/Services/OrderingService.cs | 115 ++- .../Web.Bff.Shopping/aggregator/Startup.cs | 324 ++++----- .../Devspaces.Support.csproj | 4 +- .../DevspacesMessageHandler.cs | 35 +- .../Devspaces.Support/GlobalUsings.cs | 6 + .../HttpClientBuilderDevspacesExtensions.cs | 13 +- .../ServiceCollectionDevspacesExtensions.cs | 13 +- .../IDynamicIntegrationEventHandler.cs | 9 +- .../EventBus/Abstractions/IEventBus.cs | 29 +- .../Abstractions/IIntegrationEventHandler.cs | 18 +- .../EventBus/Events/IntegrationEvent.cs | 40 +- .../Extensions/GenericTypeExtensions.cs | 38 +- .../EventBus/EventBus/GlobalUsings.cs | 8 + .../EventBus/IEventBusSubscriptionsManager.cs | 51 +- .../InMemoryEventBusSubscriptionsManager.cs | 227 +++--- .../EventBus/EventBus/SubscriptionInfo.cs | 34 +- .../DefaultRabbitMQPersistentConnection.cs | 186 +++-- .../EventBusRabbitMQ/EventBusRabbitMQ.cs | 430 ++++++----- .../EventBus/EventBusRabbitMQ/GlobalUsings.cs | 17 + .../IRabbitMQPersistentConnection.cs | 16 +- .../DefaultServiceBusPersisterConnection.cs | 88 ++- .../EventBusServiceBus/EventBusServiceBus.cs | 308 ++++---- .../EventBusServiceBus/GlobalUsings.cs | 22 + .../IServiceBusPersisterConnection.cs | 16 +- .../IntegrationEventLogEF/EventStateEnum.cs | 16 +- .../IntegrationEventLogEF/GlobalUsings.cs | 12 + .../IntegrationEventLogContext.cs | 54 +- .../IntegrationEventLogEF.csproj | 8 +- .../IntegrationEventLogEntry.cs | 65 +- .../Services/IIntegrationEventLogService.cs | 21 +- .../Services/IntegrationEventLogService.cs | 157 ++--- .../Utilities/ResilientTransaction.cs | 40 +- .../Middlewares/FailingMiddleware.cs | 6 +- .../Base/AutoAuthorizeMiddleware.cs | 37 +- .../Base/BasketScenarioBase.cs | 57 +- .../Base/BasketTestStartup.cs | 7 +- .../Base/HttpClientExtensions.cs | 17 +- .../Basket.FunctionalTests.csproj | 4 +- .../Basket.FunctionalTests/BasketScenarios.cs | 130 ++-- .../Basket.FunctionalTests/GlobalUsings.cs | 23 + .../RedisBasketRepositoryTests.cs | 10 +- .../Application/BasketWebApiTest.cs | 247 +++---- .../Application/CartControllerTest.cs | 219 +++--- .../Basket.UnitTests/Basket.UnitTests.csproj | 4 +- .../Basket/Basket.UnitTests/GlobalUsings.cs | 19 + .../Catalog/Catalog.API/Catalog.API.csproj | 39 +- .../Catalog/Catalog.API/CatalogSettings.cs | 15 +- .../Controllers/CatalogController.cs | 463 ++++++------ .../Catalog.API/Controllers/HomeController.cs | 16 +- .../Catalog.API/Controllers/PicController.cs | 146 ++-- .../Extensions/CatalogItemExtensions.cs | 17 +- .../Catalog.API/Extensions/HostExtensions.cs | 164 +++-- .../Extensions/LinqSelectExtensions.cs | 67 +- .../Extensions/WebHostExtensions.cs | 112 ++- .../Catalog/Catalog.API/GlobalUsings.cs | 62 ++ .../Catalog.API/Grpc/CatalogService.cs | 277 ++++---- .../InternalServerErrorObjectResult.cs | 14 +- .../Infrastructure/CatalogContext.cs | 48 +- .../Infrastructure/CatalogContextSeed.cs | 576 ++++++++------- .../CatalogBrandEntityTypeConfiguration.cs | 29 +- .../CatalogItemEntityTypeConfiguration.cs | 49 +- .../CatalogTypeEntityTypeConfiguration.cs | 29 +- .../Exceptions/CatalogDomainException.cs | 29 +- .../Filters/HttpGlobalExceptionFilter.cs | 93 ++- .../CatalogIntegrationEventService.cs | 124 ++-- ...aitingValidationIntegrationEventHandler.cs | 80 +-- ...tusChangedToPaidIntegrationEventHandler.cs | 54 +- ...gedToAwaitingValidationIntegrationEvent.cs | 42 +- ...rderStatusChangedToPaidIntegrationEvent.cs | 24 +- .../OrderStockConfirmedIntegrationEvent.cs | 15 +- .../OrderStockRejectedIntegrationEvent.cs | 44 +- .../ProductPriceChangedIntegrationEvent.cs | 31 +- .../ICatalogIntegrationEventService.cs | 12 +- .../Catalog/Catalog.API/Model/CatalogBrand.cs | 11 +- .../Catalog/Catalog.API/Model/CatalogItem.cs | 146 ++-- .../Catalog/Catalog.API/Model/CatalogType.cs | 11 +- src/Services/Catalog/Catalog.API/Program.cs | 27 +- src/Services/Catalog/Catalog.API/Startup.cs | 566 +++++++-------- .../ViewModel/PaginatedItemsViewModel.cs | 30 +- .../Catalog.FunctionalTests.csproj | 4 +- .../CatalogScenarioBase.cs | 131 ++-- .../CatalogScenarios.cs | 195 +++-- .../Catalog.FunctionalTests/GlobalUsings.cs | 16 + .../Application/CatalogControllerTest.cs | 196 +++--- .../Catalog.UnitTests.csproj | 4 +- .../Data/ApplicationDbContextSeed.cs | 7 +- .../DevspacesRedirectUriValidator.cs | 4 +- .../Application/Behaviors/LoggingBehavior.cs | 29 +- .../Behaviors/TransactionBehaviour.cs | 96 ++- .../Behaviors/ValidatorBehavior.cs | 62 +- .../Commands/CancelOrderCommand.cs | 24 +- .../Commands/CancelOrderCommandHandler.cs | 77 +- .../Commands/CreateOrderCommand.cs | 157 ++--- .../Commands/CreateOrderCommandHandler.cs | 122 ++-- .../Commands/CreateOrderDraftCommand.cs | 23 +- .../CreateOrderDraftCommandHandler.cs | 99 ++- .../Application/Commands/IdentifiedCommand.cs | 20 +- .../Commands/IdentifiedCommandHandler.cs | 181 +++-- ...SetAwaitingValidationOrderStatusCommand.cs | 20 +- ...tingValidationOrderStatusCommandHandler.cs | 77 +- .../Commands/SetPaidOrderStatusCommand.cs | 20 +- .../SetPaidOrderStatusCommandHandler.cs | 85 +-- .../SetStockConfirmedOrderStatusCommand.cs | 20 +- ...StockConfirmedOrderStatusCommandHandler.cs | 85 +-- .../SetStockRejectedOrderStatusCommand.cs | 27 +- ...tStockRejectedOrderStatusCommandHandler.cs | 85 +-- .../Application/Commands/ShipOrderCommand.cs | 20 +- .../Commands/ShipOrderCommandHandler.cs | 77 +- ...PaymentMethodVerifiedDomainEventHandler.cs | 53 +- .../OrderCancelledDomainEventHandler.cs | 68 +- ...dToAwaitingValidationDomainEventHandler.cs | 73 +- ...erStatusChangedToPaidDomainEventHandler.cs | 83 +-- .../OrderShippedDomainEventHandler.cs | 66 +- ...tomerWhenOrderStartedDomainEventHandler.cs | 21 +- ...egateWhenOrderStartedDomainEventHandler.cs | 104 ++- ...angedToStockConfirmedDomainEventHandler.cs | 68 +- ...ePeriodConfirmedIntegrationEventHandler.cs | 72 +- ...derPaymentFailedIntegrationEventHandler.cs | 59 +- ...PaymentSucceededIntegrationEventHandler.cs | 61 +- ...erStockConfirmedIntegrationEventHandler.cs | 61 +- ...derStockRejectedIntegrationEventHandler.cs | 66 +- ...CheckoutAcceptedIntegrationEventHandler.cs | 115 ++- .../GracePeriodConfirmedIntegrationEvent.cs | 16 +- .../OrderPaymentFailedIntegrationEvent .cs | 15 +- .../OrderPaymentSucceededIntegrationEvent.cs | 15 +- .../Events/OrderStartedIntegrationEvent.cs | 21 +- ...gedToAwaitingValidationIntegrationEvent.cs | 50 +- ...tatusChangedToCancelledIntegrationEvent.cs | 23 +- ...rderStatusChangedToPaidIntegrationEvent.cs | 35 +- ...rStatusChangedToShippedIntegrationEvent.cs | 23 +- ...ChangedToStockConfirmedIntegrationEvent.cs | 25 +- ...tatusChangedTosubmittedIntegrationEvent.cs | 23 +- .../OrderStockConfirmedIntegrationEvent.cs | 15 +- .../OrderStockRejectedIntegrationEvent.cs | 44 +- .../UserCheckoutAcceptedIntegrationEvent.cs | 85 ++- .../IOrderingIntegrationEventService.cs | 13 +- .../OrderingIntegrationEventService.cs | 88 +-- .../Application/Models/BasketItem.cs | 22 +- .../Application/Models/CustomerBasket.cs | 19 +- .../Application/Queries/IOrderQueries.cs | 17 +- .../Application/Queries/OrderQueries.cs | 151 ++-- .../Application/Queries/OrderViewModel.cs | 66 +- .../CancelOrderCommandValidator.cs | 17 +- .../CreateOrderCommandValidator.cs | 57 +- .../Validations/IdentifiedCommandValidator.cs | 15 +- .../Validations/ShipOrderCommandValidator.cs | 17 +- .../Controllers/HomeController.cs | 13 +- .../Controllers/OrdersController.cs | 232 +++--- .../Extensions/BasketItemExtensions.cs | 35 +- .../Extensions/LinqSelectExtensions.cs | 67 +- .../Ordering/Ordering.API/GlobalUsings.cs | 85 +++ .../Ordering.API/Grpc/OrderingService.cs | 132 ++-- .../InternalServerErrorObjectResult.cs | 16 +- ...orizationHeaderParameterOperationFilter.cs | 43 +- .../AutofacModules/ApplicationModule.cs | 62 +- .../AutofacModules/MediatorModule.cs | 61 +- .../Factories/OrderingDbContextFactory.cs | 8 +- .../Filters/AuthorizeCheckOperationFilter.cs | 47 +- .../Filters/HttpGlobalExceptionFilter.cs | 97 ++- .../Infrastructure/OrderingContextSeed.cs | 273 ++++--- .../Services/IIdentityService.cs | 12 +- .../Services/IdentityService.cs | 33 +- .../Ordering/Ordering.API/Ordering.API.csproj | 31 +- .../Ordering/Ordering.API/OrderingSettings.cs | 17 +- src/Services/Ordering/Ordering.API/Program.cs | 20 +- src/Services/Ordering/Ordering.API/Startup.cs | 666 ++++++++---------- .../AggregatesModel/BuyerAggregate/Buyer.cs | 69 +- .../BuyerAggregate/CardType.cs | 29 +- .../BuyerAggregate/IBuyerRepository.cs | 23 +- .../BuyerAggregate/PaymentMethod.cs | 63 +- .../AggregatesModel/OrderAggregate/Address.cs | 53 +- .../OrderAggregate/IOrderRepository.cs | 20 +- .../AggregatesModel/OrderAggregate/Order.cs | 295 ++++---- .../OrderAggregate/OrderItem.cs | 118 ++-- .../OrderAggregate/OrderStatus.cs | 75 +- .../BuyerPaymentMethodVerifiedDomainEvent.cs | 26 +- .../Events/OrderCancelledDomainEvent.cs | 17 +- .../Events/OrderShippedDomainEvent.cs | 16 +- .../Events/OrderStartedDomainEvent.cs | 7 +- ...sChangedToAwaitingValidationDomainEvent.cs | 33 +- .../OrderStatusChangedToPaidDomainEvent.cs | 33 +- ...tatusChangedToStockConfirmedDomainEvent.cs | 25 +- .../Exceptions/OrderingDomainException.cs | 29 +- .../Ordering/Ordering.Domain/GlobalUsings.cs | 13 + .../Ordering.Domain/SeedWork/Entity.cs | 135 ++-- .../Ordering.Domain/SeedWork/Enumeration.cs | 96 ++- .../SeedWork/IAggregateRoot.cs | 7 +- .../Ordering.Domain/SeedWork/IRepository.cs | 9 +- .../Ordering.Domain/SeedWork/IUnitOfWork.cs | 13 +- .../Ordering.Domain/SeedWork/ValueObject.cs | 62 +- .../AutoAuthorizeMiddleware.cs | 37 +- .../Ordering.FunctionalTests/GlobalUsings.cs | 19 + .../HttpClientExtensions.cs | 17 +- .../Ordering.FunctionalTests.csproj | 4 +- .../OrderingScenarioBase.cs | 88 +-- .../OrderingTestStartup.cs | 42 +- .../BuyerEntityTypeConfiguration.cs | 48 +- .../CardTypeEntityTypeConfiguration.cs | 32 +- .../ClientRequestEntityTypeConfiguration.cs | 22 +- .../OrderEntityTypeConfiguration.cs | 126 ++-- .../OrderItemEntityTypeConfiguration.cs | 102 ++- .../OrderStatusEntityTypeConfiguration.cs | 32 +- .../PaymentMethodEntityTypeConfiguration.cs | 113 ++- .../Ordering.Infrastructure/GlobalUsings.cs | 17 + .../Idempotency/ClientRequest.cs | 13 +- .../Idempotency/IRequestManager.cs | 12 +- .../Idempotency/RequestManager.cs | 57 +- .../MediatorExtension.cs | 33 +- .../Ordering.Infrastructure.csproj | 6 +- .../OrderingContext.cs | 225 +++--- .../Repositories/BuyerRepository.cs | 94 ++- .../Repositories/OrderRepository.cs | 90 ++- .../AutofacModules/ApplicationModule.cs | 28 +- .../Ordering.SignalrHub/GlobalUsings.cs | 35 + ...aitingValidationIntegrationEventHandler.cs | 44 +- ...angedToCancelledIntegrationEventHandler.cs | 45 +- ...tusChangedToPaidIntegrationEventHandler.cs | 45 +- ...ChangedToShippedIntegrationEventHandler.cs | 45 +- ...ToStockConfirmedIntegrationEventHandler.cs | 47 +- ...angedToSubmittedIntegrationEventHandler.cs | 49 +- ...gedToAwaitingValidationIntegrationEvent.cs | 24 +- ...tatusChangedToCancelledIntegrationEvent.cs | 24 +- ...rderStatusChangedToPaidIntegrationEvent.cs | 25 +- ...rStatusChangedToShippedIntegrationEvent.cs | 24 +- ...ChangedToStockConfirmedIntegrationEvent.cs | 23 +- ...tatusChangedToSubmittedIntegrationEvent.cs | 23 +- .../Ordering.SignalrHub/NotificationHub.cs | 30 +- .../Ordering.SignalrHub.csproj | 40 +- .../Ordering/Ordering.SignalrHub/Program.cs | 10 +- .../Ordering/Ordering.SignalrHub/Startup.cs | 413 +++++------ .../IdentifiedCommandHandlerTest.cs | 138 ++-- .../Application/NewOrderCommandHandlerTest.cs | 168 ++--- .../Application/OrdersWebApiTest.cs | 272 ++++--- .../Ordering/Ordering.UnitTests/Builders.cs | 74 +- .../Domain/BuyerAggregateTest.cs | 7 +- .../Domain/OrderAggregateTest.cs | 8 +- .../Domain/SeedWork/ValueObjectTests.cs | 301 ++++---- .../Ordering.UnitTests/GlobalUsings.cs | 23 + .../Ordering.UnitTests.csproj | 2 +- .../Payment/Payment.API/GlobalUsings.cs | 21 + ...ToStockConfirmedIntegrationEventHandler.cs | 87 +-- .../OrderPaymentFailedIntegrationEvent.cs | 15 +- .../OrderPaymentSucceededIntegrationEvent.cs | 15 +- ...ChangedToStockConfirmedIntegrationEvent.cs | 17 +- .../Payment/Payment.API/PaymentSettings.cs | 12 +- src/Services/Payment/Payment.API/Program.cs | 14 +- src/Services/Payment/Payment.API/Startup.cs | 310 ++++---- .../Controllers/HomeController.cs | 16 +- .../Controllers/WebhookSubscriptionRequest.cs | 46 +- .../Controllers/WebhooksController.cs | 167 ++--- .../Exceptions/WebhooksDomainException.cs | 7 +- .../Webhooks/Webhooks.API/GlobalUsings.cs | 50 ++ .../InternalServerErrorObjectResult.cs | 12 +- .../AuthorizeCheckOperationFilter.cs | 45 +- .../HttpGlobalExceptionFilter.cs | 93 ++- .../Infrastructure/WebhooksContext.cs | 29 +- ...rderStatusChangedToPaidIntegrationEvent.cs | 40 +- ...tusChangedToPaidIntegrationEventHandler.cs | 40 +- ...rStatusChangedToShippedIntegrationEvent.cs | 23 +- ...ChangedToShippedIntegrationEventHandler.cs | 40 +- .../ProductPriceChangedIntegrationEvent.cs | 23 +- ...ductPriceChangedIntegrationEventHandler.cs | 12 +- .../Webhooks.API/Model/WebhookData.cs | 24 +- .../Webhooks.API/Model/WebhookSubscription.cs | 19 +- .../Webhooks.API/Model/WebhookType.cs | 13 +- src/Services/Webhooks/Webhooks.API/Program.cs | 9 +- .../Services/GrantUrlTesterService.cs | 83 +-- .../Services/IGrantUrlTesterService.cs | 9 +- .../Webhooks.API/Services/IIdentityService.cs | 9 +- .../Services/IWebhooksRetriever.cs | 11 +- .../Webhooks.API/Services/IWebhooksSender.cs | 11 +- .../Webhooks.API/Services/IdentityService.cs | 25 +- .../Services/WebhooksRetriever.cs | 28 +- .../Webhooks.API/Services/WebhooksSender.cs | 67 +- src/Services/Webhooks/Webhooks.API/Startup.cs | 528 +++++++------- 329 files changed, 10041 insertions(+), 11421 deletions(-) create mode 100644 src/ApiGateways/Mobile.Bff.Shopping/aggregator/GlobalUsings.cs create mode 100644 src/ApiGateways/Web.Bff.Shopping/aggregator/GlobalUsings.cs create mode 100644 src/BuildingBlocks/Devspaces.Support/GlobalUsings.cs create mode 100644 src/BuildingBlocks/EventBus/EventBus/GlobalUsings.cs create mode 100644 src/BuildingBlocks/EventBus/EventBusRabbitMQ/GlobalUsings.cs create mode 100644 src/BuildingBlocks/EventBus/EventBusServiceBus/GlobalUsings.cs create mode 100644 src/BuildingBlocks/EventBus/IntegrationEventLogEF/GlobalUsings.cs create mode 100644 src/Services/Basket/Basket.FunctionalTests/GlobalUsings.cs create mode 100644 src/Services/Basket/Basket.UnitTests/GlobalUsings.cs create mode 100644 src/Services/Catalog/Catalog.API/GlobalUsings.cs create mode 100644 src/Services/Catalog/Catalog.FunctionalTests/GlobalUsings.cs create mode 100644 src/Services/Ordering/Ordering.API/GlobalUsings.cs create mode 100644 src/Services/Ordering/Ordering.Domain/GlobalUsings.cs create mode 100644 src/Services/Ordering/Ordering.FunctionalTests/GlobalUsings.cs create mode 100644 src/Services/Ordering/Ordering.Infrastructure/GlobalUsings.cs create mode 100644 src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs create mode 100644 src/Services/Ordering/Ordering.UnitTests/GlobalUsings.cs create mode 100644 src/Services/Payment/Payment.API/GlobalUsings.cs create mode 100644 src/Services/Webhooks/Webhooks.API/GlobalUsings.cs diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs index 90fa010cd..ccd733bc3 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs @@ -1,38 +1,35 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config +public class UrlsConfig { - public class UrlsConfig + public class CatalogOperations { - public class CatalogOperations - { - public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; + public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; - public static string GetItemsById(IEnumerable ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}"; - } + public static string GetItemsById(IEnumerable ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}"; + } - public class BasketOperations - { - public static string GetItemById(string id) => $"/api/v1/basket/{id}"; + public class BasketOperations + { + public static string GetItemById(string id) => $"/api/v1/basket/{id}"; - public static string UpdateBasket() => "/api/v1/basket"; - } + public static string UpdateBasket() => "/api/v1/basket"; + } - public class OrdersOperations - { - public static string GetOrderDraft() => "/api/v1/orders/draft"; - } + public class OrdersOperations + { + public static string GetOrderDraft() => "/api/v1/orders/draft"; + } - public string Basket { get; set; } + public string Basket { get; set; } - public string Catalog { get; set; } + public string Catalog { get; set; } - public string Orders { get; set; } + public string Orders { get; set; } - public string GrpcBasket { get; set; } + public string GrpcBasket { get; set; } - public string GrpcCatalog { get; set; } + public string GrpcCatalog { get; set; } - public string GrpcOrdering { get; set; } - } + public string GrpcOrdering { get; set; } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs index 64399465a..9f39cdd6d 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs @@ -1,156 +1,146 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -using System; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers; + +[Route("api/v1/[controller]")] +[Authorize] +[ApiController] +public class BasketController : ControllerBase { - [Route("api/v1/[controller]")] - [Authorize] - [ApiController] - public class BasketController : ControllerBase + private readonly ICatalogService _catalog; + private readonly IBasketService _basket; + + public BasketController(ICatalogService catalogService, IBasketService basketService) { - private readonly ICatalogService _catalog; - private readonly IBasketService _basket; + _catalog = catalogService; + _basket = basketService; + } - public BasketController(ICatalogService catalogService, IBasketService basketService) + [HttpPost] + [HttpPut] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] + public async Task> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data) + { + if (data.Items == null || !data.Items.Any()) { - _catalog = catalogService; - _basket = basketService; + return BadRequest("Need to pass at least one basket line"); } - [HttpPost] - [HttpPut] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] - public async Task> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data) - { - if (data.Items == null || !data.Items.Any()) - { - return BadRequest("Need to pass at least one basket line"); - } + // Retrieve the current basket + var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId); - // Retrieve the current basket - var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId); - - var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId)); - // group by product id to avoid duplicates - var itemsCalculated = data - .Items - .GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i }) - .Select(groupedItem => - { - var item = groupedItem.items.First(); - item.Quantity = groupedItem.items.Sum(i => i.Quantity); - return item; - }); - - foreach (var bitem in itemsCalculated) - { - var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); - if (catalogItem == null) + var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId)); + // group by product id to avoid duplicates + var itemsCalculated = data + .Items + .GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i }) + .Select(groupedItem => { - return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); - } + var item = groupedItem.items.First(); + item.Quantity = groupedItem.items.Sum(i => i.Quantity); + return item; + }); - var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId); - if (itemInBasket == null) - { - basket.Items.Add(new BasketDataItem() - { - Id = bitem.Id, - ProductId = catalogItem.Id, - ProductName = catalogItem.Name, - PictureUrl = catalogItem.PictureUri, - UnitPrice = catalogItem.Price, - Quantity = bitem.Quantity - }); - } - else - { - itemInBasket.Quantity = bitem.Quantity; - } - } - - await _basket.UpdateAsync(basket); - - return basket; - } - - [HttpPut] - [Route("items")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] - public async Task> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data) + foreach (var bitem in itemsCalculated) { - if (!data.Updates.Any()) + var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); + if (catalogItem == null) { - return BadRequest("No updates sent"); + return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); } - // Retrieve the current basket - var currentBasket = await _basket.GetById(data.BasketId); - if (currentBasket == null) + var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId); + if (itemInBasket == null) { - return BadRequest($"Basket with id {data.BasketId} not found."); + basket.Items.Add(new BasketDataItem() + { + Id = bitem.Id, + ProductId = catalogItem.Id, + ProductName = catalogItem.Name, + PictureUrl = catalogItem.PictureUri, + UnitPrice = catalogItem.Price, + Quantity = bitem.Quantity + }); } - - // Update with new quantities - foreach (var update in data.Updates) + else { - var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId); + itemInBasket.Quantity = bitem.Quantity; + } + } - if (basketItem == null) - { - return BadRequest($"Basket item with id {update.BasketItemId} not found"); - } + await _basket.UpdateAsync(basket); - basketItem.Quantity = update.NewQty; - } + return basket; + } - // Save the updated basket - await _basket.UpdateAsync(currentBasket); + [HttpPut] + [Route("items")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] + public async Task> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data) + { + if (!data.Updates.Any()) + { + return BadRequest("No updates sent"); + } - return currentBasket; + // Retrieve the current basket + var currentBasket = await _basket.GetById(data.BasketId); + if (currentBasket == null) + { + return BadRequest($"Basket with id {data.BasketId} not found."); } - [HttpPost] - [Route("items")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.OK)] - public async Task AddBasketItemAsync([FromBody] AddBasketItemRequest data) + // Update with new quantities + foreach (var update in data.Updates) { - if (data == null || data.Quantity == 0) + var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId); + + if (basketItem == null) { - return BadRequest("Invalid payload"); + return BadRequest($"Basket item with id {update.BasketItemId} not found"); } - // Step 1: Get the item from catalog - var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId); + basketItem.Quantity = update.NewQty; + } + + // Save the updated basket + await _basket.UpdateAsync(currentBasket); - //item.PictureUri = + return currentBasket; + } - // Step 2: Get current basket status - var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId); - // Step 3: Merge current status with new product - currentBasket.Items.Add(new BasketDataItem() - { - UnitPrice = item.Price, - PictureUrl = item.PictureUri, - ProductId = item.Id, - ProductName = item.Name, - Quantity = data.Quantity, - Id = Guid.NewGuid().ToString() - }); - - // Step 4: Update basket - await _basket.UpdateAsync(currentBasket); - - return Ok(); + [HttpPost] + [Route("items")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.OK)] + public async Task AddBasketItemAsync([FromBody] AddBasketItemRequest data) + { + if (data == null || data.Quantity == 0) + { + return BadRequest("Invalid payload"); } + + // Step 1: Get the item from catalog + var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId); + + //item.PictureUri = + + // Step 2: Get current basket status + var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId); + // Step 3: Merge current status with new product + currentBasket.Items.Add(new BasketDataItem() + { + UnitPrice = item.Price, + PictureUrl = item.PictureUri, + ProductId = item.Id, + ProductName = item.Name, + Quantity = data.Quantity, + Id = Guid.NewGuid().ToString() + }); + + // Step 4: Update basket + await _basket.UpdateAsync(currentBasket); + + return Ok(); } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs index e72e23ca7..5328f308d 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs @@ -1,14 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers +[Route("")] +public class HomeController : Controller { - [Route("")] - public class HomeController : Controller + [HttpGet] + public IActionResult Index() { - [HttpGet()] - public IActionResult Index() - { - return new RedirectResult("~/swagger"); - } + return new RedirectResult("~/swagger"); } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs index 8bacc80d4..2203db664 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs @@ -1,45 +1,37 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -using System.Net; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers +[Route("api/v1/[controller]")] +[Authorize] +[ApiController] +public class OrderController : ControllerBase { - [Route("api/v1/[controller]")] - [Authorize] - [ApiController] - public class OrderController : ControllerBase + private readonly IBasketService _basketService; + private readonly IOrderingService _orderingService; + + public OrderController(IBasketService basketService, IOrderingService orderingService) { - private readonly IBasketService _basketService; - private readonly IOrderingService _orderingService; + _basketService = basketService; + _orderingService = orderingService; + } - public OrderController(IBasketService basketService, IOrderingService orderingService) + [Route("draft/{basketId}")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)] + public async Task> GetOrderDraftAsync(string basketId) + { + if (string.IsNullOrEmpty(basketId)) { - _basketService = basketService; - _orderingService = orderingService; + return BadRequest("Need a valid basketid"); } + // Get the basket data and build a order draft based on it + var basket = await _basketService.GetById(basketId); - [Route("draft/{basketId}")] - [HttpGet] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)] - public async Task> GetOrderDraftAsync(string basketId) + if (basket == null) { - if (string.IsNullOrEmpty(basketId)) - { - return BadRequest("Need a valid basketid"); - } - // Get the basket data and build a order draft based on it - var basket = await _basketService.GetById(basketId); - - if (basket == null) - { - return BadRequest($"No basket found for id {basketId}"); - } - - return await _orderingService.GetOrderDraftAsync(basket); + return BadRequest($"No basket found for id {basketId}"); } + + return await _orderingService.GetOrderDraftAsync(basket); } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs index e975edb23..e6a0bf56b 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs @@ -1,12 +1,5 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters { - namespace Basket.API.Infrastructure.Filters { public class AuthorizeCheckOperationFilter : IOperationFilter diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/GlobalUsings.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/GlobalUsings.cs new file mode 100644 index 000000000..881670f5e --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/GlobalUsings.cs @@ -0,0 +1,41 @@ +global using CatalogApi; +global using Devspaces.Support; +global using Grpc.Core.Interceptors; +global using Grpc.Core; +global using GrpcBasket; +global using GrpcOrdering; +global using HealthChecks.UI.Client; +global using Microsoft.AspNetCore.Authentication.JwtBearer; +global using Microsoft.AspNetCore.Authentication; +global using Microsoft.AspNetCore.Authorization; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore; +global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; +global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; +global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure; +global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; +global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; +global using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Microsoft.OpenApi.Models; +global using Serilog; +global using Swashbuckle.AspNetCore.SwaggerGen; +global using System.Collections.Generic; +global using System.IdentityModel.Tokens.Jwt; +global using System.Linq; +global using System.Net.Http.Headers; +global using System.Net.Http; +global using System.Net; +global using System.Text.Json; +global using System.Threading.Tasks; +global using System.Threading; +global using System; diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs index e74a4b2f5..c434074d3 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs @@ -1,41 +1,35 @@ -using Grpc.Core; -using Grpc.Core.Interceptors; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure +public class GrpcExceptionInterceptor : Interceptor { - public class GrpcExceptionInterceptor : Interceptor + private readonly ILogger _logger; + + public GrpcExceptionInterceptor(ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + } - public GrpcExceptionInterceptor(ILogger logger) - { - _logger = logger; - } + public override AsyncUnaryCall AsyncUnaryCall( + TRequest request, + ClientInterceptorContext context, + AsyncUnaryCallContinuation continuation) + { + var call = continuation(request, context); - public override AsyncUnaryCall AsyncUnaryCall( - TRequest request, - ClientInterceptorContext context, - AsyncUnaryCallContinuation continuation) - { - var call = continuation(request, context); + return new AsyncUnaryCall(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose); + } - return new AsyncUnaryCall(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose); + private async Task HandleResponse(Task t) + { + try + { + var response = await t; + return response; } - - private async Task HandleResponse(Task t) + catch (RpcException e) { - try - { - var response = await t; - return response; - } - catch (RpcException e) - { - _logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message); - return default; - } + _logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message); + return default; } } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs index 937a745ee..24914ca33 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs @@ -1,54 +1,44 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure -{ - public class HttpClientAuthorizationDelegatingHandler : DelegatingHandler - { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure; - public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor, ILogger logger) - { - _httpContextAccessor = httpContextAccessor; - _logger = logger; - } +public class HttpClientAuthorizationDelegatingHandler : DelegatingHandler +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - request.Version = new System.Version(2, 0); - request.Method = HttpMethod.Get; + public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor, ILogger logger) + { + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } - var authorizationHeader = _httpContextAccessor.HttpContext - .Request.Headers["Authorization"]; + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + request.Version = new System.Version(2, 0); + request.Method = HttpMethod.Get; - if (!string.IsNullOrEmpty(authorizationHeader)) - { - request.Headers.Add("Authorization", new List() { authorizationHeader }); - } + var authorizationHeader = _httpContextAccessor.HttpContext + .Request.Headers["Authorization"]; - var token = await GetToken(); + if (!string.IsNullOrEmpty(authorizationHeader)) + { + request.Headers.Add("Authorization", new List() { authorizationHeader }); + } - if (token != null) - { - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); - } + var token = await GetToken(); - return await base.SendAsync(request, cancellationToken); + if (token != null) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } - async Task GetToken() - { - const string ACCESS_TOKEN = "access_token"; + return await base.SendAsync(request, cancellationToken); + } - return await _httpContextAccessor.HttpContext - .GetTokenAsync(ACCESS_TOKEN); - } + async Task GetToken() + { + const string ACCESS_TOKEN = "access_token"; + + return await _httpContextAccessor.HttpContext + .GetTokenAsync(ACCESS_TOKEN); } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs index d0271d84b..f7037d196 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs @@ -1,16 +1,15 @@ -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; + +public class AddBasketItemRequest { - public class AddBasketItemRequest - { - public int CatalogItemId { get; set; } + public int CatalogItemId { get; set; } - public string BasketId { get; set; } + public string BasketId { get; set; } - public int Quantity { get; set; } + public int Quantity { get; set; } - public AddBasketItemRequest() - { - Quantity = 1; - } + public AddBasketItemRequest() + { + Quantity = 1; } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs index 7a43d58e5..d47e6efbd 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs @@ -1,22 +1,17 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models +public class BasketData { + public string BuyerId { get; set; } - public class BasketData - { - public string BuyerId { get; set; } - - public List Items { get; set; } = new List(); - - public BasketData() - { - } + public List Items { get; set; } = new(); - public BasketData(string buyerId) - { - BuyerId = buyerId; - } + public BasketData() + { } + public BasketData(string buyerId) + { + BuyerId = buyerId; + } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketDataItem.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketDataItem.cs index 3ff91df4f..95bda8d37 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketDataItem.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketDataItem.cs @@ -1,21 +1,18 @@ -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models -{ - - public class BasketDataItem - { - public string Id { get; set; } +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; - public int ProductId { get; set; } +public class BasketDataItem +{ + public string Id { get; set; } - public string ProductName { get; set; } + public int ProductId { get; set; } - public decimal UnitPrice { get; set; } + public string ProductName { get; set; } - public decimal OldUnitPrice { get; set; } + public decimal UnitPrice { get; set; } - public int Quantity { get; set; } + public decimal OldUnitPrice { get; set; } - public string PictureUrl { get; set; } - } + public int Quantity { get; set; } + public string PictureUrl { get; set; } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs index 603c70bd7..7b673de3b 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs @@ -1,13 +1,12 @@ -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; + +public class CatalogItem { - public class CatalogItem - { - public int Id { get; set; } + public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public decimal Price { get; set; } + public decimal Price { get; set; } - public string PictureUri { get; set; } - } + public string PictureUri { get; set; } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs index 3ecddcf36..8761417ab 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs @@ -1,48 +1,42 @@ -using System; -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models +public class OrderData { + public string OrderNumber { get; set; } - public class OrderData - { - public string OrderNumber { get; set; } + public DateTime Date { get; set; } - public DateTime Date { get; set; } + public string Status { get; set; } - public string Status { get; set; } + public decimal Total { get; set; } - public decimal Total { get; set; } + public string Description { get; set; } - public string Description { get; set; } + public string City { get; set; } - public string City { get; set; } + public string Street { get; set; } - public string Street { get; set; } + public string State { get; set; } - public string State { get; set; } + public string Country { get; set; } - public string Country { get; set; } + public string ZipCode { get; set; } - public string ZipCode { get; set; } + public string CardNumber { get; set; } - public string CardNumber { get; set; } + public string CardHolderName { get; set; } - public string CardHolderName { get; set; } + public bool IsDraft { get; set; } - public bool IsDraft { get; set; } + public DateTime CardExpiration { get; set; } - public DateTime CardExpiration { get; set; } + public string CardExpirationShort { get; set; } - public string CardExpirationShort { get; set; } + public string CardSecurityNumber { get; set; } - public string CardSecurityNumber { get; set; } + public int CardTypeId { get; set; } - public int CardTypeId { get; set; } - - public string Buyer { get; set; } - - public List OrderItems { get; } = new List(); - } + public string Buyer { get; set; } + public List OrderItems { get; } = new(); } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs index b3832fa21..f763a6923 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs @@ -1,19 +1,16 @@ -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models -{ - - public class OrderItemData - { - public int ProductId { get; set; } +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; - public string ProductName { get; set; } +public class OrderItemData +{ + public int ProductId { get; set; } - public decimal UnitPrice { get; set; } + public string ProductName { get; set; } - public decimal Discount { get; set; } + public decimal UnitPrice { get; set; } - public int Units { get; set; } + public decimal Discount { get; set; } - public string PictureUrl { get; set; } - } + public int Units { get; set; } + public string PictureUrl { get; set; } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs index f8ec70d5f..d1476c55a 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs @@ -1,16 +1,13 @@ -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models -{ +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; - public class UpdateBasketItemData - { - public string BasketItemId { get; set; } +public class UpdateBasketItemData +{ + public string BasketItemId { get; set; } - public int NewQty { get; set; } + public int NewQty { get; set; } - public UpdateBasketItemData() - { - NewQty = 0; - } + public UpdateBasketItemData() + { + NewQty = 0; } - } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs index 066f01a9c..d0686ef51 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs @@ -1,19 +1,14 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models +public class UpdateBasketItemsRequest { - public class UpdateBasketItemsRequest - { - - public string BasketId { get; set; } + public string BasketId { get; set; } - public ICollection Updates { get; set; } + public ICollection Updates { get; set; } - public UpdateBasketItemsRequest() - { - Updates = new List(); - } + public UpdateBasketItemsRequest() + { + Updates = new List(); } - } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs index f8fb7eb03..200f3c375 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs @@ -1,13 +1,8 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models +public class UpdateBasketRequest { + public string BuyerId { get; set; } - public class UpdateBasketRequest - { - public string BuyerId { get; set; } - - public IEnumerable Items { get; set; } - } - -} + public IEnumerable Items { get; set; } +} \ No newline at end of file diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs index af28e0157..48ed593a1 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs @@ -1,13 +1,10 @@ -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models -{ - - public class UpdateBasketRequestItemData - { - public string Id { get; set; } // Basket id +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; - public int ProductId { get; set; } // Catalog item id +public class UpdateBasketRequestItemData +{ + public string Id { get; set; } // Basket id - public int Quantity { get; set; } // Quantity - } + public int ProductId { get; set; } // Catalog item id + public int Quantity { get; set; } // Quantity } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs index 3e80a543f..c5d6e10ff 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs @@ -1,10 +1,4 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator; -using Serilog; - - -BuildWebHost(args).Run(); +await BuildWebHost(args).RunAsync(); IWebHost BuildWebHost(string[] args) => WebHost .CreateDefaultBuilder(args) diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs index d6217175b..1b081bf85 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs @@ -1,90 +1,83 @@ -using GrpcBasket; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public class BasketService : IBasketService { - public class BasketService : IBasketService + private readonly Basket.BasketClient _basketClient; + private readonly ILogger _logger; + + public BasketService(Basket.BasketClient basketClient, ILogger logger) { - private readonly Basket.BasketClient _basketClient; - private readonly ILogger _logger; + _basketClient = basketClient; + _logger = logger; + } - public BasketService(Basket.BasketClient basketClient, ILogger logger) - { - _basketClient = basketClient; - _logger = logger; - } + public async Task GetById(string id) + { + _logger.LogDebug("grpc client created, request = {@id}", id); + var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id }); + _logger.LogDebug("grpc response {@response}", response); - public async Task GetById(string id) - { - _logger.LogDebug("grpc client created, request = {@id}", id); - var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id }); - _logger.LogDebug("grpc response {@response}", response); + return MapToBasketData(response); + } - return MapToBasketData(response); - } + public async Task UpdateAsync(BasketData currentBasket) + { + _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); + var request = MapToCustomerBasketRequest(currentBasket); + _logger.LogDebug("Grpc update basket request {@request}", request); - public async Task UpdateAsync(BasketData currentBasket) - { - _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); - var request = MapToCustomerBasketRequest(currentBasket); - _logger.LogDebug("Grpc update basket request {@request}", request); + await _basketClient.UpdateBasketAsync(request); + } - await _basketClient.UpdateBasketAsync(request); + private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) + { + if (customerBasketRequest == null) + { + return null; } - private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) + var map = new BasketData { - if (customerBasketRequest == null) - { - return null; - } + BuyerId = customerBasketRequest.Buyerid + }; - var map = new BasketData - { - BuyerId = customerBasketRequest.Buyerid - }; + customerBasketRequest.Items.ToList().ForEach(item => map.Items.Add(new BasketDataItem + { + Id = item.Id, + OldUnitPrice = (decimal)item.Oldunitprice, + PictureUrl = item.Pictureurl, + ProductId = item.Productid, + ProductName = item.Productname, + Quantity = item.Quantity, + UnitPrice = (decimal)item.Unitprice + })); - customerBasketRequest.Items.ToList().ForEach(item => map.Items.Add(new BasketDataItem - { - Id = item.Id, - OldUnitPrice = (decimal)item.Oldunitprice, - PictureUrl = item.Pictureurl, - ProductId = item.Productid, - ProductName = item.Productname, - Quantity = item.Quantity, - UnitPrice = (decimal)item.Unitprice - })); + return map; + } - return map; + private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData) + { + if (basketData == null) + { + return null; } - private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData) + var map = new CustomerBasketRequest { - if (basketData == null) - { - return null; - } - - var map = new CustomerBasketRequest - { - Buyerid = basketData.BuyerId - }; + Buyerid = basketData.BuyerId + }; - basketData.Items.ToList().ForEach(item => map.Items.Add(new BasketItemResponse - { - Id = item.Id, - Oldunitprice = (double)item.OldUnitPrice, - Pictureurl = item.PictureUrl, - Productid = item.ProductId, - Productname = item.ProductName, - Quantity = item.Quantity, - Unitprice = (double)item.UnitPrice - })); + basketData.Items.ToList().ForEach(item => map.Items.Add(new BasketItemResponse + { + Id = item.Id, + Oldunitprice = (double)item.OldUnitPrice, + Pictureurl = item.PictureUrl, + Productid = item.ProductId, + Productname = item.ProductName, + Quantity = item.Quantity, + Unitprice = (double)item.UnitPrice + })); - return map; - } + return map; } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs index 0101a7058..9fe1bdd5b 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs @@ -1,43 +1,36 @@ -using CatalogApi; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public class CatalogService : ICatalogService { - public class CatalogService : ICatalogService - { - private readonly Catalog.CatalogClient _client; + private readonly Catalog.CatalogClient _client; - public CatalogService(Catalog.CatalogClient client) - { - _client = client; - } + public CatalogService(Catalog.CatalogClient client) + { + _client = client; + } - public async Task GetCatalogItemAsync(int id) - { - var request = new CatalogItemRequest { Id = id }; - var response = await _client.GetItemByIdAsync(request); - return MapToCatalogItemResponse(response); - } + public async Task GetCatalogItemAsync(int id) + { + var request = new CatalogItemRequest { Id = id }; + var response = await _client.GetItemByIdAsync(request); + return MapToCatalogItemResponse(response); + } - public async Task> GetCatalogItemsAsync(IEnumerable ids) - { - var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 }; - var response = await _client.GetItemsByIdsAsync(request); - return response.Data.Select(MapToCatalogItemResponse); - } + public async Task> GetCatalogItemsAsync(IEnumerable ids) + { + var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 }; + var response = await _client.GetItemsByIdsAsync(request); + return response.Data.Select(MapToCatalogItemResponse); + } - private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse) + private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse) + { + return new CatalogItem { - return new CatalogItem - { - Id = catalogItemResponse.Id, - Name = catalogItemResponse.Name, - PictureUri = catalogItemResponse.PictureUri, - Price = (decimal)catalogItemResponse.Price - }; - } + Id = catalogItemResponse.Id, + Name = catalogItemResponse.Name, + PictureUri = catalogItemResponse.PictureUri, + Price = (decimal)catalogItemResponse.Price + }; } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs index d7a390c75..e1c9a24bf 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs @@ -1,13 +1,9 @@ -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public interface IBasketService { - public interface IBasketService - { - Task GetById(string id); + Task GetByIdAsync(string id); - Task UpdateAsync(BasketData currentBasket); + Task UpdateAsync(BasketData currentBasket); - } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs index 832dfc740..83c95706e 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs @@ -1,13 +1,8 @@ -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using System.Collections.Generic; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public interface ICatalogService { - public interface ICatalogService - { - Task GetCatalogItemAsync(int id); + Task GetCatalogItemAsync(int id); - Task> GetCatalogItemsAsync(IEnumerable ids); - } + Task> GetCatalogItemsAsync(IEnumerable ids); } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs index 1abe545bd..5cda86223 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs @@ -1,10 +1,6 @@ -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public interface IOrderApiClient { - public interface IOrderApiClient - { - Task GetOrderDraftFromBasketAsync(BasketData basket); - } + Task GetOrderDraftFromBasketAsync(BasketData basket); } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderingService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderingService.cs index 5518a4bbf..fd492f698 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderingService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderingService.cs @@ -1,10 +1,6 @@ -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public interface IOrderingService { - public interface IOrderingService - { - Task GetOrderDraftAsync(BasketData basketData); - } -} \ No newline at end of file + Task GetOrderDraftAsync(BasketData basketData); +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs index 8c0b7c90e..6a8465df6 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -1,40 +1,31 @@ -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.Net.Http; -using System.Threading.Tasks; -using System.Text.Json; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public class OrderApiClient : IOrderApiClient { - public class OrderApiClient : IOrderApiClient - { - private readonly HttpClient _apiClient; - private readonly ILogger _logger; - private readonly UrlsConfig _urls; + private readonly HttpClient _apiClient; + private readonly ILogger _logger; + private readonly UrlsConfig _urls; - public OrderApiClient(HttpClient httpClient, ILogger logger, IOptions config) - { - _apiClient = httpClient; - _logger = logger; - _urls = config.Value; - } + public OrderApiClient(HttpClient httpClient, ILogger logger, IOptions config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } - public async Task GetOrderDraftFromBasketAsync(BasketData basket) - { - var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); - var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); - var response = await _apiClient.PostAsync(uri, content); + public async Task GetOrderDraftFromBasketAsync(BasketData basket) + { + var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); + var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); + var response = await _apiClient.PostAsync(uri, content); - response.EnsureSuccessStatusCode(); + response.EnsureSuccessStatusCode(); - var ordersDraftResponse = await response.Content.ReadAsStringAsync(); + var ordersDraftResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(ordersDraftResponse, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - } + return JsonSerializer.Deserialize(ordersDraftResponse, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs index 7f9d5deb7..2a7de50a7 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs @@ -1,79 +1,72 @@ -using GrpcOrdering; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services +public class OrderingService : IOrderingService { - public class OrderingService : IOrderingService + private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient; + private readonly ILogger _logger; + + public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger logger) { - private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient; - private readonly ILogger _logger; + _orderingGrpcClient = orderingGrpcClient; + _logger = logger; + } - public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger logger) - { - _orderingGrpcClient = orderingGrpcClient; - _logger = logger; - } + public async Task GetOrderDraftAsync(BasketData basketData) + { + _logger.LogDebug(" grpc client created, basketData={@basketData}", basketData); - public async Task GetOrderDraftAsync(BasketData basketData) - { - _logger.LogDebug(" grpc client created, basketData={@basketData}", basketData); + var command = MapToOrderDraftCommand(basketData); + var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command); + _logger.LogDebug(" grpc response: {@response}", response); - var command = MapToOrderDraftCommand(basketData); - var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command); - _logger.LogDebug(" grpc response: {@response}", response); + return MapToResponse(response, basketData); + } - return MapToResponse(response, basketData); + private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData) + { + if (orderDraft == null) + { + return null; } - private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData) + var data = new OrderData { - if (orderDraft == null) - { - return null; - } + Buyer = basketData.BuyerId, + Total = (decimal)orderDraft.Total, + }; - var data = new OrderData - { - Buyer = basketData.BuyerId, - Total = (decimal)orderDraft.Total, - }; - - orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData - { - Discount = (decimal)o.Discount, - PictureUrl = o.PictureUrl, - ProductId = o.ProductId, - ProductName = o.ProductName, - UnitPrice = (decimal)o.UnitPrice, - Units = o.Units, - })); + orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData + { + Discount = (decimal)o.Discount, + PictureUrl = o.PictureUrl, + ProductId = o.ProductId, + ProductName = o.ProductName, + UnitPrice = (decimal)o.UnitPrice, + Units = o.Units, + })); - return data; - } + return data; + } - private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData) + private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData) + { + var command = new CreateOrderDraftCommand { - var command = new CreateOrderDraftCommand - { - BuyerId = basketData.BuyerId, - }; + BuyerId = basketData.BuyerId, + }; - basketData.Items.ForEach(i => command.Items.Add(new BasketItem - { - Id = i.Id, - OldUnitPrice = (double)i.OldUnitPrice, - PictureUrl = i.PictureUrl, - ProductId = i.ProductId, - ProductName = i.ProductName, - Quantity = i.Quantity, - UnitPrice = (double)i.UnitPrice, - })); - - return command; - } + basketData.Items.ForEach(i => command.Items.Add(new BasketItem + { + Id = i.Id, + OldUnitPrice = (double)i.OldUnitPrice, + PictureUrl = i.PictureUrl, + ProductId = i.ProductId, + ProductName = i.ProductName, + Quantity = i.Quantity, + UnitPrice = (double)i.UnitPrice, + })); + return command; } + } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs index b8853aee7..3f988395a 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs @@ -1,222 +1,196 @@ -using CatalogApi; -using Devspaces.Support; -using GrpcBasket; -using GrpcOrdering; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; - -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy()) + .AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) + .AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) + .AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) + .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) + .AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }); + + services.AddCustomMvc(Configuration) + .AddCustomAuthentication(Configuration) + .AddDevspaces() + .AddHttpServices() + .AddGrpcServices(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + { + var pathBase = Configuration["PATH_BASE"]; + + if (!string.IsNullOrEmpty(pathBase)) { - services.AddHealthChecks() - .AddCheck("self", () => HealthCheckResult.Healthy()) - .AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) - .AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) - .AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) - .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) - .AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }); - - services.AddCustomMvc(Configuration) - .AddCustomAuthentication(Configuration) - .AddDevspaces() - .AddHttpServices() - .AddGrpcServices(); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); + app.UsePathBase(pathBase); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + if (env.IsDevelopment()) { - var pathBase = Configuration["PATH_BASE"]; - - if (!string.IsNullOrEmpty(pathBase)) - { - loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); - app.UsePathBase(pathBase); - } - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + app.UseDeveloperExceptionPage(); + } - app.UseSwagger().UseSwaggerUI(c => + app.UseSwagger().UseSwaggerUI(c => + { + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); + + c.OAuthClientId("mobileshoppingaggswaggerui"); + c.OAuthClientSecret(string.Empty); + c.OAuthRealm(string.Empty); + c.OAuthAppName("Purchase BFF Swagger UI"); + }); + + app.UseRouting(); + app.UseCors("CorsPolicy"); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() { - c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); - - c.OAuthClientId("mobileshoppingaggswaggerui"); - c.OAuthClientSecret(string.Empty); - c.OAuthRealm(string.Empty); - c.OAuthAppName("Purchase BFF Swagger UI"); + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); - - app.UseRouting(); - app.UseCors("CorsPolicy"); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions { - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); + Predicate = r => r.Name.Contains("self") }); - } + }); } +} - public static class ServiceCollectionExtensions +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) - { - services.AddOptions(); - services.Configure(configuration.GetSection("urls")); + services.AddOptions(); + services.Configure(configuration.GetSection("urls")); - services.AddControllers() - .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); + services.AddControllers() + .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); - services.AddSwaggerGen(options => + services.AddSwaggerGen(options => + { + options.DescribeAllEnumsAsStrings(); + options.SwaggerDoc("v1", new OpenApiInfo { - options.DescribeAllEnumsAsStrings(); - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "Shopping Aggregator for Mobile Clients", - Version = "v1", - Description = "Shopping Aggregator for Mobile Clients" - }); - options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + Title = "Shopping Aggregator for Mobile Clients", + Version = "v1", + Description = "Shopping Aggregator for Mobile Clients" + }); + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OAuth2, + Flows = new OpenApiOAuthFlows() { - Type = SecuritySchemeType.OAuth2, - Flows = new OpenApiOAuthFlows() + Implicit = new OpenApiOAuthFlow() { - Implicit = new OpenApiOAuthFlow() - { - AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), - TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), + AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), + TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), - Scopes = new Dictionary() - { - { "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } - } + Scopes = new Dictionary() + { + { "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" } } } - }); - - options.OperationFilter(); + } }); - services.AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder - .AllowAnyMethod() - .AllowAnyHeader() - .SetIsOriginAllowed((host) => true) - .AllowCredentials()); - }); + options.OperationFilter(); + }); - return services; - } - public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + services.AddCors(options => { - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); - - var identityUrl = configuration.GetValue("urls:identity"); - - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.AddPolicy("CorsPolicy", + builder => builder + .AllowAnyMethod() + .AllowAnyHeader() + .SetIsOriginAllowed((host) => true) + .AllowCredentials()); + }); + + return services; + } + public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + { + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); - }) - .AddJwtBearer(options => - { - options.Authority = identityUrl; - options.RequireHttpsMetadata = false; - options.Audience = "mobileshoppingagg"; - }); + var identityUrl = configuration.GetValue("urls:identity"); - return services; - } + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - public static IServiceCollection AddHttpServices(this IServiceCollection services) + }) + .AddJwtBearer(options => { - //register delegating handlers - services.AddTransient(); - services.AddSingleton(); + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "mobileshoppingagg"; + }); - //register http services + return services; + } - services.AddHttpClient() - .AddDevspacesSupport(); + public static IServiceCollection AddHttpServices(this IServiceCollection services) + { + //register delegating handlers + services.AddTransient(); + services.AddSingleton(); - return services; - } + //register http services - public static IServiceCollection AddGrpcServices(this IServiceCollection services) - { - services.AddTransient(); + services.AddHttpClient() + .AddDevspacesSupport(); - services.AddScoped(); + return services; + } - services.AddGrpcClient((services, options) => - { - var basketApi = services.GetRequiredService>().Value.GrpcBasket; - options.Address = new Uri(basketApi); - }).AddInterceptor(); + public static IServiceCollection AddGrpcServices(this IServiceCollection services) + { + services.AddTransient(); - services.AddScoped(); + services.AddScoped(); - services.AddGrpcClient((services, options) => - { - var catalogApi = services.GetRequiredService>().Value.GrpcCatalog; - options.Address = new Uri(catalogApi); - }).AddInterceptor(); + services.AddGrpcClient((services, options) => + { + var basketApi = services.GetRequiredService>().Value.GrpcBasket; + options.Address = new Uri(basketApi); + }).AddInterceptor(); - services.AddScoped(); + services.AddScoped(); - services.AddGrpcClient((services, options) => - { - var orderingApi = services.GetRequiredService>().Value.GrpcOrdering; - options.Address = new Uri(orderingApi); - }).AddInterceptor(); + services.AddGrpcClient((services, options) => + { + var catalogApi = services.GetRequiredService>().Value.GrpcCatalog; + options.Address = new Uri(catalogApi); + }).AddInterceptor(); - return services; - } + services.AddScoped(); + services.AddGrpcClient((services, options) => + { + var orderingApi = services.GetRequiredService>().Value.GrpcOrdering; + options.Address = new Uri(orderingApi); + }).AddInterceptor(); + + return services; } + } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs index b19874aac..9b3391692 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Config/UrlsConfig.cs @@ -1,45 +1,41 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config +public class UrlsConfig { - public class UrlsConfig + public class CatalogOperations { + // grpc call under REST must go trough port 80 + public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; - public class CatalogOperations - { - // grpc call under REST must go trough port 80 - public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; + public static string GetItemById(string ids) => $"/api/v1/catalog/items/ids/{string.Join(',', ids)}"; - public static string GetItemById(string ids) => $"/api/v1/catalog/items/ids/{string.Join(',', ids)}"; - - // REST call standard must go through port 5000 - public static string GetItemsById(IEnumerable ids) => $":5000/api/v1/catalog/items?ids={string.Join(',', ids)}"; - } - - public class BasketOperations - { - public static string GetItemById(string id) => $"/api/v1/basket/{id}"; + // REST call standard must go through port 5000 + public static string GetItemsById(IEnumerable ids) => $":5000/api/v1/catalog/items?ids={string.Join(',', ids)}"; + } - public static string UpdateBasket() => "/api/v1/basket"; - } + public class BasketOperations + { + public static string GetItemById(string id) => $"/api/v1/basket/{id}"; - public class OrdersOperations - { - public static string GetOrderDraft() => "/api/v1/orders/draft"; - } + public static string UpdateBasket() => "/api/v1/basket"; + } - public string Basket { get; set; } + public class OrdersOperations + { + public static string GetOrderDraft() => "/api/v1/orders/draft"; + } - public string Catalog { get; set; } + public string Basket { get; set; } - public string Orders { get; set; } + public string Catalog { get; set; } - public string GrpcBasket { get; set; } + public string Orders { get; set; } - public string GrpcCatalog { get; set; } + public string GrpcBasket { get; set; } - public string GrpcOrdering { get; set; } - } + public string GrpcCatalog { get; set; } + public string GrpcOrdering { get; set; } } + diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs index 9471c6e09..008ded21b 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/BasketController.cs @@ -1,164 +1,154 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -using System; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers; + +[Route("api/v1/[controller]")] +[Authorize] +[ApiController] +public class BasketController : ControllerBase { - [Route("api/v1/[controller]")] - [Authorize] - [ApiController] - public class BasketController : ControllerBase + private readonly ICatalogService _catalog; + private readonly IBasketService _basket; + + public BasketController(ICatalogService catalogService, IBasketService basketService) { - private readonly ICatalogService _catalog; - private readonly IBasketService _basket; + _catalog = catalogService; + _basket = basketService; + } - public BasketController(ICatalogService catalogService, IBasketService basketService) + [HttpPost] + [HttpPut] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] + public async Task> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data) + { + if (data.Items == null || !data.Items.Any()) { - _catalog = catalogService; - _basket = basketService; + return BadRequest("Need to pass at least one basket line"); } - [HttpPost] - [HttpPut] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] - public async Task> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data) + // Retrieve the current basket + var basket = await _basket.GetByIdAsync(data.BuyerId) ?? new BasketData(data.BuyerId); + var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId)); + + // group by product id to avoid duplicates + var itemsCalculated = data + .Items + .GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i }) + .Select(groupedItem => + { + var item = groupedItem.items.First(); + item.Quantity = groupedItem.items.Sum(i => i.Quantity); + return item; + }); + + foreach (var bitem in itemsCalculated) { - if (data.Items == null || !data.Items.Any()) + var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); + if (catalogItem == null) { - return BadRequest("Need to pass at least one basket line"); + return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); } - // Retrieve the current basket - var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId); - var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId)); - - // group by product id to avoid duplicates - var itemsCalculated = data - .Items - .GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i }) - .Select(groupedItem => - { - var item = groupedItem.items.First(); - item.Quantity = groupedItem.items.Sum(i => i.Quantity); - return item; - }); - - foreach (var bitem in itemsCalculated) + var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId); + if (itemInBasket == null) { - var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); - if (catalogItem == null) - { - return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); - } - - var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId); - if (itemInBasket == null) - { - basket.Items.Add(new BasketDataItem() - { - Id = bitem.Id, - ProductId = catalogItem.Id, - ProductName = catalogItem.Name, - PictureUrl = catalogItem.PictureUri, - UnitPrice = catalogItem.Price, - Quantity = bitem.Quantity - }); - } - else + basket.Items.Add(new BasketDataItem() { - itemInBasket.Quantity = bitem.Quantity; - } + Id = bitem.Id, + ProductId = catalogItem.Id, + ProductName = catalogItem.Name, + PictureUrl = catalogItem.PictureUri, + UnitPrice = catalogItem.Price, + Quantity = bitem.Quantity + }); } + else + { + itemInBasket.Quantity = bitem.Quantity; + } + } - await _basket.UpdateAsync(basket); + await _basket.UpdateAsync(basket); - return basket; - } + return basket; + } - [HttpPut] - [Route("items")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] - public async Task> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data) + [HttpPut] + [Route("items")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)] + public async Task> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data) + { + if (!data.Updates.Any()) { - if (!data.Updates.Any()) - { - return BadRequest("No updates sent"); - } + return BadRequest("No updates sent"); + } - // Retrieve the current basket - var currentBasket = await _basket.GetById(data.BasketId); - if (currentBasket == null) - { - return BadRequest($"Basket with id {data.BasketId} not found."); - } + // Retrieve the current basket + var currentBasket = await _basket.GetByIdAsync(data.BasketId); + if (currentBasket == null) + { + return BadRequest($"Basket with id {data.BasketId} not found."); + } - // Update with new quantities - foreach (var update in data.Updates) + // Update with new quantities + foreach (var update in data.Updates) + { + var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId); + if (basketItem == null) { - var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId); - if (basketItem == null) - { - return BadRequest($"Basket item with id {update.BasketItemId} not found"); - } - basketItem.Quantity = update.NewQty; + return BadRequest($"Basket item with id {update.BasketItemId} not found"); } + basketItem.Quantity = update.NewQty; + } - // Save the updated basket - await _basket.UpdateAsync(currentBasket); + // Save the updated basket + await _basket.UpdateAsync(currentBasket); - return currentBasket; - } + return currentBasket; + } - [HttpPost] - [Route("items")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.OK)] - public async Task AddBasketItemAsync([FromBody] AddBasketItemRequest data) + [HttpPost] + [Route("items")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.OK)] + public async Task AddBasketItemAsync([FromBody] AddBasketItemRequest data) + { + if (data == null || data.Quantity == 0) { - if (data == null || data.Quantity == 0) - { - return BadRequest("Invalid payload"); - } + return BadRequest("Invalid payload"); + } - // Step 1: Get the item from catalog - var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId); + // Step 1: Get the item from catalog + var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId); - //item.PictureUri = + //item.PictureUri = - // Step 2: Get current basket status - var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId); - // Step 3: Search if exist product into basket - var product = currentBasket.Items.SingleOrDefault(i => i.ProductId == item.Id); - if (product != null) - { - // Step 4: Update quantity for product - product.Quantity += data.Quantity; - } - else + // Step 2: Get current basket status + var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId); + // Step 3: Search if exist product into basket + var product = currentBasket.Items.SingleOrDefault(i => i.ProductId == item.Id); + if (product != null) + { + // Step 4: Update quantity for product + product.Quantity += data.Quantity; + } + else + { + // Step 4: Merge current status with new product + currentBasket.Items.Add(new BasketDataItem() { - // Step 4: Merge current status with new product - currentBasket.Items.Add(new BasketDataItem() - { - UnitPrice = item.Price, - PictureUrl = item.PictureUri, - ProductId = item.Id, - ProductName = item.Name, - Quantity = data.Quantity, - Id = Guid.NewGuid().ToString() - }); - } + UnitPrice = item.Price, + PictureUrl = item.PictureUri, + ProductId = item.Id, + ProductName = item.Name, + Quantity = data.Quantity, + Id = Guid.NewGuid().ToString() + }); + } - // Step 5: Update basket - await _basket.UpdateAsync(currentBasket); + // Step 5: Update basket + await _basket.UpdateAsync(currentBasket); - return Ok(); - } + return Ok(); } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs index e742e2bd6..55df5880b 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/HomeController.cs @@ -1,14 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers +[Route("")] +public class HomeController : Controller { - [Route("")] - public class HomeController : Controller + [HttpGet] + public IActionResult Index() { - [HttpGet()] - public IActionResult Index() - { - return new RedirectResult("~/swagger"); - } + return new RedirectResult("~/swagger"); } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs index e528b478f..5897a5c86 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Controllers/OrderController.cs @@ -1,44 +1,37 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -using System.Net; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers +[Route("api/v1/[controller]")] +[Authorize] +[ApiController] +public class OrderController : ControllerBase { - [Route("api/v1/[controller]")] - [Authorize] - [ApiController] - public class OrderController : ControllerBase + private readonly IBasketService _basketService; + private readonly IOrderingService _orderingService; + + public OrderController(IBasketService basketService, IOrderingService orderingService) + { + _basketService = basketService; + _orderingService = orderingService; + } + + [Route("draft/{basketId}")] + [HttpGet] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)] + public async Task> GetOrderDraftAsync(string basketId) { - private readonly IBasketService _basketService; - private readonly IOrderingService _orderingService; - public OrderController(IBasketService basketService, IOrderingService orderingService) + if (string.IsNullOrWhitespace(basketId)) { - _basketService = basketService; - _orderingService = orderingService; + return BadRequest("Need a valid basketid"); } + // Get the basket data and build a order draft based on it + var basket = await _basketService.GetByIdAsync(basketId); - [Route("draft/{basketId}")] - [HttpGet] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)] - public async Task> GetOrderDraftAsync(string basketId) + if (basket == null) { - if (string.IsNullOrEmpty(basketId)) - { - return BadRequest("Need a valid basketid"); - } - // Get the basket data and build a order draft based on it - var basket = await _basketService.GetById(basketId); - - if (basket == null) - { - return BadRequest($"No basket found for id {basketId}"); - } - - return await _orderingService.GetOrderDraftAsync(basket); + return BadRequest($"No basket found for id {basketId}"); } + + return await _orderingService.GetOrderDraftAsync(basket); } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs index c273fd19e..a0aed7b5d 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs @@ -1,10 +1,4 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters { namespace Basket.API.Infrastructure.Filters { @@ -14,7 +8,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters { // Check for authorize attribute var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || - context.MethodInfo.GetCustomAttributes(true).OfType().Any(); + context.MethodInfo.GetCustomAttributes(true).OfType().Any(); if (!hasAuthorize) return; @@ -27,13 +21,14 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters }; operation.Security = new List + { + new OpenApiSecurityRequirement { - new OpenApiSecurityRequirement - { - [ oAuthScheme ] = new [] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" } - } - }; + [ oAuthScheme ] = new[] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" } + } + }; } } } -} + +} \ No newline at end of file diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/GlobalUsings.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/GlobalUsings.cs new file mode 100644 index 000000000..6162c557e --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/GlobalUsings.cs @@ -0,0 +1,41 @@ +global using CatalogApi; +global using Devspaces.Support; +global using Grpc.Core.Interceptors; +global using Grpc.Core; +global using GrpcBasket; +global using GrpcOrdering; +global using HealthChecks.UI.Client; +global using Microsoft.AspNetCore.Authentication.JwtBearer; +global using Microsoft.AspNetCore.Authentication; +global using Microsoft.AspNetCore.Authorization; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore; +global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; +global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; +global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure; +global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; +global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; +global using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Microsoft.OpenApi.Models; +global using Serilog; +global using Swashbuckle.AspNetCore.SwaggerGen; +global using System.Collections.Generic; +global using System.IdentityModel.Tokens.Jwt; +global using System.Linq; +global using System.Net.Http.Headers; +global using System.Net.Http; +global using System.Net; +global using System.Text.Json; +global using System.Threading.Tasks; +global using System.Threading; +global using System; diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs index dc6b7e6f8..20adb2fc7 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs @@ -1,41 +1,35 @@ -using Grpc.Core; -using Grpc.Core.Interceptors; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure +public class GrpcExceptionInterceptor : Interceptor { - public class GrpcExceptionInterceptor : Interceptor + private readonly ILogger _logger; + + public GrpcExceptionInterceptor(ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + } - public GrpcExceptionInterceptor(ILogger logger) - { - _logger = logger; - } + public override AsyncUnaryCall AsyncUnaryCall( + TRequest request, + ClientInterceptorContext context, + AsyncUnaryCallContinuation continuation) + { + var call = continuation(request, context); - public override AsyncUnaryCall AsyncUnaryCall( - TRequest request, - ClientInterceptorContext context, - AsyncUnaryCallContinuation continuation) - { - var call = continuation(request, context); + return new AsyncUnaryCall(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose); + } - return new AsyncUnaryCall(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose); + private async Task HandleResponse(Task task) + { + try + { + var response = await task; + return response; } - - private async Task HandleResponse(Task t) + catch (RpcException e) { - try - { - var response = await t; - return response; - } - catch (RpcException e) - { - _logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message); - return default; - } + _logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message); + return default; } } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs index 7ac9cfc92..a024560c6 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs @@ -1,49 +1,40 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure; + +public class HttpClientAuthorizationDelegatingHandler + : DelegatingHandler { - public class HttpClientAuthorizationDelegatingHandler - : DelegatingHandler + private readonly IHttpContextAccessor _httpContextAccessor; + + public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor) { - private readonly IHttpContextAccessor _httpContextAccessor; + _httpContextAccessor = httpContextAccessor; + } - public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var authorizationHeader = _httpContextAccessor.HttpContext + .Request.Headers["Authorization"]; - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + if (!string.IsNullOrWhitespace(authorizationHeader)) { - var authorizationHeader = _httpContextAccessor.HttpContext - .Request.Headers["Authorization"]; - - if (!string.IsNullOrEmpty(authorizationHeader)) - { - request.Headers.Add("Authorization", new List() { authorizationHeader }); - } - - var token = await GetToken(); + request.Headers.Add("Authorization", new List() { authorizationHeader }); + } - if (token != null) - { - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); - } + var token = await GetTokenAsync(); - return await base.SendAsync(request, cancellationToken); + if (token != null) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); } - async Task GetToken() - { - const string ACCESS_TOKEN = "access_token"; + return await base.SendAsync(request, cancellationToken); + } - return await _httpContextAccessor.HttpContext - .GetTokenAsync(ACCESS_TOKEN); - } + Task GetTokenAsync() + { + const string ACCESS_TOKEN = "access_token"; + + return _httpContextAccessor.HttpContext + .GetTokenAsync(ACCESS_TOKEN); } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs index e9a6492c0..f5905a499 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs @@ -1,18 +1,16 @@ -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models -{ +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; - public class AddBasketItemRequest - { - public int CatalogItemId { get; set; } +public class AddBasketItemRequest +{ + public int CatalogItemId { get; set; } - public string BasketId { get; set; } + public string BasketId { get; set; } - public int Quantity { get; set; } + public int Quantity { get; set; } - public AddBasketItemRequest() - { - Quantity = 1; - } + public AddBasketItemRequest() + { + Quantity = 1; } - } + diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs index 18e855837..942eb6b74 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketData.cs @@ -1,22 +1,18 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +public class BasketData { + public string BuyerId { get; set; } - public class BasketData - { - public string BuyerId { get; set; } - - public List Items { get; set; } = new List(); + public List Items { get; set; } = new(); - public BasketData() - { - } - - public BasketData(string buyerId) - { - BuyerId = buyerId; - } + public BasketData() + { } + public BasketData(string buyerId) + { + BuyerId = buyerId; + } } + diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs index 05fed054e..6c11ab51f 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/BasketDataItem.cs @@ -1,21 +1,18 @@ -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models -{ - - public class BasketDataItem - { - public string Id { get; set; } +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; - public int ProductId { get; set; } +public class BasketDataItem +{ + public string Id { get; set; } - public string ProductName { get; set; } + public int ProductId { get; set; } - public decimal UnitPrice { get; set; } + public string ProductName { get; set; } - public decimal OldUnitPrice { get; set; } + public decimal UnitPrice { get; set; } - public int Quantity { get; set; } + public decimal OldUnitPrice { get; set; } - public string PictureUrl { get; set; } - } + public int Quantity { get; set; } + public string PictureUrl { get; set; } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs index 68c856970..0d598d95f 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/CatalogItem.cs @@ -1,15 +1,14 @@ -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; + +public class CatalogItem { + public int Id { get; set; } - public class CatalogItem - { - public int Id { get; set; } + public string Name { get; set; } - public string Name { get; set; } + public decimal Price { get; set; } - public decimal Price { get; set; } + public string PictureUri { get; set; } +} - public string PictureUri { get; set; } - } -} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs index c4536d697..519139e77 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderData.cs @@ -1,48 +1,43 @@ -using System; -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +public class OrderData { + public string OrderNumber { get; set; } - public class OrderData - { - public string OrderNumber { get; set; } + public DateTime Date { get; set; } - public DateTime Date { get; set; } + public string Status { get; set; } - public string Status { get; set; } + public decimal Total { get; set; } - public decimal Total { get; set; } + public string Description { get; set; } - public string Description { get; set; } + public string City { get; set; } - public string City { get; set; } + public string Street { get; set; } - public string Street { get; set; } + public string State { get; set; } - public string State { get; set; } + public string Country { get; set; } - public string Country { get; set; } + public string ZipCode { get; set; } - public string ZipCode { get; set; } + public string CardNumber { get; set; } - public string CardNumber { get; set; } + public string CardHolderName { get; set; } - public string CardHolderName { get; set; } + public bool IsDraft { get; set; } - public bool IsDraft { get; set; } + public DateTime CardExpiration { get; set; } - public DateTime CardExpiration { get; set; } + public string CardExpirationShort { get; set; } - public string CardExpirationShort { get; set; } + public string CardSecurityNumber { get; set; } - public string CardSecurityNumber { get; set; } + public int CardTypeId { get; set; } - public int CardTypeId { get; set; } - - public string Buyer { get; set; } - - public List OrderItems { get; } = new List(); - } + public string Buyer { get; set; } + public List OrderItems { get; } = new(); } + diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs index 0c2082fa2..cc533966f 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/OrderItemData.cs @@ -1,19 +1,16 @@ -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models -{ - - public class OrderItemData - { - public int ProductId { get; set; } +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; - public string ProductName { get; set; } +public class OrderItemData +{ + public int ProductId { get; set; } - public decimal UnitPrice { get; set; } + public string ProductName { get; set; } - public decimal Discount { get; set; } + public decimal UnitPrice { get; set; } - public int Units { get; set; } + public decimal Discount { get; set; } - public string PictureUrl { get; set; } - } + public int Units { get; set; } + public string PictureUrl { get; set; } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs index b75784aa2..f7fa06798 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs @@ -1,16 +1,9 @@ -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models -{ - - public class UpdateBasketItemData - { - public string BasketItemId { get; set; } +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; - public int NewQty { get; set; } +public class UpdateBasketItemData +{ + public string BasketItemId { get; set; } - public UpdateBasketItemData() - { - NewQty = 0; - } - } + public int NewQty { get; set; } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs index 3e356dfe1..26bbb58e4 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs @@ -1,18 +1,13 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +public class UpdateBasketItemsRequest { + public string BasketId { get; set; } - public class UpdateBasketItemsRequest - { - public string BasketId { get; set; } - - public ICollection Updates { get; set; } + public ICollection Updates { get; set; } - public UpdateBasketItemsRequest() - { - Updates = new List(); - } + public UpdateBasketItemsRequest() + { + Updates = new List(); } - } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs index 070016f70..fa274a746 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs @@ -1,13 +1,8 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +public class UpdateBasketRequest { + public string BuyerId { get; set; } - public class UpdateBasketRequest - { - public string BuyerId { get; set; } - - public IEnumerable Items { get; set; } - } - + public IEnumerable Items { get; set; } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs index 90800374d..6f48480b4 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs @@ -1,11 +1,10 @@ -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; + +public class UpdateBasketRequestItemData { - public class UpdateBasketRequestItemData - { - public string Id { get; set; } // Basket id + public string Id { get; set; } // Basket id - public int ProductId { get; set; } // Catalog item id + public int ProductId { get; set; } // Catalog item id - public int Quantity { get; set; } // Quantity - } + public int Quantity { get; set; } // Quantity } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs index 083292f54..70506fbda 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs @@ -1,9 +1,4 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator; -using Serilog; - -BuildWebHost(args).Run(); +await BuildWebHost(args).RunAsync(); IWebHost BuildWebHost(string[] args) => WebHost diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs index 648a386c8..64feecfa2 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs @@ -1,103 +1,96 @@ -using GrpcBasket; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public class BasketService : IBasketService { - public class BasketService : IBasketService + private readonly Basket.BasketClient _basketClient; + private readonly ILogger _logger; + + public BasketService(Basket.BasketClient basketClient, ILogger logger) { - private readonly Basket.BasketClient _basketClient; - private readonly ILogger _logger; + _basketClient = basketClient; + _logger = logger; + } - public BasketService(Basket.BasketClient basketClient, ILogger logger) - { - _basketClient = basketClient; - _logger = logger; - } + public async Task GetById(string id) + { + _logger.LogDebug("grpc client created, request = {@id}", id); + var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id }); + _logger.LogDebug("grpc response {@response}", response); - public async Task GetById(string id) - { - _logger.LogDebug("grpc client created, request = {@id}", id); - var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id }); - _logger.LogDebug("grpc response {@response}", response); + return MapToBasketData(response); + } - return MapToBasketData(response); - } + public async Task UpdateAsync(BasketData currentBasket) + { + _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); + var request = MapToCustomerBasketRequest(currentBasket); + _logger.LogDebug("Grpc update basket request {@request}", request); - public async Task UpdateAsync(BasketData currentBasket) - { - _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); - var request = MapToCustomerBasketRequest(currentBasket); - _logger.LogDebug("Grpc update basket request {@request}", request); + await _basketClient.UpdateBasketAsync(request); + } - await _basketClient.UpdateBasketAsync(request); + private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) + { + if (customerBasketRequest == null) + { + return null; } - private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) + var map = new BasketData { - if (customerBasketRequest == null) - { - return null; - } + BuyerId = customerBasketRequest.Buyerid + }; - var map = new BasketData - { - BuyerId = customerBasketRequest.Buyerid - }; - - customerBasketRequest.Items.ToList().ForEach(item => + customerBasketRequest.Items.ToList().ForEach(item => + { + if (item.Id != null) { - if (item.Id != null) + map.Items.Add(new BasketDataItem { - map.Items.Add(new BasketDataItem - { - Id = item.Id, - OldUnitPrice = (decimal)item.Oldunitprice, - PictureUrl = item.Pictureurl, - ProductId = item.Productid, - ProductName = item.Productname, - Quantity = item.Quantity, - UnitPrice = (decimal)item.Unitprice - }); - } - }); + Id = item.Id, + OldUnitPrice = (decimal)item.Oldunitprice, + PictureUrl = item.Pictureurl, + ProductId = item.Productid, + ProductName = item.Productname, + Quantity = item.Quantity, + UnitPrice = (decimal)item.Unitprice + }); + } + }); - return map; - } + return map; + } - private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData) + private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData) + { + if (basketData == null) { - if (basketData == null) - { - return null; - } + return null; + } - var map = new CustomerBasketRequest - { - Buyerid = basketData.BuyerId - }; + var map = new CustomerBasketRequest + { + Buyerid = basketData.BuyerId + }; - basketData.Items.ToList().ForEach(item => + basketData.Items.ToList().ForEach(item => + { + if (item.Id != null) { - if (item.Id != null) + map.Items.Add(new BasketItemResponse { - map.Items.Add(new BasketItemResponse - { - Id = item.Id, - Oldunitprice = (double)item.OldUnitPrice, - Pictureurl = item.PictureUrl, - Productid = item.ProductId, - Productname = item.ProductName, - Quantity = item.Quantity, - Unitprice = (double)item.UnitPrice - }); - } - }); + Id = item.Id, + Oldunitprice = (double)item.OldUnitPrice, + Pictureurl = item.PictureUrl, + Productid = item.ProductId, + Productname = item.ProductName, + Quantity = item.Quantity, + Unitprice = (double)item.UnitPrice + }); + } + }); - return map; - } + return map; } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs index f925954a6..3f7231aa3 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/CatalogService.cs @@ -1,52 +1,44 @@ -using CatalogApi; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public class CatalogService : ICatalogService { - public class CatalogService : ICatalogService - { - private readonly Catalog.CatalogClient _client; - private readonly ILogger _logger; + private readonly Catalog.CatalogClient _client; + private readonly ILogger _logger; - public CatalogService(Catalog.CatalogClient client, ILogger logger) - { - _client = client; - _logger = logger; - } + public CatalogService(Catalog.CatalogClient client, ILogger logger) + { + _client = client; + _logger = logger; + } - public async Task GetCatalogItemAsync(int id) - { - var request = new CatalogItemRequest { Id = id }; - _logger.LogInformation("grpc request {@request}", request); - var response = await _client.GetItemByIdAsync(request); - _logger.LogInformation("grpc response {@response}", response); - return MapToCatalogItemResponse(response); + public async Task GetCatalogItemAsync(int id) + { + var request = new CatalogItemRequest { Id = id }; + _logger.LogInformation("grpc request {@request}", request); + var response = await _client.GetItemByIdAsync(request); + _logger.LogInformation("grpc response {@response}", response); + return MapToCatalogItemResponse(response); - } + } - public async Task> GetCatalogItemsAsync(IEnumerable ids) - { - var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 }; - _logger.LogInformation("grpc request {@request}", request); - var response = await _client.GetItemsByIdsAsync(request); - _logger.LogInformation("grpc response {@response}", response); - return response.Data.Select(this.MapToCatalogItemResponse); + public async Task> GetCatalogItemsAsync(IEnumerable ids) + { + var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 }; + _logger.LogInformation("grpc request {@request}", request); + var response = await _client.GetItemsByIdsAsync(request); + _logger.LogInformation("grpc response {@response}", response); + return response.Data.Select(this.MapToCatalogItemResponse); - } + } - private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse) + private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse) + { + return new CatalogItem { - return new CatalogItem - { - Id = catalogItemResponse.Id, - Name = catalogItemResponse.Name, - PictureUri = catalogItemResponse.PictureUri, - Price = (decimal)catalogItemResponse.Price - }; - } + Id = catalogItemResponse.Id, + Name = catalogItemResponse.Name, + PictureUri = catalogItemResponse.PictureUri, + Price = (decimal)catalogItemResponse.Price + }; } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs index fba539b7a..1049642d2 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IBasketService.cs @@ -1,12 +1,8 @@ -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public interface IBasketService { - public interface IBasketService - { - Task GetById(string id); + Task GetByIdAsync(string id); - Task UpdateAsync(BasketData currentBasket); - } + Task UpdateAsync(BasketData currentBasket); } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs index 9f86b84f9..7673a2706 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/ICatalogService.cs @@ -1,13 +1,8 @@ -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using System.Collections.Generic; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public interface ICatalogService { - public interface ICatalogService - { - Task GetCatalogItemAsync(int id); + Task GetCatalogItemAsync(int id); - Task> GetCatalogItemsAsync(IEnumerable ids); - } + Task> GetCatalogItemsAsync(IEnumerable ids); } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs index 0e972833c..989fc5fc0 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderApiClient.cs @@ -1,10 +1,6 @@ -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public interface IOrderApiClient { - public interface IOrderApiClient - { - Task GetOrderDraftFromBasketAsync(BasketData basket); - } + Task GetOrderDraftFromBasketAsync(BasketData basket); } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderingService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderingService.cs index a20520747..6d21a7a87 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderingService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/IOrderingService.cs @@ -1,10 +1,6 @@ -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public interface IOrderingService { - public interface IOrderingService - { - Task GetOrderDraftAsync(BasketData basketData); - } -} \ No newline at end of file + Task GetOrderDraftAsync(BasketData basketData); +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs index fb7fa70e1..53fb65bb8 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -1,40 +1,31 @@ -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.Net.Http; -using System.Threading.Tasks; -using System.Text.Json; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public class OrderApiClient : IOrderApiClient { - public class OrderApiClient : IOrderApiClient - { - private readonly HttpClient _apiClient; - private readonly ILogger _logger; - private readonly UrlsConfig _urls; + private readonly HttpClient _apiClient; + private readonly ILogger _logger; + private readonly UrlsConfig _urls; - public OrderApiClient(HttpClient httpClient, ILogger logger, IOptions config) - { - _apiClient = httpClient; - _logger = logger; - _urls = config.Value; - } + public OrderApiClient(HttpClient httpClient, ILogger logger, IOptions config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } - public async Task GetOrderDraftFromBasketAsync(BasketData basket) - { - var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); - var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); - var response = await _apiClient.PostAsync(url, content); + public async Task GetOrderDraftFromBasketAsync(BasketData basket) + { + var url = $"{_urls.Orders}{UrlsConfig.OrdersOperations.GetOrderDraft()}"; + var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); + var response = await _apiClient.PostAsync(url, content); - response.EnsureSuccessStatusCode(); + response.EnsureSuccessStatusCode(); - var ordersDraftResponse = await response.Content.ReadAsStringAsync(); + var ordersDraftResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(ordersDraftResponse, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); - } + return JsonSerializer.Deserialize(ordersDraftResponse, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderingService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderingService.cs index ab01b20f5..afa86b31b 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderingService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderingService.cs @@ -1,79 +1,72 @@ -using GrpcOrdering; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +public class OrderingService : IOrderingService { - public class OrderingService : IOrderingService + private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient; + private readonly ILogger _logger; + + public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger logger) { - private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient; - private readonly ILogger _logger; + _orderingGrpcClient = orderingGrpcClient; + _logger = logger; + } - public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger logger) - { - _orderingGrpcClient = orderingGrpcClient; - _logger = logger; - } + public async Task GetOrderDraftAsync(BasketData basketData) + { + _logger.LogDebug(" grpc client created, basketData={@basketData}", basketData); - public async Task GetOrderDraftAsync(BasketData basketData) - { - _logger.LogDebug(" grpc client created, basketData={@basketData}", basketData); + var command = MapToOrderDraftCommand(basketData); + var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command); + _logger.LogDebug(" grpc response: {@response}", response); - var command = MapToOrderDraftCommand(basketData); - var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command); - _logger.LogDebug(" grpc response: {@response}", response); + return MapToResponse(response, basketData); + } - return MapToResponse(response, basketData); + private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData) + { + if (orderDraft == null) + { + return null; } - private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData) + var data = new OrderData { - if (orderDraft == null) - { - return null; - } + Buyer = basketData.BuyerId, + Total = (decimal)orderDraft.Total, + }; - var data = new OrderData - { - Buyer = basketData.BuyerId, - Total = (decimal)orderDraft.Total, - }; - - orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData - { - Discount = (decimal)o.Discount, - PictureUrl = o.PictureUrl, - ProductId = o.ProductId, - ProductName = o.ProductName, - UnitPrice = (decimal)o.UnitPrice, - Units = o.Units, - })); + orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData + { + Discount = (decimal)o.Discount, + PictureUrl = o.PictureUrl, + ProductId = o.ProductId, + ProductName = o.ProductName, + UnitPrice = (decimal)o.UnitPrice, + Units = o.Units, + })); - return data; - } + return data; + } - private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData) + private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData) + { + var command = new CreateOrderDraftCommand { - var command = new CreateOrderDraftCommand - { - BuyerId = basketData.BuyerId, - }; + BuyerId = basketData.BuyerId, + }; - basketData.Items.ForEach(i => command.Items.Add(new BasketItem - { - Id = i.Id, - OldUnitPrice = (double)i.OldUnitPrice, - PictureUrl = i.PictureUrl, - ProductId = i.ProductId, - ProductName = i.ProductName, - Quantity = i.Quantity, - UnitPrice = (double)i.UnitPrice, - })); - - return command; - } + basketData.Items.ForEach(i => command.Items.Add(new BasketItem + { + Id = i.Id, + OldUnitPrice = (double)i.OldUnitPrice, + PictureUrl = i.PictureUrl, + ProductId = i.ProductId, + ProductName = i.ProductName, + Quantity = i.Quantity, + UnitPrice = (double)i.UnitPrice, + })); + return command; } + } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs index 96ba8d2eb..6e8e66931 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs @@ -1,225 +1,199 @@ -using CatalogApi; -using Devspaces.Support; -using GrpcBasket; -using GrpcOrdering; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure; -using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; - -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddHealthChecks() + .AddCheck("self", () => HealthCheckResult.Healthy()) + .AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) + .AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) + .AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) + .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) + .AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }); + + services.AddCustomMvc(Configuration) + .AddCustomAuthentication(Configuration) + .AddDevspaces() + .AddApplicationServices() + .AddGrpcServices(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { - public Startup(IConfiguration configuration) + var pathBase = Configuration["PATH_BASE"]; + if (!string.IsNullOrEmpty(pathBase)) { - Configuration = configuration; + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); + app.UsePathBase(pathBase); } - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) + if (env.IsDevelopment()) { - services.AddHealthChecks() - .AddCheck("self", () => HealthCheckResult.Healthy()) - .AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" }) - .AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" }) - .AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" }) - .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }) - .AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }); - - services.AddCustomMvc(Configuration) - .AddCustomAuthentication(Configuration) - .AddDevspaces() - .AddApplicationServices() - .AddGrpcServices(); + app.UseDeveloperExceptionPage(); } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + app.UseHttpsRedirection(); + + app.UseSwagger().UseSwaggerUI(c => { - var pathBase = Configuration["PATH_BASE"]; - if (!string.IsNullOrEmpty(pathBase)) - { - loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); - app.UsePathBase(pathBase); - } + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } + c.OAuthClientId("webshoppingaggswaggerui"); + c.OAuthClientSecret(string.Empty); + c.OAuthRealm(string.Empty); + c.OAuthAppName("web shopping bff Swagger UI"); + }); - app.UseHttpsRedirection(); + app.UseRouting(); + app.UseCors("CorsPolicy"); + app.UseAuthentication(); + app.UseAuthorization(); - app.UseSwagger().UseSwaggerUI(c => + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() { - c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); - - c.OAuthClientId("webshoppingaggswaggerui"); - c.OAuthClientSecret(string.Empty); - c.OAuthRealm(string.Empty); - c.OAuthAppName("web shopping bff Swagger UI"); + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); - - app.UseRouting(); - app.UseCors("CorsPolicy"); - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions { - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); + Predicate = r => r.Name.Contains("self") }); - } + }); } +} - public static class ServiceCollectionExtensions +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + + var identityUrl = configuration.GetValue("urls:identity"); + services.AddAuthentication(options => { - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - var identityUrl = configuration.GetValue("urls:identity"); - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "webshoppingagg"; + }); - }) - .AddJwtBearer(options => - { - options.Authority = identityUrl; - options.RequireHttpsMetadata = false; - options.Audience = "webshoppingagg"; - }); + return services; + } - return services; - } + public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) + { + services.AddOptions(); + services.Configure(configuration.GetSection("urls")); - public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) - { - services.AddOptions(); - services.Configure(configuration.GetSection("urls")); + services.AddControllers() + .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); - services.AddControllers() - .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); + services.AddSwaggerGen(options => + { + options.DescribeAllEnumsAsStrings(); - services.AddSwaggerGen(options => + options.SwaggerDoc("v1", new OpenApiInfo { - options.DescribeAllEnumsAsStrings(); - - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "Shopping Aggregator for Web Clients", - Version = "v1", - Description = "Shopping Aggregator for Web Clients" - }); + Title = "Shopping Aggregator for Web Clients", + Version = "v1", + Description = "Shopping Aggregator for Web Clients" + }); - options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OAuth2, + Flows = new OpenApiOAuthFlows() { - Type = SecuritySchemeType.OAuth2, - Flows = new OpenApiOAuthFlows() + Implicit = new OpenApiOAuthFlow() { - Implicit = new OpenApiOAuthFlow() - { - AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), - TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), + AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), + TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), - Scopes = new Dictionary() - { - { "webshoppingagg", "Shopping Aggregator for Web Clients" } - } + Scopes = new Dictionary() + { + { "webshoppingagg", "Shopping Aggregator for Web Clients" } } } - }); - - options.OperationFilter(); + } }); - services.AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder - .SetIsOriginAllowed((host) => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); - }); + options.OperationFilter(); + }); - return services; - } - public static IServiceCollection AddApplicationServices(this IServiceCollection services) + services.AddCors(options => { - //register delegating handlers - services.AddTransient(); - services.AddSingleton(); + options.AddPolicy("CorsPolicy", + builder => builder + .SetIsOriginAllowed((host) => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + return services; + } + public static IServiceCollection AddApplicationServices(this IServiceCollection services) + { + //register delegating handlers + services.AddTransient(); + services.AddSingleton(); - //register http services + //register http services - services.AddHttpClient() - .AddHttpMessageHandler() - .AddDevspacesSupport(); + services.AddHttpClient() + .AddHttpMessageHandler() + .AddDevspacesSupport(); - return services; - } + return services; + } - public static IServiceCollection AddGrpcServices(this IServiceCollection services) - { - services.AddTransient(); + public static IServiceCollection AddGrpcServices(this IServiceCollection services) + { + services.AddTransient(); - services.AddScoped(); + services.AddScoped(); - services.AddGrpcClient((services, options) => - { - var basketApi = services.GetRequiredService>().Value.GrpcBasket; - options.Address = new Uri(basketApi); - }).AddInterceptor(); + services.AddGrpcClient((services, options) => + { + var basketApi = services.GetRequiredService>().Value.GrpcBasket; + options.Address = new Uri(basketApi); + }).AddInterceptor(); - services.AddScoped(); + services.AddScoped(); - services.AddGrpcClient((services, options) => - { - var catalogApi = services.GetRequiredService>().Value.GrpcCatalog; - options.Address = new Uri(catalogApi); - }).AddInterceptor(); + services.AddGrpcClient((services, options) => + { + var catalogApi = services.GetRequiredService>().Value.GrpcCatalog; + options.Address = new Uri(catalogApi); + }).AddInterceptor(); - services.AddScoped(); + services.AddScoped(); - services.AddGrpcClient((services, options) => - { - var orderingApi = services.GetRequiredService>().Value.GrpcOrdering; - options.Address = new Uri(orderingApi); - }).AddInterceptor(); + services.AddGrpcClient((services, options) => + { + var orderingApi = services.GetRequiredService>().Value.GrpcOrdering; + options.Address = new Uri(orderingApi); + }).AddInterceptor(); - return services; - } + return services; } } diff --git a/src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj b/src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj index c7703ff9c..8e4fc382c 100644 --- a/src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj +++ b/src/BuildingBlocks/Devspaces.Support/Devspaces.Support.csproj @@ -5,7 +5,7 @@ - - + + diff --git a/src/BuildingBlocks/Devspaces.Support/DevspacesMessageHandler.cs b/src/BuildingBlocks/Devspaces.Support/DevspacesMessageHandler.cs index 43dfa82f1..ac054693f 100644 --- a/src/BuildingBlocks/Devspaces.Support/DevspacesMessageHandler.cs +++ b/src/BuildingBlocks/Devspaces.Support/DevspacesMessageHandler.cs @@ -1,29 +1,22 @@ -using Microsoft.AspNetCore.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; +namespace Devspaces.Support; -namespace Devspaces.Support +public class DevspacesMessageHandler : DelegatingHandler { - public class DevspacesMessageHandler : DelegatingHandler + private const string DevspacesHeaderName = "azds-route-as"; + private readonly IHttpContextAccessor _httpContextAccessor; + public DevspacesMessageHandler(IHttpContextAccessor httpContextAccessor) { - private const string DevspacesHeaderName = "azds-route-as"; - private readonly IHttpContextAccessor _httpContextAccessor; - public DevspacesMessageHandler(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } + _httpContextAccessor = httpContextAccessor; + } - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var req = _httpContextAccessor.HttpContext.Request; + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var req = _httpContextAccessor.HttpContext.Request; - if (req.Headers.ContainsKey(DevspacesHeaderName)) - { - request.Headers.Add(DevspacesHeaderName, req.Headers[DevspacesHeaderName] as IEnumerable); - } - return base.SendAsync(request, cancellationToken); + if (req.Headers.ContainsKey(DevspacesHeaderName)) + { + request.Headers.Add(DevspacesHeaderName, req.Headers[DevspacesHeaderName] as IEnumerable); } + return base.SendAsync(request, cancellationToken); } } diff --git a/src/BuildingBlocks/Devspaces.Support/GlobalUsings.cs b/src/BuildingBlocks/Devspaces.Support/GlobalUsings.cs new file mode 100644 index 000000000..06c07e8fb --- /dev/null +++ b/src/BuildingBlocks/Devspaces.Support/GlobalUsings.cs @@ -0,0 +1,6 @@ +global using Microsoft.AspNetCore.Http; +global using Microsoft.Extensions.DependencyInjection; +global using System.Collections.Generic; +global using System.Net.Http; +global using System.Threading.Tasks; +global using System.Threading; diff --git a/src/BuildingBlocks/Devspaces.Support/HttpClientBuilderDevspacesExtensions.cs b/src/BuildingBlocks/Devspaces.Support/HttpClientBuilderDevspacesExtensions.cs index 00367b26c..fd78b9a40 100644 --- a/src/BuildingBlocks/Devspaces.Support/HttpClientBuilderDevspacesExtensions.cs +++ b/src/BuildingBlocks/Devspaces.Support/HttpClientBuilderDevspacesExtensions.cs @@ -1,13 +1,10 @@ -using Microsoft.Extensions.DependencyInjection; +namespace Devspaces.Support; -namespace Devspaces.Support +public static class HttpClientBuilderDevspacesExtensions { - public static class HttpClientBuilderDevspacesExtensions + public static IHttpClientBuilder AddDevspacesSupport(this IHttpClientBuilder builder) { - public static IHttpClientBuilder AddDevspacesSupport(this IHttpClientBuilder builder) - { - builder.AddHttpMessageHandler(); - return builder; - } + builder.AddHttpMessageHandler(); + return builder; } } diff --git a/src/BuildingBlocks/Devspaces.Support/ServiceCollectionDevspacesExtensions.cs b/src/BuildingBlocks/Devspaces.Support/ServiceCollectionDevspacesExtensions.cs index d196c9184..15c3b1515 100644 --- a/src/BuildingBlocks/Devspaces.Support/ServiceCollectionDevspacesExtensions.cs +++ b/src/BuildingBlocks/Devspaces.Support/ServiceCollectionDevspacesExtensions.cs @@ -1,13 +1,10 @@ -using Microsoft.Extensions.DependencyInjection; +namespace Devspaces.Support; -namespace Devspaces.Support +public static class ServiceCollectionDevspacesExtensions { - public static class ServiceCollectionDevspacesExtensions + public static IServiceCollection AddDevspaces(this IServiceCollection services) { - public static IServiceCollection AddDevspaces(this IServiceCollection services) - { - services.AddTransient(); - return services; - } + services.AddTransient(); + return services; } } diff --git a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs index c26ee6a81..8b9fbe497 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs +++ b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IDynamicIntegrationEventHandler.cs @@ -1,9 +1,6 @@ -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions +public interface IDynamicIntegrationEventHandler { - public interface IDynamicIntegrationEventHandler - { - Task Handle(dynamic eventData); - } + Task Handle(dynamic eventData); } diff --git a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs index 4c9758a40..492a10e42 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs +++ b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IEventBus.cs @@ -1,23 +1,20 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions +public interface IEventBus { - public interface IEventBus - { - void Publish(IntegrationEvent @event); + void Publish(IntegrationEvent @event); - void Subscribe() - where T : IntegrationEvent - where TH : IIntegrationEventHandler; + void Subscribe() + where T : IntegrationEvent + where TH : IIntegrationEventHandler; - void SubscribeDynamic(string eventName) - where TH : IDynamicIntegrationEventHandler; + void SubscribeDynamic(string eventName) + where TH : IDynamicIntegrationEventHandler; - void UnsubscribeDynamic(string eventName) - where TH : IDynamicIntegrationEventHandler; + void UnsubscribeDynamic(string eventName) + where TH : IDynamicIntegrationEventHandler; - void Unsubscribe() - where TH : IIntegrationEventHandler - where T : IntegrationEvent; - } + void Unsubscribe() + where TH : IIntegrationEventHandler + where T : IntegrationEvent; } diff --git a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IIntegrationEventHandler.cs b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IIntegrationEventHandler.cs index 99735b871..030c604b5 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Abstractions/IIntegrationEventHandler.cs +++ b/src/BuildingBlocks/EventBus/EventBus/Abstractions/IIntegrationEventHandler.cs @@ -1,15 +1,11 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions +public interface IIntegrationEventHandler : IIntegrationEventHandler + where TIntegrationEvent : IntegrationEvent { - public interface IIntegrationEventHandler : IIntegrationEventHandler - where TIntegrationEvent : IntegrationEvent - { - Task Handle(TIntegrationEvent @event); - } + Task Handle(TIntegrationEvent @event); +} - public interface IIntegrationEventHandler - { - } +public interface IIntegrationEventHandler +{ } diff --git a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs b/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs index 1f185a691..31118c4da 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs +++ b/src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs @@ -1,27 +1,23 @@ -using System; -using System.Text.Json.Serialization; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events -{ - public record IntegrationEvent - { - public IntegrationEvent() - { - Id = Guid.NewGuid(); - CreationDate = DateTime.UtcNow; - } +public record IntegrationEvent +{ + public IntegrationEvent() + { + Id = Guid.NewGuid(); + CreationDate = DateTime.UtcNow; + } - [JsonConstructor] - public IntegrationEvent(Guid id, DateTime createDate) - { - Id = id; - CreationDate = createDate; - } + [JsonConstructor] + public IntegrationEvent(Guid id, DateTime createDate) + { + Id = id; + CreationDate = createDate; + } - [JsonInclude] - public Guid Id { get; private init; } + [JsonInclude] + public Guid Id { get; private init; } - [JsonInclude] - public DateTime CreationDate { get; private init; } - } + [JsonInclude] + public DateTime CreationDate { get; private init; } } diff --git a/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs b/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs index 775c29c3e..70e295beb 100644 --- a/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs +++ b/src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs @@ -1,30 +1,26 @@ -using System; -using System.Linq; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions +public static class GenericTypeExtensions { - public static class GenericTypeExtensions + public static string GetGenericTypeName(this Type type) { - public static string GetGenericTypeName(this Type type) - { - var typeName = string.Empty; - - if (type.IsGenericType) - { - var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray()); - typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>"; - } - else - { - typeName = type.Name; - } + var typeName = string.Empty; - return typeName; + if (type.IsGenericType) + { + var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray()); + typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>"; } - - public static string GetGenericTypeName(this object @object) + else { - return @object.GetType().GetGenericTypeName(); + typeName = type.Name; } + + return typeName; + } + + public static string GetGenericTypeName(this object @object) + { + return @object.GetType().GetGenericTypeName(); } } diff --git a/src/BuildingBlocks/EventBus/EventBus/GlobalUsings.cs b/src/BuildingBlocks/EventBus/EventBus/GlobalUsings.cs new file mode 100644 index 000000000..faddc5a13 --- /dev/null +++ b/src/BuildingBlocks/EventBus/EventBus/GlobalUsings.cs @@ -0,0 +1,8 @@ +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using static Microsoft.eShopOnContainers.BuildingBlocks.EventBus.InMemoryEventBusSubscriptionsManager; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text.Json.Serialization; +global using System.Threading.Tasks; +global using System; diff --git a/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs b/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs index c83c505b1..a140bfc75 100644 --- a/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs +++ b/src/BuildingBlocks/EventBus/EventBus/IEventBusSubscriptionsManager.cs @@ -1,34 +1,27 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System; -using System.Collections.Generic; -using static Microsoft.eShopOnContainers.BuildingBlocks.EventBus.InMemoryEventBusSubscriptionsManager; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus +public interface IEventBusSubscriptionsManager { - public interface IEventBusSubscriptionsManager - { - bool IsEmpty { get; } - event EventHandler OnEventRemoved; - void AddDynamicSubscription(string eventName) - where TH : IDynamicIntegrationEventHandler; + bool IsEmpty { get; } + event EventHandler OnEventRemoved; + void AddDynamicSubscription(string eventName) + where TH : IDynamicIntegrationEventHandler; - void AddSubscription() - where T : IntegrationEvent - where TH : IIntegrationEventHandler; + void AddSubscription() + where T : IntegrationEvent + where TH : IIntegrationEventHandler; - void RemoveSubscription() - where TH : IIntegrationEventHandler - where T : IntegrationEvent; - void RemoveDynamicSubscription(string eventName) - where TH : IDynamicIntegrationEventHandler; + void RemoveSubscription() + where TH : IIntegrationEventHandler + where T : IntegrationEvent; + void RemoveDynamicSubscription(string eventName) + where TH : IDynamicIntegrationEventHandler; - bool HasSubscriptionsForEvent() where T : IntegrationEvent; - bool HasSubscriptionsForEvent(string eventName); - Type GetEventTypeByName(string eventName); - void Clear(); - IEnumerable GetHandlersForEvent() where T : IntegrationEvent; - IEnumerable GetHandlersForEvent(string eventName); - string GetEventKey(); - } -} \ No newline at end of file + bool HasSubscriptionsForEvent() where T : IntegrationEvent; + bool HasSubscriptionsForEvent(string eventName); + Type GetEventTypeByName(string eventName); + void Clear(); + IEnumerable GetHandlersForEvent() where T : IntegrationEvent; + IEnumerable GetHandlersForEvent(string eventName); + string GetEventKey(); +} diff --git a/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs index a86248f01..5de35b292 100644 --- a/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs +++ b/src/BuildingBlocks/EventBus/EventBus/InMemoryEventBusSubscriptionsManager.cs @@ -1,162 +1,155 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus +public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager { - public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager + + + private readonly Dictionary> _handlers; + private readonly List _eventTypes; + + public event EventHandler OnEventRemoved; + + public InMemoryEventBusSubscriptionsManager() { + _handlers = new Dictionary>(); + _eventTypes = new List(); + } + public bool IsEmpty => _handlers is { Count: 0 }; + public void Clear() => _handlers.Clear(); - private readonly Dictionary> _handlers; - private readonly List _eventTypes; + public void AddDynamicSubscription(string eventName) + where TH : IDynamicIntegrationEventHandler + { + DoAddSubscription(typeof(TH), eventName, isDynamic: true); + } - public event EventHandler OnEventRemoved; + public void AddSubscription() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = GetEventKey(); - public InMemoryEventBusSubscriptionsManager() + DoAddSubscription(typeof(TH), eventName, isDynamic: false); + + if (!_eventTypes.Contains(typeof(T))) { - _handlers = new Dictionary>(); - _eventTypes = new List(); + _eventTypes.Add(typeof(T)); } + } - public bool IsEmpty => !_handlers.Keys.Any(); - public void Clear() => _handlers.Clear(); - - public void AddDynamicSubscription(string eventName) - where TH : IDynamicIntegrationEventHandler + private void DoAddSubscription(Type handlerType, string eventName, bool isDynamic) + { + if (!HasSubscriptionsForEvent(eventName)) { - DoAddSubscription(typeof(TH), eventName, isDynamic: true); + _handlers.Add(eventName, new List()); } - public void AddSubscription() - where T : IntegrationEvent - where TH : IIntegrationEventHandler + if (_handlers[eventName].Any(s => s.HandlerType == handlerType)) { - var eventName = GetEventKey(); - - DoAddSubscription(typeof(TH), eventName, isDynamic: false); - - if (!_eventTypes.Contains(typeof(T))) - { - _eventTypes.Add(typeof(T)); - } + throw new ArgumentException( + $"Handler Type {handlerType.Name} already registered for '{eventName}'", nameof(handlerType)); } - private void DoAddSubscription(Type handlerType, string eventName, bool isDynamic) + if (isDynamic) { - if (!HasSubscriptionsForEvent(eventName)) - { - _handlers.Add(eventName, new List()); - } - - if (_handlers[eventName].Any(s => s.HandlerType == handlerType)) - { - throw new ArgumentException( - $"Handler Type {handlerType.Name} already registered for '{eventName}'", nameof(handlerType)); - } - - if (isDynamic) - { - _handlers[eventName].Add(SubscriptionInfo.Dynamic(handlerType)); - } - else - { - _handlers[eventName].Add(SubscriptionInfo.Typed(handlerType)); - } + _handlers[eventName].Add(SubscriptionInfo.Dynamic(handlerType)); } - - - public void RemoveDynamicSubscription(string eventName) - where TH : IDynamicIntegrationEventHandler + else { - var handlerToRemove = FindDynamicSubscriptionToRemove(eventName); - DoRemoveHandler(eventName, handlerToRemove); + _handlers[eventName].Add(SubscriptionInfo.Typed(handlerType)); } + } - public void RemoveSubscription() - where TH : IIntegrationEventHandler - where T : IntegrationEvent - { - var handlerToRemove = FindSubscriptionToRemove(); - var eventName = GetEventKey(); - DoRemoveHandler(eventName, handlerToRemove); - } + public void RemoveDynamicSubscription(string eventName) + where TH : IDynamicIntegrationEventHandler + { + var handlerToRemove = FindDynamicSubscriptionToRemove(eventName); + DoRemoveHandler(eventName, handlerToRemove); + } + + + public void RemoveSubscription() + where TH : IIntegrationEventHandler + where T : IntegrationEvent + { + var handlerToRemove = FindSubscriptionToRemove(); + var eventName = GetEventKey(); + DoRemoveHandler(eventName, handlerToRemove); + } - private void DoRemoveHandler(string eventName, SubscriptionInfo subsToRemove) + private void DoRemoveHandler(string eventName, SubscriptionInfo subsToRemove) + { + if (subsToRemove != null) { - if (subsToRemove != null) + _handlers[eventName].Remove(subsToRemove); + if (!_handlers[eventName].Any()) { - _handlers[eventName].Remove(subsToRemove); - if (!_handlers[eventName].Any()) + _handlers.Remove(eventName); + var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName); + if (eventType != null) { - _handlers.Remove(eventName); - var eventType = _eventTypes.SingleOrDefault(e => e.Name == eventName); - if (eventType != null) - { - _eventTypes.Remove(eventType); - } - RaiseOnEventRemoved(eventName); + _eventTypes.Remove(eventType); } - + RaiseOnEventRemoved(eventName); } - } - public IEnumerable GetHandlersForEvent() where T : IntegrationEvent - { - var key = GetEventKey(); - return GetHandlersForEvent(key); } - public IEnumerable GetHandlersForEvent(string eventName) => _handlers[eventName]; + } - private void RaiseOnEventRemoved(string eventName) - { - var handler = OnEventRemoved; - handler?.Invoke(this, eventName); - } + public IEnumerable GetHandlersForEvent() where T : IntegrationEvent + { + var key = GetEventKey(); + return GetHandlersForEvent(key); + } + public IEnumerable GetHandlersForEvent(string eventName) => _handlers[eventName]; + private void RaiseOnEventRemoved(string eventName) + { + var handler = OnEventRemoved; + handler?.Invoke(this, eventName); + } - private SubscriptionInfo FindDynamicSubscriptionToRemove(string eventName) - where TH : IDynamicIntegrationEventHandler - { - return DoFindSubscriptionToRemove(eventName, typeof(TH)); - } + private SubscriptionInfo FindDynamicSubscriptionToRemove(string eventName) + where TH : IDynamicIntegrationEventHandler + { + return DoFindSubscriptionToRemove(eventName, typeof(TH)); + } - private SubscriptionInfo FindSubscriptionToRemove() - where T : IntegrationEvent - where TH : IIntegrationEventHandler - { - var eventName = GetEventKey(); - return DoFindSubscriptionToRemove(eventName, typeof(TH)); - } - private SubscriptionInfo DoFindSubscriptionToRemove(string eventName, Type handlerType) + private SubscriptionInfo FindSubscriptionToRemove() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = GetEventKey(); + return DoFindSubscriptionToRemove(eventName, typeof(TH)); + } + + private SubscriptionInfo DoFindSubscriptionToRemove(string eventName, Type handlerType) + { + if (!HasSubscriptionsForEvent(eventName)) { - if (!HasSubscriptionsForEvent(eventName)) - { - return null; - } + return null; + } - return _handlers[eventName].SingleOrDefault(s => s.HandlerType == handlerType); + return _handlers[eventName].SingleOrDefault(s => s.HandlerType == handlerType); - } + } - public bool HasSubscriptionsForEvent() where T : IntegrationEvent - { - var key = GetEventKey(); - return HasSubscriptionsForEvent(key); - } - public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName); + public bool HasSubscriptionsForEvent() where T : IntegrationEvent + { + var key = GetEventKey(); + return HasSubscriptionsForEvent(key); + } + public bool HasSubscriptionsForEvent(string eventName) => _handlers.ContainsKey(eventName); - public Type GetEventTypeByName(string eventName) => _eventTypes.SingleOrDefault(t => t.Name == eventName); + public Type GetEventTypeByName(string eventName) => _eventTypes.SingleOrDefault(t => t.Name == eventName); - public string GetEventKey() - { - return typeof(T).Name; - } + public string GetEventKey() + { + return typeof(T).Name; } } diff --git a/src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs b/src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs index 9e61034a0..17e29d965 100644 --- a/src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs +++ b/src/BuildingBlocks/EventBus/EventBus/SubscriptionInfo.cs @@ -1,28 +1,22 @@ -using System; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus +public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager { - public partial class InMemoryEventBusSubscriptionsManager : IEventBusSubscriptionsManager + public class SubscriptionInfo { - public class SubscriptionInfo + public bool IsDynamic { get; } + public Type HandlerType { get; } + + private SubscriptionInfo(bool isDynamic, Type handlerType) { - public bool IsDynamic { get; } - public Type HandlerType { get; } + IsDynamic = isDynamic; + HandlerType = handlerType; + } - private SubscriptionInfo(bool isDynamic, Type handlerType) - { - IsDynamic = isDynamic; - HandlerType = handlerType; - } + public static SubscriptionInfo Dynamic(Type handlerType) => + new SubscriptionInfo(true, handlerType); - public static SubscriptionInfo Dynamic(Type handlerType) - { - return new SubscriptionInfo(true, handlerType); - } - public static SubscriptionInfo Typed(Type handlerType) - { - return new SubscriptionInfo(false, handlerType); - } - } + public static SubscriptionInfo Typed(Type handlerType) => + new SubscriptionInfo(false, handlerType); } } diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs index 93e5b2917..17fdba7da 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs @@ -1,131 +1,123 @@ -using Microsoft.Extensions.Logging; -using Polly; -using Polly.Retry; -using RabbitMQ.Client; -using RabbitMQ.Client.Events; -using RabbitMQ.Client.Exceptions; -using System; -using System.IO; -using System.Net.Sockets; - -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; + +public class DefaultRabbitMQPersistentConnection + : IRabbitMQPersistentConnection { - public class DefaultRabbitMQPersistentConnection - : IRabbitMQPersistentConnection - { - private readonly IConnectionFactory _connectionFactory; - private readonly ILogger _logger; - private readonly int _retryCount; - IConnection _connection; - bool _disposed; + private readonly IConnectionFactory _connectionFactory; + private readonly ILogger _logger; + private readonly int _retryCount; + IConnection _connection; + bool _disposed; - object sync_root = new object(); + object sync_root = new object(); - public DefaultRabbitMQPersistentConnection(IConnectionFactory connectionFactory, ILogger logger, int retryCount = 5) - { - _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _retryCount = retryCount; - } + public DefaultRabbitMQPersistentConnection(IConnectionFactory connectionFactory, ILogger logger, int retryCount = 5) + { + _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _retryCount = retryCount; + } - public bool IsConnected + public bool IsConnected + { + get { - get - { - return _connection != null && _connection.IsOpen && !_disposed; - } + return _connection != null && _connection.IsOpen && !_disposed; } + } - public IModel CreateModel() + public IModel CreateModel() + { + if (!IsConnected) { - if (!IsConnected) - { - throw new InvalidOperationException("No RabbitMQ connections are available to perform this action"); - } - - return _connection.CreateModel(); + throw new InvalidOperationException("No RabbitMQ connections are available to perform this action"); } - public void Dispose() - { - if (_disposed) return; + return _connection.CreateModel(); + } - _disposed = true; + public void Dispose() + { + if (_disposed) return; - try - { - _connection.Dispose(); - } - catch (IOException ex) - { - _logger.LogCritical(ex.ToString()); - } + _disposed = true; + + try + { + _connection.ConnectionShutdown -= OnConnectionShutdown; + _connection.CallbackException -= OnCallbackException; + _connection.ConnectionBlocked -= OnConnectionBlocked; + _connection.Dispose(); + } + catch (IOException ex) + { + _logger.LogCritical(ex.ToString()); } + } + + public bool TryConnect() + { + _logger.LogInformation("RabbitMQ Client is trying to connect"); - public bool TryConnect() + lock (sync_root) { - _logger.LogInformation("RabbitMQ Client is trying to connect"); + var policy = RetryPolicy.Handle() + .Or() + .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => + { + _logger.LogWarning(ex, "RabbitMQ Client could not connect after {TimeOut}s ({ExceptionMessage})", $"{time.TotalSeconds:n1}", ex.Message); + } + ); - lock (sync_root) + policy.Execute(() => { - var policy = RetryPolicy.Handle() - .Or() - .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => - { - _logger.LogWarning(ex, "RabbitMQ Client could not connect after {TimeOut}s ({ExceptionMessage})", $"{time.TotalSeconds:n1}", ex.Message); - } - ); - - policy.Execute(() => - { - _connection = _connectionFactory - .CreateConnection(); - }); + _connection = _connectionFactory + .CreateConnection(); + }); - if (IsConnected) - { - _connection.ConnectionShutdown += OnConnectionShutdown; - _connection.CallbackException += OnCallbackException; - _connection.ConnectionBlocked += OnConnectionBlocked; + if (IsConnected) + { + _connection.ConnectionShutdown += OnConnectionShutdown; + _connection.CallbackException += OnCallbackException; + _connection.ConnectionBlocked += OnConnectionBlocked; - _logger.LogInformation("RabbitMQ Client acquired a persistent connection to '{HostName}' and is subscribed to failure events", _connection.Endpoint.HostName); + _logger.LogInformation("RabbitMQ Client acquired a persistent connection to '{HostName}' and is subscribed to failure events", _connection.Endpoint.HostName); - return true; - } - else - { - _logger.LogCritical("FATAL ERROR: RabbitMQ connections could not be created and opened"); + return true; + } + else + { + _logger.LogCritical("FATAL ERROR: RabbitMQ connections could not be created and opened"); - return false; - } + return false; } } + } - private void OnConnectionBlocked(object sender, ConnectionBlockedEventArgs e) - { - if (_disposed) return; + private void OnConnectionBlocked(object sender, ConnectionBlockedEventArgs e) + { + if (_disposed) return; - _logger.LogWarning("A RabbitMQ connection is shutdown. Trying to re-connect..."); + _logger.LogWarning("A RabbitMQ connection is shutdown. Trying to re-connect..."); - TryConnect(); - } + TryConnect(); + } - void OnCallbackException(object sender, CallbackExceptionEventArgs e) - { - if (_disposed) return; + void OnCallbackException(object sender, CallbackExceptionEventArgs e) + { + if (_disposed) return; - _logger.LogWarning("A RabbitMQ connection throw exception. Trying to re-connect..."); + _logger.LogWarning("A RabbitMQ connection throw exception. Trying to re-connect..."); - TryConnect(); - } + TryConnect(); + } - void OnConnectionShutdown(object sender, ShutdownEventArgs reason) - { - if (_disposed) return; + void OnConnectionShutdown(object sender, ShutdownEventArgs reason) + { + if (_disposed) return; - _logger.LogWarning("A RabbitMQ connection is on shutdown. Trying to re-connect..."); + _logger.LogWarning("A RabbitMQ connection is on shutdown. Trying to re-connect..."); - TryConnect(); - } + TryConnect(); } } diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs index 935b10ec8..9b45eb996 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs @@ -1,297 +1,279 @@ -using Autofac; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.Extensions.Logging; -using Polly; -using Polly.Retry; -using RabbitMQ.Client; -using RabbitMQ.Client.Events; -using RabbitMQ.Client.Exceptions; -using System; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; -using System.Text.Json; - -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; + +public class EventBusRabbitMQ : IEventBus, IDisposable { - public class EventBusRabbitMQ : IEventBus, IDisposable - { - const string BROKER_NAME = "eshop_event_bus"; - const string AUTOFAC_SCOPE_NAME = "eshop_event_bus"; + const string BROKER_NAME = "eshop_event_bus"; + const string AUTOFAC_SCOPE_NAME = "eshop_event_bus"; - private readonly IRabbitMQPersistentConnection _persistentConnection; - private readonly ILogger _logger; - private readonly IEventBusSubscriptionsManager _subsManager; - private readonly ILifetimeScope _autofac; - private readonly int _retryCount; + private readonly IRabbitMQPersistentConnection _persistentConnection; + private readonly ILogger _logger; + private readonly IEventBusSubscriptionsManager _subsManager; + private readonly ILifetimeScope _autofac; + private readonly int _retryCount; - private IModel _consumerChannel; - private string _queueName; + private IModel _consumerChannel; + private string _queueName; - public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger logger, - ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, string queueName = null, int retryCount = 5) + public EventBusRabbitMQ(IRabbitMQPersistentConnection persistentConnection, ILogger logger, + ILifetimeScope autofac, IEventBusSubscriptionsManager subsManager, string queueName = null, int retryCount = 5) + { + _persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); + _queueName = queueName; + _consumerChannel = CreateConsumerChannel(); + _autofac = autofac; + _retryCount = retryCount; + _subsManager.OnEventRemoved += SubsManager_OnEventRemoved; + } + + private void SubsManager_OnEventRemoved(object sender, string eventName) + { + if (!_persistentConnection.IsConnected) { - _persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); - _queueName = queueName; - _consumerChannel = CreateConsumerChannel(); - _autofac = autofac; - _retryCount = retryCount; - _subsManager.OnEventRemoved += SubsManager_OnEventRemoved; + _persistentConnection.TryConnect(); } - private void SubsManager_OnEventRemoved(object sender, string eventName) + using (var channel = _persistentConnection.CreateModel()) { - if (!_persistentConnection.IsConnected) - { - _persistentConnection.TryConnect(); - } + channel.QueueUnbind(queue: _queueName, + exchange: BROKER_NAME, + routingKey: eventName); - using (var channel = _persistentConnection.CreateModel()) + if (_subsManager.IsEmpty) { - channel.QueueUnbind(queue: _queueName, - exchange: BROKER_NAME, - routingKey: eventName); - - if (_subsManager.IsEmpty) - { - _queueName = string.Empty; - _consumerChannel.Close(); - } + _queueName = string.Empty; + _consumerChannel.Close(); } } + } - public void Publish(IntegrationEvent @event) + public void Publish(IntegrationEvent @event) + { + if (!_persistentConnection.IsConnected) { - if (!_persistentConnection.IsConnected) + _persistentConnection.TryConnect(); + } + + var policy = RetryPolicy.Handle() + .Or() + .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => { - _persistentConnection.TryConnect(); - } + _logger.LogWarning(ex, "Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id, $"{time.TotalSeconds:n1}", ex.Message); + }); - var policy = RetryPolicy.Handle() - .Or() - .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => - { - _logger.LogWarning(ex, "Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id, $"{time.TotalSeconds:n1}", ex.Message); - }); + var eventName = @event.GetType().Name; - var eventName = @event.GetType().Name; + _logger.LogTrace("Creating RabbitMQ channel to publish event: {EventId} ({EventName})", @event.Id, eventName); - _logger.LogTrace("Creating RabbitMQ channel to publish event: {EventId} ({EventName})", @event.Id, eventName); + using (var channel = _persistentConnection.CreateModel()) + { + _logger.LogTrace("Declaring RabbitMQ exchange to publish event: {EventId}", @event.Id); - using (var channel = _persistentConnection.CreateModel()) + channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct"); + + var body = JsonSerializer.SerializeToUtf8Bytes(@event, @event.GetType(), new JsonSerializerOptions { - _logger.LogTrace("Declaring RabbitMQ exchange to publish event: {EventId}", @event.Id); + WriteIndented = true + }); - channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct"); - - var body = JsonSerializer.SerializeToUtf8Bytes(@event, @event.GetType(), new JsonSerializerOptions - { - WriteIndented = true - }); + policy.Execute(() => + { + var properties = channel.CreateBasicProperties(); + properties.DeliveryMode = 2; // persistent - policy.Execute(() => - { - var properties = channel.CreateBasicProperties(); - properties.DeliveryMode = 2; // persistent - - _logger.LogTrace("Publishing event to RabbitMQ: {EventId}", @event.Id); - - channel.BasicPublish( - exchange: BROKER_NAME, - routingKey: eventName, - mandatory: true, - basicProperties: properties, - body: body); - }); - } + _logger.LogTrace("Publishing event to RabbitMQ: {EventId}", @event.Id); + + channel.BasicPublish( + exchange: BROKER_NAME, + routingKey: eventName, + mandatory: true, + basicProperties: properties, + body: body); + }); } + } - public void SubscribeDynamic(string eventName) - where TH : IDynamicIntegrationEventHandler - { - _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName()); + public void SubscribeDynamic(string eventName) + where TH : IDynamicIntegrationEventHandler + { + _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName()); - DoInternalSubscription(eventName); - _subsManager.AddDynamicSubscription(eventName); - StartBasicConsume(); - } + DoInternalSubscription(eventName); + _subsManager.AddDynamicSubscription(eventName); + StartBasicConsume(); + } - public void Subscribe() - where T : IntegrationEvent - where TH : IIntegrationEventHandler - { - var eventName = _subsManager.GetEventKey(); - DoInternalSubscription(eventName); + public void Subscribe() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = _subsManager.GetEventKey(); + DoInternalSubscription(eventName); - _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName()); + _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName()); - _subsManager.AddSubscription(); - StartBasicConsume(); - } + _subsManager.AddSubscription(); + StartBasicConsume(); + } - private void DoInternalSubscription(string eventName) + private void DoInternalSubscription(string eventName) + { + var containsKey = _subsManager.HasSubscriptionsForEvent(eventName); + if (!containsKey) { - var containsKey = _subsManager.HasSubscriptionsForEvent(eventName); - if (!containsKey) + if (!_persistentConnection.IsConnected) { - if (!_persistentConnection.IsConnected) - { - _persistentConnection.TryConnect(); - } - - _consumerChannel.QueueBind(queue: _queueName, - exchange: BROKER_NAME, - routingKey: eventName); + _persistentConnection.TryConnect(); } + + _consumerChannel.QueueBind(queue: _queueName, + exchange: BROKER_NAME, + routingKey: eventName); } + } - public void Unsubscribe() - where T : IntegrationEvent - where TH : IIntegrationEventHandler - { - var eventName = _subsManager.GetEventKey(); + public void Unsubscribe() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = _subsManager.GetEventKey(); - _logger.LogInformation("Unsubscribing from event {EventName}", eventName); + _logger.LogInformation("Unsubscribing from event {EventName}", eventName); - _subsManager.RemoveSubscription(); - } + _subsManager.RemoveSubscription(); + } - public void UnsubscribeDynamic(string eventName) - where TH : IDynamicIntegrationEventHandler + public void UnsubscribeDynamic(string eventName) + where TH : IDynamicIntegrationEventHandler + { + _subsManager.RemoveDynamicSubscription(eventName); + } + + public void Dispose() + { + if (_consumerChannel != null) { - _subsManager.RemoveDynamicSubscription(eventName); + _consumerChannel.Dispose(); } - public void Dispose() - { - if (_consumerChannel != null) - { - _consumerChannel.Dispose(); - } + _subsManager.Clear(); + } - _subsManager.Clear(); - } + private void StartBasicConsume() + { + _logger.LogTrace("Starting RabbitMQ basic consume"); - private void StartBasicConsume() + if (_consumerChannel != null) { - _logger.LogTrace("Starting RabbitMQ basic consume"); + var consumer = new AsyncEventingBasicConsumer(_consumerChannel); - if (_consumerChannel != null) - { - var consumer = new AsyncEventingBasicConsumer(_consumerChannel); - - consumer.Received += Consumer_Received; + consumer.Received += Consumer_Received; - _consumerChannel.BasicConsume( - queue: _queueName, - autoAck: false, - consumer: consumer); - } - else - { - _logger.LogError("StartBasicConsume can't call on _consumerChannel == null"); - } + _consumerChannel.BasicConsume( + queue: _queueName, + autoAck: false, + consumer: consumer); } - - private async Task Consumer_Received(object sender, BasicDeliverEventArgs eventArgs) + else { - var eventName = eventArgs.RoutingKey; - var message = Encoding.UTF8.GetString(eventArgs.Body.Span); + _logger.LogError("StartBasicConsume can't call on _consumerChannel == null"); + } + } - try - { - if (message.ToLowerInvariant().Contains("throw-fake-exception")) - { - throw new InvalidOperationException($"Fake exception requested: \"{message}\""); - } + private async Task Consumer_Received(object sender, BasicDeliverEventArgs eventArgs) + { + var eventName = eventArgs.RoutingKey; + var message = Encoding.UTF8.GetString(eventArgs.Body.Span); - await ProcessEvent(eventName, message); - } - catch (Exception ex) + try + { + if (message.ToLowerInvariant().Contains("throw-fake-exception")) { - _logger.LogWarning(ex, "----- ERROR Processing message \"{Message}\"", message); + throw new InvalidOperationException($"Fake exception requested: \"{message}\""); } - // Even on exception we take the message off the queue. - // in a REAL WORLD app this should be handled with a Dead Letter Exchange (DLX). - // For more information see: https://www.rabbitmq.com/dlx.html - _consumerChannel.BasicAck(eventArgs.DeliveryTag, multiple: false); + await ProcessEvent(eventName, message); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "----- ERROR Processing message \"{Message}\"", message); } - private IModel CreateConsumerChannel() + // Even on exception we take the message off the queue. + // in a REAL WORLD app this should be handled with a Dead Letter Exchange (DLX). + // For more information see: https://www.rabbitmq.com/dlx.html + _consumerChannel.BasicAck(eventArgs.DeliveryTag, multiple: false); + } + + private IModel CreateConsumerChannel() + { + if (!_persistentConnection.IsConnected) { - if (!_persistentConnection.IsConnected) - { - _persistentConnection.TryConnect(); - } + _persistentConnection.TryConnect(); + } - _logger.LogTrace("Creating RabbitMQ consumer channel"); + _logger.LogTrace("Creating RabbitMQ consumer channel"); - var channel = _persistentConnection.CreateModel(); + var channel = _persistentConnection.CreateModel(); - channel.ExchangeDeclare(exchange: BROKER_NAME, - type: "direct"); + channel.ExchangeDeclare(exchange: BROKER_NAME, + type: "direct"); - channel.QueueDeclare(queue: _queueName, - durable: true, - exclusive: false, - autoDelete: false, - arguments: null); + channel.QueueDeclare(queue: _queueName, + durable: true, + exclusive: false, + autoDelete: false, + arguments: null); - channel.CallbackException += (sender, ea) => - { - _logger.LogWarning(ea.Exception, "Recreating RabbitMQ consumer channel"); + channel.CallbackException += (sender, ea) => + { + _logger.LogWarning(ea.Exception, "Recreating RabbitMQ consumer channel"); - _consumerChannel.Dispose(); - _consumerChannel = CreateConsumerChannel(); - StartBasicConsume(); - }; + _consumerChannel.Dispose(); + _consumerChannel = CreateConsumerChannel(); + StartBasicConsume(); + }; - return channel; - } + return channel; + } - private async Task ProcessEvent(string eventName, string message) - { - _logger.LogTrace("Processing RabbitMQ event: {EventName}", eventName); + private async Task ProcessEvent(string eventName, string message) + { + _logger.LogTrace("Processing RabbitMQ event: {EventName}", eventName); - if (_subsManager.HasSubscriptionsForEvent(eventName)) + if (_subsManager.HasSubscriptionsForEvent(eventName)) + { + using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) { - using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) + var subscriptions = _subsManager.GetHandlersForEvent(eventName); + foreach (var subscription in subscriptions) { - var subscriptions = _subsManager.GetHandlersForEvent(eventName); - foreach (var subscription in subscriptions) + if (subscription.IsDynamic) { - if (subscription.IsDynamic) - { - var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler; - if (handler == null) continue; - using dynamic eventData = JsonDocument.Parse(message); - await Task.Yield(); - await handler.Handle(eventData); - } - else - { - var handler = scope.ResolveOptional(subscription.HandlerType); - if (handler == null) continue; - var eventType = _subsManager.GetEventTypeByName(eventName); - var integrationEvent = JsonSerializer.Deserialize(message, eventType, new JsonSerializerOptions() { PropertyNameCaseInsensitive= true}); - var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); - - await Task.Yield(); - await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); - } + var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler; + if (handler == null) continue; + using dynamic eventData = JsonDocument.Parse(message); + await Task.Yield(); + await handler.Handle(eventData); + } + else + { + var handler = scope.ResolveOptional(subscription.HandlerType); + if (handler == null) continue; + var eventType = _subsManager.GetEventTypeByName(eventName); + var integrationEvent = JsonSerializer.Deserialize(message, eventType, new JsonSerializerOptions() { PropertyNameCaseInsensitive= true}); + var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); + + await Task.Yield(); + await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); } } } - else - { - _logger.LogWarning("No subscription for RabbitMQ event: {EventName}", eventName); - } + } + else + { + _logger.LogWarning("No subscription for RabbitMQ event: {EventName}", eventName); } } } diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/GlobalUsings.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/GlobalUsings.cs new file mode 100644 index 000000000..6fa5f0bf4 --- /dev/null +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/GlobalUsings.cs @@ -0,0 +1,17 @@ +global using Microsoft.Extensions.Logging; +global using Polly; +global using Polly.Retry; +global using RabbitMQ.Client; +global using RabbitMQ.Client.Events; +global using RabbitMQ.Client.Exceptions; +global using System; +global using System.IO; +global using System.Net.Sockets; +global using Autofac; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; +global using System.Text; +global using System.Threading.Tasks; +global using System.Text.Json; diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs index 5893791c5..9d51e82a8 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs @@ -1,15 +1,11 @@ -using RabbitMQ.Client; -using System; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ +public interface IRabbitMQPersistentConnection + : IDisposable { - public interface IRabbitMQPersistentConnection - : IDisposable - { - bool IsConnected { get; } + bool IsConnected { get; } - bool TryConnect(); + bool TryConnect(); - IModel CreateModel(); - } + IModel CreateModel(); } diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/DefaultServiceBusPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/DefaultServiceBusPersisterConnection.cs index 0dff4154b..84aaa7faa 100644 --- a/src/BuildingBlocks/EventBus/EventBusServiceBus/DefaultServiceBusPersisterConnection.cs +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/DefaultServiceBusPersisterConnection.cs @@ -1,68 +1,64 @@ -using Microsoft.Azure.ServiceBus; -using System; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus +public class DefaultServiceBusPersisterConnection : IServiceBusPersisterConnection { - public class DefaultServiceBusPersisterConnection : IServiceBusPersisterConnection - { - private readonly ServiceBusConnectionStringBuilder _serviceBusConnectionStringBuilder; - private readonly string _subscriptionClientName; - private SubscriptionClient _subscriptionClient; - private ITopicClient _topicClient; + private readonly ServiceBusConnectionStringBuilder _serviceBusConnectionStringBuilder; + private readonly string _subscriptionClientName; + private SubscriptionClient _subscriptionClient; + private ITopicClient _topicClient; - bool _disposed; + bool _disposed; - public DefaultServiceBusPersisterConnection(ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder, - string subscriptionClientName) - { - _serviceBusConnectionStringBuilder = serviceBusConnectionStringBuilder ?? - throw new ArgumentNullException(nameof(serviceBusConnectionStringBuilder)); - _subscriptionClientName = subscriptionClientName; - _subscriptionClient = new SubscriptionClient(_serviceBusConnectionStringBuilder, subscriptionClientName); - _topicClient = new TopicClient(_serviceBusConnectionStringBuilder, RetryPolicy.Default); - } + public DefaultServiceBusPersisterConnection(ServiceBusConnectionStringBuilder serviceBusConnectionStringBuilder, + string subscriptionClientName) + { + _serviceBusConnectionStringBuilder = serviceBusConnectionStringBuilder ?? + throw new ArgumentNullException(nameof(serviceBusConnectionStringBuilder)); + _subscriptionClientName = subscriptionClientName; + _subscriptionClient = new SubscriptionClient(_serviceBusConnectionStringBuilder, subscriptionClientName); + _topicClient = new TopicClient(_serviceBusConnectionStringBuilder, RetryPolicy.Default); + } - public ITopicClient TopicClient + public ITopicClient TopicClient + { + get { - get + if (_topicClient.IsClosedOrClosing) { - if (_topicClient.IsClosedOrClosing) - { - _topicClient = new TopicClient(_serviceBusConnectionStringBuilder, RetryPolicy.Default); - } - return _topicClient; + _topicClient = new TopicClient(_serviceBusConnectionStringBuilder, RetryPolicy.Default); } + return _topicClient; } + } - public ISubscriptionClient SubscriptionClient + public ISubscriptionClient SubscriptionClient + { + get { - get + if (_subscriptionClient.IsClosedOrClosing) { - if (_subscriptionClient.IsClosedOrClosing) - { - _subscriptionClient = new SubscriptionClient(_serviceBusConnectionStringBuilder, _subscriptionClientName); - } - return _subscriptionClient; + _subscriptionClient = new SubscriptionClient(_serviceBusConnectionStringBuilder, _subscriptionClientName); } + return _subscriptionClient; } + } - public ServiceBusConnectionStringBuilder ServiceBusConnectionStringBuilder => _serviceBusConnectionStringBuilder; + public ServiceBusConnectionStringBuilder ServiceBusConnectionStringBuilder => _serviceBusConnectionStringBuilder; - public ITopicClient CreateModel() + public ITopicClient CreateModel() + { + if (_topicClient.IsClosedOrClosing) { - if (_topicClient.IsClosedOrClosing) - { - _topicClient = new TopicClient(_serviceBusConnectionStringBuilder, RetryPolicy.Default); - } - - return _topicClient; + _topicClient = new TopicClient(_serviceBusConnectionStringBuilder, RetryPolicy.Default); } - public void Dispose() - { - if (_disposed) return; + return _topicClient; + } - _disposed = true; - } + public void Dispose() + { + if (_disposed) return; + + _disposed = true; } } diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs index 84229b238..fcf28640c 100644 --- a/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs @@ -1,203 +1,191 @@ -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; + +public class EventBusServiceBus : IEventBus { - using Autofac; - using Microsoft.Azure.ServiceBus; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; - using Microsoft.Extensions.Logging; - using System; - using System.Text; - using System.Text.Json; - using System.Threading.Tasks; - - public class EventBusServiceBus : IEventBus + private readonly IServiceBusPersisterConnection _serviceBusPersisterConnection; + private readonly ILogger _logger; + private readonly IEventBusSubscriptionsManager _subsManager; + private readonly ILifetimeScope _autofac; + private readonly string AUTOFAC_SCOPE_NAME = "eshop_event_bus"; + private const string INTEGRATION_EVENT_SUFFIX = "IntegrationEvent"; + + public EventBusServiceBus(IServiceBusPersisterConnection serviceBusPersisterConnection, + ILogger logger, IEventBusSubscriptionsManager subsManager, ILifetimeScope autofac) { - private readonly IServiceBusPersisterConnection _serviceBusPersisterConnection; - private readonly ILogger _logger; - private readonly IEventBusSubscriptionsManager _subsManager; - private readonly ILifetimeScope _autofac; - private readonly string AUTOFAC_SCOPE_NAME = "eshop_event_bus"; - private const string INTEGRATION_EVENT_SUFFIX = "IntegrationEvent"; - - public EventBusServiceBus(IServiceBusPersisterConnection serviceBusPersisterConnection, - ILogger logger, IEventBusSubscriptionsManager subsManager, ILifetimeScope autofac) - { - _serviceBusPersisterConnection = serviceBusPersisterConnection; - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); - _autofac = autofac; - - RemoveDefaultRule(); - RegisterSubscriptionClientMessageHandler(); - } - - public void Publish(IntegrationEvent @event) - { - var eventName = @event.GetType().Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); - var jsonMessage = JsonSerializer.Serialize(@event); - var body = Encoding.UTF8.GetBytes(jsonMessage); - - var message = new Message - { - MessageId = Guid.NewGuid().ToString(), - Body = body, - Label = eventName, - }; - - _serviceBusPersisterConnection.TopicClient.SendAsync(message) - .GetAwaiter() - .GetResult(); - } + _serviceBusPersisterConnection = serviceBusPersisterConnection; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); + _autofac = autofac; - public void SubscribeDynamic(string eventName) - where TH : IDynamicIntegrationEventHandler - { - _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).Name); + RemoveDefaultRule(); + RegisterSubscriptionClientMessageHandler(); + } - _subsManager.AddDynamicSubscription(eventName); - } + public void Publish(IntegrationEvent @event) + { + var eventName = @event.GetType().Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); + var jsonMessage = JsonSerializer.Serialize(@event); + var body = Encoding.UTF8.GetBytes(jsonMessage); - public void Subscribe() - where T : IntegrationEvent - where TH : IIntegrationEventHandler + var message = new Message { - var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); + MessageId = Guid.NewGuid().ToString(), + Body = body, + Label = eventName, + }; + + _serviceBusPersisterConnection.TopicClient.SendAsync(message) + .GetAwaiter() + .GetResult(); + } - var containsKey = _subsManager.HasSubscriptionsForEvent(); - if (!containsKey) - { - try - { - _serviceBusPersisterConnection.SubscriptionClient.AddRuleAsync(new RuleDescription - { - Filter = new CorrelationFilter { Label = eventName }, - Name = eventName - }).GetAwaiter().GetResult(); - } - catch (ServiceBusException) - { - _logger.LogWarning("The messaging entity {eventName} already exists.", eventName); - } - } + public void SubscribeDynamic(string eventName) + where TH : IDynamicIntegrationEventHandler + { + _logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).Name); - _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).Name); + _subsManager.AddDynamicSubscription(eventName); + } - _subsManager.AddSubscription(); - } + public void Subscribe() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); - public void Unsubscribe() - where T : IntegrationEvent - where TH : IIntegrationEventHandler + var containsKey = _subsManager.HasSubscriptionsForEvent(); + if (!containsKey) { - var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); - try { - _serviceBusPersisterConnection - .SubscriptionClient - .RemoveRuleAsync(eventName) - .GetAwaiter() - .GetResult(); + _serviceBusPersisterConnection.SubscriptionClient.AddRuleAsync(new RuleDescription + { + Filter = new CorrelationFilter { Label = eventName }, + Name = eventName + }).GetAwaiter().GetResult(); } - catch (MessagingEntityNotFoundException) + catch (ServiceBusException) { - _logger.LogWarning("The messaging entity {eventName} Could not be found.", eventName); + _logger.LogWarning("The messaging entity {eventName} already exists.", eventName); } + } - _logger.LogInformation("Unsubscribing from event {EventName}", eventName); + _logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).Name); - _subsManager.RemoveSubscription(); - } + _subsManager.AddSubscription(); + } - public void UnsubscribeDynamic(string eventName) - where TH : IDynamicIntegrationEventHandler - { - _logger.LogInformation("Unsubscribing from dynamic event {EventName}", eventName); + public void Unsubscribe() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = typeof(T).Name.Replace(INTEGRATION_EVENT_SUFFIX, ""); - _subsManager.RemoveDynamicSubscription(eventName); + try + { + _serviceBusPersisterConnection + .SubscriptionClient + .RemoveRuleAsync(eventName) + .GetAwaiter() + .GetResult(); } - - public void Dispose() + catch (MessagingEntityNotFoundException) { - _subsManager.Clear(); + _logger.LogWarning("The messaging entity {eventName} Could not be found.", eventName); } - private void RegisterSubscriptionClientMessageHandler() - { - _serviceBusPersisterConnection.SubscriptionClient.RegisterMessageHandler( - async (message, token) => - { - var eventName = $"{message.Label}{INTEGRATION_EVENT_SUFFIX}"; - var messageData = Encoding.UTF8.GetString(message.Body); + _logger.LogInformation("Unsubscribing from event {EventName}", eventName); - // Complete the message so that it is not received again. - if (await ProcessEvent(eventName, messageData)) - { - await _serviceBusPersisterConnection.SubscriptionClient.CompleteAsync(message.SystemProperties.LockToken); - } - }, - new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 10, AutoComplete = false }); - } + _subsManager.RemoveSubscription(); + } - private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs) - { - var ex = exceptionReceivedEventArgs.Exception; - var context = exceptionReceivedEventArgs.ExceptionReceivedContext; + public void UnsubscribeDynamic(string eventName) + where TH : IDynamicIntegrationEventHandler + { + _logger.LogInformation("Unsubscribing from dynamic event {EventName}", eventName); - _logger.LogError(ex, "ERROR handling message: {ExceptionMessage} - Context: {@ExceptionContext}", ex.Message, context); + _subsManager.RemoveDynamicSubscription(eventName); + } - return Task.CompletedTask; - } + public void Dispose() + { + _subsManager.Clear(); + } + + private void RegisterSubscriptionClientMessageHandler() + { + _serviceBusPersisterConnection.SubscriptionClient.RegisterMessageHandler( + async (message, token) => + { + var eventName = $"{message.Label}{INTEGRATION_EVENT_SUFFIX}"; + var messageData = Encoding.UTF8.GetString(message.Body); + + // Complete the message so that it is not received again. + if (await ProcessEventAsync(eventName, messageData)) + { + await _serviceBusPersisterConnection.SubscriptionClient.CompleteAsync(message.SystemProperties.LockToken); + } + }, + new MessageHandlerOptions(ExceptionReceivedHandlerAsync) { MaxConcurrentCalls = 10, AutoComplete = false }); + } - private async Task ProcessEvent(string eventName, string message) + private Task ExceptionReceivedHandlerAsync(ExceptionReceivedEventArgs exceptionReceivedEventArgs) + { + var ex = exceptionReceivedEventArgs.Exception; + var context = exceptionReceivedEventArgs.ExceptionReceivedContext; + + _logger.LogError(ex, "ERROR handling message: {ExceptionMessage} - Context: {@ExceptionContext}", ex.Message, context); + + return Task.CompletedTask; + } + + private async Task ProcessEventAsync(string eventName, string message) + { + var processed = false; + if (_subsManager.HasSubscriptionsForEvent(eventName)) { - var processed = false; - if (_subsManager.HasSubscriptionsForEvent(eventName)) + using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) { - using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) + var subscriptions = _subsManager.GetHandlersForEvent(eventName); + foreach (var subscription in subscriptions) { - var subscriptions = _subsManager.GetHandlersForEvent(eventName); - foreach (var subscription in subscriptions) + if (subscription.IsDynamic) { - if (subscription.IsDynamic) - { - var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler; - if (handler == null) continue; + var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler; + if (handler == null) continue; - using dynamic eventData = JsonDocument.Parse(message); - await handler.Handle(eventData); - } - else - { - var handler = scope.ResolveOptional(subscription.HandlerType); - if (handler == null) continue; - var eventType = _subsManager.GetEventTypeByName(eventName); - var integrationEvent = JsonSerializer.Deserialize(message, eventType); - var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); - await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); - } + using dynamic eventData = JsonDocument.Parse(message); + await handler.Handle(eventData); + } + else + { + var handler = scope.ResolveOptional(subscription.HandlerType); + if (handler == null) continue; + var eventType = _subsManager.GetEventTypeByName(eventName); + var integrationEvent = JsonSerializer.Deserialize(message, eventType); + var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); + await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent }); } } - processed = true; } - return processed; + processed = true; } + return processed; + } - private void RemoveDefaultRule() + private void RemoveDefaultRule() + { + try { - try - { - _serviceBusPersisterConnection - .SubscriptionClient - .RemoveRuleAsync(RuleDescription.DefaultRuleName) - .GetAwaiter() - .GetResult(); - } - catch (MessagingEntityNotFoundException) - { - _logger.LogWarning("The messaging entity {DefaultRuleName} Could not be found.", RuleDescription.DefaultRuleName); - } + _serviceBusPersisterConnection + .SubscriptionClient + .RemoveRuleAsync(RuleDescription.DefaultRuleName) + .GetAwaiter() + .GetResult(); + } + catch (MessagingEntityNotFoundException) + { + _logger.LogWarning("The messaging entity {DefaultRuleName} Could not be found.", RuleDescription.DefaultRuleName); } } } diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/GlobalUsings.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/GlobalUsings.cs new file mode 100644 index 000000000..c4f7ab020 --- /dev/null +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/GlobalUsings.cs @@ -0,0 +1,22 @@ +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using static Microsoft.eShopOnContainers.BuildingBlocks.EventBus.InMemoryEventBusSubscriptionsManager; +global using System.Collections.Generic; +global using System.Linq; +global using System.Text.Json.Serialization; +global using System.Threading.Tasks; +global using System; +global using Microsoft.Azure.ServiceBus; +global using Autofac; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +global using Microsoft.Extensions.Logging; +global using System.Text; +global using System.Text.Json; + + + + + + + + diff --git a/src/BuildingBlocks/EventBus/EventBusServiceBus/IServiceBusPersisterConnection.cs b/src/BuildingBlocks/EventBus/EventBusServiceBus/IServiceBusPersisterConnection.cs index 8863db62e..c02e957d4 100644 --- a/src/BuildingBlocks/EventBus/EventBusServiceBus/IServiceBusPersisterConnection.cs +++ b/src/BuildingBlocks/EventBus/EventBusServiceBus/IServiceBusPersisterConnection.cs @@ -1,11 +1,7 @@ -namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus -{ - using Microsoft.Azure.ServiceBus; - using System; +namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; - public interface IServiceBusPersisterConnection : IDisposable - { - ITopicClient TopicClient { get; } - ISubscriptionClient SubscriptionClient { get; } - } -} \ No newline at end of file +public interface IServiceBusPersisterConnection : IDisposable +{ + ITopicClient TopicClient { get; } + ISubscriptionClient SubscriptionClient { get; } +} diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs index 731ba17e8..caf305bab 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/EventStateEnum.cs @@ -1,10 +1,10 @@ -namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; + +public enum EventStateEnum { - public enum EventStateEnum - { - NotPublished = 0, - InProgress = 1, - Published = 2, - PublishedFailed = 3 - } + NotPublished = 0, + InProgress = 1, + Published = 2, + PublishedFailed = 3 } + diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/GlobalUsings.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/GlobalUsings.cs new file mode 100644 index 000000000..866b0cea6 --- /dev/null +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/GlobalUsings.cs @@ -0,0 +1,12 @@ +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.Metadata.Builders; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using System; +global using System.Text.Json; +global using System.ComponentModel.DataAnnotations.Schema; +global using System.Linq; +global using System.Threading.Tasks; +global using Microsoft.EntityFrameworkCore.Storage; +global using System.Collections.Generic; +global using System.Data.Common; +global using System.Reflection; diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs index b51705538..1b59a4191 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogContext.cs @@ -1,45 +1,41 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF +public class IntegrationEventLogContext : DbContext { - public class IntegrationEventLogContext : DbContext + public IntegrationEventLogContext(DbContextOptions options) : base(options) { - public IntegrationEventLogContext(DbContextOptions options) : base(options) - { - } + } - public DbSet IntegrationEventLogs { get; set; } + public DbSet IntegrationEventLogs { get; set; } - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity(ConfigureIntegrationEventLogEntry); - } + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity(ConfigureIntegrationEventLogEntry); + } - void ConfigureIntegrationEventLogEntry(EntityTypeBuilder builder) - { - builder.ToTable("IntegrationEventLog"); + void ConfigureIntegrationEventLogEntry(EntityTypeBuilder builder) + { + builder.ToTable("IntegrationEventLog"); - builder.HasKey(e => e.EventId); + builder.HasKey(e => e.EventId); - builder.Property(e => e.EventId) - .IsRequired(); + builder.Property(e => e.EventId) + .IsRequired(); - builder.Property(e => e.Content) - .IsRequired(); + builder.Property(e => e.Content) + .IsRequired(); - builder.Property(e => e.CreationTime) - .IsRequired(); + builder.Property(e => e.CreationTime) + .IsRequired(); - builder.Property(e => e.State) - .IsRequired(); + builder.Property(e => e.State) + .IsRequired(); - builder.Property(e => e.TimesSent) - .IsRequired(); + builder.Property(e => e.TimesSent) + .IsRequired(); - builder.Property(e => e.EventTypeName) - .IsRequired(); + builder.Property(e => e.EventTypeName) + .IsRequired(); - } } } diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj index abe319465..4d14c50a5 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj @@ -6,13 +6,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs index b3109d214..90826f22e 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs @@ -1,43 +1,36 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System; -using System.Text.Json; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF +public class IntegrationEventLogEntry { - public class IntegrationEventLogEntry + private IntegrationEventLogEntry() { } + public IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId) { - private IntegrationEventLogEntry() { } - public IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId) + EventId = @event.Id; + CreationTime = @event.CreationDate; + EventTypeName = @event.GetType().FullName; + Content = JsonSerializer.Serialize(@event, @event.GetType(), new JsonSerializerOptions { - EventId = @event.Id; - CreationTime = @event.CreationDate; - EventTypeName = @event.GetType().FullName; - Content = JsonSerializer.Serialize(@event, @event.GetType(), new JsonSerializerOptions - { - WriteIndented = true - }); - State = EventStateEnum.NotPublished; - TimesSent = 0; - TransactionId = transactionId.ToString(); - } - public Guid EventId { get; private set; } - public string EventTypeName { get; private set; } - [NotMapped] - public string EventTypeShortName => EventTypeName.Split('.')?.Last(); - [NotMapped] - public IntegrationEvent IntegrationEvent { get; private set; } - public EventStateEnum State { get; set; } - public int TimesSent { get; set; } - public DateTime CreationTime { get; private set; } - public string Content { get; private set; } - public string TransactionId { get; private set; } + WriteIndented = true + }); + State = EventStateEnum.NotPublished; + TimesSent = 0; + TransactionId = transactionId.ToString(); + } + public Guid EventId { get; private set; } + public string EventTypeName { get; private set; } + [NotMapped] + public string EventTypeShortName => EventTypeName.Split('.')?.Last(); + [NotMapped] + public IntegrationEvent IntegrationEvent { get; private set; } + public EventStateEnum State { get; set; } + public int TimesSent { get; set; } + public DateTime CreationTime { get; private set; } + public string Content { get; private set; } + public string TransactionId { get; private set; } - public IntegrationEventLogEntry DeserializeJsonContent(Type type) - { - IntegrationEvent = JsonSerializer.Deserialize(Content, type, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) as IntegrationEvent; - return this; - } + public IntegrationEventLogEntry DeserializeJsonContent(Type type) + { + IntegrationEvent = JsonSerializer.Deserialize(Content, type, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) as IntegrationEvent; + return this; } } diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs index 93fd11143..44e03b414 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IIntegrationEventLogService.cs @@ -1,17 +1,10 @@ -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; -namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services +public interface IIntegrationEventLogService { - public interface IIntegrationEventLogService - { - Task> RetrieveEventLogsPendingToPublishAsync(Guid transactionId); - Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction); - Task MarkEventAsPublishedAsync(Guid eventId); - Task MarkEventAsInProgressAsync(Guid eventId); - Task MarkEventAsFailedAsync(Guid eventId); - } + Task> RetrieveEventLogsPendingToPublishAsync(Guid transactionId); + Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction); + Task MarkEventAsPublishedAsync(Guid eventId); + Task MarkEventAsInProgressAsync(Guid eventId); + Task MarkEventAsFailedAsync(Guid eventId); } diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs index 12957479b..472fba6e9 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Services/IntegrationEventLogService.cs @@ -1,110 +1,99 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services -{ - public class IntegrationEventLogService : IIntegrationEventLogService, IDisposable - { - private readonly IntegrationEventLogContext _integrationEventLogContext; - private readonly DbConnection _dbConnection; - private readonly List _eventTypes; - private volatile bool disposedValue; +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; - public IntegrationEventLogService(DbConnection dbConnection) - { - _dbConnection = dbConnection ?? throw new ArgumentNullException(nameof(dbConnection)); - _integrationEventLogContext = new IntegrationEventLogContext( - new DbContextOptionsBuilder() - .UseSqlServer(_dbConnection) - .Options); - - _eventTypes = Assembly.Load(Assembly.GetEntryAssembly().FullName) - .GetTypes() - .Where(t => t.Name.EndsWith(nameof(IntegrationEvent))) - .ToList(); - } +public class IntegrationEventLogService : IIntegrationEventLogService, IDisposable +{ + private readonly IntegrationEventLogContext _integrationEventLogContext; + private readonly DbConnection _dbConnection; + private readonly List _eventTypes; + private volatile bool _disposedValue; - public async Task> RetrieveEventLogsPendingToPublishAsync(Guid transactionId) - { - var tid = transactionId.ToString(); + public IntegrationEventLogService(DbConnection dbConnection) + { + _dbConnection = dbConnection ?? throw new ArgumentNullException(nameof(dbConnection)); + _integrationEventLogContext = new IntegrationEventLogContext( + new DbContextOptionsBuilder() + .UseSqlServer(_dbConnection) + .Options); + + _eventTypes = Assembly.Load(Assembly.GetEntryAssembly().FullName) + .GetTypes() + .Where(t => t.Name.EndsWith(nameof(IntegrationEvent))) + .ToList(); + } - var result = await _integrationEventLogContext.IntegrationEventLogs - .Where(e => e.TransactionId == tid && e.State == EventStateEnum.NotPublished).ToListAsync(); + public async Task> RetrieveEventLogsPendingToPublishAsync(Guid transactionId) + { + var tid = transactionId.ToString(); - if (result != null && result.Any()) - { - return result.OrderBy(o => o.CreationTime) - .Select(e => e.DeserializeJsonContent(_eventTypes.Find(t => t.Name == e.EventTypeShortName))); - } + var result = await _integrationEventLogContext.IntegrationEventLogs + .Where(e => e.TransactionId == tid && e.State == EventStateEnum.NotPublished).ToListAsync(); - return new List(); + if (result != null && result.Any()) + { + return result.OrderBy(o => o.CreationTime) + .Select(e => e.DeserializeJsonContent(_eventTypes.Find(t => t.Name == e.EventTypeShortName))); } - public Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction) - { - if (transaction == null) throw new ArgumentNullException(nameof(transaction)); + return new List(); + } - var eventLogEntry = new IntegrationEventLogEntry(@event, transaction.TransactionId); + public Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction) + { + if (transaction == null) throw new ArgumentNullException(nameof(transaction)); - _integrationEventLogContext.Database.UseTransaction(transaction.GetDbTransaction()); - _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); + var eventLogEntry = new IntegrationEventLogEntry(@event, transaction.TransactionId); - return _integrationEventLogContext.SaveChangesAsync(); - } + _integrationEventLogContext.Database.UseTransaction(transaction.GetDbTransaction()); + _integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry); - public Task MarkEventAsPublishedAsync(Guid eventId) - { - return UpdateEventStatus(eventId, EventStateEnum.Published); - } + return _integrationEventLogContext.SaveChangesAsync(); + } - public Task MarkEventAsInProgressAsync(Guid eventId) - { - return UpdateEventStatus(eventId, EventStateEnum.InProgress); - } + public Task MarkEventAsPublishedAsync(Guid eventId) + { + return UpdateEventStatus(eventId, EventStateEnum.Published); + } - public Task MarkEventAsFailedAsync(Guid eventId) - { - return UpdateEventStatus(eventId, EventStateEnum.PublishedFailed); - } + public Task MarkEventAsInProgressAsync(Guid eventId) + { + return UpdateEventStatus(eventId, EventStateEnum.InProgress); + } - private Task UpdateEventStatus(Guid eventId, EventStateEnum status) - { - var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == eventId); - eventLogEntry.State = status; + public Task MarkEventAsFailedAsync(Guid eventId) + { + return UpdateEventStatus(eventId, EventStateEnum.PublishedFailed); + } + + private Task UpdateEventStatus(Guid eventId, EventStateEnum status) + { + var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == eventId); + eventLogEntry.State = status; - if (status == EventStateEnum.InProgress) - eventLogEntry.TimesSent++; + if (status == EventStateEnum.InProgress) + eventLogEntry.TimesSent++; - _integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry); + _integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry); - return _integrationEventLogContext.SaveChangesAsync(); - } + return _integrationEventLogContext.SaveChangesAsync(); + } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) { - if (!disposedValue) + if (disposing) { - if (disposing) - { - _integrationEventLogContext?.Dispose(); - } + _integrationEventLogContext?.Dispose(); + } - disposedValue = true; - } + _disposedValue = true; } + } - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); } } diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs index 8dbb070d3..23022729b 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/Utilities/ResilientTransaction.cs @@ -1,31 +1,25 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; -namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities +public class ResilientTransaction { - public class ResilientTransaction - { - private DbContext _context; - private ResilientTransaction(DbContext context) => - _context = context ?? throw new ArgumentNullException(nameof(context)); + private DbContext _context; + private ResilientTransaction(DbContext context) => + _context = context ?? throw new ArgumentNullException(nameof(context)); - public static ResilientTransaction New(DbContext context) => - new ResilientTransaction(context); + public static ResilientTransaction New(DbContext context) => new(context); - public async Task ExecuteAsync(Func action) + public async Task ExecuteAsync(Func action) + { + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): + //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + var strategy = _context.Database.CreateExecutionStrategy(); + await strategy.ExecuteAsync(async () => { - //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): - //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - var strategy = _context.Database.CreateExecutionStrategy(); - await strategy.ExecuteAsync(async () => + using (var transaction = _context.Database.BeginTransaction()) { - using (var transaction = _context.Database.BeginTransaction()) - { - await action(); - transaction.Commit(); - } - }); - } + await action(); + transaction.Commit(); + } + }); } } diff --git a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs index c2a50467d..60fbdb655 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs @@ -1,13 +1,15 @@ namespace Basket.API.Infrastructure.Middlewares; +using Microsoft.Extensions.Logging; + public class FailingMiddleware { private readonly RequestDelegate _next; private bool _mustFail; private readonly FailingOptions _options; - private readonly Microsoft.Extensions.Logging.ILogger _logger; + private readonly ILogger _logger; - public FailingMiddleware(RequestDelegate next, Microsoft.Extensions.Logging.ILogger logger, FailingOptions options) + public FailingMiddleware(RequestDelegate next, ILogger logger, FailingOptions options) { _next = next; _options = options; diff --git a/src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs b/src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs index d83f81996..6343dbe68 100644 --- a/src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs +++ b/src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs @@ -1,31 +1,26 @@ -using Microsoft.AspNetCore.Http; -using System.Security.Claims; -using System.Threading.Tasks; +namespace Basket.FunctionalTests.Base; -namespace Basket.FunctionalTests.Base +class AutoAuthorizeMiddleware { - class AutoAuthorizeMiddleware - { - public const string IDENTITY_ID = "9e3163b9-1ae6-4652-9dc6-7898ab7b7a00"; + public const string IDENTITY_ID = "9e3163b9-1ae6-4652-9dc6-7898ab7b7a00"; - private readonly RequestDelegate _next; + private readonly RequestDelegate _next; - public AutoAuthorizeMiddleware(RequestDelegate rd) - { - _next = rd; - } + public AutoAuthorizeMiddleware(RequestDelegate rd) + { + _next = rd; + } - public async Task Invoke(HttpContext httpContext) - { - var identity = new ClaimsIdentity("cookies"); + public async Task Invoke(HttpContext httpContext) + { + var identity = new ClaimsIdentity("cookies"); - identity.AddClaim(new Claim("sub", IDENTITY_ID)); - identity.AddClaim(new Claim("unique_name", IDENTITY_ID)); - identity.AddClaim(new Claim(ClaimTypes.Name, IDENTITY_ID)); + identity.AddClaim(new Claim("sub", IDENTITY_ID)); + identity.AddClaim(new Claim("unique_name", IDENTITY_ID)); + identity.AddClaim(new Claim(ClaimTypes.Name, IDENTITY_ID)); - httpContext.User.AddIdentity(identity); + httpContext.User.AddIdentity(identity); - await _next.Invoke(httpContext); - } + await _next.Invoke(httpContext); } } diff --git a/src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs b/src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs index b5a62ce9d..b50b58bbd 100644 --- a/src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs +++ b/src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs @@ -1,43 +1,36 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Configuration; -using System.IO; -using System.Reflection; +namespace Basket.FunctionalTests.Base; -namespace Basket.FunctionalTests.Base +public class BasketScenarioBase { - public class BasketScenarioBase - { - private const string ApiUrlBase = "api/v1/basket"; + private const string ApiUrlBase = "api/v1/basket"; - public TestServer CreateServer() - { - var path = Assembly.GetAssembly(typeof(BasketScenarioBase)) - .Location; + public TestServer CreateServer() + { + var path = Assembly.GetAssembly(typeof(BasketScenarioBase)) + .Location; - var hostBuilder = new WebHostBuilder() - .UseContentRoot(Path.GetDirectoryName(path)) - .ConfigureAppConfiguration(cb => - { - cb.AddJsonFile("appsettings.json", optional: false) - .AddEnvironmentVariables(); - }).UseStartup(); + var hostBuilder = new WebHostBuilder() + .UseContentRoot(Path.GetDirectoryName(path)) + .ConfigureAppConfiguration(cb => + { + cb.AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + }).UseStartup(); - return new TestServer(hostBuilder); - } + return new TestServer(hostBuilder); + } - public static class Get + public static class Get + { + public static string GetBasket(int id) { - public static string GetBasket(int id) - { - return $"{ApiUrlBase}/{id}"; - } + return $"{ApiUrlBase}/{id}"; } + } - public static class Post - { - public static string Basket = $"{ApiUrlBase}/"; - public static string CheckoutOrder = $"{ApiUrlBase}/checkout"; - } + public static class Post + { + public static string Basket = $"{ApiUrlBase}/"; + public static string CheckoutOrder = $"{ApiUrlBase}/checkout"; } } diff --git a/src/Services/Basket/Basket.FunctionalTests/Base/BasketTestStartup.cs b/src/Services/Basket/Basket.FunctionalTests/Base/BasketTestStartup.cs index d0237407d..b19d825cd 100644 --- a/src/Services/Basket/Basket.FunctionalTests/Base/BasketTestStartup.cs +++ b/src/Services/Basket/Basket.FunctionalTests/Base/BasketTestStartup.cs @@ -1,9 +1,4 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.eShopOnContainers.Services.Basket.API; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System; + namespace Basket.FunctionalTests.Base { diff --git a/src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs b/src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs index f2da0b13e..45910df14 100644 --- a/src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs +++ b/src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs @@ -1,18 +1,13 @@ -using Microsoft.AspNetCore.TestHost; -using System; -using System.Net.Http; +namespace Basket.FunctionalTests.Base; -namespace Basket.FunctionalTests.Base +static class HttpClientExtensions { - static class HttpClientExtensions + public static HttpClient CreateIdempotentClient(this TestServer server) { - public static HttpClient CreateIdempotentClient(this TestServer server) - { - var client = server.CreateClient(); + var client = server.CreateClient(); - client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString()); + client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString()); - return client; - } + return client; } } diff --git a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj index 9749404b9..d9276f175 100644 --- a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj +++ b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs index 9474ca461..7032dcbe2 100644 --- a/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs +++ b/src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs @@ -1,95 +1,85 @@ -using Basket.FunctionalTests.Base; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using System; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Xunit; +namespace Basket.FunctionalTests; -namespace Basket.FunctionalTests +public class BasketScenarios + : BasketScenarioBase { - public class BasketScenarios - : BasketScenarioBase + [Fact] + public async Task Post_basket_and_response_ok_status_code() { - [Fact] - public async Task Post_basket_and_response_ok_status_code() + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var content = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); - var response = await server.CreateClient() - .PostAsync(Post.Basket, content); + var content = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); + var response = await server.CreateClient() + .PostAsync(Post.Basket, content); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_basket_and_response_ok_status_code() + [Fact] + public async Task Get_basket_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.GetBasket(1)); + var response = await server.CreateClient() + .GetAsync(Get.GetBasket(1)); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Send_Checkout_basket_and_response_ok_status_code() + [Fact] + public async Task Send_Checkout_basket_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var contentBasket = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); + var contentBasket = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json"); - await server.CreateClient() - .PostAsync(Post.Basket, contentBasket); + await server.CreateClient() + .PostAsync(Post.Basket, contentBasket); - var contentCheckout = new StringContent(BuildCheckout(), UTF8Encoding.UTF8, "application/json"); + var contentCheckout = new StringContent(BuildCheckout(), UTF8Encoding.UTF8, "application/json"); - var response = await server.CreateIdempotentClient() - .PostAsync(Post.CheckoutOrder, contentCheckout); + var response = await server.CreateIdempotentClient() + .PostAsync(Post.CheckoutOrder, contentCheckout); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - string BuildBasket() - { - var order = new CustomerBasket(AutoAuthorizeMiddleware.IDENTITY_ID); + string BuildBasket() + { + var order = new CustomerBasket(AutoAuthorizeMiddleware.IDENTITY_ID); - order.Items.Add(new BasketItem - { - ProductId = 1, - ProductName = ".NET Bot Black Hoodie", - UnitPrice = 10, - Quantity = 1 - }); + order.Items.Add(new BasketItem + { + ProductId = 1, + ProductName = ".NET Bot Black Hoodie", + UnitPrice = 10, + Quantity = 1 + }); - return JsonSerializer.Serialize(order); - } + return JsonSerializer.Serialize(order); + } - string BuildCheckout() + string BuildCheckout() + { + var checkoutBasket = new { - var checkoutBasket = new - { - City = "city", - Street = "street", - State = "state", - Country = "coutry", - ZipCode = "zipcode", - CardNumber = "1234567890123456", - CardHolderName = "CardHolderName", - CardExpiration = DateTime.UtcNow.AddDays(1), - CardSecurityNumber = "123", - CardTypeId = 1, - Buyer = "Buyer", - RequestId = Guid.NewGuid() - }; + City = "city", + Street = "street", + State = "state", + Country = "coutry", + ZipCode = "zipcode", + CardNumber = "1234567890123456", + CardHolderName = "CardHolderName", + CardExpiration = DateTime.UtcNow.AddDays(1), + CardSecurityNumber = "123", + CardTypeId = 1, + Buyer = "Buyer", + RequestId = Guid.NewGuid() + }; - return JsonSerializer.Serialize(checkoutBasket); - } + return JsonSerializer.Serialize(checkoutBasket); } } diff --git a/src/Services/Basket/Basket.FunctionalTests/GlobalUsings.cs b/src/Services/Basket/Basket.FunctionalTests/GlobalUsings.cs new file mode 100644 index 000000000..d3657c2d3 --- /dev/null +++ b/src/Services/Basket/Basket.FunctionalTests/GlobalUsings.cs @@ -0,0 +1,23 @@ +global using Basket.FunctionalTests.Base; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Routing; +global using Microsoft.AspNetCore.TestHost; +global using Microsoft.eShopOnContainers.Services.Basket.API.Infrastructure.Repositories; +global using Microsoft.eShopOnContainers.Services.Basket.API.Model; +global using Microsoft.eShopOnContainers.Services.Basket.API; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using StackExchange.Redis; +global using System.Collections.Generic; +global using System.IO; +global using System.Net.Http; +global using System.Reflection; +global using System.Security.Claims; +global using System.Text.Json; +global using System.Text; +global using System.Threading.Tasks; +global using System; +global using Xunit; diff --git a/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs b/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs index a13c1f6a2..e5d79c1e4 100644 --- a/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs +++ b/src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs @@ -1,12 +1,4 @@ -using Basket.FunctionalTests.Base; -using Microsoft.eShopOnContainers.Services.Basket.API.Infrastructure.Repositories; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using StackExchange.Redis; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; + namespace Basket.FunctionalTests { diff --git a/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs index 000183dec..89f1c2b33 100644 --- a/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs +++ b/src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs @@ -1,155 +1,140 @@ -using Basket.API.IntegrationEvents.Events; -using Basket.API.Model; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.Services.Basket.API.Controllers; +namespace UnitTest.Basket.Application; + using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.Extensions.Logging; -using Moq; -using System; -using System.Collections.Generic; -using System.Security.Claims; -using System.Threading.Tasks; -using Xunit; -using IBasketIdentityService = Microsoft.eShopOnContainers.Services.Basket.API.Services.IIdentityService; - -namespace UnitTest.Basket.Application + +public class BasketWebApiTest { - public class BasketWebApiTest + private readonly Mock _basketRepositoryMock; + private readonly Mock _identityServiceMock; + private readonly Mock _serviceBusMock; + private readonly Mock> _loggerMock; + + public BasketWebApiTest() { - private readonly Mock _basketRepositoryMock; - private readonly Mock _identityServiceMock; - private readonly Mock _serviceBusMock; - private readonly Mock> _loggerMock; + _basketRepositoryMock = new Mock(); + _identityServiceMock = new Mock(); + _serviceBusMock = new Mock(); + _loggerMock = new Mock>(); + } - public BasketWebApiTest() - { - _basketRepositoryMock = new Mock(); - _identityServiceMock = new Mock(); - _serviceBusMock = new Mock(); - _loggerMock = new Mock>(); - } - - [Fact] - public async Task Get_customer_basket_success() - { - //Arrange - var fakeCustomerId = "1"; - var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); + [Fact] + public async Task Get_customer_basket_success() + { + //Arrange + var fakeCustomerId = "1"; + var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); - _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) - .Returns(Task.FromResult(fakeCustomerBasket)); - _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) + .Returns(Task.FromResult(fakeCustomerBasket)); + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); - _serviceBusMock.Setup(x => x.Publish(It.IsAny())); + _serviceBusMock.Setup(x => x.Publish(It.IsAny())); - //Act - var basketController = new BasketController( - _loggerMock.Object, - _basketRepositoryMock.Object, - _identityServiceMock.Object, - _serviceBusMock.Object); + //Act + var basketController = new BasketController( + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); - var actionResult = await basketController.GetBasketByIdAsync(fakeCustomerId); + var actionResult = await basketController.GetBasketByIdAsync(fakeCustomerId); - //Assert - Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); - Assert.Equal((((ObjectResult)actionResult.Result).Value as CustomerBasket).BuyerId, fakeCustomerId); - } + //Assert + Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); + Assert.Equal((((ObjectResult)actionResult.Result).Value as CustomerBasket).BuyerId, fakeCustomerId); + } - [Fact] - public async Task Post_customer_basket_success() - { - //Arrange - var fakeCustomerId = "1"; - var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); + [Fact] + public async Task Post_customer_basket_success() + { + //Arrange + var fakeCustomerId = "1"; + var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); + + _basketRepositoryMock.Setup(x => x.UpdateBasketAsync(It.IsAny())) + .Returns(Task.FromResult(fakeCustomerBasket)); + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + _serviceBusMock.Setup(x => x.Publish(It.IsAny())); + + //Act + var basketController = new BasketController( + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); + + var actionResult = await basketController.UpdateBasketAsync(fakeCustomerBasket); + + //Assert + Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); + Assert.Equal((((ObjectResult)actionResult.Result).Value as CustomerBasket).BuyerId, fakeCustomerId); + } - _basketRepositoryMock.Setup(x => x.UpdateBasketAsync(It.IsAny())) - .Returns(Task.FromResult(fakeCustomerBasket)); - _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); - _serviceBusMock.Setup(x => x.Publish(It.IsAny())); + [Fact] + public async Task Doing_Checkout_Without_Basket_Should_Return_Bad_Request() + { + var fakeCustomerId = "2"; + _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) + .Returns(Task.FromResult((CustomerBasket)null)); + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + + //Act + var basketController = new BasketController( + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); + + var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as BadRequestResult; + Assert.NotNull(result); + } - //Act - var basketController = new BasketController( - _loggerMock.Object, - _basketRepositoryMock.Object, - _identityServiceMock.Object, - _serviceBusMock.Object); + [Fact] + public async Task Doing_Checkout_Wit_Basket_Should_Publish_UserCheckoutAccepted_Integration_Event() + { + var fakeCustomerId = "1"; + var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); - var actionResult = await basketController.UpdateBasketAsync(fakeCustomerBasket); + _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) + .Returns(Task.FromResult(fakeCustomerBasket)); - //Assert - Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); - Assert.Equal((((ObjectResult)actionResult.Result).Value as CustomerBasket).BuyerId, fakeCustomerId); - } + _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); - [Fact] - public async Task Doing_Checkout_Without_Basket_Should_Return_Bad_Request() - { - var fakeCustomerId = "2"; - _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) - .Returns(Task.FromResult((CustomerBasket)null)); - _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); - - //Act - var basketController = new BasketController( - _loggerMock.Object, - _basketRepositoryMock.Object, - _identityServiceMock.Object, - _serviceBusMock.Object); - - var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as BadRequestResult; - Assert.NotNull(result); - } - - [Fact] - public async Task Doing_Checkout_Wit_Basket_Should_Publish_UserCheckoutAccepted_Integration_Event() + var basketController = new BasketController( + _loggerMock.Object, + _basketRepositoryMock.Object, + _identityServiceMock.Object, + _serviceBusMock.Object); + + basketController.ControllerContext = new ControllerContext() { - var fakeCustomerId = "1"; - var fakeCustomerBasket = GetCustomerBasketFake(fakeCustomerId); + HttpContext = new DefaultHttpContext() + { + User = new ClaimsPrincipal( + new ClaimsIdentity(new Claim[] { + new Claim("sub", "testuser"), + new Claim("unique_name", "testuser"), + new Claim(ClaimTypes.Name, "testuser") + })) + } + }; - _basketRepositoryMock.Setup(x => x.GetBasketAsync(It.IsAny())) - .Returns(Task.FromResult(fakeCustomerBasket)); + //Act + var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as AcceptedResult; - _identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId); + _serviceBusMock.Verify(mock => mock.Publish(It.IsAny()), Times.Once); - var basketController = new BasketController( - _loggerMock.Object, - _basketRepositoryMock.Object, - _identityServiceMock.Object, - _serviceBusMock.Object); + Assert.NotNull(result); + } - basketController.ControllerContext = new ControllerContext() - { - HttpContext = new DefaultHttpContext() - { - User = new ClaimsPrincipal( - new ClaimsIdentity(new Claim[] { - new Claim("sub", "testuser"), - new Claim("unique_name", "testuser"), - new Claim(ClaimTypes.Name, "testuser") - })) - } - }; - - //Act - var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as AcceptedResult; - - _serviceBusMock.Verify(mock => mock.Publish(It.IsAny()), Times.Once); - - Assert.NotNull(result); - } - - private CustomerBasket GetCustomerBasketFake(string fakeCustomerId) + private CustomerBasket GetCustomerBasketFake(string fakeCustomerId) + { + return new CustomerBasket(fakeCustomerId) { - return new CustomerBasket(fakeCustomerId) + Items = new List() { - Items = new List() - { - new BasketItem() - } - }; - } + new BasketItem() + } + }; } } diff --git a/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs b/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs index bdbf8afd4..4231d6a9e 100644 --- a/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs +++ b/src/Services/Basket/Basket.UnitTests/Application/CartControllerTest.cs @@ -1,130 +1,117 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.WebMVC.Controllers; -using Microsoft.eShopOnContainers.WebMVC.Services; -using Microsoft.eShopOnContainers.WebMVC.ViewModels; -using Moq; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Xunit; -using BasketModel = Microsoft.eShopOnContainers.WebMVC.ViewModels.Basket; - -namespace UnitTest.Basket.Application +namespace UnitTest.Basket.Application; + +public class CartControllerTest { - public class CartControllerTest + private readonly Mock _catalogServiceMock; + private readonly Mock _basketServiceMock; + private readonly Mock> _identityParserMock; + private readonly Mock _contextMock; + + public CartControllerTest() { - private readonly Mock _catalogServiceMock; - private readonly Mock _basketServiceMock; - private readonly Mock> _identityParserMock; - private readonly Mock _contextMock; + _catalogServiceMock = new Mock(); + _basketServiceMock = new Mock(); + _identityParserMock = new Mock>(); + _contextMock = new Mock(); + } - public CartControllerTest() - { - _catalogServiceMock = new Mock(); - _basketServiceMock = new Mock(); - _identityParserMock = new Mock>(); - _contextMock = new Mock(); - } - - [Fact] - public async Task Post_cart_success() - { - //Arrange - var fakeBuyerId = "1"; - var action = string.Empty; - var fakeBasket = GetFakeBasket(fakeBuyerId); - var fakeQuantities = new Dictionary() - { - ["fakeProdA"] = 1, - ["fakeProdB"] = 2 - }; - - _basketServiceMock.Setup(x => x.SetQuantities(It.IsAny(), It.IsAny>())) - .Returns(Task.FromResult(fakeBasket)); - - _basketServiceMock.Setup(x => x.UpdateBasket(It.IsAny())) - .Returns(Task.FromResult(fakeBasket)); - - //Act - var cartController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); - cartController.ControllerContext.HttpContext = _contextMock.Object; - var actionResult = await cartController.Index(fakeQuantities, action); - - //Assert - var viewResult = Assert.IsType(actionResult); - } - - [Fact] - public async Task Post_cart_checkout_success() + [Fact] + public async Task Post_cart_success() + { + //Arrange + var fakeBuyerId = "1"; + var action = string.Empty; + var fakeBasket = GetFakeBasket(fakeBuyerId); + var fakeQuantities = new Dictionary() { - //Arrange - var fakeBuyerId = "1"; - var action = "[ Checkout ]"; - var fakeBasket = GetFakeBasket(fakeBuyerId); - var fakeQuantities = new Dictionary() - { - ["fakeProdA"] = 1, - ["fakeProdB"] = 2 - }; - - _basketServiceMock.Setup(x => x.SetQuantities(It.IsAny(), It.IsAny>())) - .Returns(Task.FromResult(fakeBasket)); - - _basketServiceMock.Setup(x => x.UpdateBasket(It.IsAny())) - .Returns(Task.FromResult(fakeBasket)); - - //Act - var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); - orderController.ControllerContext.HttpContext = _contextMock.Object; - var actionResult = await orderController.Index(fakeQuantities, action); - - //Assert - var redirectToActionResult = Assert.IsType(actionResult); - Assert.Equal("Order", redirectToActionResult.ControllerName); - Assert.Equal("Create", redirectToActionResult.ActionName); - } - - [Fact] - public async Task Add_to_cart_success() + ["fakeProdA"] = 1, + ["fakeProdB"] = 2 + }; + + _basketServiceMock.Setup(x => x.SetQuantities(It.IsAny(), It.IsAny>())) + .Returns(Task.FromResult(fakeBasket)); + + _basketServiceMock.Setup(x => x.UpdateBasket(It.IsAny())) + .Returns(Task.FromResult(fakeBasket)); + + //Act + var cartController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); + cartController.ControllerContext.HttpContext = _contextMock.Object; + var actionResult = await cartController.Index(fakeQuantities, action); + + //Assert + var viewResult = Assert.IsType(actionResult); + } + + [Fact] + public async Task Post_cart_checkout_success() + { + //Arrange + var fakeBuyerId = "1"; + var action = "[ Checkout ]"; + var fakeBasket = GetFakeBasket(fakeBuyerId); + var fakeQuantities = new Dictionary() { - //Arrange - var fakeCatalogItem = GetFakeCatalogItem(); + ["fakeProdA"] = 1, + ["fakeProdB"] = 2 + }; + + _basketServiceMock.Setup(x => x.SetQuantities(It.IsAny(), It.IsAny>())) + .Returns(Task.FromResult(fakeBasket)); + + _basketServiceMock.Setup(x => x.UpdateBasket(It.IsAny())) + .Returns(Task.FromResult(fakeBasket)); + + //Act + var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); + orderController.ControllerContext.HttpContext = _contextMock.Object; + var actionResult = await orderController.Index(fakeQuantities, action); + + //Assert + var redirectToActionResult = Assert.IsType(actionResult); + Assert.Equal("Order", redirectToActionResult.ControllerName); + Assert.Equal("Create", redirectToActionResult.ActionName); + } - _basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny(), It.IsAny())) - .Returns(Task.FromResult(1)); + [Fact] + public async Task Add_to_cart_success() + { + //Arrange + var fakeCatalogItem = GetFakeCatalogItem(); + + _basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(1)); - //Act - var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); - orderController.ControllerContext.HttpContext = _contextMock.Object; - var actionResult = await orderController.AddToCart(fakeCatalogItem); + //Act + var orderController = new CartController(_basketServiceMock.Object, _catalogServiceMock.Object, _identityParserMock.Object); + orderController.ControllerContext.HttpContext = _contextMock.Object; + var actionResult = await orderController.AddToCart(fakeCatalogItem); - //Assert - var redirectToActionResult = Assert.IsType(actionResult); - Assert.Equal("Catalog", redirectToActionResult.ControllerName); - Assert.Equal("Index", redirectToActionResult.ActionName); - } + //Assert + var redirectToActionResult = Assert.IsType(actionResult); + Assert.Equal("Catalog", redirectToActionResult.ControllerName); + Assert.Equal("Index", redirectToActionResult.ActionName); + } - private BasketModel GetFakeBasket(string buyerId) + private BasketModel GetFakeBasket(string buyerId) + { + return new BasketModel() { - return new BasketModel() - { - BuyerId = buyerId - }; - } + BuyerId = buyerId + }; + } - private CatalogItem GetFakeCatalogItem() + private CatalogItem GetFakeCatalogItem() + { + return new CatalogItem() { - return new CatalogItem() - { - Id = 1, - Name = "fakeName", - CatalogBrand = "fakeBrand", - CatalogType = "fakeType", - CatalogBrandId = 2, - CatalogTypeId = 5, - Price = 20 - }; - } + Id = 1, + Name = "fakeName", + CatalogBrand = "fakeBrand", + CatalogType = "fakeType", + CatalogBrandId = 2, + CatalogTypeId = 5, + Price = 20 + }; } } diff --git a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj index c3f42b5b1..e80bf7339 100644 --- a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj +++ b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/src/Services/Basket/Basket.UnitTests/GlobalUsings.cs b/src/Services/Basket/Basket.UnitTests/GlobalUsings.cs new file mode 100644 index 000000000..6e5443b74 --- /dev/null +++ b/src/Services/Basket/Basket.UnitTests/GlobalUsings.cs @@ -0,0 +1,19 @@ +global using Basket.API.IntegrationEvents.Events; +global using Basket.API.Model; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.Services.Basket.API.Controllers; +global using Microsoft.eShopOnContainers.Services.Basket.API.Model; +global using Microsoft.Extensions.Logging; +global using Moq; +global using System; +global using System.Collections.Generic; +global using System.Security.Claims; +global using System.Threading.Tasks; +global using Xunit; +global using IBasketIdentityService = Microsoft.eShopOnContainers.Services.Basket.API.Services.IIdentityService; +global using Microsoft.eShopOnContainers.WebMVC.Controllers; +global using Microsoft.eShopOnContainers.WebMVC.Services; +global using Microsoft.eShopOnContainers.WebMVC.ViewModels; +global using BasketModel = Microsoft.eShopOnContainers.WebMVC.ViewModels.Basket; diff --git a/src/Services/Catalog/Catalog.API/Catalog.API.csproj b/src/Services/Catalog/Catalog.API/Catalog.API.csproj index ddaf22de5..ecd334d69 100644 --- a/src/Services/Catalog/Catalog.API/Catalog.API.csproj +++ b/src/Services/Catalog/Catalog.API/Catalog.API.csproj @@ -42,30 +42,37 @@ - + + + - + - - - + - - - + + + - - - - - - + + + + + + + + + - - + + + + + + diff --git a/src/Services/Catalog/Catalog.API/CatalogSettings.cs b/src/Services/Catalog/Catalog.API/CatalogSettings.cs index 297c68914..2fba164a0 100644 --- a/src/Services/Catalog/Catalog.API/CatalogSettings.cs +++ b/src/Services/Catalog/Catalog.API/CatalogSettings.cs @@ -1,13 +1,12 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API +namespace Microsoft.eShopOnContainers.Services.Catalog.API; + +public class CatalogSettings { - public class CatalogSettings - { - public string PicBaseUrl { get; set; } + public string PicBaseUrl { get; set; } - public string EventBusConnection { get; set; } + public string EventBusConnection { get; set; } - public bool UseCustomizationData { get; set; } + public bool UseCustomizationData { get; set; } - public bool AzureStorageEnabled { get; set; } - } + public bool AzureStorageEnabled { get; set; } } diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index d58c06f66..4dd1143f6 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,315 +1,300 @@ -using Catalog.API.IntegrationEvents; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; -using Microsoft.eShopOnContainers.Services.Catalog.API.Model; -using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers; + +[Route("api/v1/[controller]")] +[ApiController] +public class CatalogController : ControllerBase { - [Route("api/v1/[controller]")] - [ApiController] - public class CatalogController : ControllerBase - { - private readonly CatalogContext _catalogContext; - private readonly CatalogSettings _settings; - private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; + private readonly CatalogContext _catalogContext; + private readonly CatalogSettings _settings; + private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; - public CatalogController(CatalogContext context, IOptionsSnapshot settings, ICatalogIntegrationEventService catalogIntegrationEventService) - { - _catalogContext = context ?? throw new ArgumentNullException(nameof(context)); - _catalogIntegrationEventService = catalogIntegrationEventService ?? throw new ArgumentNullException(nameof(catalogIntegrationEventService)); - _settings = settings.Value; + public CatalogController(CatalogContext context, IOptionsSnapshot settings, ICatalogIntegrationEventService catalogIntegrationEventService) + { + _catalogContext = context ?? throw new ArgumentNullException(nameof(context)); + _catalogIntegrationEventService = catalogIntegrationEventService ?? throw new ArgumentNullException(nameof(catalogIntegrationEventService)); + _settings = settings.Value; - context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - } + context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + } - // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] - [HttpGet] - [Route("items")] - [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public async Task ItemsAsync([FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0, string ids = null) + // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] + [HttpGet] + [Route("items")] + [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public async Task ItemsAsync([FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0, string ids = null) + { + if (!string.IsNullOrEmpty(ids)) { - if (!string.IsNullOrEmpty(ids)) - { - var items = await GetItemsByIdsAsync(ids); - - if (!items.Any()) - { - return BadRequest("ids value invalid. Must be comma-separated list of numbers"); - } + var items = await GetItemsByIdsAsync(ids); - return Ok(items); + if (!items.Any()) + { + return BadRequest("ids value invalid. Must be comma-separated list of numbers"); } - var totalItems = await _catalogContext.CatalogItems - .LongCountAsync(); - - var itemsOnPage = await _catalogContext.CatalogItems - .OrderBy(c => c.Name) - .Skip(pageSize * pageIndex) - .Take(pageSize) - .ToListAsync(); - - /* The "awesome" fix for testing Devspaces */ - - /* - foreach (var pr in itemsOnPage) { - pr.Name = "Awesome " + pr.Name; - } + return Ok(items); + } - */ + var totalItems = await _catalogContext.CatalogItems + .LongCountAsync(); - itemsOnPage = ChangeUriPlaceholder(itemsOnPage); + var itemsOnPage = await _catalogContext.CatalogItems + .OrderBy(c => c.Name) + .Skip(pageSize * pageIndex) + .Take(pageSize) + .ToListAsync(); - var model = new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); + /* The "awesome" fix for testing Devspaces */ - return Ok(model); + /* + foreach (var pr in itemsOnPage) { + pr.Name = "Awesome " + pr.Name; } - private async Task> GetItemsByIdsAsync(string ids) - { - var numIds = ids.Split(',').Select(id => (Ok: int.TryParse(id, out int x), Value: x)); + */ - if (!numIds.All(nid => nid.Ok)) - { - return new List(); - } + itemsOnPage = ChangeUriPlaceholder(itemsOnPage); - var idsToSelect = numIds - .Select(id => id.Value); + var model = new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); - var items = await _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToListAsync(); - - items = ChangeUriPlaceholder(items); + return Ok(model); + } - return items; - } + private async Task> GetItemsByIdsAsync(string ids) + { + var numIds = ids.Split(',').Select(id => (Ok: int.TryParse(id, out int x), Value: x)); - [HttpGet] - [Route("items/{id:int}")] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(typeof(CatalogItem), (int)HttpStatusCode.OK)] - public async Task> ItemByIdAsync(int id) + if (!numIds.All(nid => nid.Ok)) { - if (id <= 0) - { - return BadRequest(); - } + return new List(); + } - var item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(ci => ci.Id == id); + var idsToSelect = numIds + .Select(id => id.Value); - var baseUri = _settings.PicBaseUrl; - var azureStorageEnabled = _settings.AzureStorageEnabled; + var items = await _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToListAsync(); - item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); + items = ChangeUriPlaceholder(items); - if (item != null) - { - return item; - } + return items; + } - return NotFound(); + [HttpGet] + [Route("items/{id:int}")] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(typeof(CatalogItem), (int)HttpStatusCode.OK)] + public async Task> ItemByIdAsync(int id) + { + if (id <= 0) + { + return BadRequest(); } - // GET api/v1/[controller]/items/withname/samplename[?pageSize=3&pageIndex=10] - [HttpGet] - [Route("items/withname/{name:minlength(1)}")] - [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] - public async Task>> ItemsWithNameAsync(string name, [FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0) - { - var totalItems = await _catalogContext.CatalogItems - .Where(c => c.Name.StartsWith(name)) - .LongCountAsync(); + var item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(ci => ci.Id == id); - var itemsOnPage = await _catalogContext.CatalogItems - .Where(c => c.Name.StartsWith(name)) - .Skip(pageSize * pageIndex) - .Take(pageSize) - .ToListAsync(); + var baseUri = _settings.PicBaseUrl; + var azureStorageEnabled = _settings.AzureStorageEnabled; - itemsOnPage = ChangeUriPlaceholder(itemsOnPage); + item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); - return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); + if (item != null) + { + return item; } - // GET api/v1/[controller]/items/type/1/brand[?pageSize=3&pageIndex=10] - [HttpGet] - [Route("items/type/{catalogTypeId}/brand/{catalogBrandId:int?}")] - [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] - public async Task>> ItemsByTypeIdAndBrandIdAsync(int catalogTypeId, int? catalogBrandId, [FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0) - { - var root = (IQueryable)_catalogContext.CatalogItems; + return NotFound(); + } - root = root.Where(ci => ci.CatalogTypeId == catalogTypeId); + // GET api/v1/[controller]/items/withname/samplename[?pageSize=3&pageIndex=10] + [HttpGet] + [Route("items/withname/{name:minlength(1)}")] + [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] + public async Task>> ItemsWithNameAsync(string name, [FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0) + { + var totalItems = await _catalogContext.CatalogItems + .Where(c => c.Name.StartsWith(name)) + .LongCountAsync(); - if (catalogBrandId.HasValue) - { - root = root.Where(ci => ci.CatalogBrandId == catalogBrandId); - } + var itemsOnPage = await _catalogContext.CatalogItems + .Where(c => c.Name.StartsWith(name)) + .Skip(pageSize * pageIndex) + .Take(pageSize) + .ToListAsync(); - var totalItems = await root - .LongCountAsync(); + itemsOnPage = ChangeUriPlaceholder(itemsOnPage); - var itemsOnPage = await root - .Skip(pageSize * pageIndex) - .Take(pageSize) - .ToListAsync(); + return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); + } - itemsOnPage = ChangeUriPlaceholder(itemsOnPage); + // GET api/v1/[controller]/items/type/1/brand[?pageSize=3&pageIndex=10] + [HttpGet] + [Route("items/type/{catalogTypeId}/brand/{catalogBrandId:int?}")] + [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] + public async Task>> ItemsByTypeIdAndBrandIdAsync(int catalogTypeId, int? catalogBrandId, [FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0) + { + var root = (IQueryable)_catalogContext.CatalogItems; - return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); - } + root = root.Where(ci => ci.CatalogTypeId == catalogTypeId); - // GET api/v1/[controller]/items/type/all/brand[?pageSize=3&pageIndex=10] - [HttpGet] - [Route("items/type/all/brand/{catalogBrandId:int?}")] - [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] - public async Task>> ItemsByBrandIdAsync(int? catalogBrandId, [FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0) + if (catalogBrandId.HasValue) { - var root = (IQueryable)_catalogContext.CatalogItems; - - if (catalogBrandId.HasValue) - { - root = root.Where(ci => ci.CatalogBrandId == catalogBrandId); - } + root = root.Where(ci => ci.CatalogBrandId == catalogBrandId); + } - var totalItems = await root - .LongCountAsync(); + var totalItems = await root + .LongCountAsync(); - var itemsOnPage = await root - .Skip(pageSize * pageIndex) - .Take(pageSize) - .ToListAsync(); + var itemsOnPage = await root + .Skip(pageSize * pageIndex) + .Take(pageSize) + .ToListAsync(); - itemsOnPage = ChangeUriPlaceholder(itemsOnPage); + itemsOnPage = ChangeUriPlaceholder(itemsOnPage); - return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); - } + return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); + } - // GET api/v1/[controller]/CatalogTypes - [HttpGet] - [Route("catalogtypes")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - public async Task>> CatalogTypesAsync() - { - return await _catalogContext.CatalogTypes.ToListAsync(); - } + // GET api/v1/[controller]/items/type/all/brand[?pageSize=3&pageIndex=10] + [HttpGet] + [Route("items/type/all/brand/{catalogBrandId:int?}")] + [ProducesResponseType(typeof(PaginatedItemsViewModel), (int)HttpStatusCode.OK)] + public async Task>> ItemsByBrandIdAsync(int? catalogBrandId, [FromQuery] int pageSize = 10, [FromQuery] int pageIndex = 0) + { + var root = (IQueryable)_catalogContext.CatalogItems; - // GET api/v1/[controller]/CatalogBrands - [HttpGet] - [Route("catalogbrands")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - public async Task>> CatalogBrandsAsync() + if (catalogBrandId.HasValue) { - return await _catalogContext.CatalogBrands.ToListAsync(); + root = root.Where(ci => ci.CatalogBrandId == catalogBrandId); } - //PUT api/v1/[controller]/items - [Route("items")] - [HttpPut] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Created)] - public async Task UpdateProductAsync([FromBody] CatalogItem productToUpdate) - { - var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); + var totalItems = await root + .LongCountAsync(); - if (catalogItem == null) - { - return NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." }); - } + var itemsOnPage = await root + .Skip(pageSize * pageIndex) + .Take(pageSize) + .ToListAsync(); - var oldPrice = catalogItem.Price; - var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; + itemsOnPage = ChangeUriPlaceholder(itemsOnPage); - // Update current product - catalogItem = productToUpdate; - _catalogContext.CatalogItems.Update(catalogItem); + return new PaginatedItemsViewModel(pageIndex, pageSize, totalItems, itemsOnPage); + } - if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed - { - //Create Integration Event to be published through the Event Bus - var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); + // GET api/v1/[controller]/CatalogTypes + [HttpGet] + [Route("catalogtypes")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + public async Task>> CatalogTypesAsync() + { + return await _catalogContext.CatalogTypes.ToListAsync(); + } - // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction - await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent); + // GET api/v1/[controller]/CatalogBrands + [HttpGet] + [Route("catalogbrands")] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + public async Task>> CatalogBrandsAsync() + { + return await _catalogContext.CatalogBrands.ToListAsync(); + } - // Publish through the Event Bus and mark the saved event as published - await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); - } - else // Just save the updated product because the Product's Price hasn't changed. - { - await _catalogContext.SaveChangesAsync(); - } + //PUT api/v1/[controller]/items + [Route("items")] + [HttpPut] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Created)] + public async Task UpdateProductAsync([FromBody] CatalogItem productToUpdate) + { + var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id); - return CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null); + if (catalogItem == null) + { + return NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." }); } - //POST api/v1/[controller]/items - [Route("items")] - [HttpPost] - [ProducesResponseType((int)HttpStatusCode.Created)] - public async Task CreateProductAsync([FromBody] CatalogItem product) + var oldPrice = catalogItem.Price; + var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price; + + // Update current product + catalogItem = productToUpdate; + _catalogContext.CatalogItems.Update(catalogItem); + + if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed { - var item = new CatalogItem - { - CatalogBrandId = product.CatalogBrandId, - CatalogTypeId = product.CatalogTypeId, - Description = product.Description, - Name = product.Name, - PictureFileName = product.PictureFileName, - Price = product.Price - }; + //Create Integration Event to be published through the Event Bus + var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); - _catalogContext.CatalogItems.Add(item); + // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction + await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent); + // Publish through the Event Bus and mark the saved event as published + await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); + } + else // Just save the updated product because the Product's Price hasn't changed. + { await _catalogContext.SaveChangesAsync(); - - return CreatedAtAction(nameof(ItemByIdAsync), new { id = item.Id }, null); } - //DELETE api/v1/[controller]/id - [Route("{id}")] - [HttpDelete] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task DeleteProductAsync(int id) + return CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null); + } + + //POST api/v1/[controller]/items + [Route("items")] + [HttpPost] + [ProducesResponseType((int)HttpStatusCode.Created)] + public async Task CreateProductAsync([FromBody] CatalogItem product) + { + var item = new CatalogItem { - var product = _catalogContext.CatalogItems.SingleOrDefault(x => x.Id == id); + CatalogBrandId = product.CatalogBrandId, + CatalogTypeId = product.CatalogTypeId, + Description = product.Description, + Name = product.Name, + PictureFileName = product.PictureFileName, + Price = product.Price + }; - if (product == null) - { - return NotFound(); - } + _catalogContext.CatalogItems.Add(item); - _catalogContext.CatalogItems.Remove(product); + await _catalogContext.SaveChangesAsync(); - await _catalogContext.SaveChangesAsync(); + return CreatedAtAction(nameof(ItemByIdAsync), new { id = item.Id }, null); + } - return NoContent(); - } + //DELETE api/v1/[controller]/id + [Route("{id}")] + [HttpDelete] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public async Task DeleteProductAsync(int id) + { + var product = _catalogContext.CatalogItems.SingleOrDefault(x => x.Id == id); - private List ChangeUriPlaceholder(List items) + if (product == null) { - var baseUri = _settings.PicBaseUrl; - var azureStorageEnabled = _settings.AzureStorageEnabled; + return NotFound(); + } - foreach (var item in items) - { - item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); - } + _catalogContext.CatalogItems.Remove(product); + + await _catalogContext.SaveChangesAsync(); + + return NoContent(); + } - return items; + private List ChangeUriPlaceholder(List items) + { + var baseUri = _settings.PicBaseUrl; + var azureStorageEnabled = _settings.AzureStorageEnabled; + + foreach (var item in items) + { + item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); } + + return items; } } diff --git a/src/Services/Catalog/Catalog.API/Controllers/HomeController.cs b/src/Services/Catalog/Catalog.API/Controllers/HomeController.cs index 37792fb04..cd86a2966 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/HomeController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/HomeController.cs @@ -1,15 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers; -// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 - -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers +public class HomeController : Controller { - public class HomeController : Controller + // GET: // + public IActionResult Index() { - // GET: // - public IActionResult Index() - { - return new RedirectResult("~/swagger"); - } + return new RedirectResult("~/swagger"); } } diff --git a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs index 31c93683c..51ea6a7e4 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs @@ -1,96 +1,86 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using System.IO; -using System.Net; -using System.Threading.Tasks; +// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers; -// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 - -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers +[ApiController] +public class PicController : ControllerBase { - [ApiController] - public class PicController : ControllerBase + private readonly IWebHostEnvironment _env; + private readonly CatalogContext _catalogContext; + + public PicController(IWebHostEnvironment env, + CatalogContext catalogContext) { - private readonly IWebHostEnvironment _env; - private readonly CatalogContext _catalogContext; + _env = env; + _catalogContext = catalogContext; + } - public PicController(IWebHostEnvironment env, - CatalogContext catalogContext) + [HttpGet] + [Route("api/v1/catalog/items/{catalogItemId:int}/pic")] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + // GET: // + public async Task GetImageAsync(int catalogItemId) + { + if (catalogItemId <= 0) { - _env = env; - _catalogContext = catalogContext; + return BadRequest(); } - [HttpGet] - [Route("api/v1/catalog/items/{catalogItemId:int}/pic")] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - // GET: // - public async Task GetImageAsync(int catalogItemId) - { - if (catalogItemId <= 0) - { - return BadRequest(); - } - - var item = await _catalogContext.CatalogItems - .SingleOrDefaultAsync(ci => ci.Id == catalogItemId); - - if (item != null) - { - var webRoot = _env.WebRootPath; - var path = Path.Combine(webRoot, item.PictureFileName); + var item = await _catalogContext.CatalogItems + .SingleOrDefaultAsync(ci => ci.Id == catalogItemId); - string imageFileExtension = Path.GetExtension(item.PictureFileName); - string mimetype = GetImageMimeTypeFromImageFileExtension(imageFileExtension); + if (item != null) + { + var webRoot = _env.WebRootPath; + var path = Path.Combine(webRoot, item.PictureFileName); - var buffer = await System.IO.File.ReadAllBytesAsync(path); + string imageFileExtension = Path.GetExtension(item.PictureFileName); + string mimetype = GetImageMimeTypeFromImageFileExtension(imageFileExtension); - return File(buffer, mimetype); - } + var buffer = await System.IO.File.ReadAllBytesAsync(path); - return NotFound(); + return File(buffer, mimetype); } - private string GetImageMimeTypeFromImageFileExtension(string extension) - { - string mimetype; + return NotFound(); + } - switch (extension) - { - case ".png": - mimetype = "image/png"; - break; - case ".gif": - mimetype = "image/gif"; - break; - case ".jpg": - case ".jpeg": - mimetype = "image/jpeg"; - break; - case ".bmp": - mimetype = "image/bmp"; - break; - case ".tiff": - mimetype = "image/tiff"; - break; - case ".wmf": - mimetype = "image/wmf"; - break; - case ".jp2": - mimetype = "image/jp2"; - break; - case ".svg": - mimetype = "image/svg+xml"; - break; - default: - mimetype = "application/octet-stream"; - break; - } + private string GetImageMimeTypeFromImageFileExtension(string extension) + { + string mimetype; - return mimetype; + switch (extension) + { + case ".png": + mimetype = "image/png"; + break; + case ".gif": + mimetype = "image/gif"; + break; + case ".jpg": + case ".jpeg": + mimetype = "image/jpeg"; + break; + case ".bmp": + mimetype = "image/bmp"; + break; + case ".tiff": + mimetype = "image/tiff"; + break; + case ".wmf": + mimetype = "image/wmf"; + break; + case ".jp2": + mimetype = "image/jp2"; + break; + case ".svg": + mimetype = "image/svg+xml"; + break; + default: + mimetype = "application/octet-stream"; + break; } + + return mimetype; } } diff --git a/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs b/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs index e953df581..efebc7615 100644 --- a/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs +++ b/src/Services/Catalog/Catalog.API/Extensions/CatalogItemExtensions.cs @@ -1,15 +1,14 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model; + +public static class CatalogItemExtensions { - public static class CatalogItemExtensions + public static void FillProductUrl(this CatalogItem item, string picBaseUrl, bool azureStorageEnabled) { - public static void FillProductUrl(this CatalogItem item, string picBaseUrl, bool azureStorageEnabled) + if (item != null) { - if (item != null) - { - item.PictureUri = azureStorageEnabled - ? picBaseUrl + item.PictureFileName - : picBaseUrl.Replace("[0]", item.Id.ToString()); - } + item.PictureUri = azureStorageEnabled + ? picBaseUrl + item.PictureFileName + : picBaseUrl.Replace("[0]", item.Id.ToString()); } } } diff --git a/src/Services/Catalog/Catalog.API/Extensions/HostExtensions.cs b/src/Services/Catalog/Catalog.API/Extensions/HostExtensions.cs index ccdd73558..8720fa809 100644 --- a/src/Services/Catalog/Catalog.API/Extensions/HostExtensions.cs +++ b/src/Services/Catalog/Catalog.API/Extensions/HostExtensions.cs @@ -1,80 +1,122 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Polly; -using System; -using System.Data.SqlClient; - -namespace Catalog.API.Extensions +namespace Catalog.API.Extensions; + +public static class HostExtensions { - public static class HostExtensions + public static bool IsInKubernetes(this IHost host) { - public static bool IsInKubernetes(this IHost host) - { - var cfg = host.Services.GetService(); - var orchestratorType = cfg.GetValue("OrchestratorType"); - return orchestratorType?.ToUpper() == "K8S"; - } + var cfg = host.Services.GetService(); + var orchestratorType = cfg.GetValue("OrchestratorType"); + return orchestratorType?.ToUpper() == "K8S"; + } + + public static IHost MigrateDbContext(this IHost host, Action seeder) where TContext : DbContext + { + var underK8s = host.IsInKubernetes(); - public static IHost MigrateDbContext(this IHost host, Action seeder) where TContext : DbContext + using (var scope = host.Services.CreateScope()) { - var underK8s = host.IsInKubernetes(); + var services = scope.ServiceProvider; - using (var scope = host.Services.CreateScope()) - { - var services = scope.ServiceProvider; + var logger = services.GetRequiredService>(); - var logger = services.GetRequiredService>(); + var context = services.GetService(); - var context = services.GetService(); + try + { + logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); - try + if (underK8s) { - logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); - - if (underK8s) - { - InvokeSeeder(seeder, context, services); - } - else - { - var retry = Policy.Handle() - .WaitAndRetry(new TimeSpan[] - { - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(8), - }); - - //if the sql server container is not created on run docker compose this - //migration can't fail for network related exception. The retry options for DbContext only - //apply to transient exceptions - // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) - retry.Execute(() => InvokeSeeder(seeder, context, services)); - } - - logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); + InvokeSeeder(seeder, context, services); } - catch (Exception ex) + else { - logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); - if (underK8s) - { - throw; // Rethrow under k8s because we rely on k8s to re-run the pod - } + var retry = Policy.Handle() + .WaitAndRetry(new TimeSpan[] + { + TimeSpan.FromSeconds(3), + TimeSpan.FromSeconds(5), + TimeSpan.FromSeconds(8), + }); + + //if the sql server container is not created on run docker compose this + //migration can't fail for network related exception. The retry options for DbContext only + //apply to transient exceptions + // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) + retry.Execute(() => InvokeSeeder(seeder, context, services)); } - } - return host; + logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); + } + catch (Exception ex) + { + logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); + if (underK8s) + { + throw; // Rethrow under k8s because we rely on k8s to re-run the pod + } + } } - private static void InvokeSeeder(Action seeder, TContext context, IServiceProvider services) - where TContext : DbContext + return host; + } + + public static IWebHost MigrateDbContext(this IWebHost host, Action seeder) where TContext : DbContext + { + var underK8s = host.IsInKubernetes(); + + using (var scope = host.Services.CreateScope()) { - context.Database.Migrate(); - seeder(context, services); + var services = scope.ServiceProvider; + + var logger = services.GetRequiredService>(); + + var context = services.GetService(); + + try + { + logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); + + if (underK8s) + { + InvokeSeeder(seeder, context, services); + } + else + { + var retry = Policy.Handle() + .WaitAndRetry(new TimeSpan[] + { + TimeSpan.FromSeconds(3), + TimeSpan.FromSeconds(5), + TimeSpan.FromSeconds(8), + }); + + //if the sql server container is not created on run docker compose this + //migration can't fail for network related exception. The retry options for DbContext only + //apply to transient exceptions + // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) + retry.Execute(() => InvokeSeeder(seeder, context, services)); + } + + logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); + } + catch (Exception ex) + { + logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); + if (underK8s) + { + throw; // Rethrow under k8s because we rely on k8s to re-run the pod + } + } } + + return host; + } + + private static void InvokeSeeder(Action seeder, TContext context, IServiceProvider services) + where TContext : DbContext + { + context.Database.Migrate(); + seeder(context, services); } } diff --git a/src/Services/Catalog/Catalog.API/Extensions/LinqSelectExtensions.cs b/src/Services/Catalog/Catalog.API/Extensions/LinqSelectExtensions.cs index 1e5fbf789..85fa9300c 100644 --- a/src/Services/Catalog/Catalog.API/Extensions/LinqSelectExtensions.cs +++ b/src/Services/Catalog/Catalog.API/Extensions/LinqSelectExtensions.cs @@ -1,50 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Extensions; -namespace Catalog.API.Extensions +public static class LinqSelectExtensions { - public static class LinqSelectExtensions + public static IEnumerable> SelectTry(this IEnumerable enumerable, Func selector) { - public static IEnumerable> SelectTry(this IEnumerable enumerable, Func selector) + foreach (TSource element in enumerable) { - foreach (TSource element in enumerable) + SelectTryResult returnedValue; + try { - SelectTryResult returnedValue; - try - { - returnedValue = new SelectTryResult(element, selector(element), null); - } - catch (Exception ex) - { - returnedValue = new SelectTryResult(element, default(TResult), ex); - } - yield return returnedValue; + returnedValue = new SelectTryResult(element, selector(element), null); } + catch (Exception ex) + { + returnedValue = new SelectTryResult(element, default(TResult), ex); + } + yield return returnedValue; } + } - public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) - { - return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); - } + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); + } - public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) - { - return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); - } + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); + } - public class SelectTryResult + public class SelectTryResult + { + internal SelectTryResult(TSource source, TResult result, Exception exception) { - internal SelectTryResult(TSource source, TResult result, Exception exception) - { - Source = source; - Result = result; - CaughtException = exception; - } - - public TSource Source { get; private set; } - public TResult Result { get; private set; } - public Exception CaughtException { get; private set; } + Source = source; + Result = result; + CaughtException = exception; } + + public TSource Source { get; private set; } + public TResult Result { get; private set; } + public Exception CaughtException { get; private set; } } } diff --git a/src/Services/Catalog/Catalog.API/Extensions/WebHostExtensions.cs b/src/Services/Catalog/Catalog.API/Extensions/WebHostExtensions.cs index 07fc11770..b3ac16968 100644 --- a/src/Services/Catalog/Catalog.API/Extensions/WebHostExtensions.cs +++ b/src/Services/Catalog/Catalog.API/Extensions/WebHostExtensions.cs @@ -1,80 +1,70 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Polly; -using System; -using System.Data.SqlClient; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Extensions; -namespace Catalog.API.Extensions +public static class WebHostExtensions { - public static class WebHostExtensions + public static bool IsInKubernetes(this IWebHost host) { - public static bool IsInKubernetes(this IWebHost host) - { - var cfg = host.Services.GetService(); - var orchestratorType = cfg.GetValue("OrchestratorType"); - return orchestratorType?.ToUpper() == "K8S"; - } + var cfg = host.Services.GetService(); + var orchestratorType = cfg.GetValue("OrchestratorType"); + return orchestratorType?.ToUpper() == "K8S"; + } + + public static IWebHost MigrateDbContext(this IWebHost host, Action seeder) where TContext : DbContext + { + var underK8s = host.IsInKubernetes(); - public static IWebHost MigrateDbContext(this IWebHost host, Action seeder) where TContext : DbContext + using (var scope = host.Services.CreateScope()) { - var underK8s = host.IsInKubernetes(); + var services = scope.ServiceProvider; - using (var scope = host.Services.CreateScope()) - { - var services = scope.ServiceProvider; + var logger = services.GetRequiredService>(); - var logger = services.GetRequiredService>(); + var context = services.GetService(); - var context = services.GetService(); + try + { + logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); - try + if (underK8s) { - logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name); - - if (underK8s) - { - InvokeSeeder(seeder, context, services); - } - else - { - var retry = Policy.Handle() - .WaitAndRetry(new TimeSpan[] - { - TimeSpan.FromSeconds(3), - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(8), - }); - - //if the sql server container is not created on run docker compose this - //migration can't fail for network related exception. The retry options for DbContext only - //apply to transient exceptions - // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) - retry.Execute(() => InvokeSeeder(seeder, context, services)); - } + InvokeSeeder(seeder, context, services); + } + else + { + var retry = Policy.Handle() + .WaitAndRetry(new TimeSpan[] + { + TimeSpan.FromSeconds(3), + TimeSpan.FromSeconds(5), + TimeSpan.FromSeconds(8), + }); - logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); + //if the sql server container is not created on run docker compose this + //migration can't fail for network related exception. The retry options for DbContext only + //apply to transient exceptions + // Note that this is NOT applied when running some orchestrators (let the orchestrator to recreate the failing service) + retry.Execute(() => InvokeSeeder(seeder, context, services)); } - catch (Exception ex) + + logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name); + } + catch (Exception ex) + { + logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); + if (underK8s) { - logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name); - if (underK8s) - { - throw; // Rethrow under k8s because we rely on k8s to re-run the pod - } + throw; // Rethrow under k8s because we rely on k8s to re-run the pod } } - - return host; } - private static void InvokeSeeder(Action seeder, TContext context, IServiceProvider services) - where TContext : DbContext - { - context.Database.Migrate(); - seeder(context, services); - } + return host; + } + + private static void InvokeSeeder(Action seeder, TContext context, IServiceProvider services) + where TContext : DbContext + { + context.Database.Migrate(); + seeder(context, services); } } diff --git a/src/Services/Catalog/Catalog.API/GlobalUsings.cs b/src/Services/Catalog/Catalog.API/GlobalUsings.cs new file mode 100644 index 000000000..dc0b6f62a --- /dev/null +++ b/src/Services/Catalog/Catalog.API/GlobalUsings.cs @@ -0,0 +1,62 @@ +global using Azure.Core; +global using Azure.Identity; +global using Autofac.Extensions.DependencyInjection; +global using Autofac; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Extensions; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.ActionResults; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.Exceptions; +global using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents; +global using Grpc.Core; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Server.Kestrel.Core; +global using Microsoft.AspNetCore; +global using Microsoft.Extensions.Logging; +global using Microsoft.EntityFrameworkCore.Design; +global using Microsoft.EntityFrameworkCore.Metadata.Builders; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.EntityConfigurations; +global using Microsoft.eShopOnContainers.Services.Catalog.API; +global using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Model; +global using Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Grpc; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Options; +global using Polly.Retry; +global using Polly; +global using Serilog.Context; +global using Serilog; +global using System.Collections.Generic; +global using System.Data.Common; +global using System.Data.SqlClient; +global using System.Globalization; +global using System.IO.Compression; +global using System.IO; +global using System.Linq; +global using System.Net; +global using System.Text.RegularExpressions; +global using System.Threading.Tasks; +global using System; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.Filters; +global using HealthChecks.UI.Client; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.Azure.ServiceBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +global using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.OpenApi.Models; +global using RabbitMQ.Client; +global using System.Reflection; \ No newline at end of file diff --git a/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs b/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs index 91270b576..ebd785462 100644 --- a/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs +++ b/src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs @@ -1,188 +1,177 @@ using CatalogApi; -using Grpc.Core; -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.Services.Catalog.API; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.eShopOnContainers.Services.Catalog.API.Model; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using static CatalogApi.Catalog; -namespace Catalog.API.Grpc +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Grpc; +using Microsoft.Extensions.Logging; + +public class CatalogService : CatalogBase { - public class CatalogService : CatalogBase + private readonly CatalogContext _catalogContext; + private readonly CatalogSettings _settings; + private readonly ILogger _logger; + + public CatalogService(CatalogContext dbContext, IOptions settings, ILogger logger) { - private readonly CatalogContext _catalogContext; - private readonly CatalogSettings _settings; - private readonly ILogger _logger; + _settings = settings.Value; + _catalogContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + _logger = logger; + } - public CatalogService(CatalogContext dbContext, IOptions settings, ILogger logger) + public override async Task GetItemById(CatalogItemRequest request, ServerCallContext context) + { + _logger.LogInformation("Begin grpc call CatalogService.GetItemById for product id {Id}", request.Id); + if (request.Id <= 0) { - _settings = settings.Value; - _catalogContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); - _logger = logger; + context.Status = new Status(StatusCode.FailedPrecondition, $"Id must be > 0 (received {request.Id})"); + return null; } - public override async Task GetItemById(CatalogItemRequest request, ServerCallContext context) - { - _logger.LogInformation("Begin grpc call CatalogService.GetItemById for product id {Id}", request.Id); - if (request.Id <= 0) - { - context.Status = new Status(StatusCode.FailedPrecondition, $"Id must be > 0 (received {request.Id})"); - return null; - } + var item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(ci => ci.Id == request.Id); + var baseUri = _settings.PicBaseUrl; + var azureStorageEnabled = _settings.AzureStorageEnabled; + item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); - var item = await _catalogContext.CatalogItems.SingleOrDefaultAsync(ci => ci.Id == request.Id); - var baseUri = _settings.PicBaseUrl; - var azureStorageEnabled = _settings.AzureStorageEnabled; - item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); - - if (item != null) + if (item != null) + { + return new CatalogItemResponse() { - return new CatalogItemResponse() - { - AvailableStock = item.AvailableStock, - Description = item.Description, - Id = item.Id, - MaxStockThreshold = item.MaxStockThreshold, - Name = item.Name, - OnReorder = item.OnReorder, - PictureFileName = item.PictureFileName, - PictureUri = item.PictureUri, - Price = (double)item.Price, - RestockThreshold = item.RestockThreshold - }; - } - - context.Status = new Status(StatusCode.NotFound, $"Product with id {request.Id} do not exist"); - return null; + AvailableStock = item.AvailableStock, + Description = item.Description, + Id = item.Id, + MaxStockThreshold = item.MaxStockThreshold, + Name = item.Name, + OnReorder = item.OnReorder, + PictureFileName = item.PictureFileName, + PictureUri = item.PictureUri, + Price = (double)item.Price, + RestockThreshold = item.RestockThreshold + }; } - public override async Task GetItemsByIds(CatalogItemsRequest request, ServerCallContext context) + context.Status = new Status(StatusCode.NotFound, $"Product with id {request.Id} do not exist"); + return null; + } + + public override async Task GetItemsByIds(CatalogItemsRequest request, ServerCallContext context) + { + if (!string.IsNullOrEmpty(request.Ids)) { - if (!string.IsNullOrEmpty(request.Ids)) - { - var items = await GetItemsByIdsAsync(request.Ids); + var items = await GetItemsByIdsAsync(request.Ids); - context.Status = !items.Any() ? - new Status(StatusCode.NotFound, $"ids value invalid. Must be comma-separated list of numbers") : - new Status(StatusCode.OK, string.Empty); + context.Status = !items.Any() ? + new Status(StatusCode.NotFound, $"ids value invalid. Must be comma-separated list of numbers") : + new Status(StatusCode.OK, string.Empty); - return this.MapToResponse(items); - } + return this.MapToResponse(items); + } - var totalItems = await _catalogContext.CatalogItems - .LongCountAsync(); + var totalItems = await _catalogContext.CatalogItems + .LongCountAsync(); - var itemsOnPage = await _catalogContext.CatalogItems - .OrderBy(c => c.Name) - .Skip(request.PageSize * request.PageIndex) - .Take(request.PageSize) - .ToListAsync(); + var itemsOnPage = await _catalogContext.CatalogItems + .OrderBy(c => c.Name) + .Skip(request.PageSize * request.PageIndex) + .Take(request.PageSize) + .ToListAsync(); - /* The "awesome" fix for testing Devspaces */ + /* The "awesome" fix for testing Devspaces */ - /* - foreach (var pr in itemsOnPage) { - pr.Name = "Awesome " + pr.Name; - } + /* + foreach (var pr in itemsOnPage) { + pr.Name = "Awesome " + pr.Name; + } - */ + */ - itemsOnPage = ChangeUriPlaceholder(itemsOnPage); + itemsOnPage = ChangeUriPlaceholder(itemsOnPage); - var model = this.MapToResponse(itemsOnPage, totalItems, request.PageIndex, request.PageSize); - context.Status = new Status(StatusCode.OK, string.Empty); + var model = this.MapToResponse(itemsOnPage, totalItems, request.PageIndex, request.PageSize); + context.Status = new Status(StatusCode.OK, string.Empty); - return model; - } + return model; + } - private PaginatedItemsResponse MapToResponse(List items) - { - return this.MapToResponse(items, items.Count, 1, items.Count); - } + private PaginatedItemsResponse MapToResponse(List items) + { + return this.MapToResponse(items, items.Count, 1, items.Count); + } - private PaginatedItemsResponse MapToResponse(List items, long count, int pageIndex, int pageSize) + private PaginatedItemsResponse MapToResponse(List items, long count, int pageIndex, int pageSize) + { + var result = new PaginatedItemsResponse() { - var result = new PaginatedItemsResponse() - { - Count = count, - PageIndex = pageIndex, - PageSize = pageSize, - }; + Count = count, + PageIndex = pageIndex, + PageSize = pageSize, + }; - items.ForEach(i => + items.ForEach(i => + { + var brand = i.CatalogBrand == null + ? null + : new CatalogApi.CatalogBrand() + { + Id = i.CatalogBrand.Id, + Name = i.CatalogBrand.Brand, + }; + var catalogType = i.CatalogType == null + ? null + : new CatalogApi.CatalogType() + { + Id = i.CatalogType.Id, + Type = i.CatalogType.Type, + }; + + result.Data.Add(new CatalogItemResponse() { - var brand = i.CatalogBrand == null - ? null - : new CatalogApi.CatalogBrand() - { - Id = i.CatalogBrand.Id, - Name = i.CatalogBrand.Brand, - }; - var catalogType = i.CatalogType == null - ? null - : new CatalogApi.CatalogType() - { - Id = i.CatalogType.Id, - Type = i.CatalogType.Type, - }; - - result.Data.Add(new CatalogItemResponse() - { - AvailableStock = i.AvailableStock, - Description = i.Description, - Id = i.Id, - MaxStockThreshold = i.MaxStockThreshold, - Name = i.Name, - OnReorder = i.OnReorder, - PictureFileName = i.PictureFileName, - PictureUri = i.PictureUri, - RestockThreshold = i.RestockThreshold, - CatalogBrand = brand, - CatalogType = catalogType, - Price = (double)i.Price, - }); + AvailableStock = i.AvailableStock, + Description = i.Description, + Id = i.Id, + MaxStockThreshold = i.MaxStockThreshold, + Name = i.Name, + OnReorder = i.OnReorder, + PictureFileName = i.PictureFileName, + PictureUri = i.PictureUri, + RestockThreshold = i.RestockThreshold, + CatalogBrand = brand, + CatalogType = catalogType, + Price = (double)i.Price, }); + }); - return result; - } + return result; + } - private async Task> GetItemsByIdsAsync(string ids) + private async Task> GetItemsByIdsAsync(string ids) + { + var numIds = ids.Split(',').Select(id => (Ok: int.TryParse(id, out int x), Value: x)); + + if (!numIds.All(nid => nid.Ok)) { - var numIds = ids.Split(',').Select(id => (Ok: int.TryParse(id, out int x), Value: x)); + return new List(); + } - if (!numIds.All(nid => nid.Ok)) - { - return new List(); - } + var idsToSelect = numIds + .Select(id => id.Value); - var idsToSelect = numIds - .Select(id => id.Value); + var items = await _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToListAsync(); - var items = await _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToListAsync(); + items = ChangeUriPlaceholder(items); - items = ChangeUriPlaceholder(items); + return items; + } - return items; - } + private List ChangeUriPlaceholder(List items) + { + var baseUri = _settings.PicBaseUrl; + var azureStorageEnabled = _settings.AzureStorageEnabled; - private List ChangeUriPlaceholder(List items) + foreach (var item in items) { - var baseUri = _settings.PicBaseUrl; - var azureStorageEnabled = _settings.AzureStorageEnabled; - - foreach (var item in items) - { - item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); - } - - return items; + item.FillProductUrl(baseUri, azureStorageEnabled: azureStorageEnabled); } + + return items; } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs b/src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs index a6138b476..53af0f0b8 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs @@ -1,14 +1,10 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.ActionResults; -namespace Catalog.API.Infrastructure.ActionResults +public class InternalServerErrorObjectResult : ObjectResult { - public class InternalServerErrorObjectResult : ObjectResult + public InternalServerErrorObjectResult(object error) + : base(error) { - public InternalServerErrorObjectResult(object error) - : base(error) - { - StatusCode = StatusCodes.Status500InternalServerError; - } + StatusCode = StatusCodes.Status500InternalServerError; } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs index ee440c1ef..cf152f40f 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContext.cs @@ -1,36 +1,32 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure -{ - using EntityConfigurations; - using Microsoft.EntityFrameworkCore; - using Microsoft.EntityFrameworkCore.Design; - using Model; +using Microsoft.eShopOnContainers.Services.Catalog.API.Model; + +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; - public class CatalogContext : DbContext +public class CatalogContext : DbContext +{ + public CatalogContext(DbContextOptions options) : base(options) { - public CatalogContext(DbContextOptions options) : base(options) - { - } - public DbSet CatalogItems { get; set; } - public DbSet CatalogBrands { get; set; } - public DbSet CatalogTypes { get; set; } + } + public DbSet CatalogItems { get; set; } + public DbSet CatalogBrands { get; set; } + public DbSet CatalogTypes { get; set; } - protected override void OnModelCreating(ModelBuilder builder) - { - builder.ApplyConfiguration(new CatalogBrandEntityTypeConfiguration()); - builder.ApplyConfiguration(new CatalogTypeEntityTypeConfiguration()); - builder.ApplyConfiguration(new CatalogItemEntityTypeConfiguration()); - } + protected override void OnModelCreating(ModelBuilder builder) + { + builder.ApplyConfiguration(new CatalogBrandEntityTypeConfiguration()); + builder.ApplyConfiguration(new CatalogTypeEntityTypeConfiguration()); + builder.ApplyConfiguration(new CatalogItemEntityTypeConfiguration()); } +} - public class CatalogContextDesignFactory : IDesignTimeDbContextFactory +public class CatalogContextDesignFactory : IDesignTimeDbContextFactory +{ + public CatalogContext CreateDbContext(string[] args) { - public CatalogContext CreateDbContext(string[] args) - { - var optionsBuilder = new DbContextOptionsBuilder() - .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;Integrated Security=true"); + var optionsBuilder = new DbContextOptionsBuilder() + .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;Integrated Security=true"); - return new CatalogContext(optionsBuilder.Options); - } + return new CatalogContext(optionsBuilder.Options); } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs index 5416b377a..ba7589293 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs @@ -1,386 +1,370 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure +using Microsoft.eShopOnContainers.Services.Catalog.API.Model; + +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; + +public class CatalogContextSeed { - using Extensions.Logging; - using global::Catalog.API.Extensions; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Options; - using Model; - using Polly; - using Polly.Retry; - using System; - using System.Collections.Generic; - using System.Data.SqlClient; - using System.Globalization; - using System.IO; - using System.IO.Compression; - using System.Linq; - using System.Text.RegularExpressions; - using System.Threading.Tasks; - - public class CatalogContextSeed + public async Task SeedAsync(CatalogContext context, IWebHostEnvironment env, IOptions settings, ILogger logger) { - public async Task SeedAsync(CatalogContext context, IWebHostEnvironment env, IOptions settings, ILogger logger) + var policy = CreatePolicy(logger, nameof(CatalogContextSeed)); + + await policy.ExecuteAsync(async () => { - var policy = CreatePolicy(logger, nameof(CatalogContextSeed)); + var useCustomizationData = settings.Value.UseCustomizationData; + var contentRootPath = env.ContentRootPath; + var picturePath = env.WebRootPath; - await policy.ExecuteAsync(async () => + if (!context.CatalogBrands.Any()) { - var useCustomizationData = settings.Value.UseCustomizationData; - var contentRootPath = env.ContentRootPath; - var picturePath = env.WebRootPath; + await context.CatalogBrands.AddRangeAsync(useCustomizationData + ? GetCatalogBrandsFromFile(contentRootPath, logger) + : GetPreconfiguredCatalogBrands()); - if (!context.CatalogBrands.Any()) - { - await context.CatalogBrands.AddRangeAsync(useCustomizationData - ? GetCatalogBrandsFromFile(contentRootPath, logger) - : GetPreconfiguredCatalogBrands()); + await context.SaveChangesAsync(); + } - await context.SaveChangesAsync(); - } + if (!context.CatalogTypes.Any()) + { + await context.CatalogTypes.AddRangeAsync(useCustomizationData + ? GetCatalogTypesFromFile(contentRootPath, logger) + : GetPreconfiguredCatalogTypes()); - if (!context.CatalogTypes.Any()) - { - await context.CatalogTypes.AddRangeAsync(useCustomizationData - ? GetCatalogTypesFromFile(contentRootPath, logger) - : GetPreconfiguredCatalogTypes()); + await context.SaveChangesAsync(); + } - await context.SaveChangesAsync(); - } + if (!context.CatalogItems.Any()) + { + await context.CatalogItems.AddRangeAsync(useCustomizationData + ? GetCatalogItemsFromFile(contentRootPath, context, logger) + : GetPreconfiguredItems()); - if (!context.CatalogItems.Any()) - { - await context.CatalogItems.AddRangeAsync(useCustomizationData - ? GetCatalogItemsFromFile(contentRootPath, context, logger) - : GetPreconfiguredItems()); + await context.SaveChangesAsync(); - await context.SaveChangesAsync(); + GetCatalogItemPictures(contentRootPath, picturePath); + } + }); + } - GetCatalogItemPictures(contentRootPath, picturePath); - } - }); + private IEnumerable GetCatalogBrandsFromFile(string contentRootPath, ILogger logger) + { + string csvFileCatalogBrands = Path.Combine(contentRootPath, "Setup", "CatalogBrands.csv"); + + if (!File.Exists(csvFileCatalogBrands)) + { + return GetPreconfiguredCatalogBrands(); } - private IEnumerable GetCatalogBrandsFromFile(string contentRootPath, ILogger logger) + string[] csvheaders; + try { - string csvFileCatalogBrands = Path.Combine(contentRootPath, "Setup", "CatalogBrands.csv"); + string[] requiredHeaders = { "catalogbrand" }; + csvheaders = GetHeaders(csvFileCatalogBrands, requiredHeaders); + } + catch (Exception ex) + { + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); + return GetPreconfiguredCatalogBrands(); + } - if (!File.Exists(csvFileCatalogBrands)) - { - return GetPreconfiguredCatalogBrands(); - } + return File.ReadAllLines(csvFileCatalogBrands) + .Skip(1) // skip header row + .SelectTry(x => CreateCatalogBrand(x)) + .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) + .Where(x => x != null); + } - string[] csvheaders; - try - { - string[] requiredHeaders = { "catalogbrand" }; - csvheaders = GetHeaders(csvFileCatalogBrands, requiredHeaders); - } - catch (Exception ex) - { - logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); - return GetPreconfiguredCatalogBrands(); - } + private CatalogBrand CreateCatalogBrand(string brand) + { + brand = brand.Trim('"').Trim(); - return File.ReadAllLines(csvFileCatalogBrands) - .Skip(1) // skip header row - .SelectTry(x => CreateCatalogBrand(x)) - .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) - .Where(x => x != null); + if (String.IsNullOrEmpty(brand)) + { + throw new Exception("catalog Brand Name is empty"); } - private CatalogBrand CreateCatalogBrand(string brand) + return new CatalogBrand { - brand = brand.Trim('"').Trim(); + Brand = brand, + }; + } - if (String.IsNullOrEmpty(brand)) - { - throw new Exception("catalog Brand Name is empty"); - } + private IEnumerable GetPreconfiguredCatalogBrands() + { + return new List() + { + new CatalogBrand() { Brand = "Azure"}, + new CatalogBrand() { Brand = ".NET" }, + new CatalogBrand() { Brand = "Visual Studio" }, + new CatalogBrand() { Brand = "SQL Server" }, + new CatalogBrand() { Brand = "Other" } + }; + } - return new CatalogBrand - { - Brand = brand, - }; - } + private IEnumerable GetCatalogTypesFromFile(string contentRootPath, ILogger logger) + { + string csvFileCatalogTypes = Path.Combine(contentRootPath, "Setup", "CatalogTypes.csv"); - private IEnumerable GetPreconfiguredCatalogBrands() + if (!File.Exists(csvFileCatalogTypes)) { - return new List() - { - new CatalogBrand() { Brand = "Azure"}, - new CatalogBrand() { Brand = ".NET" }, - new CatalogBrand() { Brand = "Visual Studio" }, - new CatalogBrand() { Brand = "SQL Server" }, - new CatalogBrand() { Brand = "Other" } - }; + return GetPreconfiguredCatalogTypes(); } - private IEnumerable GetCatalogTypesFromFile(string contentRootPath, ILogger logger) + string[] csvheaders; + try { - string csvFileCatalogTypes = Path.Combine(contentRootPath, "Setup", "CatalogTypes.csv"); + string[] requiredHeaders = { "catalogtype" }; + csvheaders = GetHeaders(csvFileCatalogTypes, requiredHeaders); + } + catch (Exception ex) + { + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); + return GetPreconfiguredCatalogTypes(); + } - if (!File.Exists(csvFileCatalogTypes)) - { - return GetPreconfiguredCatalogTypes(); - } + return File.ReadAllLines(csvFileCatalogTypes) + .Skip(1) // skip header row + .SelectTry(x => CreateCatalogType(x)) + .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) + .Where(x => x != null); + } - string[] csvheaders; - try - { - string[] requiredHeaders = { "catalogtype" }; - csvheaders = GetHeaders(csvFileCatalogTypes, requiredHeaders); - } - catch (Exception ex) - { - logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); - return GetPreconfiguredCatalogTypes(); - } + private CatalogType CreateCatalogType(string type) + { + type = type.Trim('"').Trim(); - return File.ReadAllLines(csvFileCatalogTypes) - .Skip(1) // skip header row - .SelectTry(x => CreateCatalogType(x)) - .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) - .Where(x => x != null); + if (String.IsNullOrEmpty(type)) + { + throw new Exception("catalog Type Name is empty"); } - private CatalogType CreateCatalogType(string type) + return new CatalogType { - type = type.Trim('"').Trim(); + Type = type, + }; + } - if (String.IsNullOrEmpty(type)) - { - throw new Exception("catalog Type Name is empty"); - } + private IEnumerable GetPreconfiguredCatalogTypes() + { + return new List() + { + new CatalogType() { Type = "Mug"}, + new CatalogType() { Type = "T-Shirt" }, + new CatalogType() { Type = "Sheet" }, + new CatalogType() { Type = "USB Memory Stick" } + }; + } - return new CatalogType - { - Type = type, - }; - } + private IEnumerable GetCatalogItemsFromFile(string contentRootPath, CatalogContext context, ILogger logger) + { + string csvFileCatalogItems = Path.Combine(contentRootPath, "Setup", "CatalogItems.csv"); - private IEnumerable GetPreconfiguredCatalogTypes() + if (!File.Exists(csvFileCatalogItems)) { - return new List() - { - new CatalogType() { Type = "Mug"}, - new CatalogType() { Type = "T-Shirt" }, - new CatalogType() { Type = "Sheet" }, - new CatalogType() { Type = "USB Memory Stick" } - }; + return GetPreconfiguredItems(); } - private IEnumerable GetCatalogItemsFromFile(string contentRootPath, CatalogContext context, ILogger logger) + string[] csvheaders; + try { - string csvFileCatalogItems = Path.Combine(contentRootPath, "Setup", "CatalogItems.csv"); - - if (!File.Exists(csvFileCatalogItems)) - { - return GetPreconfiguredItems(); - } + string[] requiredHeaders = { "catalogtypename", "catalogbrandname", "description", "name", "price", "picturefilename" }; + string[] optionalheaders = { "availablestock", "restockthreshold", "maxstockthreshold", "onreorder" }; + csvheaders = GetHeaders(csvFileCatalogItems, requiredHeaders, optionalheaders); + } + catch (Exception ex) + { + logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); + return GetPreconfiguredItems(); + } - string[] csvheaders; - try - { - string[] requiredHeaders = { "catalogtypename", "catalogbrandname", "description", "name", "price", "picturefilename" }; - string[] optionalheaders = { "availablestock", "restockthreshold", "maxstockthreshold", "onreorder" }; - csvheaders = GetHeaders(csvFileCatalogItems, requiredHeaders, optionalheaders); - } - catch (Exception ex) - { - logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); - return GetPreconfiguredItems(); - } + var catalogTypeIdLookup = context.CatalogTypes.ToDictionary(ct => ct.Type, ct => ct.Id); + var catalogBrandIdLookup = context.CatalogBrands.ToDictionary(ct => ct.Brand, ct => ct.Id); - var catalogTypeIdLookup = context.CatalogTypes.ToDictionary(ct => ct.Type, ct => ct.Id); - var catalogBrandIdLookup = context.CatalogBrands.ToDictionary(ct => ct.Brand, ct => ct.Id); + return File.ReadAllLines(csvFileCatalogItems) + .Skip(1) // skip header row + .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")) + .SelectTry(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup)) + .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) + .Where(x => x != null); + } - return File.ReadAllLines(csvFileCatalogItems) - .Skip(1) // skip header row - .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")) - .SelectTry(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup)) - .OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) - .Where(x => x != null); + private CatalogItem CreateCatalogItem(string[] column, string[] headers, Dictionary catalogTypeIdLookup, Dictionary catalogBrandIdLookup) + { + if (column.Count() != headers.Count()) + { + throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); } - private CatalogItem CreateCatalogItem(string[] column, string[] headers, Dictionary catalogTypeIdLookup, Dictionary catalogBrandIdLookup) + string catalogTypeName = column[Array.IndexOf(headers, "catalogtypename")].Trim('"').Trim(); + if (!catalogTypeIdLookup.ContainsKey(catalogTypeName)) { - if (column.Count() != headers.Count()) - { - throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); - } - - string catalogTypeName = column[Array.IndexOf(headers, "catalogtypename")].Trim('"').Trim(); - if (!catalogTypeIdLookup.ContainsKey(catalogTypeName)) - { - throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); - } + throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); + } - string catalogBrandName = column[Array.IndexOf(headers, "catalogbrandname")].Trim('"').Trim(); - if (!catalogBrandIdLookup.ContainsKey(catalogBrandName)) - { - throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); - } + string catalogBrandName = column[Array.IndexOf(headers, "catalogbrandname")].Trim('"').Trim(); + if (!catalogBrandIdLookup.ContainsKey(catalogBrandName)) + { + throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); + } - string priceString = column[Array.IndexOf(headers, "price")].Trim('"').Trim(); - if (!Decimal.TryParse(priceString, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out Decimal price)) - { - throw new Exception($"price={priceString}is not a valid decimal number"); - } + string priceString = column[Array.IndexOf(headers, "price")].Trim('"').Trim(); + if (!Decimal.TryParse(priceString, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out Decimal price)) + { + throw new Exception($"price={priceString}is not a valid decimal number"); + } - var catalogItem = new CatalogItem() - { - CatalogTypeId = catalogTypeIdLookup[catalogTypeName], - CatalogBrandId = catalogBrandIdLookup[catalogBrandName], - Description = column[Array.IndexOf(headers, "description")].Trim('"').Trim(), - Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(), - Price = price, - PictureFileName = column[Array.IndexOf(headers, "picturefilename")].Trim('"').Trim(), - }; - - int availableStockIndex = Array.IndexOf(headers, "availablestock"); - if (availableStockIndex != -1) + var catalogItem = new CatalogItem() + { + CatalogTypeId = catalogTypeIdLookup[catalogTypeName], + CatalogBrandId = catalogBrandIdLookup[catalogBrandName], + Description = column[Array.IndexOf(headers, "description")].Trim('"').Trim(), + Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(), + Price = price, + PictureFileName = column[Array.IndexOf(headers, "picturefilename")].Trim('"').Trim(), + }; + + int availableStockIndex = Array.IndexOf(headers, "availablestock"); + if (availableStockIndex != -1) + { + string availableStockString = column[availableStockIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(availableStockString)) { - string availableStockString = column[availableStockIndex].Trim('"').Trim(); - if (!String.IsNullOrEmpty(availableStockString)) + if (int.TryParse(availableStockString, out int availableStock)) { - if (int.TryParse(availableStockString, out int availableStock)) - { - catalogItem.AvailableStock = availableStock; - } - else - { - throw new Exception($"availableStock={availableStockString} is not a valid integer"); - } + catalogItem.AvailableStock = availableStock; + } + else + { + throw new Exception($"availableStock={availableStockString} is not a valid integer"); } } + } - int restockThresholdIndex = Array.IndexOf(headers, "restockthreshold"); - if (restockThresholdIndex != -1) + int restockThresholdIndex = Array.IndexOf(headers, "restockthreshold"); + if (restockThresholdIndex != -1) + { + string restockThresholdString = column[restockThresholdIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(restockThresholdString)) { - string restockThresholdString = column[restockThresholdIndex].Trim('"').Trim(); - if (!String.IsNullOrEmpty(restockThresholdString)) + if (int.TryParse(restockThresholdString, out int restockThreshold)) { - if (int.TryParse(restockThresholdString, out int restockThreshold)) - { - catalogItem.RestockThreshold = restockThreshold; - } - else - { - throw new Exception($"restockThreshold={restockThreshold} is not a valid integer"); - } + catalogItem.RestockThreshold = restockThreshold; + } + else + { + throw new Exception($"restockThreshold={restockThreshold} is not a valid integer"); } } + } - int maxStockThresholdIndex = Array.IndexOf(headers, "maxstockthreshold"); - if (maxStockThresholdIndex != -1) + int maxStockThresholdIndex = Array.IndexOf(headers, "maxstockthreshold"); + if (maxStockThresholdIndex != -1) + { + string maxStockThresholdString = column[maxStockThresholdIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(maxStockThresholdString)) { - string maxStockThresholdString = column[maxStockThresholdIndex].Trim('"').Trim(); - if (!String.IsNullOrEmpty(maxStockThresholdString)) + if (int.TryParse(maxStockThresholdString, out int maxStockThreshold)) { - if (int.TryParse(maxStockThresholdString, out int maxStockThreshold)) - { - catalogItem.MaxStockThreshold = maxStockThreshold; - } - else - { - throw new Exception($"maxStockThreshold={maxStockThreshold} is not a valid integer"); - } + catalogItem.MaxStockThreshold = maxStockThreshold; + } + else + { + throw new Exception($"maxStockThreshold={maxStockThreshold} is not a valid integer"); } } + } - int onReorderIndex = Array.IndexOf(headers, "onreorder"); - if (onReorderIndex != -1) + int onReorderIndex = Array.IndexOf(headers, "onreorder"); + if (onReorderIndex != -1) + { + string onReorderString = column[onReorderIndex].Trim('"').Trim(); + if (!String.IsNullOrEmpty(onReorderString)) { - string onReorderString = column[onReorderIndex].Trim('"').Trim(); - if (!String.IsNullOrEmpty(onReorderString)) + if (bool.TryParse(onReorderString, out bool onReorder)) { - if (bool.TryParse(onReorderString, out bool onReorder)) - { - catalogItem.OnReorder = onReorder; - } - else - { - throw new Exception($"onReorder={onReorderString} is not a valid boolean"); - } + catalogItem.OnReorder = onReorder; + } + else + { + throw new Exception($"onReorder={onReorderString} is not a valid boolean"); } } - - return catalogItem; } - private IEnumerable GetPreconfiguredItems() + return catalogItem; + } + + private IEnumerable GetPreconfiguredItems() + { + return new List() { - return new List() - { - new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureFileName = "1.png" }, - new CatalogItem { CatalogTypeId = 1, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureFileName = "2.png" }, - new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureFileName = "3.png" }, - new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureFileName = "4.png" }, - new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 5, AvailableStock = 100, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureFileName = "5.png" }, - new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureFileName = "6.png" }, - new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureFileName = "7.png" }, - new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureFileName = "8.png" }, - new CatalogItem { CatalogTypeId = 1, CatalogBrandId = 5, AvailableStock = 100, Description = "Cup White Mug", Name = "Cup White Mug", Price = 12, PictureFileName = "9.png" }, - new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureFileName = "10.png" }, - new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 2, AvailableStock = 100, Description = "Cup Sheet", Name = "Cup Sheet", Price = 8.5M, PictureFileName = "11.png" }, - new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureFileName = "12.png" }, - }; - } + new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureFileName = "1.png" }, + new CatalogItem { CatalogTypeId = 1, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureFileName = "2.png" }, + new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureFileName = "3.png" }, + new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureFileName = "4.png" }, + new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 5, AvailableStock = 100, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureFileName = "5.png" }, + new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureFileName = "6.png" }, + new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureFileName = "7.png" }, + new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureFileName = "8.png" }, + new CatalogItem { CatalogTypeId = 1, CatalogBrandId = 5, AvailableStock = 100, Description = "Cup White Mug", Name = "Cup White Mug", Price = 12, PictureFileName = "9.png" }, + new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 2, AvailableStock = 100, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureFileName = "10.png" }, + new CatalogItem { CatalogTypeId = 3, CatalogBrandId = 2, AvailableStock = 100, Description = "Cup Sheet", Name = "Cup Sheet", Price = 8.5M, PictureFileName = "11.png" }, + new CatalogItem { CatalogTypeId = 2, CatalogBrandId = 5, AvailableStock = 100, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureFileName = "12.png" }, + }; + } - private string[] GetHeaders(string csvfile, string[] requiredHeaders, string[] optionalHeaders = null) + private string[] GetHeaders(string csvfile, string[] requiredHeaders, string[] optionalHeaders = null) + { + string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); + + if (csvheaders.Count() < requiredHeaders.Count()) { - string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); + throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is bigger then csv header count '{csvheaders.Count()}' "); + } - if (csvheaders.Count() < requiredHeaders.Count()) + if (optionalHeaders != null) + { + if (csvheaders.Count() > (requiredHeaders.Count() + optionalHeaders.Count())) { - throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is bigger then csv header count '{csvheaders.Count()}' "); + throw new Exception($"csv header count '{csvheaders.Count()}' is larger then required '{requiredHeaders.Count()}' and optional '{optionalHeaders.Count()}' headers count"); } + } - if (optionalHeaders != null) + foreach (var requiredHeader in requiredHeaders) + { + if (!csvheaders.Contains(requiredHeader)) { - if (csvheaders.Count() > (requiredHeaders.Count() + optionalHeaders.Count())) - { - throw new Exception($"csv header count '{csvheaders.Count()}' is larger then required '{requiredHeaders.Count()}' and optional '{optionalHeaders.Count()}' headers count"); - } + throw new Exception($"does not contain required header '{requiredHeader}'"); } + } + + return csvheaders; + } - foreach (var requiredHeader in requiredHeaders) + private void GetCatalogItemPictures(string contentRootPath, string picturePath) + { + if (picturePath != null) + { + DirectoryInfo directory = new DirectoryInfo(picturePath); + foreach (FileInfo file in directory.GetFiles()) { - if (!csvheaders.Contains(requiredHeader)) - { - throw new Exception($"does not contain required header '{requiredHeader}'"); - } + file.Delete(); } - return csvheaders; + string zipFileCatalogItemPictures = Path.Combine(contentRootPath, "Setup", "CatalogItems.zip"); + ZipFile.ExtractToDirectory(zipFileCatalogItemPictures, picturePath); } + } - private void GetCatalogItemPictures(string contentRootPath, string picturePath) - { - if (picturePath != null) - { - DirectoryInfo directory = new DirectoryInfo(picturePath); - foreach (FileInfo file in directory.GetFiles()) + private AsyncRetryPolicy CreatePolicy(ILogger logger, string prefix, int retries = 3) + { + return Policy.Handle(). + WaitAndRetryAsync( + retryCount: retries, + sleepDurationProvider: retry => TimeSpan.FromSeconds(5), + onRetry: (exception, timeSpan, retry, ctx) => { - file.Delete(); + logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); } - - string zipFileCatalogItemPictures = Path.Combine(contentRootPath, "Setup", "CatalogItems.zip"); - ZipFile.ExtractToDirectory(zipFileCatalogItemPictures, picturePath); - } - } - - private AsyncRetryPolicy CreatePolicy(ILogger logger, string prefix, int retries = 3) - { - return Policy.Handle(). - WaitAndRetryAsync( - retryCount: retries, - sleepDurationProvider: retry => TimeSpan.FromSeconds(5), - onRetry: (exception, timeSpan, retry, ctx) => - { - logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); - } - ); - } + ); } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs b/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs index 35d307ee3..1263891ad 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogBrandEntityTypeConfiguration.cs @@ -1,25 +1,20 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Catalog.API.Model; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.EntityConfigurations; -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.EntityConfigurations +class CatalogBrandEntityTypeConfiguration + : IEntityTypeConfiguration { - class CatalogBrandEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("CatalogBrand"); + builder.ToTable("CatalogBrand"); - builder.HasKey(ci => ci.Id); + builder.HasKey(ci => ci.Id); - builder.Property(ci => ci.Id) - .UseHiLo("catalog_brand_hilo") - .IsRequired(); + builder.Property(ci => ci.Id) + .UseHiLo("catalog_brand_hilo") + .IsRequired(); - builder.Property(cb => cb.Brand) - .IsRequired() - .HasMaxLength(100); - } + builder.Property(cb => cb.Brand) + .IsRequired() + .HasMaxLength(100); } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs b/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs index 33079099c..d7cc8b2d5 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogItemEntityTypeConfiguration.cs @@ -1,39 +1,34 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Catalog.API.Model; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.EntityConfigurations; -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.EntityConfigurations +class CatalogItemEntityTypeConfiguration + : IEntityTypeConfiguration { - class CatalogItemEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("Catalog"); + builder.ToTable("Catalog"); - builder.Property(ci => ci.Id) - .UseHiLo("catalog_hilo") - .IsRequired(); + builder.Property(ci => ci.Id) + .UseHiLo("catalog_hilo") + .IsRequired(); - builder.Property(ci => ci.Name) - .IsRequired(true) - .HasMaxLength(50); + builder.Property(ci => ci.Name) + .IsRequired(true) + .HasMaxLength(50); - builder.Property(ci => ci.Price) - .IsRequired(true); + builder.Property(ci => ci.Price) + .IsRequired(true); - builder.Property(ci => ci.PictureFileName) - .IsRequired(false); + builder.Property(ci => ci.PictureFileName) + .IsRequired(false); - builder.Ignore(ci => ci.PictureUri); + builder.Ignore(ci => ci.PictureUri); - builder.HasOne(ci => ci.CatalogBrand) - .WithMany() - .HasForeignKey(ci => ci.CatalogBrandId); + builder.HasOne(ci => ci.CatalogBrand) + .WithMany() + .HasForeignKey(ci => ci.CatalogBrandId); - builder.HasOne(ci => ci.CatalogType) - .WithMany() - .HasForeignKey(ci => ci.CatalogTypeId); - } + builder.HasOne(ci => ci.CatalogType) + .WithMany() + .HasForeignKey(ci => ci.CatalogTypeId); } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs b/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs index df42826c9..698de0d8f 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/EntityConfigurations/CatalogTypeEntityTypeConfiguration.cs @@ -1,25 +1,20 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Catalog.API.Model; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.EntityConfigurations; -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.EntityConfigurations +class CatalogTypeEntityTypeConfiguration + : IEntityTypeConfiguration { - class CatalogTypeEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder builder) { - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable("CatalogType"); + builder.ToTable("CatalogType"); - builder.HasKey(ci => ci.Id); + builder.HasKey(ci => ci.Id); - builder.Property(ci => ci.Id) - .UseHiLo("catalog_type_hilo") - .IsRequired(); + builder.Property(ci => ci.Id) + .UseHiLo("catalog_type_hilo") + .IsRequired(); - builder.Property(cb => cb.Type) - .IsRequired() - .HasMaxLength(100); - } + builder.Property(cb => cb.Type) + .IsRequired() + .HasMaxLength(100); } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs index 45295994e..4bc8db208 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Exceptions/CatalogDomainException.cs @@ -1,21 +1,18 @@ -using System; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.Exceptions; -namespace Catalog.API.Infrastructure.Exceptions +/// +/// Exception type for app exceptions +/// +public class CatalogDomainException : Exception { - /// - /// Exception type for app exceptions - /// - public class CatalogDomainException : Exception - { - public CatalogDomainException() - { } + public CatalogDomainException() + { } - public CatalogDomainException(string message) - : base(message) - { } + public CatalogDomainException(string message) + : base(message) + { } - public CatalogDomainException(string message, Exception innerException) - : base(message, innerException) - { } - } + public CatalogDomainException(string message, Exception innerException) + : base(message, innerException) + { } } diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 5ddb81d63..66ee35e7b 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -1,69 +1,58 @@ -using Catalog.API.Infrastructure.ActionResults; -using Catalog.API.Infrastructure.Exceptions; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System.Net; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure.Filters; -namespace Catalog.API.Infrastructure.Filters +public class HttpGlobalExceptionFilter : IExceptionFilter { - public class HttpGlobalExceptionFilter : IExceptionFilter + private readonly IWebHostEnvironment env; + private readonly ILogger logger; + + public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) { - private readonly IWebHostEnvironment env; - private readonly ILogger logger; + this.env = env; + this.logger = logger; + } - public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) - { - this.env = env; - this.logger = logger; - } + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); - public void OnException(ExceptionContext context) + if (context.Exception.GetType() == typeof(CatalogDomainException)) { - logger.LogError(new EventId(context.Exception.HResult), - context.Exception, - context.Exception.Message); - - if (context.Exception.GetType() == typeof(CatalogDomainException)) + var problemDetails = new ValidationProblemDetails() { - var problemDetails = new ValidationProblemDetails() - { - Instance = context.HttpContext.Request.Path, - Status = StatusCodes.Status400BadRequest, - Detail = "Please refer to the errors property for additional details." - }; + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; - problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); + problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); - context.Result = new BadRequestObjectResult(problemDetails); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; - } - else + context.Result = new BadRequestObjectResult(problemDetails); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse { - var json = new JsonErrorResponse - { - Messages = new[] { "An error ocurred." } - }; - - if (env.IsDevelopment()) - { - json.DeveloperMessage = context.Exception; - } + Messages = new[] { "An error ocurred." } + }; - context.Result = new InternalServerErrorObjectResult(json); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + if (env.IsDevelopment()) + { + json.DeveloperMessage = context.Exception; } - context.ExceptionHandled = true; + + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } + context.ExceptionHandled = true; + } - private class JsonErrorResponse - { - public string[] Messages { get; set; } + private class JsonErrorResponse + { + public string[] Messages { get; set; } - public object DeveloperMessage { get; set; } - } + public object DeveloperMessage { get; set; } } } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs index 1ed2783df..282bea5b3 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs @@ -1,86 +1,74 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.Extensions.Logging; -using System; -using System.Data.Common; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents; -namespace Catalog.API.IntegrationEvents +public class CatalogIntegrationEventService : ICatalogIntegrationEventService, IDisposable { - public class CatalogIntegrationEventService : ICatalogIntegrationEventService, IDisposable + private readonly Func _integrationEventLogServiceFactory; + private readonly IEventBus _eventBus; + private readonly CatalogContext _catalogContext; + private readonly IIntegrationEventLogService _eventLogService; + private readonly ILogger _logger; + private volatile bool disposedValue; + + public CatalogIntegrationEventService( + ILogger logger, + IEventBus eventBus, + CatalogContext catalogContext, + Func integrationEventLogServiceFactory) { - private readonly Func _integrationEventLogServiceFactory; - private readonly IEventBus _eventBus; - private readonly CatalogContext _catalogContext; - private readonly IIntegrationEventLogService _eventLogService; - private readonly ILogger _logger; - private volatile bool disposedValue; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext)); + _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); + _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); + _eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); + } - public CatalogIntegrationEventService( - ILogger logger, - IEventBus eventBus, - CatalogContext catalogContext, - Func integrationEventLogServiceFactory) + public async Task PublishThroughEventBusAsync(IntegrationEvent evt) + { + try { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext)); - _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); - _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); - _eventLogService = _integrationEventLogServiceFactory(_catalogContext.Database.GetDbConnection()); - } + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId_published} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt); - public async Task PublishThroughEventBusAsync(IntegrationEvent evt) + await _eventLogService.MarkEventAsInProgressAsync(evt.Id); + _eventBus.Publish(evt); + await _eventLogService.MarkEventAsPublishedAsync(evt.Id); + } + catch (Exception ex) { - try - { - _logger.LogInformation("----- Publishing integration event: {IntegrationEventId_published} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt); - - await _eventLogService.MarkEventAsInProgressAsync(evt.Id); - _eventBus.Publish(evt); - await _eventLogService.MarkEventAsPublishedAsync(evt.Id); - } - catch (Exception ex) - { - _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt); - await _eventLogService.MarkEventAsFailedAsync(evt.Id); - } + _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt); + await _eventLogService.MarkEventAsFailedAsync(evt.Id); } + } - public async Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt) - { - _logger.LogInformation("----- CatalogIntegrationEventService - Saving changes and integrationEvent: {IntegrationEventId}", evt.Id); + public async Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt) + { + _logger.LogInformation("----- CatalogIntegrationEventService - Saving changes and integrationEvent: {IntegrationEventId}", evt.Id); - //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): - //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - await ResilientTransaction.New(_catalogContext).ExecuteAsync(async () => - { - // Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction - await _catalogContext.SaveChangesAsync(); - await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction); - }); - } + //Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction(): + //See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + await ResilientTransaction.New(_catalogContext).ExecuteAsync(async () => + { + // Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction + await _catalogContext.SaveChangesAsync(); + await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction); + }); + } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) { - if (!disposedValue) + if (disposing) { - if (disposing) - { - (_eventLogService as IDisposable)?.Dispose(); - } - - disposedValue = true; + (_eventLogService as IDisposable)?.Dispose(); } - } - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); + disposedValue = true; } } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs index 95da93f75..35c523c39 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs @@ -1,58 +1,46 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; + +public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : + IIntegrationEventHandler { - using BuildingBlocks.EventBus.Abstractions; - using BuildingBlocks.EventBus.Events; - using global::Catalog.API.IntegrationEvents; - using Infrastructure; - using IntegrationEvents.Events; - using Microsoft.Extensions.Logging; - using Serilog.Context; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - - public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : - IIntegrationEventHandler + private readonly CatalogContext _catalogContext; + private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; + private readonly ILogger _logger; + + public OrderStatusChangedToAwaitingValidationIntegrationEventHandler( + CatalogContext catalogContext, + ICatalogIntegrationEventService catalogIntegrationEventService, + ILogger logger) { - private readonly CatalogContext _catalogContext; - private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; - private readonly ILogger _logger; - - public OrderStatusChangedToAwaitingValidationIntegrationEventHandler( - CatalogContext catalogContext, - ICatalogIntegrationEventService catalogIntegrationEventService, - ILogger logger) - { - _catalogContext = catalogContext; - _catalogIntegrationEventService = catalogIntegrationEventService; - _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - } + _catalogContext = catalogContext; + _catalogIntegrationEventService = catalogIntegrationEventService; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var confirmedOrderStockItems = new List(); + var confirmedOrderStockItems = new List(); - foreach (var orderStockItem in @event.OrderStockItems) - { - var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); - var hasStock = catalogItem.AvailableStock >= orderStockItem.Units; - var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock); + foreach (var orderStockItem in @event.OrderStockItems) + { + var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); + var hasStock = catalogItem.AvailableStock >= orderStockItem.Units; + var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock); - confirmedOrderStockItems.Add(confirmedOrderStockItem); - } + confirmedOrderStockItems.Add(confirmedOrderStockItem); + } - var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock) - ? (IntegrationEvent)new OrderStockRejectedIntegrationEvent(@event.OrderId, confirmedOrderStockItems) - : new OrderStockConfirmedIntegrationEvent(@event.OrderId); + var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock) + ? (IntegrationEvent)new OrderStockRejectedIntegrationEvent(@event.OrderId, confirmedOrderStockItems) + : new OrderStockConfirmedIntegrationEvent(@event.OrderId); - await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent); - await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent); + await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent); + await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent); - } } } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs index 5a9b4a0f8..8882e78a6 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs @@ -1,43 +1,35 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; + +public class OrderStatusChangedToPaidIntegrationEventHandler : + IIntegrationEventHandler { - using BuildingBlocks.EventBus.Abstractions; - using Infrastructure; - using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; - using Microsoft.Extensions.Logging; - using Serilog.Context; - using System.Threading.Tasks; + private readonly CatalogContext _catalogContext; + private readonly ILogger _logger; - public class OrderStatusChangedToPaidIntegrationEventHandler : - IIntegrationEventHandler + public OrderStatusChangedToPaidIntegrationEventHandler( + CatalogContext catalogContext, + ILogger logger) { - private readonly CatalogContext _catalogContext; - private readonly ILogger _logger; + _catalogContext = catalogContext; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + } - public OrderStatusChangedToPaidIntegrationEventHandler( - CatalogContext catalogContext, - ILogger logger) + public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - _catalogContext = catalogContext; - _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - } + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) - { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) + //we're not blocking stock/inventory + foreach (var orderStockItem in @event.OrderStockItems) { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - - //we're not blocking stock/inventory - foreach (var orderStockItem in @event.OrderStockItems) - { - var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); + var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); - catalogItem.RemoveStock(orderStockItem.Units); - } + catalogItem.RemoveStock(orderStockItem.Units); + } - await _catalogContext.SaveChangesAsync(); + await _catalogContext.SaveChangesAsync(); - } } } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs index b1061f60e..925b5fbcc 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs @@ -1,30 +1,26 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; + +public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent { - using BuildingBlocks.EventBus.Events; - using System.Collections.Generic; + public int OrderId { get; } + public IEnumerable OrderStockItems { get; } - public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent + public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, + IEnumerable orderStockItems) { - public int OrderId { get; } - public IEnumerable OrderStockItems { get; } - - public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, - IEnumerable orderStockItems) - { - OrderId = orderId; - OrderStockItems = orderStockItems; - } + OrderId = orderId; + OrderStockItems = orderStockItems; } +} - public record OrderStockItem - { - public int ProductId { get; } - public int Units { get; } +public record OrderStockItem +{ + public int ProductId { get; } + public int Units { get; } - public OrderStockItem(int productId, int units) - { - ProductId = productId; - Units = units; - } + public OrderStockItem(int productId, int units) + { + ProductId = productId; + Units = units; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs index b89465f7a..1ca5cc6db 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs @@ -1,18 +1,14 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; + +public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent { - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; - using System.Collections.Generic; + public int OrderId { get; } + public IEnumerable OrderStockItems { get; } - public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent + public OrderStatusChangedToPaidIntegrationEvent(int orderId, + IEnumerable orderStockItems) { - public int OrderId { get; } - public IEnumerable OrderStockItems { get; } - - public OrderStatusChangedToPaidIntegrationEvent(int orderId, - IEnumerable orderStockItems) - { - OrderId = orderId; - OrderStockItems = orderStockItems; - } + OrderId = orderId; + OrderStockItems = orderStockItems; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs index 6c6a3cfa4..30e4dfce2 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs @@ -1,11 +1,8 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events -{ - using BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; - public record OrderStockConfirmedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderStockConfirmedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId; - } -} \ No newline at end of file + public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId; +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs index 7c098edf4..7c8d03b18 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs @@ -1,31 +1,27 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events -{ - using BuildingBlocks.EventBus.Events; - using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; - public record OrderStockRejectedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderStockRejectedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public List OrderStockItems { get; } + public List OrderStockItems { get; } - public OrderStockRejectedIntegrationEvent(int orderId, - List orderStockItems) - { - OrderId = orderId; - OrderStockItems = orderStockItems; - } + public OrderStockRejectedIntegrationEvent(int orderId, + List orderStockItems) + { + OrderId = orderId; + OrderStockItems = orderStockItems; } +} - public record ConfirmedOrderStockItem - { - public int ProductId { get; } - public bool HasStock { get; } +public record ConfirmedOrderStockItem +{ + public int ProductId { get; } + public bool HasStock { get; } - public ConfirmedOrderStockItem(int productId, bool hasStock) - { - ProductId = productId; - HasStock = hasStock; - } + public ConfirmedOrderStockItem(int productId, bool hasStock) + { + ProductId = productId; + HasStock = hasStock; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs index 9f1f504af..69a821935 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs @@ -1,23 +1,20 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events -{ - using BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; - // Integration Events notes: - // An Event is “something that has happened in the past”, therefore its name has to be past tense - // An Integration Event is an event that can cause side effects to other microservices, Bounded-Contexts or external systems. - public record ProductPriceChangedIntegrationEvent : IntegrationEvent - { - public int ProductId { get; private init; } +// Integration Events notes: +// An Event is “something that has happened in the past”, therefore its name has to be past tense +// An Integration Event is an event that can cause side effects to other microservices, Bounded-Contexts or external systems. +public record ProductPriceChangedIntegrationEvent : IntegrationEvent +{ + public int ProductId { get; private init; } - public decimal NewPrice { get; private init; } + public decimal NewPrice { get; private init; } - public decimal OldPrice { get; private init; } + public decimal OldPrice { get; private init; } - public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice) - { - ProductId = productId; - NewPrice = newPrice; - OldPrice = oldPrice; - } + public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice) + { + ProductId = productId; + NewPrice = newPrice; + OldPrice = oldPrice; } } diff --git a/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs b/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs index 4d87e8e94..f5e78b803 100644 --- a/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs +++ b/src/Services/Catalog/Catalog.API/IntegrationEvents/ICatalogIntegrationEventService.cs @@ -1,11 +1,7 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents; -namespace Catalog.API.IntegrationEvents +public interface ICatalogIntegrationEventService { - public interface ICatalogIntegrationEventService - { - Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt); - Task PublishThroughEventBusAsync(IntegrationEvent evt); - } + Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt); + Task PublishThroughEventBusAsync(IntegrationEvent evt); } diff --git a/src/Services/Catalog/Catalog.API/Model/CatalogBrand.cs b/src/Services/Catalog/Catalog.API/Model/CatalogBrand.cs index 84d72899e..68223177e 100644 --- a/src/Services/Catalog/Catalog.API/Model/CatalogBrand.cs +++ b/src/Services/Catalog/Catalog.API/Model/CatalogBrand.cs @@ -1,9 +1,8 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model; + +public class CatalogBrand { - public class CatalogBrand - { - public int Id { get; set; } + public int Id { get; set; } - public string Brand { get; set; } - } + public string Brand { get; set; } } diff --git a/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs b/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs index 2e4579f7f..f5a4f9654 100644 --- a/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs +++ b/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs @@ -1,103 +1,99 @@ -using Catalog.API.Infrastructure.Exceptions; -using System; +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model; -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +public class CatalogItem { - public class CatalogItem - { - public int Id { get; set; } + public int Id { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public string Description { get; set; } + public string Description { get; set; } - public decimal Price { get; set; } + public decimal Price { get; set; } - public string PictureFileName { get; set; } + public string PictureFileName { get; set; } - public string PictureUri { get; set; } + public string PictureUri { get; set; } - public int CatalogTypeId { get; set; } + public int CatalogTypeId { get; set; } - public CatalogType CatalogType { get; set; } + public CatalogType CatalogType { get; set; } - public int CatalogBrandId { get; set; } + public int CatalogBrandId { get; set; } - public CatalogBrand CatalogBrand { get; set; } + public CatalogBrand CatalogBrand { get; set; } - // Quantity in stock - public int AvailableStock { get; set; } + // Quantity in stock + public int AvailableStock { get; set; } - // Available stock at which we should reorder - public int RestockThreshold { get; set; } + // Available stock at which we should reorder + public int RestockThreshold { get; set; } - // Maximum number of units that can be in-stock at any time (due to physicial/logistical constraints in warehouses) - public int MaxStockThreshold { get; set; } + // Maximum number of units that can be in-stock at any time (due to physicial/logistical constraints in warehouses) + public int MaxStockThreshold { get; set; } - /// - /// True if item is on reorder - /// - public bool OnReorder { get; set; } + /// + /// True if item is on reorder + /// + public bool OnReorder { get; set; } - public CatalogItem() { } + public CatalogItem() { } - /// - /// Decrements the quantity of a particular item in inventory and ensures the restockThreshold hasn't - /// been breached. If so, a RestockRequest is generated in CheckThreshold. - /// - /// If there is sufficient stock of an item, then the integer returned at the end of this call should be the same as quantityDesired. - /// In the event that there is not sufficient stock available, the method will remove whatever stock is available and return that quantity to the client. - /// In this case, it is the responsibility of the client to determine if the amount that is returned is the same as quantityDesired. - /// It is invalid to pass in a negative number. - /// - /// - /// int: Returns the number actually removed from stock. - /// - public int RemoveStock(int quantityDesired) + /// + /// Decrements the quantity of a particular item in inventory and ensures the restockThreshold hasn't + /// been breached. If so, a RestockRequest is generated in CheckThreshold. + /// + /// If there is sufficient stock of an item, then the integer returned at the end of this call should be the same as quantityDesired. + /// In the event that there is not sufficient stock available, the method will remove whatever stock is available and return that quantity to the client. + /// In this case, it is the responsibility of the client to determine if the amount that is returned is the same as quantityDesired. + /// It is invalid to pass in a negative number. + /// + /// + /// int: Returns the number actually removed from stock. + /// + public int RemoveStock(int quantityDesired) + { + if (AvailableStock == 0) { - if (AvailableStock == 0) - { - throw new CatalogDomainException($"Empty stock, product item {Name} is sold out"); - } + throw new CatalogDomainException($"Empty stock, product item {Name} is sold out"); + } - if (quantityDesired <= 0) - { - throw new CatalogDomainException($"Item units desired should be greater than zero"); - } + if (quantityDesired <= 0) + { + throw new CatalogDomainException($"Item units desired should be greater than zero"); + } - int removed = Math.Min(quantityDesired, this.AvailableStock); + int removed = Math.Min(quantityDesired, this.AvailableStock); - this.AvailableStock -= removed; + this.AvailableStock -= removed; - return removed; - } + return removed; + } - /// - /// Increments the quantity of a particular item in inventory. - /// - /// int: Returns the quantity that has been added to stock - /// - public int AddStock(int quantity) + /// + /// Increments the quantity of a particular item in inventory. + /// + /// int: Returns the quantity that has been added to stock + /// + public int AddStock(int quantity) + { + int original = this.AvailableStock; + + // The quantity that the client is trying to add to stock is greater than what can be physically accommodated in the Warehouse + if ((this.AvailableStock + quantity) > this.MaxStockThreshold) + { + // For now, this method only adds new units up maximum stock threshold. In an expanded version of this application, we + //could include tracking for the remaining units and store information about overstock elsewhere. + this.AvailableStock += (this.MaxStockThreshold - this.AvailableStock); + } + else { - int original = this.AvailableStock; - - // The quantity that the client is trying to add to stock is greater than what can be physically accommodated in the Warehouse - if ((this.AvailableStock + quantity) > this.MaxStockThreshold) - { - // For now, this method only adds new units up maximum stock threshold. In an expanded version of this application, we - //could include tracking for the remaining units and store information about overstock elsewhere. - this.AvailableStock += (this.MaxStockThreshold - this.AvailableStock); - } - else - { - this.AvailableStock += quantity; - } - - this.OnReorder = false; - - return this.AvailableStock - original; + this.AvailableStock += quantity; } + + this.OnReorder = false; + + return this.AvailableStock - original; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog.API/Model/CatalogType.cs b/src/Services/Catalog/Catalog.API/Model/CatalogType.cs index 0bc640dee..17754536e 100644 --- a/src/Services/Catalog/Catalog.API/Model/CatalogType.cs +++ b/src/Services/Catalog/Catalog.API/Model/CatalogType.cs @@ -1,9 +1,8 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model; + +public class CatalogType { - public class CatalogType - { - public int Id { get; set; } + public int Id { get; set; } - public string Type { get; set; } - } + public string Type { get; set; } } diff --git a/src/Services/Catalog/Catalog.API/Program.cs b/src/Services/Catalog/Catalog.API/Program.cs index cb6c1e1a6..33172aea9 100644 --- a/src/Services/Catalog/Catalog.API/Program.cs +++ b/src/Services/Catalog/Catalog.API/Program.cs @@ -1,23 +1,4 @@ -using Catalog.API.Extensions; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.eShopOnContainers.Services.Catalog.API; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Serilog; -using System; -using System.IO; -using System.Net; -using Azure.Identity; -using Azure.Core; - -var configuration = GetConfiguration(); +var configuration = GetConfiguration(); Log.Logger = CreateSerilogLogger(configuration); @@ -33,9 +14,7 @@ try var settings = services.GetService>(); var logger = services.GetService>(); - new CatalogContextSeed() - .SeedAsync(context, env, settings, logger) - .Wait(); + new CatalogContextSeed().SeedAsync(context, env, settings, logger).Wait(); }) .MigrateDbContext((_, __) => { }); @@ -114,7 +93,7 @@ IConfiguration GetConfiguration() config["Vault:TenantId"], config["Vault:ClientId"], config["Vault:ClientSecret"]); - builder.AddAzureKeyVault(new Uri($"https://{config["Vault:Name"]}.vault.azure.net/"), credential); + //builder.AddAzureKeyVault(new Uri($"https://{config["Vault:Name"]}.vault.azure.net/"), credential); } return builder.Build(); diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 4c17fb63f..f93877562 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,369 +1,333 @@ -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Catalog.API.Grpc; -using global::Catalog.API.Infrastructure.Filters; -using global::Catalog.API.IntegrationEvents; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.ServiceBus; -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling; -using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using RabbitMQ.Client; -using System; -using System.Data.Common; -using System.IO; -using System.Reflection; - -namespace Microsoft.eShopOnContainers.Services.Catalog.API +namespace Microsoft.eShopOnContainers.Services.Catalog.API; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - public IServiceProvider ConfigureServices(IServiceCollection services) - { - services.AddAppInsight(Configuration) - .AddGrpc().Services - .AddCustomMVC(Configuration) - .AddCustomDbContext(Configuration) - .AddCustomOptions(Configuration) - .AddIntegrationServices(Configuration) - .AddEventBus(Configuration) - .AddSwagger(Configuration) - .AddCustomHealthCheck(Configuration); - - var container = new ContainerBuilder(); - container.Populate(services); - - return new AutofacServiceProvider(container.Build()); - } + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddAppInsight(Configuration) + .AddGrpc().Services + .AddCustomMVC(Configuration) + .AddCustomDbContext(Configuration) + .AddCustomOptions(Configuration) + .AddIntegrationServices(Configuration) + .AddEventBus(Configuration) + .AddSwagger(Configuration) + .AddCustomHealthCheck(Configuration); + + var container = new ContainerBuilder(); + container.Populate(services); + + return new AutofacServiceProvider(container.Build()); + } - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) - { - //Configure logs + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + { + //Configure logs - //loggerFactory.AddAzureWebAppDiagnostics(); - //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); - var pathBase = Configuration["PATH_BASE"]; + var pathBase = Configuration["PATH_BASE"]; - if (!string.IsNullOrEmpty(pathBase)) + if (!string.IsNullOrEmpty(pathBase)) + { + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); + app.UsePathBase(pathBase); + } + + app.UseSwagger() + .UseSwaggerUI(c => { - loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); - app.UsePathBase(pathBase); - } - - app.UseSwagger() - .UseSwaggerUI(c => - { - c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Catalog.API V1"); - }); - - app.UseRouting(); - app.UseCors("CorsPolicy"); - app.UseEndpoints(endpoints => + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Catalog.API V1"); + }); + + app.UseRouting(); + app.UseCors("CorsPolicy"); + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + endpoints.MapGet("/_proto/", async ctx => { - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - endpoints.MapGet("/_proto/", async ctx => + ctx.Response.ContentType = "text/plain"; + using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "catalog.proto"), FileMode.Open, FileAccess.Read); + using var sr = new StreamReader(fs); + while (!sr.EndOfStream) { - ctx.Response.ContentType = "text/plain"; - using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "catalog.proto"), FileMode.Open, FileAccess.Read); - using var sr = new StreamReader(fs); - while (!sr.EndOfStream) + var line = await sr.ReadLineAsync(); + if (line != "/* >>" || line != "<< */") { - var line = await sr.ReadLineAsync(); - if (line != "/* >>" || line != "<< */") - { - await ctx.Response.WriteAsync(line); - } + await ctx.Response.WriteAsync(line); } - }); - endpoints.MapGrpcService(); - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); + } + }); + endpoints.MapGrpcService(); + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions + { + Predicate = r => r.Name.Contains("self") }); + }); - ConfigureEventBus(app); - } + ConfigureEventBus(app); + } - protected virtual void ConfigureEventBus(IApplicationBuilder app) - { - var eventBus = app.ApplicationServices.GetRequiredService(); - eventBus.Subscribe(); - eventBus.Subscribe(); - } + protected virtual void ConfigureEventBus(IApplicationBuilder app) + { + var eventBus = app.ApplicationServices.GetRequiredService(); + eventBus.Subscribe(); + eventBus.Subscribe(); } +} - public static class CustomExtensionMethods +public static class CustomExtensionMethods +{ + public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) - { - services.AddApplicationInsightsTelemetry(configuration); - services.AddApplicationInsightsKubernetesEnricher(); + services.AddApplicationInsightsTelemetry(configuration); + services.AddApplicationInsightsKubernetesEnricher(); - return services; - } + return services; + } - public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration) + { + services.AddControllers(options => { - services.AddControllers(options => - { - options.Filters.Add(typeof(HttpGlobalExceptionFilter)); - }) - .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }) + .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); - services.AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder - .SetIsOriginAllowed((host) => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); - }); + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder + .SetIsOriginAllowed((host) => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + return services; + } - return services; - } + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var accountName = configuration.GetValue("AzureStorageAccountName"); + var accountKey = configuration.GetValue("AzureStorageAccountKey"); - public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) - { - var accountName = configuration.GetValue("AzureStorageAccountName"); - var accountKey = configuration.GetValue("AzureStorageAccountKey"); + var hcBuilder = services.AddHealthChecks(); - var hcBuilder = services.AddHealthChecks(); + hcBuilder + .AddCheck("self", () => HealthCheckResult.Healthy()) + .AddSqlServer( + configuration["ConnectionString"], + name: "CatalogDB-check", + tags: new string[] { "catalogdb" }); + if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) + { hcBuilder - .AddCheck("self", () => HealthCheckResult.Healthy()) - .AddSqlServer( - configuration["ConnectionString"], - name: "CatalogDB-check", - tags: new string[] { "catalogdb" }); - - if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) - { - hcBuilder - .AddAzureBlobStorage( - $"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey};EndpointSuffix=core.windows.net", - name: "catalog-storage-check", - tags: new string[] { "catalogstorage" }); - } - - if (configuration.GetValue("AzureServiceBusEnabled")) - { - hcBuilder - .AddAzureServiceBusTopic( - configuration["EventBusConnection"], - topicName: "eshop_event_bus", - name: "catalog-servicebus-check", - tags: new string[] { "servicebus" }); - } - else - { - hcBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", - name: "catalog-rabbitmqbus-check", - tags: new string[] { "rabbitmqbus" }); - } - - return services; + .AddAzureBlobStorage( + $"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey};EndpointSuffix=core.windows.net", + name: "catalog-storage-check", + tags: new string[] { "catalogstorage" }); } - public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) + if (configuration.GetValue("AzureServiceBusEnabled")) { - services.AddEntityFrameworkSqlServer() - .AddDbContext(options => - { - options.UseSqlServer(configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); - }); + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "catalog-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "catalog-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } - services.AddDbContext(options => - { - options.UseSqlServer(configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); - }); + return services; + } - return services; - } + public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) + { + services.AddEntityFrameworkSqlServer() + .AddDbContext(options => + { + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }); + + services.AddDbContext(options => + { + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }); + + return services; + } - public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration); + services.Configure(options => { - services.Configure(configuration); - services.Configure(options => + options.InvalidModelStateResponseFactory = context => { - options.InvalidModelStateResponseFactory = context => + var problemDetails = new ValidationProblemDetails(context.ModelState) { - var problemDetails = new ValidationProblemDetails(context.ModelState) - { - Instance = context.HttpContext.Request.Path, - Status = StatusCodes.Status400BadRequest, - Detail = "Please refer to the errors property for additional details." - }; + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; - return new BadRequestObjectResult(problemDetails) - { - ContentTypes = { "application/problem+json", "application/problem+xml" } - }; + return new BadRequestObjectResult(problemDetails) + { + ContentTypes = { "application/problem+json", "application/problem+xml" } }; - }); + }; + }); - return services; - } + return services; + } - public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) - { - services.AddSwaggerGen(options => + public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) + { + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo { - options.DescribeAllEnumsAsStrings(); - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "eShopOnContainers - Catalog HTTP API", - Version = "v1", - Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample" - }); + Title = "eShopOnContainers - Catalog HTTP API", + Version = "v1", + Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample" }); + }); - return services; + return services; - } + } - public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) - { - services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); + public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); - services.AddTransient(); + services.AddTransient(); - if (configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var settings = sp.GetRequiredService>().Value; - var serviceBusConnection = new ServiceBusConnectionStringBuilder(settings.EventBusConnection); - var subscriptionClientName = configuration["SubscriptionClientName"]; - - return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); - }); - } - else + if (configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => { - services.AddSingleton(sp => - { - var settings = sp.GetRequiredService>().Value; - var logger = sp.GetRequiredService>(); + var settings = sp.GetRequiredService>().Value; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(settings.EventBusConnection); + var subscriptionClientName = configuration["SubscriptionClientName"]; - var factory = new ConnectionFactory() - { - HostName = configuration["EventBusConnection"], - DispatchConsumersAsync = true - }; + return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); + }); + } + else + { + services.AddSingleton(sp => + { + var settings = sp.GetRequiredService>().Value; + var logger = sp.GetRequiredService>(); - if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) - { - factory.UserName = configuration["EventBusUserName"]; - } + var factory = new ConnectionFactory() + { + HostName = configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; - if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) - { - factory.Password = configuration["EventBusPassword"]; - } + if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) + { + factory.UserName = configuration["EventBusUserName"]; + } - var retryCount = 5; - if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(configuration["EventBusRetryCount"]); - } + if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) + { + factory.Password = configuration["EventBusPassword"]; + } - return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); - }); - } + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } - return services; + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); + }); } - public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) + return services; + } + + public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) + { + if (configuration.GetValue("AzureServiceBusEnabled")) { - if (configuration.GetValue("AzureServiceBusEnabled")) + services.AddSingleton(sp => { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope); - }); + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, iLifetimeScope); + }); - } - else + } + else + { + services.AddSingleton(sp => { - services.AddSingleton(sp => + var subscriptionClientName = configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) { - var subscriptionClientName = configuration["SubscriptionClientName"]; - var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - var retryCount = 5; - if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(configuration["EventBusRetryCount"]); - } + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); - }); - } + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } - services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); - return services; - } + return services; } } diff --git a/src/Services/Catalog/Catalog.API/ViewModel/PaginatedItemsViewModel.cs b/src/Services/Catalog/Catalog.API/ViewModel/PaginatedItemsViewModel.cs index d088d5bb7..d6673012b 100644 --- a/src/Services/Catalog/Catalog.API/ViewModel/PaginatedItemsViewModel.cs +++ b/src/Services/Catalog/Catalog.API/ViewModel/PaginatedItemsViewModel.cs @@ -1,24 +1,20 @@ -namespace Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel -{ - using System.Collections.Generic; - +namespace Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel; - public class PaginatedItemsViewModel where TEntity : class - { - public int PageIndex { get; private set; } +public class PaginatedItemsViewModel where TEntity : class +{ + public int PageIndex { get; private set; } - public int PageSize { get; private set; } + public int PageSize { get; private set; } - public long Count { get; private set; } + public long Count { get; private set; } - public IEnumerable Data { get; private set; } + public IEnumerable Data { get; private set; } - public PaginatedItemsViewModel(int pageIndex, int pageSize, long count, IEnumerable data) - { - PageIndex = pageIndex; - PageSize = pageSize; - Count = count; - Data = data; - } + public PaginatedItemsViewModel(int pageIndex, int pageSize, long count, IEnumerable data) + { + PageIndex = pageIndex; + PageSize = pageSize; + Count = count; + Data = data; } } diff --git a/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj index ab2975756..ae2d23dce 100644 --- a/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj +++ b/src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj @@ -33,8 +33,8 @@ - - + + all diff --git a/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs index 8545d14aa..793d170ae 100644 --- a/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs +++ b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs @@ -1,93 +1,78 @@ -using Catalog.API.Extensions; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.eShopOnContainers.Services.Catalog.API; -using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.IO; -using System.Reflection; +namespace Catalog.FunctionalTests; -namespace Catalog.FunctionalTests +public class CatalogScenariosBase { - public class CatalogScenariosBase + public TestServer CreateServer() { - public TestServer CreateServer() - { - var path = Assembly.GetAssembly(typeof(CatalogScenariosBase)) - .Location; + var path = Assembly.GetAssembly(typeof(CatalogScenariosBase)) + .Location; - var hostBuilder = new WebHostBuilder() - .UseContentRoot(Path.GetDirectoryName(path)) - .ConfigureAppConfiguration(cb => - { - cb.AddJsonFile("appsettings.json", optional: false) - .AddEnvironmentVariables(); - }) - .UseStartup(); + var hostBuilder = new WebHostBuilder() + .UseContentRoot(Path.GetDirectoryName(path)) + .ConfigureAppConfiguration(cb => + { + cb.AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + }) + .UseStartup(); - var testServer = new TestServer(hostBuilder); + var testServer = new TestServer(hostBuilder); - testServer.Host - .MigrateDbContext((context, services) => - { - var env = services.GetService(); - var settings = services.GetService>(); - var logger = services.GetService>(); + testServer.Host + .MigrateDbContext((context, services) => + { + var env = services.GetService(); + var settings = services.GetService>(); + var logger = services.GetService>(); - new CatalogContextSeed() - .SeedAsync(context, env, settings, logger) - .Wait(); - }) - .MigrateDbContext((_, __) => { }); + new CatalogContextSeed() + .SeedAsync(context, env, settings, logger) + .Wait(); + }) + .MigrateDbContext((_, __) => { }); - return testServer; - } + return testServer; + } - public static class Get - { - private const int PageIndex = 0; - private const int PageCount = 4; + public static class Get + { + private const int PageIndex = 0; + private const int PageCount = 4; - public static string Items(bool paginated = false) - { - return paginated - ? "api/v1/catalog/items" + Paginated(PageIndex, PageCount) - : "api/v1/catalog/items"; - } + public static string Items(bool paginated = false) + { + return paginated + ? "api/v1/catalog/items" + Paginated(PageIndex, PageCount) + : "api/v1/catalog/items"; + } - public static string ItemById(int id) - { - return $"api/v1/catalog/items/{id}"; - } + public static string ItemById(int id) + { + return $"api/v1/catalog/items/{id}"; + } - public static string ItemByName(string name, bool paginated = false) - { - return paginated - ? $"api/v1/catalog/items/withname/{name}" + Paginated(PageIndex, PageCount) - : $"api/v1/catalog/items/withname/{name}"; - } + public static string ItemByName(string name, bool paginated = false) + { + return paginated + ? $"api/v1/catalog/items/withname/{name}" + Paginated(PageIndex, PageCount) + : $"api/v1/catalog/items/withname/{name}"; + } - public static string Types = "api/v1/catalog/catalogtypes"; + public static string Types = "api/v1/catalog/catalogtypes"; - public static string Brands = "api/v1/catalog/catalogbrands"; + public static string Brands = "api/v1/catalog/catalogbrands"; - public static string Filtered(int catalogTypeId, int catalogBrandId, bool paginated = false) - { - return paginated - ? $"api/v1/catalog/items/type/{catalogTypeId}/brand/{catalogBrandId}" + Paginated(PageIndex, PageCount) - : $"api/v1/catalog/items/type/{catalogTypeId}/brand/{catalogBrandId}"; - } + public static string Filtered(int catalogTypeId, int catalogBrandId, bool paginated = false) + { + return paginated + ? $"api/v1/catalog/items/type/{catalogTypeId}/brand/{catalogBrandId}" + Paginated(PageIndex, PageCount) + : $"api/v1/catalog/items/type/{catalogTypeId}/brand/{catalogBrandId}"; + } - private static string Paginated(int pageIndex, int pageCount) - { - return $"?pageIndex={pageIndex}&pageSize={pageCount}"; - } + private static string Paginated(int pageIndex, int pageCount) + { + return $"?pageIndex={pageIndex}&pageSize={pageCount}"; } } } diff --git a/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarios.cs b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarios.cs index abb6a6cbf..574d20bcb 100644 --- a/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarios.cs +++ b/src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarios.cs @@ -1,145 +1,140 @@ -using System.Net; -using System.Threading.Tasks; -using Xunit; +namespace Catalog.FunctionalTests; -namespace Catalog.FunctionalTests +public class CatalogScenarios + : CatalogScenariosBase { - public class CatalogScenarios - : CatalogScenariosBase + [Fact] + public async Task Get_get_all_catalogitems_and_response_ok_status_code() { - [Fact] - public async Task Get_get_all_catalogitems_and_response_ok_status_code() + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.Items()); + var response = await server.CreateClient() + .GetAsync(Get.Items()); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_get_catalogitem_by_id_and_response_ok_status_code() + [Fact] + public async Task Get_get_catalogitem_by_id_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.ItemById(1)); + var response = await server.CreateClient() + .GetAsync(Get.ItemById(1)); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_get_catalogitem_by_id_and_response_bad_request_status_code() + [Fact] + public async Task Get_get_catalogitem_by_id_and_response_bad_request_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.ItemById(int.MinValue)); + var response = await server.CreateClient() + .GetAsync(Get.ItemById(int.MinValue)); - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - } + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } + } - [Fact] - public async Task Get_get_catalogitem_by_id_and_response_not_found_status_code() + [Fact] + public async Task Get_get_catalogitem_by_id_and_response_not_found_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.ItemById(int.MaxValue)); + var response = await server.CreateClient() + .GetAsync(Get.ItemById(int.MaxValue)); - Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); - } + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } + } - [Fact] - public async Task Get_get_catalogitem_by_name_and_response_ok_status_code() + [Fact] + public async Task Get_get_catalogitem_by_name_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.ItemByName(".NET")); + var response = await server.CreateClient() + .GetAsync(Get.ItemByName(".NET")); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_get_paginated_catalogitem_by_name_and_response_ok_status_code() + [Fact] + public async Task Get_get_paginated_catalogitem_by_name_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - const bool paginated = true; - var response = await server.CreateClient() - .GetAsync(Get.ItemByName(".NET", paginated)); - - response.EnsureSuccessStatusCode(); - } + const bool paginated = true; + var response = await server.CreateClient() + .GetAsync(Get.ItemByName(".NET", paginated)); + + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_get_paginated_catalog_items_and_response_ok_status_code() + [Fact] + public async Task Get_get_paginated_catalog_items_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - const bool paginated = true; - var response = await server.CreateClient() - .GetAsync(Get.Items(paginated)); - - response.EnsureSuccessStatusCode(); - } + const bool paginated = true; + var response = await server.CreateClient() + .GetAsync(Get.Items(paginated)); + + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_get_filtered_catalog_items_and_response_ok_status_code() + [Fact] + public async Task Get_get_filtered_catalog_items_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.Filtered(1, 1)); + var response = await server.CreateClient() + .GetAsync(Get.Filtered(1, 1)); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_get_paginated_filtered_catalog_items_and_response_ok_status_code() + [Fact] + public async Task Get_get_paginated_filtered_catalog_items_and_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - const bool paginated = true; - var response = await server.CreateClient() - .GetAsync(Get.Filtered(1, 1, paginated)); - - response.EnsureSuccessStatusCode(); - } + const bool paginated = true; + var response = await server.CreateClient() + .GetAsync(Get.Filtered(1, 1, paginated)); + + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_catalog_types_response_ok_status_code() + [Fact] + public async Task Get_catalog_types_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.Types); + var response = await server.CreateClient() + .GetAsync(Get.Types); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } + } - [Fact] - public async Task Get_catalog_brands_response_ok_status_code() + [Fact] + public async Task Get_catalog_brands_response_ok_status_code() + { + using (var server = CreateServer()) { - using (var server = CreateServer()) - { - var response = await server.CreateClient() - .GetAsync(Get.Brands); + var response = await server.CreateClient() + .GetAsync(Get.Brands); - response.EnsureSuccessStatusCode(); - } + response.EnsureSuccessStatusCode(); } } } diff --git a/src/Services/Catalog/Catalog.FunctionalTests/GlobalUsings.cs b/src/Services/Catalog/Catalog.FunctionalTests/GlobalUsings.cs new file mode 100644 index 000000000..d1910b9c2 --- /dev/null +++ b/src/Services/Catalog/Catalog.FunctionalTests/GlobalUsings.cs @@ -0,0 +1,16 @@ +global using Catalog.API.Extensions; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.TestHost; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +global using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; +global using Microsoft.eShopOnContainers.Services.Catalog.API; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using System.IO; +global using System.Net; +global using System.Reflection; +global using System.Threading.Tasks; +global using Xunit; diff --git a/src/Services/Catalog/Catalog.UnitTests/Application/CatalogControllerTest.cs b/src/Services/Catalog/Catalog.UnitTests/Application/CatalogControllerTest.cs index f7529f73c..1a9cdcc9b 100644 --- a/src/Services/Catalog/Catalog.UnitTests/Application/CatalogControllerTest.cs +++ b/src/Services/Catalog/Catalog.UnitTests/Application/CatalogControllerTest.cs @@ -1,4 +1,4 @@ -using Catalog.API.IntegrationEvents; +using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.eShopOnContainers.Services.Catalog.API; @@ -13,120 +13,118 @@ using System.Linq; using System.Threading.Tasks; using Xunit; -namespace UnitTest.Catalog.Application +namespace UnitTest.Catalog.Application; + +public class CatalogControllerTest { - public class CatalogControllerTest + private readonly DbContextOptions _dbOptions; + + public CatalogControllerTest() { - private readonly DbContextOptions _dbOptions; + _dbOptions = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "in-memory") + .Options; - public CatalogControllerTest() + using (var dbContext = new CatalogContext(_dbOptions)) { - _dbOptions = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: "in-memory") - .Options; - - using (var dbContext = new CatalogContext(_dbOptions)) - { - dbContext.AddRange(GetFakeCatalog()); - dbContext.SaveChanges(); - } + dbContext.AddRange(GetFakeCatalog()); + dbContext.SaveChanges(); } + } - [Fact] - public async Task Get_catalog_items_success() - { - //Arrange - var brandFilterApplied = 1; - var typesFilterApplied = 2; - var pageSize = 4; - var pageIndex = 1; - - var expectedItemsInPage = 2; - var expectedTotalItems = 6; + [Fact] + public async Task Get_catalog_items_success() + { + //Arrange + var brandFilterApplied = 1; + var typesFilterApplied = 2; + var pageSize = 4; + var pageIndex = 1; - var catalogContext = new CatalogContext(_dbOptions); - var catalogSettings = new TestCatalogSettings(); + var expectedItemsInPage = 2; + var expectedTotalItems = 6; - var integrationServicesMock = new Mock(); + var catalogContext = new CatalogContext(_dbOptions); + var catalogSettings = new TestCatalogSettings(); - //Act - var orderController = new CatalogController(catalogContext, catalogSettings, integrationServicesMock.Object); - var actionResult = await orderController.ItemsByTypeIdAndBrandIdAsync(typesFilterApplied, brandFilterApplied, pageSize, pageIndex); + var integrationServicesMock = new Mock(); - //Assert - Assert.IsType>>(actionResult); - var page = Assert.IsAssignableFrom>(actionResult.Value); - Assert.Equal(expectedTotalItems, page.Count); - Assert.Equal(pageIndex, page.PageIndex); - Assert.Equal(pageSize, page.PageSize); - Assert.Equal(expectedItemsInPage, page.Data.Count()); - } + //Act + var orderController = new CatalogController(catalogContext, catalogSettings, integrationServicesMock.Object); + var actionResult = await orderController.ItemsByTypeIdAndBrandIdAsync(typesFilterApplied, brandFilterApplied, pageSize, pageIndex); - private List GetFakeCatalog() - { - return new List() - { - new CatalogItem() - { - Id = 1, - Name = "fakeItemA", - CatalogTypeId = 2, - CatalogBrandId = 1, - PictureFileName = "fakeItemA.png" - }, - new CatalogItem() - { - Id = 2, - Name = "fakeItemB", - CatalogTypeId = 2, - CatalogBrandId = 1, - PictureFileName = "fakeItemB.png" - }, - new CatalogItem() - { - Id = 3, - Name = "fakeItemC", - CatalogTypeId = 2, - CatalogBrandId = 1, - PictureFileName = "fakeItemC.png" - }, - new CatalogItem() - { - Id = 4, - Name = "fakeItemD", - CatalogTypeId = 2, - CatalogBrandId = 1, - PictureFileName = "fakeItemD.png" - }, - new CatalogItem() - { - Id = 5, - Name = "fakeItemE", - CatalogTypeId = 2, - CatalogBrandId = 1, - PictureFileName = "fakeItemE.png" - }, - new CatalogItem() - { - Id = 6, - Name = "fakeItemF", - CatalogTypeId = 2, - CatalogBrandId = 1, - PictureFileName = "fakeItemF.png" - } - }; - } + //Assert + Assert.IsType>>(actionResult); + var page = Assert.IsAssignableFrom>(actionResult.Value); + Assert.Equal(expectedTotalItems, page.Count); + Assert.Equal(pageIndex, page.PageIndex); + Assert.Equal(pageSize, page.PageSize); + Assert.Equal(expectedItemsInPage, page.Data.Count()); } - public class TestCatalogSettings : IOptionsSnapshot + private List GetFakeCatalog() { - public CatalogSettings Value => new CatalogSettings + return new List() { - PicBaseUrl = "http://image-server.com/", - AzureStorageEnabled = true + new CatalogItem() + { + Id = 1, + Name = "fakeItemA", + CatalogTypeId = 2, + CatalogBrandId = 1, + PictureFileName = "fakeItemA.png" + }, + new CatalogItem() + { + Id = 2, + Name = "fakeItemB", + CatalogTypeId = 2, + CatalogBrandId = 1, + PictureFileName = "fakeItemB.png" + }, + new CatalogItem() + { + Id = 3, + Name = "fakeItemC", + CatalogTypeId = 2, + CatalogBrandId = 1, + PictureFileName = "fakeItemC.png" + }, + new CatalogItem() + { + Id = 4, + Name = "fakeItemD", + CatalogTypeId = 2, + CatalogBrandId = 1, + PictureFileName = "fakeItemD.png" + }, + new CatalogItem() + { + Id = 5, + Name = "fakeItemE", + CatalogTypeId = 2, + CatalogBrandId = 1, + PictureFileName = "fakeItemE.png" + }, + new CatalogItem() + { + Id = 6, + Name = "fakeItemF", + CatalogTypeId = 2, + CatalogBrandId = 1, + PictureFileName = "fakeItemF.png" + } }; - - public CatalogSettings Get(string name) => Value; } +} + +public class TestCatalogSettings : IOptionsSnapshot +{ + public CatalogSettings Value => new CatalogSettings + { + PicBaseUrl = "http://image-server.com/", + AzureStorageEnabled = true + }; + public CatalogSettings Get(string name) => Value; } diff --git a/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj index cd3f41928..1053a4df2 100644 --- a/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj +++ b/src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj @@ -7,9 +7,9 @@ - + - + all diff --git a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs index 1ce33daa3..74feaa4ce 100644 --- a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs @@ -1,7 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data { - - + using Microsoft.Extensions.Logging; public class ApplicationDbContextSeed { private readonly IPasswordHasher _passwordHasher = new PasswordHasher(); @@ -44,7 +43,7 @@ } } - private IEnumerable GetUsersFromFile(string contentRootPath, Microsoft.Extensions.Logging.ILogger logger) + private IEnumerable GetUsersFromFile(string contentRootPath, ILogger logger) { string csvFileUsers = Path.Combine(contentRootPath, "Setup", "Users.csv"); @@ -178,7 +177,7 @@ return csvheaders; } - static void GetPreconfiguredImages(string contentRootPath, string webroot, Microsoft.Extensions.Logging.ILogger logger) + static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger logger) { try { diff --git a/src/Services/Identity/Identity.API/Devspaces/DevspacesRedirectUriValidator.cs b/src/Services/Identity/Identity.API/Devspaces/DevspacesRedirectUriValidator.cs index aed143f22..8912882f4 100644 --- a/src/Services/Identity/Identity.API/Devspaces/DevspacesRedirectUriValidator.cs +++ b/src/Services/Identity/Identity.API/Devspaces/DevspacesRedirectUriValidator.cs @@ -1,8 +1,10 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Devspaces { + using Microsoft.Extensions.Logging; + public class DevspacesRedirectUriValidator : IRedirectUriValidator { - private readonly Microsoft.Extensions.Logging.ILogger _logger; + private readonly ILogger _logger; public DevspacesRedirectUriValidator(ILogger logger) { _logger = logger; diff --git a/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs b/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs index 8bcb07b89..9521ff6c4 100644 --- a/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs +++ b/src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs @@ -1,23 +1,16 @@ -using MediatR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; - -namespace Ordering.API.Application.Behaviors +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors; +public class LoggingBehavior : IPipelineBehavior { - public class LoggingBehavior : IPipelineBehavior - { - private readonly ILogger> _logger; - public LoggingBehavior(ILogger> logger) => _logger = logger; + private readonly ILogger> _logger; + public LoggingBehavior(ILogger> logger) => _logger = logger; - public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) - { - _logger.LogInformation("----- Handling command {CommandName} ({@Command})", request.GetGenericTypeName(), request); - var response = await next(); - _logger.LogInformation("----- Command {CommandName} handled - response: {@Response}", request.GetGenericTypeName(), response); + public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) + { + _logger.LogInformation("----- Handling command {CommandName} ({@Command})", request.GetGenericTypeName(), request); + var response = await next(); + _logger.LogInformation("----- Command {CommandName} handled - response: {@Response}", request.GetGenericTypeName(), response); - return response; - } + return response; } } + diff --git a/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs b/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs index be32daeee..0417d40f6 100644 --- a/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs +++ b/src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs @@ -1,74 +1,64 @@ -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors; + using Microsoft.Extensions.Logging; -using Ordering.API.Application.IntegrationEvents; -using Serilog.Context; -using System; -using System.Threading; -using System.Threading.Tasks; -namespace Ordering.API.Application.Behaviors +public class TransactionBehaviour : IPipelineBehavior { - public class TransactionBehaviour : IPipelineBehavior + private readonly ILogger> _logger; + private readonly OrderingContext _dbContext; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + + public TransactionBehaviour(OrderingContext dbContext, + IOrderingIntegrationEventService orderingIntegrationEventService, + ILogger> logger) { - private readonly ILogger> _logger; - private readonly OrderingContext _dbContext; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + _dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext)); + _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentException(nameof(orderingIntegrationEventService)); + _logger = logger ?? throw new ArgumentException(nameof(ILogger)); + } - public TransactionBehaviour(OrderingContext dbContext, - IOrderingIntegrationEventService orderingIntegrationEventService, - ILogger> logger) - { - _dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext)); - _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentException(nameof(orderingIntegrationEventService)); - _logger = logger ?? throw new ArgumentException(nameof(ILogger)); - } + public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) + { + var response = default(TResponse); + var typeName = request.GetGenericTypeName(); - public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) + try { - var response = default(TResponse); - var typeName = request.GetGenericTypeName(); - - try + if (_dbContext.HasActiveTransaction) { - if (_dbContext.HasActiveTransaction) - { - return await next(); - } + return await next(); + } - var strategy = _dbContext.Database.CreateExecutionStrategy(); + var strategy = _dbContext.Database.CreateExecutionStrategy(); - await strategy.ExecuteAsync(async () => - { - Guid transactionId; + await strategy.ExecuteAsync(async () => + { + Guid transactionId; - using (var transaction = await _dbContext.BeginTransactionAsync()) - using (LogContext.PushProperty("TransactionContext", transaction.TransactionId)) - { - _logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request); + using (var transaction = await _dbContext.BeginTransactionAsync()) + using (LogContext.PushProperty("TransactionContext", transaction.TransactionId)) + { + _logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request); - response = await next(); + response = await next(); - _logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName); + _logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName); - await _dbContext.CommitTransactionAsync(transaction); + await _dbContext.CommitTransactionAsync(transaction); - transactionId = transaction.TransactionId; - } + transactionId = transaction.TransactionId; + } - await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync(transactionId); - }); + await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync(transactionId); + }); - return response; - } - catch (Exception ex) - { - _logger.LogError(ex, "ERROR Handling transaction for {CommandName} ({@Command})", typeName, request); + return response; + } + catch (Exception ex) + { + _logger.LogError(ex, "ERROR Handling transaction for {CommandName} ({@Command})", typeName, request); - throw; - } + throw; } } } diff --git a/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs b/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs index 00d88da5f..14ae57da3 100644 --- a/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs +++ b/src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs @@ -1,46 +1,36 @@ -using FluentValidation; -using MediatR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.Extensions.Logging; -using Ordering.Domain.Exceptions; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace Ordering.API.Application.Behaviors -{ - public class ValidatorBehavior : IPipelineBehavior - { - private readonly ILogger> _logger; - private readonly IValidator[] _validators; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors; - public ValidatorBehavior(IValidator[] validators, ILogger> logger) - { - _validators = validators; - _logger = logger; - } +public class ValidatorBehavior : IPipelineBehavior +{ + private readonly ILogger> _logger; + private readonly IValidator[] _validators; - public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) - { - var typeName = request.GetGenericTypeName(); + public ValidatorBehavior(IValidator[] validators, ILogger> logger) + { + _validators = validators; + _logger = logger; + } - _logger.LogInformation("----- Validating command {CommandType}", typeName); + public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) + { + var typeName = request.GetGenericTypeName(); - var failures = _validators - .Select(v => v.Validate(request)) - .SelectMany(result => result.Errors) - .Where(error => error != null) - .ToList(); + _logger.LogInformation("----- Validating command {CommandType}", typeName); - if (failures.Any()) - { - _logger.LogWarning("Validation errors - {CommandType} - Command: {@Command} - Errors: {@ValidationErrors}", typeName, request, failures); + var failures = _validators + .Select(v => v.Validate(request)) + .SelectMany(result => result.Errors) + .Where(error => error != null) + .ToList(); - throw new OrderingDomainException( - $"Command Validation Errors for type {typeof(TRequest).Name}", new ValidationException("Validation exception", failures)); - } + if (failures.Any()) + { + _logger.LogWarning("Validation errors - {CommandType} - Command: {@Command} - Errors: {@ValidationErrors}", typeName, request, failures); - return await next(); + throw new OrderingDomainException( + $"Command Validation Errors for type {typeof(TRequest).Name}", new ValidationException("Validation exception", failures)); } + + return await next(); } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs index 408abf2b8..0c7dcb940 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs @@ -1,20 +1,16 @@ -using MediatR; -using System.Runtime.Serialization; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +public class CancelOrderCommand : IRequest { - public class CancelOrderCommand : IRequest - { - [DataMember] - public int OrderNumber { get; set; } - public CancelOrderCommand() - { + [DataMember] + public int OrderNumber { get; set; } + public CancelOrderCommand() + { - } - public CancelOrderCommand(int orderNumber) - { - OrderNumber = orderNumber; - } + } + public CancelOrderCommand(int orderNumber) + { + OrderNumber = orderNumber; } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs index 11313d9f1..74535c3f8 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs @@ -1,57 +1,48 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +// Regular CommandHandler +public class CancelOrderCommandHandler : IRequestHandler { - // Regular CommandHandler - public class CancelOrderCommandHandler : IRequestHandler + private readonly IOrderRepository _orderRepository; + + public CancelOrderCommandHandler(IOrderRepository orderRepository) { - private readonly IOrderRepository _orderRepository; + _orderRepository = orderRepository; + } - public CancelOrderCommandHandler(IOrderRepository orderRepository) + /// + /// Handler which processes the command when + /// customer executes cancel order from app + /// + /// + /// + public async Task Handle(CancelOrderCommand command, CancellationToken cancellationToken) + { + var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); + if (orderToUpdate == null) { - _orderRepository = orderRepository; + return false; } - /// - /// Handler which processes the command when - /// customer executes cancel order from app - /// - /// - /// - public async Task Handle(CancelOrderCommand command, CancellationToken cancellationToken) - { - var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); - if (orderToUpdate == null) - { - return false; - } - - orderToUpdate.SetCancelledStatus(); - return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); - } + orderToUpdate.SetCancelledStatus(); + return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); } +} - // Use for Idempotency in Command process - public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler +// Use for Idempotency in Command process +public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler +{ + public CancelOrderIdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { - public CancelOrderIdentifiedCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - : base(mediator, requestManager, logger) - { - } + } - protected override bool CreateResultForDuplicateRequest() - { - return true; // Ignore duplicate requests for processing order. - } + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs index fa7a2f706..071f0657c 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs @@ -1,106 +1,101 @@ -using MediatR; -using Ordering.API.Application.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; - -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; + +// DDD and CQRS patterns comment: Note that it is recommended to implement immutable Commands +// In this case, its immutability is achieved by having all the setters as private +// plus only being able to update the data just once, when creating the object through its constructor. +// References on Immutable Commands: +// http://cqrs.nu/Faq +// https://docs.spine3.org/motivation/immutability.html +// http://blog.gauffin.org/2012/06/griffin-container-introducing-command-support/ +// https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/how-to-implement-a-lightweight-class-with-auto-implemented-properties + +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; + +[DataContract] +public class CreateOrderCommand + : IRequest { - // DDD and CQRS patterns comment: Note that it is recommended to implement immutable Commands - // In this case, its immutability is achieved by having all the setters as private - // plus only being able to update the data just once, when creating the object through its constructor. - // References on Immutable Commands: - // http://cqrs.nu/Faq - // https://docs.spine3.org/motivation/immutability.html - // http://blog.gauffin.org/2012/06/griffin-container-introducing-command-support/ - // https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/how-to-implement-a-lightweight-class-with-auto-implemented-properties - - [DataContract] - public class CreateOrderCommand - : IRequest - { - [DataMember] - private readonly List _orderItems; + [DataMember] + private readonly List _orderItems; - [DataMember] - public string UserId { get; private set; } + [DataMember] + public string UserId { get; private set; } - [DataMember] - public string UserName { get; private set; } + [DataMember] + public string UserName { get; private set; } - [DataMember] - public string City { get; private set; } + [DataMember] + public string City { get; private set; } - [DataMember] - public string Street { get; private set; } + [DataMember] + public string Street { get; private set; } - [DataMember] - public string State { get; private set; } + [DataMember] + public string State { get; private set; } - [DataMember] - public string Country { get; private set; } + [DataMember] + public string Country { get; private set; } - [DataMember] - public string ZipCode { get; private set; } + [DataMember] + public string ZipCode { get; private set; } - [DataMember] - public string CardNumber { get; private set; } + [DataMember] + public string CardNumber { get; private set; } - [DataMember] - public string CardHolderName { get; private set; } + [DataMember] + public string CardHolderName { get; private set; } - [DataMember] - public DateTime CardExpiration { get; private set; } + [DataMember] + public DateTime CardExpiration { get; private set; } - [DataMember] - public string CardSecurityNumber { get; private set; } + [DataMember] + public string CardSecurityNumber { get; private set; } - [DataMember] - public int CardTypeId { get; private set; } + [DataMember] + public int CardTypeId { get; private set; } - [DataMember] - public IEnumerable OrderItems => _orderItems; + [DataMember] + public IEnumerable OrderItems => _orderItems; - public CreateOrderCommand() - { - _orderItems = new List(); - } + public CreateOrderCommand() + { + _orderItems = new List(); + } - public CreateOrderCommand(List basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode, - string cardNumber, string cardHolderName, DateTime cardExpiration, - string cardSecurityNumber, int cardTypeId) : this() - { - _orderItems = basketItems.ToOrderItemsDTO().ToList(); - UserId = userId; - UserName = userName; - City = city; - Street = street; - State = state; - Country = country; - ZipCode = zipcode; - CardNumber = cardNumber; - CardHolderName = cardHolderName; - CardExpiration = cardExpiration; - CardSecurityNumber = cardSecurityNumber; - CardTypeId = cardTypeId; - CardExpiration = cardExpiration; - } + public CreateOrderCommand(List basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode, + string cardNumber, string cardHolderName, DateTime cardExpiration, + string cardSecurityNumber, int cardTypeId) : this() + { + _orderItems = basketItems.ToOrderItemsDTO().ToList(); + UserId = userId; + UserName = userName; + City = city; + Street = street; + State = state; + Country = country; + ZipCode = zipcode; + CardNumber = cardNumber; + CardHolderName = cardHolderName; + CardExpiration = cardExpiration; + CardSecurityNumber = cardSecurityNumber; + CardTypeId = cardTypeId; + CardExpiration = cardExpiration; + } - public record OrderItemDTO - { - public int ProductId { get; init; } + public record OrderItemDTO + { + public int ProductId { get; init; } - public string ProductName { get; init; } + public string ProductName { get; init; } - public decimal UnitPrice { get; init; } + public decimal UnitPrice { get; init; } - public decimal Discount { get; init; } + public decimal Discount { get; init; } - public int Units { get; init; } + public int Units { get; init; } - public string PictureUrl { get; init; } - } + public string PictureUrl { get; init; } } } + diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs index b2fff253d..e445e5de1 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs @@ -1,82 +1,72 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; + +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + +// Regular CommandHandler +public class CreateOrderCommandHandler + : IRequestHandler { - using Domain.AggregatesModel.OrderAggregate; - using global::Ordering.API.Application.IntegrationEvents; - using global::Ordering.API.Application.IntegrationEvents.Events; - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; - using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; - using Microsoft.Extensions.Logging; - using System; - using System.Threading; - using System.Threading.Tasks; + private readonly IOrderRepository _orderRepository; + private readonly IIdentityService _identityService; + private readonly IMediator _mediator; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + private readonly ILogger _logger; - // Regular CommandHandler - public class CreateOrderCommandHandler - : IRequestHandler + // Using DI to inject infrastructure persistence Repositories + public CreateOrderCommandHandler(IMediator mediator, + IOrderingIntegrationEventService orderingIntegrationEventService, + IOrderRepository orderRepository, + IIdentityService identityService, + ILogger logger) { - private readonly IOrderRepository _orderRepository; - private readonly IIdentityService _identityService; - private readonly IMediator _mediator; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - private readonly ILogger _logger; - - // Using DI to inject infrastructure persistence Repositories - public CreateOrderCommandHandler(IMediator mediator, - IOrderingIntegrationEventService orderingIntegrationEventService, - IOrderRepository orderRepository, - IIdentityService identityService, - ILogger logger) - { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(CreateOrderCommand message, CancellationToken cancellationToken) - { - // Add Integration event to clean the basket - var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId); - await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStartedIntegrationEvent); + public async Task Handle(CreateOrderCommand message, CancellationToken cancellationToken) + { + // Add Integration event to clean the basket + var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId); + await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStartedIntegrationEvent); - // Add/Update the Buyer AggregateRoot - // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root - // methods and constructor so validations, invariants and business logic - // make sure that consistency is preserved across the whole aggregate - var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode); - var order = new Order(message.UserId, message.UserName, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration); + // Add/Update the Buyer AggregateRoot + // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root + // methods and constructor so validations, invariants and business logic + // make sure that consistency is preserved across the whole aggregate + var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode); + var order = new Order(message.UserId, message.UserName, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration); - foreach (var item in message.OrderItems) - { - order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); - } + foreach (var item in message.OrderItems) + { + order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); + } - _logger.LogInformation("----- Creating Order - Order: {@Order}", order); + _logger.LogInformation("----- Creating Order - Order: {@Order}", order); - _orderRepository.Add(order); + _orderRepository.Add(order); - return await _orderRepository.UnitOfWork - .SaveEntitiesAsync(cancellationToken); - } + return await _orderRepository.UnitOfWork + .SaveEntitiesAsync(cancellationToken); } +} - // Use for Idempotency in Command process - public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler +// Use for Idempotency in Command process +public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler +{ + public CreateOrderIdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { - public CreateOrderIdentifiedCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - : base(mediator, requestManager, logger) - { - } + } - protected override bool CreateResultForDuplicateRequest() - { - return true; // Ignore duplicate requests for creating order. - } + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for creating order. } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs index bb3e92378..4866212d9 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs @@ -1,21 +1,16 @@ -using MediatR; -using Ordering.API.Application.Models; -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands +public class CreateOrderDraftCommand : IRequest { - public class CreateOrderDraftCommand : IRequest - { - public string BuyerId { get; private set; } + public string BuyerId { get; private set; } - public IEnumerable Items { get; private set; } + public IEnumerable Items { get; private set; } - public CreateOrderDraftCommand(string buyerId, IEnumerable items) - { - BuyerId = buyerId; - Items = items; - } + public CreateOrderDraftCommand(string buyerId, IEnumerable items) + { + BuyerId = buyerId; + Items = items; } - } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs index abca17b8c..32d965fd0 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs @@ -1,71 +1,58 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands -{ - using Domain.AggregatesModel.OrderAggregate; - using global::Ordering.API.Application.Models; - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; - - // Regular CommandHandler - public class CreateOrderDraftCommandHandler - : IRequestHandler - { - private readonly IOrderRepository _orderRepository; - private readonly IIdentityService _identityService; - private readonly IMediator _mediator; - - // Using DI to inject infrastructure persistence Repositories - public CreateOrderDraftCommandHandler(IMediator mediator, IIdentityService identityService) - { - _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - } +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; - public Task Handle(CreateOrderDraftCommand message, CancellationToken cancellationToken) - { +using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; - var order = Order.NewDraft(); - var orderItems = message.Items.Select(i => i.ToOrderItemDTO()); - foreach (var item in orderItems) - { - order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); - } +// Regular CommandHandler +public class CreateOrderDraftCommandHandler + : IRequestHandler +{ + private readonly IOrderRepository _orderRepository; + private readonly IIdentityService _identityService; + private readonly IMediator _mediator; - return Task.FromResult(OrderDraftDTO.FromOrder(order)); - } + // Using DI to inject infrastructure persistence Repositories + public CreateOrderDraftCommandHandler(IMediator mediator, IIdentityService identityService) + { + _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); } - - public record OrderDraftDTO + public Task Handle(CreateOrderDraftCommand message, CancellationToken cancellationToken) { - public IEnumerable OrderItems { get; init; } - public decimal Total { get; init; } - public static OrderDraftDTO FromOrder(Order order) + var order = Order.NewDraft(); + var orderItems = message.Items.Select(i => i.ToOrderItemDTO()); + foreach (var item in orderItems) { - return new OrderDraftDTO() - { - OrderItems = order.OrderItems.Select(oi => new OrderItemDTO - { - Discount = oi.GetCurrentDiscount(), - ProductId = oi.ProductId, - UnitPrice = oi.GetUnitPrice(), - PictureUrl = oi.GetPictureUri(), - Units = oi.GetUnits(), - ProductName = oi.GetOrderItemProductName() - }), - Total = order.GetTotal() - }; + order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); } + return Task.FromResult(OrderDraftDTO.FromOrder(order)); } +} +public record OrderDraftDTO +{ + public IEnumerable OrderItems { get; init; } + public decimal Total { get; init; } + public static OrderDraftDTO FromOrder(Order order) + { + return new OrderDraftDTO() + { + OrderItems = order.OrderItems.Select(oi => new OrderItemDTO + { + Discount = oi.GetCurrentDiscount(), + ProductId = oi.ProductId, + UnitPrice = oi.GetUnitPrice(), + PictureUrl = oi.GetPictureUri(), + Units = oi.GetUnits(), + ProductName = oi.GetOrderItemProductName() + }), + Total = order.GetTotal() + }; + } -} +} \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommand.cs index 9486cd143..5109ced4e 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommand.cs @@ -1,17 +1,13 @@ -using MediatR; -using System; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands +public class IdentifiedCommand : IRequest + where T : IRequest { - public class IdentifiedCommand : IRequest - where T : IRequest + public T Command { get; } + public Guid Id { get; } + public IdentifiedCommand(T command, Guid id) { - public T Command { get; } - public Guid Id { get; } - public IdentifiedCommand(T command, Guid id) - { - Command = command; - Id = id; - } + Command = command; + Id = id; } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs index be94e414c..38c8a356d 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs @@ -1,116 +1,107 @@ -using MediatR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.Commands; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands +/// +/// Provides a base implementation for handling duplicate request and ensuring idempotent updates, in the cases where +/// a requestid sent by client is used to detect duplicate requests. +/// +/// Type of the command handler that performs the operation if request is not duplicated +/// Return value of the inner command handler +public class IdentifiedCommandHandler : IRequestHandler, R> + where T : IRequest { + private readonly IMediator _mediator; + private readonly IRequestManager _requestManager; + private readonly ILogger> _logger; + + public IdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + { + _mediator = mediator; + _requestManager = requestManager; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + } + /// - /// Provides a base implementation for handling duplicate request and ensuring idempotent updates, in the cases where - /// a requestid sent by client is used to detect duplicate requests. + /// Creates the result value to return if a previous request was found /// - /// Type of the command handler that performs the operation if request is not duplicated - /// Return value of the inner command handler - public class IdentifiedCommandHandler : IRequestHandler, R> - where T : IRequest + /// + protected virtual R CreateResultForDuplicateRequest() { - private readonly IMediator _mediator; - private readonly IRequestManager _requestManager; - private readonly ILogger> _logger; - - public IdentifiedCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - { - _mediator = mediator; - _requestManager = requestManager; - _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - } + return default(R); + } - /// - /// Creates the result value to return if a previous request was found - /// - /// - protected virtual R CreateResultForDuplicateRequest() + /// + /// This method handles the command. It just ensures that no other request exists with the same ID, and if this is the case + /// just enqueues the original inner command. + /// + /// IdentifiedCommand which contains both original command & request ID + /// Return value of inner command or default value if request same ID was found + public async Task Handle(IdentifiedCommand message, CancellationToken cancellationToken) + { + var alreadyExists = await _requestManager.ExistAsync(message.Id); + if (alreadyExists) { - return default(R); + return CreateResultForDuplicateRequest(); } - - /// - /// This method handles the command. It just ensures that no other request exists with the same ID, and if this is the case - /// just enqueues the original inner command. - /// - /// IdentifiedCommand which contains both original command & request ID - /// Return value of inner command or default value if request same ID was found - public async Task Handle(IdentifiedCommand message, CancellationToken cancellationToken) + else { - var alreadyExists = await _requestManager.ExistAsync(message.Id); - if (alreadyExists) - { - return CreateResultForDuplicateRequest(); - } - else + await _requestManager.CreateRequestForCommandAsync(message.Id); + try { - await _requestManager.CreateRequestForCommandAsync(message.Id); - try - { - var command = message.Command; - var commandName = command.GetGenericTypeName(); - var idProperty = string.Empty; - var commandId = string.Empty; + var command = message.Command; + var commandName = command.GetGenericTypeName(); + var idProperty = string.Empty; + var commandId = string.Empty; - switch (command) - { - case CreateOrderCommand createOrderCommand: - idProperty = nameof(createOrderCommand.UserId); - commandId = createOrderCommand.UserId; - break; + switch (command) + { + case CreateOrderCommand createOrderCommand: + idProperty = nameof(createOrderCommand.UserId); + commandId = createOrderCommand.UserId; + break; - case CancelOrderCommand cancelOrderCommand: - idProperty = nameof(cancelOrderCommand.OrderNumber); - commandId = $"{cancelOrderCommand.OrderNumber}"; - break; + case CancelOrderCommand cancelOrderCommand: + idProperty = nameof(cancelOrderCommand.OrderNumber); + commandId = $"{cancelOrderCommand.OrderNumber}"; + break; - case ShipOrderCommand shipOrderCommand: - idProperty = nameof(shipOrderCommand.OrderNumber); - commandId = $"{shipOrderCommand.OrderNumber}"; - break; + case ShipOrderCommand shipOrderCommand: + idProperty = nameof(shipOrderCommand.OrderNumber); + commandId = $"{shipOrderCommand.OrderNumber}"; + break; - default: - idProperty = "Id?"; - commandId = "n/a"; - break; - } + default: + idProperty = "Id?"; + commandId = "n/a"; + break; + } - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - commandName, - idProperty, - commandId, - command); + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + commandName, + idProperty, + commandId, + command); - // Send the embeded business command to mediator so it runs its related CommandHandler - var result = await _mediator.Send(command, cancellationToken); + // Send the embeded business command to mediator so it runs its related CommandHandler + var result = await _mediator.Send(command, cancellationToken); - _logger.LogInformation( - "----- Command result: {@Result} - {CommandName} - {IdProperty}: {CommandId} ({@Command})", - result, - commandName, - idProperty, - commandId, - command); + _logger.LogInformation( + "----- Command result: {@Result} - {CommandName} - {IdProperty}: {CommandId} ({@Command})", + result, + commandName, + idProperty, + commandId, + command); - return result; - } - catch - { - return default(R); - } + return result; + } + catch + { + return default(R); } } } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommand.cs index 980211f87..99337f13e 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommand.cs @@ -1,17 +1,13 @@ -using MediatR; -using System.Runtime.Serialization; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +public class SetAwaitingValidationOrderStatusCommand : IRequest { - public class SetAwaitingValidationOrderStatusCommand : IRequest - { - [DataMember] - public int OrderNumber { get; private set; } + [DataMember] + public int OrderNumber { get; private set; } - public SetAwaitingValidationOrderStatusCommand(int orderNumber) - { - OrderNumber = orderNumber; - } + public SetAwaitingValidationOrderStatusCommand(int orderNumber) + { + OrderNumber = orderNumber; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs index da6969404..c8bd37d75 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs @@ -1,57 +1,48 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +// Regular CommandHandler +public class SetAwaitingValidationOrderStatusCommandHandler : IRequestHandler { - // Regular CommandHandler - public class SetAwaitingValidationOrderStatusCommandHandler : IRequestHandler + private readonly IOrderRepository _orderRepository; + + public SetAwaitingValidationOrderStatusCommandHandler(IOrderRepository orderRepository) { - private readonly IOrderRepository _orderRepository; + _orderRepository = orderRepository; + } - public SetAwaitingValidationOrderStatusCommandHandler(IOrderRepository orderRepository) + /// + /// Handler which processes the command when + /// graceperiod has finished + /// + /// + /// + public async Task Handle(SetAwaitingValidationOrderStatusCommand command, CancellationToken cancellationToken) + { + var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); + if (orderToUpdate == null) { - _orderRepository = orderRepository; + return false; } - /// - /// Handler which processes the command when - /// graceperiod has finished - /// - /// - /// - public async Task Handle(SetAwaitingValidationOrderStatusCommand command, CancellationToken cancellationToken) - { - var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); - if (orderToUpdate == null) - { - return false; - } - - orderToUpdate.SetAwaitingValidationStatus(); - return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); - } + orderToUpdate.SetAwaitingValidationStatus(); + return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); } +} - // Use for Idempotency in Command process - public class SetAwaitingValidationIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler +// Use for Idempotency in Command process +public class SetAwaitingValidationIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler +{ + public SetAwaitingValidationIdentifiedOrderStatusCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { - public SetAwaitingValidationIdentifiedOrderStatusCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - : base(mediator, requestManager, logger) - { - } + } - protected override bool CreateResultForDuplicateRequest() - { - return true; // Ignore duplicate requests for processing order. - } + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommand.cs index d561ad7f3..0e95aac8d 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommand.cs @@ -1,17 +1,13 @@ -using MediatR; -using System.Runtime.Serialization; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +public class SetPaidOrderStatusCommand : IRequest { - public class SetPaidOrderStatusCommand : IRequest - { - [DataMember] - public int OrderNumber { get; private set; } + [DataMember] + public int OrderNumber { get; private set; } - public SetPaidOrderStatusCommand(int orderNumber) - { - OrderNumber = orderNumber; - } + public SetPaidOrderStatusCommand(int orderNumber) + { + OrderNumber = orderNumber; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs index f4268e9ca..09520a65f 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs @@ -1,60 +1,51 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; - -namespace Ordering.API.Application.Commands +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; + +// Regular CommandHandler +public class SetPaidOrderStatusCommandHandler : IRequestHandler { - // Regular CommandHandler - public class SetPaidOrderStatusCommandHandler : IRequestHandler + private readonly IOrderRepository _orderRepository; + + public SetPaidOrderStatusCommandHandler(IOrderRepository orderRepository) { - private readonly IOrderRepository _orderRepository; + _orderRepository = orderRepository; + } - public SetPaidOrderStatusCommandHandler(IOrderRepository orderRepository) - { - _orderRepository = orderRepository; - } + /// + /// Handler which processes the command when + /// Shipment service confirms the payment + /// + /// + /// + public async Task Handle(SetPaidOrderStatusCommand command, CancellationToken cancellationToken) + { + // Simulate a work time for validating the payment + await Task.Delay(10000, cancellationToken); - /// - /// Handler which processes the command when - /// Shipment service confirms the payment - /// - /// - /// - public async Task Handle(SetPaidOrderStatusCommand command, CancellationToken cancellationToken) + var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); + if (orderToUpdate == null) { - // Simulate a work time for validating the payment - await Task.Delay(10000, cancellationToken); - - var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); - if (orderToUpdate == null) - { - return false; - } - - orderToUpdate.SetPaidStatus(); - return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); + return false; } + + orderToUpdate.SetPaidStatus(); + return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); } +} - // Use for Idempotency in Command process - public class SetPaidIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler +// Use for Idempotency in Command process +public class SetPaidIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler +{ + public SetPaidIdentifiedOrderStatusCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { - public SetPaidIdentifiedOrderStatusCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - : base(mediator, requestManager, logger) - { - } + } - protected override bool CreateResultForDuplicateRequest() - { - return true; // Ignore duplicate requests for processing order. - } + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommand.cs index 9e0a40915..b4883ec49 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommand.cs @@ -1,17 +1,13 @@ -using MediatR; -using System.Runtime.Serialization; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +public class SetStockConfirmedOrderStatusCommand : IRequest { - public class SetStockConfirmedOrderStatusCommand : IRequest - { - [DataMember] - public int OrderNumber { get; private set; } + [DataMember] + public int OrderNumber { get; private set; } - public SetStockConfirmedOrderStatusCommand(int orderNumber) - { - OrderNumber = orderNumber; - } + public SetStockConfirmedOrderStatusCommand(int orderNumber) + { + OrderNumber = orderNumber; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs index c99daa23a..df87db63c 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs @@ -1,60 +1,51 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; - -namespace Ordering.API.Application.Commands +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; + +// Regular CommandHandler +public class SetStockConfirmedOrderStatusCommandHandler : IRequestHandler { - // Regular CommandHandler - public class SetStockConfirmedOrderStatusCommandHandler : IRequestHandler + private readonly IOrderRepository _orderRepository; + + public SetStockConfirmedOrderStatusCommandHandler(IOrderRepository orderRepository) { - private readonly IOrderRepository _orderRepository; + _orderRepository = orderRepository; + } - public SetStockConfirmedOrderStatusCommandHandler(IOrderRepository orderRepository) - { - _orderRepository = orderRepository; - } + /// + /// Handler which processes the command when + /// Stock service confirms the request + /// + /// + /// + public async Task Handle(SetStockConfirmedOrderStatusCommand command, CancellationToken cancellationToken) + { + // Simulate a work time for confirming the stock + await Task.Delay(10000, cancellationToken); - /// - /// Handler which processes the command when - /// Stock service confirms the request - /// - /// - /// - public async Task Handle(SetStockConfirmedOrderStatusCommand command, CancellationToken cancellationToken) + var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); + if (orderToUpdate == null) { - // Simulate a work time for confirming the stock - await Task.Delay(10000, cancellationToken); - - var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); - if (orderToUpdate == null) - { - return false; - } - - orderToUpdate.SetStockConfirmedStatus(); - return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); + return false; } + + orderToUpdate.SetStockConfirmedStatus(); + return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); } +} - // Use for Idempotency in Command process - public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler +// Use for Idempotency in Command process +public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler +{ + public SetStockConfirmedOrderStatusIdenfifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { - public SetStockConfirmedOrderStatusIdenfifiedCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - : base(mediator, requestManager, logger) - { - } + } - protected override bool CreateResultForDuplicateRequest() - { - return true; // Ignore duplicate requests for processing order. - } + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommand.cs index cb39c5567..e8b3cac4d 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommand.cs @@ -1,22 +1,17 @@ -using MediatR; -using System.Collections.Generic; -using System.Runtime.Serialization; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +public class SetStockRejectedOrderStatusCommand : IRequest { - public class SetStockRejectedOrderStatusCommand : IRequest - { - [DataMember] - public int OrderNumber { get; private set; } + [DataMember] + public int OrderNumber { get; private set; } - [DataMember] - public List OrderStockItems { get; private set; } + [DataMember] + public List OrderStockItems { get; private set; } - public SetStockRejectedOrderStatusCommand(int orderNumber, List orderStockItems) - { - OrderNumber = orderNumber; - OrderStockItems = orderStockItems; - } + public SetStockRejectedOrderStatusCommand(int orderNumber, List orderStockItems) + { + OrderNumber = orderNumber; + OrderStockItems = orderStockItems; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs index 551b2744d..fcef729e6 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs @@ -1,61 +1,52 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; - -namespace Ordering.API.Application.Commands +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; + +// Regular CommandHandler +public class SetStockRejectedOrderStatusCommandHandler : IRequestHandler { - // Regular CommandHandler - public class SetStockRejectedOrderStatusCommandHandler : IRequestHandler + private readonly IOrderRepository _orderRepository; + + public SetStockRejectedOrderStatusCommandHandler(IOrderRepository orderRepository) { - private readonly IOrderRepository _orderRepository; + _orderRepository = orderRepository; + } - public SetStockRejectedOrderStatusCommandHandler(IOrderRepository orderRepository) - { - _orderRepository = orderRepository; - } + /// + /// Handler which processes the command when + /// Stock service rejects the request + /// + /// + /// + public async Task Handle(SetStockRejectedOrderStatusCommand command, CancellationToken cancellationToken) + { + // Simulate a work time for rejecting the stock + await Task.Delay(10000, cancellationToken); - /// - /// Handler which processes the command when - /// Stock service rejects the request - /// - /// - /// - public async Task Handle(SetStockRejectedOrderStatusCommand command, CancellationToken cancellationToken) + var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); + if (orderToUpdate == null) { - // Simulate a work time for rejecting the stock - await Task.Delay(10000, cancellationToken); - - var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); - if (orderToUpdate == null) - { - return false; - } + return false; + } - orderToUpdate.SetCancelledStatusWhenStockIsRejected(command.OrderStockItems); + orderToUpdate.SetCancelledStatusWhenStockIsRejected(command.OrderStockItems); - return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); - } + return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); } +} - // Use for Idempotency in Command process - public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler +// Use for Idempotency in Command process +public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler +{ + public SetStockRejectedOrderStatusIdenfifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { - public SetStockRejectedOrderStatusIdenfifiedCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - : base(mediator, requestManager, logger) - { - } + } - protected override bool CreateResultForDuplicateRequest() - { - return true; // Ignore duplicate requests for processing order. - } + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. } } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommand.cs index e267e6b66..c8fecde2b 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommand.cs @@ -1,17 +1,13 @@ -using MediatR; -using System.Runtime.Serialization; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +public class ShipOrderCommand : IRequest { - public class ShipOrderCommand : IRequest - { - [DataMember] - public int OrderNumber { get; private set; } + [DataMember] + public int OrderNumber { get; private set; } - public ShipOrderCommand(int orderNumber) - { - OrderNumber = orderNumber; - } + public ShipOrderCommand(int orderNumber) + { + OrderNumber = orderNumber; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs index 4e1173c05..58b09f53b 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs @@ -1,57 +1,48 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -namespace Ordering.API.Application.Commands +// Regular CommandHandler +public class ShipOrderCommandHandler : IRequestHandler { - // Regular CommandHandler - public class ShipOrderCommandHandler : IRequestHandler + private readonly IOrderRepository _orderRepository; + + public ShipOrderCommandHandler(IOrderRepository orderRepository) { - private readonly IOrderRepository _orderRepository; + _orderRepository = orderRepository; + } - public ShipOrderCommandHandler(IOrderRepository orderRepository) + /// + /// Handler which processes the command when + /// administrator executes ship order from app + /// + /// + /// + public async Task Handle(ShipOrderCommand command, CancellationToken cancellationToken) + { + var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); + if (orderToUpdate == null) { - _orderRepository = orderRepository; + return false; } - /// - /// Handler which processes the command when - /// administrator executes ship order from app - /// - /// - /// - public async Task Handle(ShipOrderCommand command, CancellationToken cancellationToken) - { - var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); - if (orderToUpdate == null) - { - return false; - } - - orderToUpdate.SetShippedStatus(); - return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); - } + orderToUpdate.SetShippedStatus(); + return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); } +} - // Use for Idempotency in Command process - public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler +// Use for Idempotency in Command process +public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler +{ + public ShipOrderIdentifiedCommandHandler( + IMediator mediator, + IRequestManager requestManager, + ILogger> logger) + : base(mediator, requestManager, logger) { - public ShipOrderIdentifiedCommandHandler( - IMediator mediator, - IRequestManager requestManager, - ILogger> logger) - : base(mediator, requestManager, logger) - { - } + } - protected override bool CreateResultForDuplicateRequest() - { - return true; // Ignore duplicate requests for processing order. - } + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. } } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs index bea8eaac5..919a78a45 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs @@ -1,38 +1,29 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.Extensions.Logging; -using Ordering.Domain.Events; -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified; -namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified +public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler + : INotificationHandler { - public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler - : INotificationHandler - { - private readonly IOrderRepository _orderRepository; - private readonly ILoggerFactory _logger; + private readonly IOrderRepository _orderRepository; + private readonly ILoggerFactory _logger; - public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler( - IOrderRepository orderRepository, ILoggerFactory logger) - { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler( + IOrderRepository orderRepository, ILoggerFactory logger) + { + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - // Domain Logic comment: - // When the Buyer and Buyer's payment method have been created or verified that they existed, - // then we can update the original Order with the BuyerId and PaymentId (foreign keys) - public async Task Handle(BuyerAndPaymentMethodVerifiedDomainEvent buyerPaymentMethodVerifiedEvent, CancellationToken cancellationToken) - { - var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId); - orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id); - orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); + // Domain Logic comment: + // When the Buyer and Buyer's payment method have been created or verified that they existed, + // then we can update the original Order with the BuyerId and PaymentId (foreign keys) + public async Task Handle(BuyerAndPaymentMethodVerifiedDomainEvent buyerPaymentMethodVerifiedEvent, CancellationToken cancellationToken) + { + var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId); + orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id); + orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); - _logger.CreateLogger() - .LogTrace("Order with Id: {OrderId} has been successfully updated with a payment method {PaymentMethod} ({Id})", - buyerPaymentMethodVerifiedEvent.OrderId, nameof(buyerPaymentMethodVerifiedEvent.Payment), buyerPaymentMethodVerifiedEvent.Payment.Id); - } + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated with a payment method {PaymentMethod} ({Id})", + buyerPaymentMethodVerifiedEvent.OrderId, nameof(buyerPaymentMethodVerifiedEvent.Payment), buyerPaymentMethodVerifiedEvent.Payment.Id); } } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs index f8fddf3a8..dbd8abceb 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs @@ -1,47 +1,37 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.IntegrationEvents; -using Ordering.API.Application.IntegrationEvents.Events; -using Ordering.Domain.Events; -using System; -using System.Threading; -using System.Threading.Tasks; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; -namespace Ordering.API.Application.DomainEventHandlers.OrderCancelled +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderCancelled; + +public class OrderCancelledDomainEventHandler + : INotificationHandler { - public class OrderCancelledDomainEventHandler - : INotificationHandler - { - private readonly IOrderRepository _orderRepository; - private readonly IBuyerRepository _buyerRepository; - private readonly ILoggerFactory _logger; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + private readonly IOrderRepository _orderRepository; + private readonly IBuyerRepository _buyerRepository; + private readonly ILoggerFactory _logger; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - public OrderCancelledDomainEventHandler( - IOrderRepository orderRepository, - ILoggerFactory logger, - IBuyerRepository buyerRepository, - IOrderingIntegrationEventService orderingIntegrationEventService) - { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); - _orderingIntegrationEventService = orderingIntegrationEventService; - } + public OrderCancelledDomainEventHandler( + IOrderRepository orderRepository, + ILoggerFactory logger, + IBuyerRepository buyerRepository, + IOrderingIntegrationEventService orderingIntegrationEventService) + { + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); + _orderingIntegrationEventService = orderingIntegrationEventService; + } - public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) - { - _logger.CreateLogger() - .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", - orderCancelledDomainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id); + public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) + { + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderCancelledDomainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id); - var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id); - var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); + var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id); + var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); - var orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); - await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToCancelledIntegrationEvent); - } + var orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); + await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToCancelledIntegrationEvent); } } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs index 82904fdd7..2aa3867be 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs @@ -1,52 +1,39 @@ -namespace Ordering.API.Application.DomainEventHandlers.OrderGracePeriodConfirmed +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderGracePeriodConfirmed; + +public class OrderStatusChangedToAwaitingValidationDomainEventHandler + : INotificationHandler { - using Domain.Events; - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; - using Microsoft.Extensions.Logging; - using Ordering.API.Application.IntegrationEvents; - using Ordering.API.Application.IntegrationEvents.Events; - using System; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; + private readonly IOrderRepository _orderRepository; + private readonly ILoggerFactory _logger; + private readonly IBuyerRepository _buyerRepository; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - public class OrderStatusChangedToAwaitingValidationDomainEventHandler - : INotificationHandler + public OrderStatusChangedToAwaitingValidationDomainEventHandler( + IOrderRepository orderRepository, ILoggerFactory logger, + IBuyerRepository buyerRepository, + IOrderingIntegrationEventService orderingIntegrationEventService) { - private readonly IOrderRepository _orderRepository; - private readonly ILoggerFactory _logger; - private readonly IBuyerRepository _buyerRepository; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - - public OrderStatusChangedToAwaitingValidationDomainEventHandler( - IOrderRepository orderRepository, ILoggerFactory logger, - IBuyerRepository buyerRepository, - IOrderingIntegrationEventService orderingIntegrationEventService) - { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _buyerRepository = buyerRepository; - _orderingIntegrationEventService = orderingIntegrationEventService; - } + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _buyerRepository = buyerRepository; + _orderingIntegrationEventService = orderingIntegrationEventService; + } - public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent, CancellationToken cancellationToken) - { - _logger.CreateLogger() - .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", - orderStatusChangedToAwaitingValidationDomainEvent.OrderId, nameof(OrderStatus.AwaitingValidation), OrderStatus.AwaitingValidation.Id); + public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent, CancellationToken cancellationToken) + { + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderStatusChangedToAwaitingValidationDomainEvent.OrderId, nameof(OrderStatus.AwaitingValidation), OrderStatus.AwaitingValidation.Id); - var order = await _orderRepository.GetAsync(orderStatusChangedToAwaitingValidationDomainEvent.OrderId); + var order = await _orderRepository.GetAsync(orderStatusChangedToAwaitingValidationDomainEvent.OrderId); - var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); + var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); - var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems - .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); + var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems + .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); - var orderStatusChangedToAwaitingValidationIntegrationEvent = new OrderStatusChangedToAwaitingValidationIntegrationEvent( - order.Id, order.OrderStatus.Name, buyer.Name, orderStockList); - await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToAwaitingValidationIntegrationEvent); - } + var orderStatusChangedToAwaitingValidationIntegrationEvent = new OrderStatusChangedToAwaitingValidationIntegrationEvent( + order.Id, order.OrderStatus.Name, buyer.Name, orderStockList); + await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToAwaitingValidationIntegrationEvent); } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs index fac62e7e2..7f0138d71 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs @@ -1,57 +1,44 @@ -namespace Ordering.API.Application.DomainEventHandlers.OrderPaid +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderPaid; + +public class OrderStatusChangedToPaidDomainEventHandler + : INotificationHandler { - using Domain.Events; - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; - using Microsoft.Extensions.Logging; - using Ordering.API.Application.IntegrationEvents; - using Ordering.API.Application.IntegrationEvents.Events; - using System; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - - public class OrderStatusChangedToPaidDomainEventHandler - : INotificationHandler - { - private readonly IOrderRepository _orderRepository; - private readonly ILoggerFactory _logger; - private readonly IBuyerRepository _buyerRepository; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + private readonly IOrderRepository _orderRepository; + private readonly ILoggerFactory _logger; + private readonly IBuyerRepository _buyerRepository; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - public OrderStatusChangedToPaidDomainEventHandler( - IOrderRepository orderRepository, ILoggerFactory logger, - IBuyerRepository buyerRepository, - IOrderingIntegrationEventService orderingIntegrationEventService - ) - { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); - _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); - } + public OrderStatusChangedToPaidDomainEventHandler( + IOrderRepository orderRepository, ILoggerFactory logger, + IBuyerRepository buyerRepository, + IOrderingIntegrationEventService orderingIntegrationEventService + ) + { + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); + _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); + } - public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken) - { - _logger.CreateLogger() - .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", - orderStatusChangedToPaidDomainEvent.OrderId, nameof(OrderStatus.Paid), OrderStatus.Paid.Id); + public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken) + { + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderStatusChangedToPaidDomainEvent.OrderId, nameof(OrderStatus.Paid), OrderStatus.Paid.Id); - var order = await _orderRepository.GetAsync(orderStatusChangedToPaidDomainEvent.OrderId); - var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); + var order = await _orderRepository.GetAsync(orderStatusChangedToPaidDomainEvent.OrderId); + var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); - var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems - .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); + var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems + .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); - var orderStatusChangedToPaidIntegrationEvent = new OrderStatusChangedToPaidIntegrationEvent( - orderStatusChangedToPaidDomainEvent.OrderId, - order.OrderStatus.Name, - buyer.Name, - orderStockList); + var orderStatusChangedToPaidIntegrationEvent = new OrderStatusChangedToPaidIntegrationEvent( + orderStatusChangedToPaidDomainEvent.OrderId, + order.OrderStatus.Name, + buyer.Name, + orderStockList); - await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToPaidIntegrationEvent); - } + await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToPaidIntegrationEvent); } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs index 57478c1aa..445c7e04f 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs @@ -1,47 +1,35 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.IntegrationEvents; -using Ordering.API.Application.IntegrationEvents.Events; -using Ordering.Domain.Events; -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderShipped; -namespace Ordering.API.Application.DomainEventHandlers.OrderShipped +public class OrderShippedDomainEventHandler + : INotificationHandler { - public class OrderShippedDomainEventHandler - : INotificationHandler - { - private readonly IOrderRepository _orderRepository; - private readonly IBuyerRepository _buyerRepository; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - private readonly ILoggerFactory _logger; + private readonly IOrderRepository _orderRepository; + private readonly IBuyerRepository _buyerRepository; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + private readonly ILoggerFactory _logger; - public OrderShippedDomainEventHandler( - IOrderRepository orderRepository, - ILoggerFactory logger, - IBuyerRepository buyerRepository, - IOrderingIntegrationEventService orderingIntegrationEventService) - { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); - _orderingIntegrationEventService = orderingIntegrationEventService; - } + public OrderShippedDomainEventHandler( + IOrderRepository orderRepository, + ILoggerFactory logger, + IBuyerRepository buyerRepository, + IOrderingIntegrationEventService orderingIntegrationEventService) + { + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); + _orderingIntegrationEventService = orderingIntegrationEventService; + } - public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken) - { - _logger.CreateLogger() - .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", - orderShippedDomainEvent.Order.Id, nameof(OrderStatus.Shipped), OrderStatus.Shipped.Id); + public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken) + { + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderShippedDomainEvent.Order.Id, nameof(OrderStatus.Shipped), OrderStatus.Shipped.Id); - var order = await _orderRepository.GetAsync(orderShippedDomainEvent.Order.Id); - var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); + var order = await _orderRepository.GetAsync(orderShippedDomainEvent.Order.Id); + var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); - var orderStatusChangedToShippedIntegrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); - await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToShippedIntegrationEvent); - } + var orderStatusChangedToShippedIntegrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); + await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToShippedIntegrationEvent); } } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/SendEmailToCustomerWhenOrderStartedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/SendEmailToCustomerWhenOrderStartedDomainEventHandler.cs index 14fa36615..720b656d6 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/SendEmailToCustomerWhenOrderStartedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/SendEmailToCustomerWhenOrderStartedDomainEventHandler.cs @@ -1,16 +1,15 @@ -namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; + +public class SendEmailToCustomerWhenOrderStartedDomainEventHandler +//: IAsyncNotificationHandler { - public class SendEmailToCustomerWhenOrderStartedDomainEventHandler - //: IAsyncNotificationHandler + public SendEmailToCustomerWhenOrderStartedDomainEventHandler() { - public SendEmailToCustomerWhenOrderStartedDomainEventHandler() - { - - } - //public async Task Handle(OrderStartedDomainEvent orderNotification) - //{ - // //TBD - Send email logic - //} } + + //public async Task Handle(OrderStartedDomainEvent orderNotification) + //{ + // //TBD - Send email logic + //} } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs index 44e8ffcf7..00b220829 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs @@ -1,67 +1,55 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.IntegrationEvents; -using Ordering.API.Application.IntegrationEvents.Events; -using Ordering.Domain.Events; -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; -namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent +public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler + : INotificationHandler { - public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler - : INotificationHandler + private readonly ILoggerFactory _logger; + private readonly IBuyerRepository _buyerRepository; + private readonly IIdentityService _identityService; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + + public ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler( + ILoggerFactory logger, + IBuyerRepository buyerRepository, + IIdentityService identityService, + IOrderingIntegrationEventService orderingIntegrationEventService) { - private readonly ILoggerFactory _logger; - private readonly IBuyerRepository _buyerRepository; - private readonly IIdentityService _identityService; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; + _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); + _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler( - ILoggerFactory logger, - IBuyerRepository buyerRepository, - IIdentityService identityService, - IOrderingIntegrationEventService orderingIntegrationEventService) - { - _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); - _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); - _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public async Task Handle(OrderStartedDomainEvent orderStartedEvent, CancellationToken cancellationToken) + { + var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1; + var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId); + bool buyerOriginallyExisted = (buyer == null) ? false : true; - public async Task Handle(OrderStartedDomainEvent orderStartedEvent, CancellationToken cancellationToken) + if (!buyerOriginallyExisted) { - var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1; - var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId); - bool buyerOriginallyExisted = (buyer == null) ? false : true; - - if (!buyerOriginallyExisted) - { - buyer = new Buyer(orderStartedEvent.UserId, orderStartedEvent.UserName); - } - - buyer.VerifyOrAddPaymentMethod(cardTypeId, - $"Payment Method on {DateTime.UtcNow}", - orderStartedEvent.CardNumber, - orderStartedEvent.CardSecurityNumber, - orderStartedEvent.CardHolderName, - orderStartedEvent.CardExpiration, - orderStartedEvent.Order.Id); - - var buyerUpdated = buyerOriginallyExisted ? - _buyerRepository.Update(buyer) : - _buyerRepository.Add(buyer); - - await _buyerRepository.UnitOfWork - .SaveEntitiesAsync(cancellationToken); - - var orderStatusChangedTosubmittedIntegrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(orderStartedEvent.Order.Id, orderStartedEvent.Order.OrderStatus.Name, buyer.Name); - await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedTosubmittedIntegrationEvent); - _logger.CreateLogger() - .LogTrace("Buyer {BuyerId} and related payment method were validated or updated for orderId: {OrderId}.", - buyerUpdated.Id, orderStartedEvent.Order.Id); + buyer = new Buyer(orderStartedEvent.UserId, orderStartedEvent.UserName); } + + buyer.VerifyOrAddPaymentMethod(cardTypeId, + $"Payment Method on {DateTime.UtcNow}", + orderStartedEvent.CardNumber, + orderStartedEvent.CardSecurityNumber, + orderStartedEvent.CardHolderName, + orderStartedEvent.CardExpiration, + orderStartedEvent.Order.Id); + + var buyerUpdated = buyerOriginallyExisted ? + _buyerRepository.Update(buyer) : + _buyerRepository.Add(buyer); + + await _buyerRepository.UnitOfWork + .SaveEntitiesAsync(cancellationToken); + + var orderStatusChangedTosubmittedIntegrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(orderStartedEvent.Order.Id, orderStartedEvent.Order.OrderStatus.Name, buyer.Name); + await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedTosubmittedIntegrationEvent); + _logger.CreateLogger() + .LogTrace("Buyer {BuyerId} and related payment method were validated or updated for orderId: {OrderId}.", + buyerUpdated.Id, orderStartedEvent.Order.Id); } } diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs index eaad5f2d6..142b6449d 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs @@ -1,47 +1,35 @@ -namespace Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed; + +public class OrderStatusChangedToStockConfirmedDomainEventHandler + : INotificationHandler { - using Domain.Events; - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; - using Microsoft.Extensions.Logging; - using Ordering.API.Application.IntegrationEvents; - using Ordering.API.Application.IntegrationEvents.Events; - using System; - using System.Threading; - using System.Threading.Tasks; + private readonly IOrderRepository _orderRepository; + private readonly IBuyerRepository _buyerRepository; + private readonly ILoggerFactory _logger; + private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - public class OrderStatusChangedToStockConfirmedDomainEventHandler - : INotificationHandler + public OrderStatusChangedToStockConfirmedDomainEventHandler( + IOrderRepository orderRepository, + IBuyerRepository buyerRepository, + ILoggerFactory logger, + IOrderingIntegrationEventService orderingIntegrationEventService) { - private readonly IOrderRepository _orderRepository; - private readonly IBuyerRepository _buyerRepository; - private readonly ILoggerFactory _logger; - private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; - - public OrderStatusChangedToStockConfirmedDomainEventHandler( - IOrderRepository orderRepository, - IBuyerRepository buyerRepository, - ILoggerFactory logger, - IOrderingIntegrationEventService orderingIntegrationEventService) - { - _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); - _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _orderingIntegrationEventService = orderingIntegrationEventService; - } + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _orderingIntegrationEventService = orderingIntegrationEventService; + } - public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent, CancellationToken cancellationToken) - { - _logger.CreateLogger() - .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", - orderStatusChangedToStockConfirmedDomainEvent.OrderId, nameof(OrderStatus.StockConfirmed), OrderStatus.StockConfirmed.Id); + public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent, CancellationToken cancellationToken) + { + _logger.CreateLogger() + .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", + orderStatusChangedToStockConfirmedDomainEvent.OrderId, nameof(OrderStatus.StockConfirmed), OrderStatus.StockConfirmed.Id); - var order = await _orderRepository.GetAsync(orderStatusChangedToStockConfirmedDomainEvent.OrderId); - var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); + var order = await _orderRepository.GetAsync(orderStatusChangedToStockConfirmedDomainEvent.OrderId); + var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); - var orderStatusChangedToStockConfirmedIntegrationEvent = new OrderStatusChangedToStockConfirmedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); - await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToStockConfirmedIntegrationEvent); - } + var orderStatusChangedToStockConfirmedIntegrationEvent = new OrderStatusChangedToStockConfirmedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); + await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToStockConfirmedIntegrationEvent); } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs index e503695c7..55a8a39b9 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs @@ -1,52 +1,42 @@ -using MediatR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.Commands; -using Ordering.API.Application.IntegrationEvents.Events; -using Serilog.Context; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; -namespace Ordering.API.Application.IntegrationEvents.EventHandling +public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler { - public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IMediator _mediator; - private readonly ILogger _logger; + private readonly IMediator _mediator; + private readonly ILogger _logger; - public GracePeriodConfirmedIntegrationEventHandler( - IMediator mediator, - ILogger logger) - { - _mediator = mediator; - _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - } + public GracePeriodConfirmedIntegrationEventHandler( + IMediator mediator, + ILogger logger) + { + _mediator = mediator; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + } - /// - /// Event handler which confirms that the grace period - /// has been completed and order will not initially be cancelled. - /// Therefore, the order process continues for validation. - /// - /// - /// - /// - public async Task Handle(GracePeriodConfirmedIntegrationEvent @event) + /// + /// Event handler which confirms that the grace period + /// has been completed and order will not initially be cancelled. + /// Therefore, the order process continues for validation. + /// + /// + /// + /// + public async Task Handle(GracePeriodConfirmedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId); + var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId); - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - command.GetGenericTypeName(), - nameof(command.OrderNumber), - command.OrderNumber, - command); + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); - await _mediator.Send(command); - } + await _mediator.Send(command); } } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs index cad6123be..4170ac18f 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs @@ -1,46 +1,35 @@ -namespace Ordering.API.Application.IntegrationEvents.EventHandling +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; + +public class OrderPaymentFailedIntegrationEventHandler : + IIntegrationEventHandler { - using MediatR; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; - using Microsoft.Extensions.Logging; - using Ordering.API.Application.Commands; - using Ordering.API.Application.IntegrationEvents.Events; - using Serilog.Context; - using System; - using System.Threading.Tasks; + private readonly IMediator _mediator; + private readonly ILogger _logger; - public class OrderPaymentFailedIntegrationEventHandler : - IIntegrationEventHandler + public OrderPaymentFailedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { - private readonly IMediator _mediator; - private readonly ILogger _logger; - - public OrderPaymentFailedIntegrationEventHandler( - IMediator mediator, - ILogger logger) - { - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderPaymentFailedIntegrationEvent @event) + public async Task Handle(OrderPaymentFailedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var command = new CancelOrderCommand(@event.OrderId); + var command = new CancelOrderCommand(@event.OrderId); - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - command.GetGenericTypeName(), - nameof(command.OrderNumber), - command.OrderNumber, - command); + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); - await _mediator.Send(command); - } + await _mediator.Send(command); } } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSucceededIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSucceededIntegrationEventHandler.cs index 9971a1a33..4bd6304d0 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSucceededIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSucceededIntegrationEventHandler.cs @@ -1,46 +1,35 @@ -namespace Ordering.API.Application.IntegrationEvents.EventHandling +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; + +public class OrderPaymentSucceededIntegrationEventHandler : + IIntegrationEventHandler { - using MediatR; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; - using Microsoft.Extensions.Logging; - using Ordering.API.Application.Commands; - using Ordering.API.Application.IntegrationEvents.Events; - using Serilog.Context; - using System; - using System.Threading.Tasks; + private readonly IMediator _mediator; + private readonly ILogger _logger; - public class OrderPaymentSucceededIntegrationEventHandler : - IIntegrationEventHandler + public OrderPaymentSucceededIntegrationEventHandler( + IMediator mediator, + ILogger logger) { - private readonly IMediator _mediator; - private readonly ILogger _logger; - - public OrderPaymentSucceededIntegrationEventHandler( - IMediator mediator, - ILogger logger) - { - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderPaymentSucceededIntegrationEvent @event) + public async Task Handle(OrderPaymentSucceededIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var command = new SetPaidOrderStatusCommand(@event.OrderId); + var command = new SetPaidOrderStatusCommand(@event.OrderId); - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - command.GetGenericTypeName(), - nameof(command.OrderNumber), - command.OrderNumber, - command); + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); - await _mediator.Send(command); - } + await _mediator.Send(command); } } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs index 8a349fca7..0c9557a5b 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs @@ -1,46 +1,35 @@ -namespace Ordering.API.Application.IntegrationEvents.EventHandling +namespace Ordering.API.Application.IntegrationEvents.EventHandling; + +public class OrderStockConfirmedIntegrationEventHandler : + IIntegrationEventHandler { - using Events; - using MediatR; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; - using Microsoft.Extensions.Logging; - using Ordering.API.Application.Commands; - using Serilog.Context; - using System; - using System.Threading.Tasks; + private readonly IMediator _mediator; + private readonly ILogger _logger; - public class OrderStockConfirmedIntegrationEventHandler : - IIntegrationEventHandler + public OrderStockConfirmedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { - private readonly IMediator _mediator; - private readonly ILogger _logger; - - public OrderStockConfirmedIntegrationEventHandler( - IMediator mediator, - ILogger logger) - { - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStockConfirmedIntegrationEvent @event) + public async Task Handle(OrderStockConfirmedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId); + var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId); - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - command.GetGenericTypeName(), - nameof(command.OrderNumber), - command.OrderNumber, - command); + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); - await _mediator.Send(command); - } + await _mediator.Send(command); } } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs index 5819b4fe3..4fa61b9b5 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs @@ -1,50 +1,38 @@ -namespace Ordering.API.Application.IntegrationEvents.EventHandling +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; +public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler { - using Events; - using MediatR; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; - using Microsoft.Extensions.Logging; - using Ordering.API.Application.Commands; - using Serilog.Context; - using System.Linq; - using System.Threading.Tasks; + private readonly IMediator _mediator; + private readonly ILogger _logger; - public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler + public OrderStockRejectedIntegrationEventHandler( + IMediator mediator, + ILogger logger) { - private readonly IMediator _mediator; - private readonly ILogger _logger; - - public OrderStockRejectedIntegrationEventHandler( - IMediator mediator, - ILogger logger) - { - _mediator = mediator; - _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - } + _mediator = mediator; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStockRejectedIntegrationEvent @event) + public async Task Handle(OrderStockRejectedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var orderStockRejectedItems = @event.OrderStockItems - .FindAll(c => !c.HasStock) - .Select(c => c.ProductId) - .ToList(); + var orderStockRejectedItems = @event.OrderStockItems + .FindAll(c => !c.HasStock) + .Select(c => c.ProductId) + .ToList(); - var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems); + var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems); - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - command.GetGenericTypeName(), - nameof(command.OrderNumber), - command.OrderNumber, - command); + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + command.GetGenericTypeName(), + nameof(command.OrderNumber), + command.OrderNumber, + command); - await _mediator.Send(command); - } + await _mediator.Send(command); } } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs index c16b554c9..a5a15c06c 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs @@ -1,80 +1,69 @@ -using MediatR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.IntegrationEvents.Events; -using Serilog.Context; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; -namespace Ordering.API.Application.IntegrationEvents.EventHandling +public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler { - public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IMediator _mediator; - private readonly ILogger _logger; + private readonly IMediator _mediator; + private readonly ILogger _logger; - public UserCheckoutAcceptedIntegrationEventHandler( - IMediator mediator, - ILogger logger) - { - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public UserCheckoutAcceptedIntegrationEventHandler( + IMediator mediator, + ILogger logger) + { + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - /// - /// Integration event handler which starts the create order process - /// - /// - /// Integration event message which is sent by the - /// basket.api once it has successfully process the - /// order items. - /// - /// - public async Task Handle(UserCheckoutAcceptedIntegrationEvent @event) + /// + /// Integration event handler which starts the create order process + /// + /// + /// Integration event message which is sent by the + /// basket.api once it has successfully process the + /// order items. + /// + /// + public async Task Handle(UserCheckoutAcceptedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var result = false; + var result = false; - if (@event.RequestId != Guid.Empty) + if (@event.RequestId != Guid.Empty) + { + using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId)) { - using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId)) - { - var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street, - @event.State, @event.Country, @event.ZipCode, - @event.CardNumber, @event.CardHolderName, @event.CardExpiration, - @event.CardSecurityNumber, @event.CardTypeId); + var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street, + @event.State, @event.Country, @event.ZipCode, + @event.CardNumber, @event.CardHolderName, @event.CardExpiration, + @event.CardSecurityNumber, @event.CardTypeId); - var requestCreateOrder = new IdentifiedCommand(createOrderCommand, @event.RequestId); + var requestCreateOrder = new IdentifiedCommand(createOrderCommand, @event.RequestId); - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - requestCreateOrder.GetGenericTypeName(), - nameof(requestCreateOrder.Id), - requestCreateOrder.Id, - requestCreateOrder); + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + requestCreateOrder.GetGenericTypeName(), + nameof(requestCreateOrder.Id), + requestCreateOrder.Id, + requestCreateOrder); - result = await _mediator.Send(requestCreateOrder); + result = await _mediator.Send(requestCreateOrder); - if (result) - { - _logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId); - } - else - { - _logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId); - } + if (result) + { + _logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId); + } + else + { + _logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId); } } - else - { - _logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event); - } + } + else + { + _logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event); } } } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/GracePeriodConfirmedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/GracePeriodConfirmedIntegrationEvent.cs index 1eac25205..3e653f8df 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/GracePeriodConfirmedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/GracePeriodConfirmedIntegrationEvent.cs @@ -1,12 +1,10 @@ -namespace Ordering.API.Application.IntegrationEvents.Events -{ - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; - public record GracePeriodConfirmedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record GracePeriodConfirmedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public GracePeriodConfirmedIntegrationEvent(int orderId) => - OrderId = orderId; - } + public GracePeriodConfirmedIntegrationEvent(int orderId) => + OrderId = orderId; } + diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent .cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent .cs index a4d1c70f6..42f247e4b 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent .cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent .cs @@ -1,11 +1,8 @@ -namespace Ordering.API.Application.IntegrationEvents.Events -{ - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; - public record OrderPaymentFailedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderPaymentFailedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId; - } -} \ No newline at end of file + public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId; +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs index 8897deb31..6dd575fe1 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs @@ -1,11 +1,8 @@ -namespace Ordering.API.Application.IntegrationEvents.Events -{ - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; - public record OrderPaymentSucceededIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderPaymentSucceededIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public OrderPaymentSucceededIntegrationEvent(int orderId) => OrderId = orderId; - } -} \ No newline at end of file + public OrderPaymentSucceededIntegrationEvent(int orderId) => OrderId = orderId; +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs index 2ff1460c7..11077d581 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs @@ -1,15 +1,12 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; -namespace Ordering.API.Application.IntegrationEvents.Events +// Integration Events notes: +// An Event is “something that has happened in the past”, therefore its name has to be +// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. +public record OrderStartedIntegrationEvent : IntegrationEvent { - // Integration Events notes: - // An Event is “something that has happened in the past”, therefore its name has to be - // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. - public record OrderStartedIntegrationEvent : IntegrationEvent - { - public string UserId { get; init; } + public string UserId { get; init; } - public OrderStartedIntegrationEvent(string userId) - => UserId = userId; - } -} \ No newline at end of file + public OrderStartedIntegrationEvent(string userId) + => UserId = userId; +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs index 72cd4b9f4..1f7ef35e2 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs @@ -1,34 +1,30 @@ -namespace Ordering.API.Application.IntegrationEvents.Events +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; + +public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent { - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; - using System.Collections.Generic; + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } + public IEnumerable OrderStockItems { get; } - public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent + public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName, + IEnumerable orderStockItems) { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } - public IEnumerable OrderStockItems { get; } - - public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName, - IEnumerable orderStockItems) - { - OrderId = orderId; - OrderStockItems = orderStockItems; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + OrderId = orderId; + OrderStockItems = orderStockItems; + OrderStatus = orderStatus; + BuyerName = buyerName; } +} - public record OrderStockItem - { - public int ProductId { get; } - public int Units { get; } +public record OrderStockItem +{ + public int ProductId { get; } + public int Units { get; } - public OrderStockItem(int productId, int units) - { - ProductId = productId; - Units = units; - } + public OrderStockItem(int productId, int units) + { + ProductId = productId; + Units = units; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs index b703e8031..170bc2d06 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs @@ -1,18 +1,15 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; -namespace Ordering.API.Application.IntegrationEvents.Events +public record OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs index ee5f07487..f40a4b625 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs @@ -1,24 +1,21 @@ -namespace Ordering.API.Application.IntegrationEvents.Events +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; + +public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent { - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; - using System.Collections.Generic; + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } + public IEnumerable OrderStockItems { get; } - public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent + public OrderStatusChangedToPaidIntegrationEvent(int orderId, + string orderStatus, + string buyerName, + IEnumerable orderStockItems) { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } - public IEnumerable OrderStockItems { get; } - - public OrderStatusChangedToPaidIntegrationEvent(int orderId, - string orderStatus, - string buyerName, - IEnumerable orderStockItems) - { - OrderId = orderId; - OrderStockItems = orderStockItems; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + OrderId = orderId; + OrderStockItems = orderStockItems; + OrderStatus = orderStatus; + BuyerName = buyerName; } } + diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs index 064d07c20..0ab7ae093 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs @@ -1,18 +1,15 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; -namespace Ordering.API.Application.IntegrationEvents.Events +public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs index d87e30b81..5450b570c 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs @@ -1,18 +1,15 @@ -namespace Ordering.API.Application.IntegrationEvents.Events +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; + +public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent { - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent + public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string buyerName) { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } - - public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedTosubmittedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedTosubmittedIntegrationEvent.cs index 77b567ec8..e007d948e 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedTosubmittedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedTosubmittedIntegrationEvent.cs @@ -1,18 +1,15 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; -namespace Ordering.API.Application.IntegrationEvents.Events +public record OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs index 2b09c8655..93d541744 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs @@ -1,11 +1,8 @@ -namespace Ordering.API.Application.IntegrationEvents.Events -{ - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; - public record OrderStockConfirmedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderStockConfirmedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId; - } -} \ No newline at end of file + public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId; +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs index c16148f84..25e1acf63 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs @@ -1,31 +1,27 @@ -namespace Ordering.API.Application.IntegrationEvents.Events -{ - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; - using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; - public record OrderStockRejectedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderStockRejectedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public List OrderStockItems { get; } + public List OrderStockItems { get; } - public OrderStockRejectedIntegrationEvent(int orderId, - List orderStockItems) - { - OrderId = orderId; - OrderStockItems = orderStockItems; - } + public OrderStockRejectedIntegrationEvent(int orderId, + List orderStockItems) + { + OrderId = orderId; + OrderStockItems = orderStockItems; } +} - public record ConfirmedOrderStockItem - { - public int ProductId { get; } - public bool HasStock { get; } +public record ConfirmedOrderStockItem +{ + public int ProductId { get; } + public bool HasStock { get; } - public ConfirmedOrderStockItem(int productId, bool hasStock) - { - ProductId = productId; - HasStock = hasStock; - } + public ConfirmedOrderStockItem(int productId, bool hasStock) + { + ProductId = productId; + HasStock = hasStock; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs index b5ecb1d6a..811be0ec4 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs @@ -1,62 +1,57 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using Ordering.API.Application.Models; -using System; - -namespace Ordering.API.Application.IntegrationEvents.Events -{ - public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent - { - public string UserId { get; } +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; + +public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent +{ + public string UserId { get; } - public string UserName { get; } + public string UserName { get; } - public string City { get; set; } + public string City { get; set; } - public string Street { get; set; } + public string Street { get; set; } - public string State { get; set; } - - public string Country { get; set; } + public string State { get; set; } - public string ZipCode { get; set; } + public string Country { get; set; } - public string CardNumber { get; set; } + public string ZipCode { get; set; } - public string CardHolderName { get; set; } + public string CardNumber { get; set; } - public DateTime CardExpiration { get; set; } + public string CardHolderName { get; set; } - public string CardSecurityNumber { get; set; } + public DateTime CardExpiration { get; set; } - public int CardTypeId { get; set; } + public string CardSecurityNumber { get; set; } - public string Buyer { get; set; } + public int CardTypeId { get; set; } - public Guid RequestId { get; set; } + public string Buyer { get; set; } - public CustomerBasket Basket { get; } + public Guid RequestId { get; set; } - public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street, - string state, string country, string zipCode, string cardNumber, string cardHolderName, - DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId, - CustomerBasket basket) - { - UserId = userId; - City = city; - Street = street; - State = state; - Country = country; - ZipCode = zipCode; - CardNumber = cardNumber; - CardHolderName = cardHolderName; - CardExpiration = cardExpiration; - CardSecurityNumber = cardSecurityNumber; - CardTypeId = cardTypeId; - Buyer = buyer; - Basket = basket; - RequestId = requestId; - UserName = userName; - } + public CustomerBasket Basket { get; } + public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street, + string state, string country, string zipCode, string cardNumber, string cardHolderName, + DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId, + CustomerBasket basket) + { + UserId = userId; + City = city; + Street = street; + State = state; + Country = country; + ZipCode = zipCode; + CardNumber = cardNumber; + CardHolderName = cardHolderName; + CardExpiration = cardExpiration; + CardSecurityNumber = cardSecurityNumber; + CardTypeId = cardTypeId; + Buyer = buyer; + Basket = basket; + RequestId = requestId; + UserName = userName; } + } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/IOrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/IOrderingIntegrationEventService.cs index ad661e3c0..18807a56b 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/IOrderingIntegrationEventService.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/IOrderingIntegrationEventService.cs @@ -1,12 +1,7 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents; -namespace Ordering.API.Application.IntegrationEvents +public interface IOrderingIntegrationEventService { - public interface IOrderingIntegrationEventService - { - Task PublishEventsThroughEventBusAsync(Guid transactionId); - Task AddAndSaveEventAsync(IntegrationEvent evt); - } + Task PublishEventsThroughEventBusAsync(Guid transactionId); + Task AddAndSaveEventAsync(IntegrationEvent evt); } diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs index a559a8ffa..e2545cae5 100644 --- a/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs @@ -1,65 +1,53 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using Microsoft.Extensions.Logging; -using System; -using System.Data.Common; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents; -namespace Ordering.API.Application.IntegrationEvents +public class OrderingIntegrationEventService : IOrderingIntegrationEventService { - public class OrderingIntegrationEventService : IOrderingIntegrationEventService + private readonly Func _integrationEventLogServiceFactory; + private readonly IEventBus _eventBus; + private readonly OrderingContext _orderingContext; + private readonly IIntegrationEventLogService _eventLogService; + private readonly ILogger _logger; + + public OrderingIntegrationEventService(IEventBus eventBus, + OrderingContext orderingContext, + IntegrationEventLogContext eventLogContext, + Func integrationEventLogServiceFactory, + ILogger logger) { - private readonly Func _integrationEventLogServiceFactory; - private readonly IEventBus _eventBus; - private readonly OrderingContext _orderingContext; - private readonly IIntegrationEventLogService _eventLogService; - private readonly ILogger _logger; + _orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext)); + _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); + _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); + _eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection()); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public OrderingIntegrationEventService(IEventBus eventBus, - OrderingContext orderingContext, - IntegrationEventLogContext eventLogContext, - Func integrationEventLogServiceFactory, - ILogger logger) - { - _orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext)); - _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); - _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); - _eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection()); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public async Task PublishEventsThroughEventBusAsync(Guid transactionId) + { + var pendingLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync(transactionId); - public async Task PublishEventsThroughEventBusAsync(Guid transactionId) + foreach (var logEvt in pendingLogEvents) { - var pendingLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync(transactionId); + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", logEvt.EventId, Program.AppName, logEvt.IntegrationEvent); - foreach (var logEvt in pendingLogEvents) + try { - _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", logEvt.EventId, Program.AppName, logEvt.IntegrationEvent); - - try - { - await _eventLogService.MarkEventAsInProgressAsync(logEvt.EventId); - _eventBus.Publish(logEvt.IntegrationEvent); - await _eventLogService.MarkEventAsPublishedAsync(logEvt.EventId); - } - catch (Exception ex) - { - _logger.LogError(ex, "ERROR publishing integration event: {IntegrationEventId} from {AppName}", logEvt.EventId, Program.AppName); + await _eventLogService.MarkEventAsInProgressAsync(logEvt.EventId); + _eventBus.Publish(logEvt.IntegrationEvent); + await _eventLogService.MarkEventAsPublishedAsync(logEvt.EventId); + } + catch (Exception ex) + { + _logger.LogError(ex, "ERROR publishing integration event: {IntegrationEventId} from {AppName}", logEvt.EventId, Program.AppName); - await _eventLogService.MarkEventAsFailedAsync(logEvt.EventId); - } + await _eventLogService.MarkEventAsFailedAsync(logEvt.EventId); } } + } - public async Task AddAndSaveEventAsync(IntegrationEvent evt) - { - _logger.LogInformation("----- Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, evt); + public async Task AddAndSaveEventAsync(IntegrationEvent evt) + { + _logger.LogInformation("----- Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, evt); - await _eventLogService.SaveEventAsync(evt, _orderingContext.GetCurrentTransaction()); - } + await _eventLogService.SaveEventAsync(evt, _orderingContext.GetCurrentTransaction()); } } diff --git a/src/Services/Ordering/Ordering.API/Application/Models/BasketItem.cs b/src/Services/Ordering/Ordering.API/Application/Models/BasketItem.cs index 5c7df3338..ca07701c0 100644 --- a/src/Services/Ordering/Ordering.API/Application/Models/BasketItem.cs +++ b/src/Services/Ordering/Ordering.API/Application/Models/BasketItem.cs @@ -1,13 +1,13 @@ -namespace Ordering.API.Application.Models +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; + +public class BasketItem { - public class BasketItem - { - public string Id { get; init; } - public int ProductId { get; init; } - public string ProductName { get; init; } - public decimal UnitPrice { get; init; } - public decimal OldUnitPrice { get; init; } - public int Quantity { get; init; } - public string PictureUrl { get; init; } - } + public string Id { get; init; } + public int ProductId { get; init; } + public string ProductName { get; init; } + public decimal UnitPrice { get; init; } + public decimal OldUnitPrice { get; init; } + public int Quantity { get; init; } + public string PictureUrl { get; init; } } + diff --git a/src/Services/Ordering/Ordering.API/Application/Models/CustomerBasket.cs b/src/Services/Ordering/Ordering.API/Application/Models/CustomerBasket.cs index 251fa881c..919c0ba21 100644 --- a/src/Services/Ordering/Ordering.API/Application/Models/CustomerBasket.cs +++ b/src/Services/Ordering/Ordering.API/Application/Models/CustomerBasket.cs @@ -1,16 +1,13 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; -namespace Ordering.API.Application.Models +public class CustomerBasket { - public class CustomerBasket - { - public string BuyerId { get; set; } - public List Items { get; set; } + public string BuyerId { get; set; } + public List Items { get; set; } - public CustomerBasket(string buyerId, List items) - { - BuyerId = buyerId; - Items = items; - } + public CustomerBasket(string buyerId, List items) + { + BuyerId = buyerId; + Items = items; } } diff --git a/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs b/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs index 89cf496e3..60c6fe286 100644 --- a/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs +++ b/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs @@ -1,15 +1,10 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries -{ - using System; - using System.Collections.Generic; - using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; - public interface IOrderQueries - { - Task GetOrderAsync(int id); +public interface IOrderQueries +{ + Task GetOrderAsync(int id); - Task> GetOrdersFromUserAsync(Guid userId); + Task> GetOrdersFromUserAsync(Guid userId); - Task> GetCardTypesAsync(); - } + Task> GetCardTypesAsync(); } diff --git a/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs b/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs index ceffa9039..c60dbca59 100644 --- a/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs +++ b/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs @@ -1,105 +1,98 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; + +public class OrderQueries + : IOrderQueries { - using Dapper; - using System; - using System.Collections.Generic; - using System.Data.SqlClient; - using System.Threading.Tasks; + private string _connectionString = string.Empty; - public class OrderQueries - : IOrderQueries + public OrderQueries(string constr) { - private string _connectionString = string.Empty; - - public OrderQueries(string constr) - { - _connectionString = !string.IsNullOrWhiteSpace(constr) ? constr : throw new ArgumentNullException(nameof(constr)); - } + _connectionString = !string.IsNullOrWhiteSpace(constr) ? constr : throw new ArgumentNullException(nameof(constr)); + } - public async Task GetOrderAsync(int id) + public async Task GetOrderAsync(int id) + { + using (var connection = new SqlConnection(_connectionString)) { - using (var connection = new SqlConnection(_connectionString)) - { - connection.Open(); + connection.Open(); - var result = await connection.QueryAsync( - @"select o.[Id] as ordernumber,o.OrderDate as date, o.Description as description, - o.Address_City as city, o.Address_Country as country, o.Address_State as state, o.Address_Street as street, o.Address_ZipCode as zipcode, - os.Name as status, - oi.ProductName as productname, oi.Units as units, oi.UnitPrice as unitprice, oi.PictureUrl as pictureurl - FROM ordering.Orders o - LEFT JOIN ordering.Orderitems oi ON o.Id = oi.orderid - LEFT JOIN ordering.orderstatus os on o.OrderStatusId = os.Id - WHERE o.Id=@id" - , new { id } - ); + var result = await connection.QueryAsync( + @"select o.[Id] as ordernumber,o.OrderDate as date, o.Description as description, + o.Address_City as city, o.Address_Country as country, o.Address_State as state, o.Address_Street as street, o.Address_ZipCode as zipcode, + os.Name as status, + oi.ProductName as productname, oi.Units as units, oi.UnitPrice as unitprice, oi.PictureUrl as pictureurl + FROM ordering.Orders o + LEFT JOIN ordering.Orderitems oi ON o.Id = oi.orderid + LEFT JOIN ordering.orderstatus os on o.OrderStatusId = os.Id + WHERE o.Id=@id" + , new { id } + ); - if (result.AsList().Count == 0) - throw new KeyNotFoundException(); + if (result.AsList().Count == 0) + throw new KeyNotFoundException(); - return MapOrderItems(result); - } + return MapOrderItems(result); } + } - public async Task> GetOrdersFromUserAsync(Guid userId) + public async Task> GetOrdersFromUserAsync(Guid userId) + { + using (var connection = new SqlConnection(_connectionString)) { - using (var connection = new SqlConnection(_connectionString)) - { - connection.Open(); + connection.Open(); - return await connection.QueryAsync(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status], SUM(oi.units*oi.unitprice) as total - FROM [ordering].[Orders] o - LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid - LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id - LEFT JOIN[ordering].[buyers] ob on o.BuyerId = ob.Id - WHERE ob.IdentityGuid = @userId - GROUP BY o.[Id], o.[OrderDate], os.[Name] - ORDER BY o.[Id]", new { userId }); - } + return await connection.QueryAsync(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status], SUM(oi.units*oi.unitprice) as total + FROM [ordering].[Orders] o + LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid + LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id + LEFT JOIN[ordering].[buyers] ob on o.BuyerId = ob.Id + WHERE ob.IdentityGuid = @userId + GROUP BY o.[Id], o.[OrderDate], os.[Name] + ORDER BY o.[Id]", new { userId }); } + } - public async Task> GetCardTypesAsync() + public async Task> GetCardTypesAsync() + { + using (var connection = new SqlConnection(_connectionString)) { - using (var connection = new SqlConnection(_connectionString)) - { - connection.Open(); + connection.Open(); - return await connection.QueryAsync("SELECT * FROM ordering.cardtypes"); - } + return await connection.QueryAsync("SELECT * FROM ordering.cardtypes"); } + } - private Order MapOrderItems(dynamic result) + private Order MapOrderItems(dynamic result) + { + var order = new Order { - var order = new Order - { - ordernumber = result[0].ordernumber, - date = result[0].date, - status = result[0].status, - description = result[0].description, - street = result[0].street, - city = result[0].city, - zipcode = result[0].zipcode, - country = result[0].country, - orderitems = new List(), - total = 0 - }; + ordernumber = result[0].ordernumber, + date = result[0].date, + status = result[0].status, + description = result[0].description, + street = result[0].street, + city = result[0].city, + zipcode = result[0].zipcode, + country = result[0].country, + orderitems = new List(), + total = 0 + }; - foreach (dynamic item in result) + foreach (dynamic item in result) + { + var orderitem = new Orderitem { - var orderitem = new Orderitem - { - productname = item.productname, - units = item.units, - unitprice = (double)item.unitprice, - pictureurl = item.pictureurl - }; - - order.total += item.units * item.unitprice; - order.orderitems.Add(orderitem); - } + productname = item.productname, + units = item.units, + unitprice = (double)item.unitprice, + pictureurl = item.pictureurl + }; - return order; + order.total += item.units * item.unitprice; + order.orderitems.Add(orderitem); } + + return order; } } diff --git a/src/Services/Ordering/Ordering.API/Application/Queries/OrderViewModel.cs b/src/Services/Ordering/Ordering.API/Application/Queries/OrderViewModel.cs index 84f9f3f40..d5ce86005 100644 --- a/src/Services/Ordering/Ordering.API/Application/Queries/OrderViewModel.cs +++ b/src/Services/Ordering/Ordering.API/Application/Queries/OrderViewModel.cs @@ -1,41 +1,37 @@ -using System; -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries +public record Orderitem { - public record Orderitem - { - public string productname { get; init; } - public int units { get; init; } - public double unitprice { get; init; } - public string pictureurl { get; init; } - } + public string productname { get; init; } + public int units { get; init; } + public double unitprice { get; init; } + public string pictureurl { get; init; } +} - public record Order - { - public int ordernumber { get; init; } - public DateTime date { get; init; } - public string status { get; init; } - public string description { get; init; } - public string street { get; init; } - public string city { get; init; } - public string zipcode { get; init; } - public string country { get; init; } - public List orderitems { get; set; } - public decimal total { get; set; } - } +public record Order +{ + public int ordernumber { get; init; } + public DateTime date { get; init; } + public string status { get; init; } + public string description { get; init; } + public string street { get; init; } + public string city { get; init; } + public string zipcode { get; init; } + public string country { get; init; } + public List orderitems { get; set; } + public decimal total { get; set; } +} - public record OrderSummary - { - public int ordernumber { get; init; } - public DateTime date { get; init; } - public string status { get; init; } - public double total { get; init; } - } +public record OrderSummary +{ + public int ordernumber { get; init; } + public DateTime date { get; init; } + public string status { get; init; } + public double total { get; init; } +} - public record CardType - { - public int Id { get; init; } - public string Name { get; init; } - } +public record CardType +{ + public int Id { get; init; } + public string Name { get; init; } } diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/CancelOrderCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/CancelOrderCommandValidator.cs index 49b4c9413..7d901b1fe 100644 --- a/src/Services/Ordering/Ordering.API/Application/Validations/CancelOrderCommandValidator.cs +++ b/src/Services/Ordering/Ordering.API/Application/Validations/CancelOrderCommandValidator.cs @@ -1,16 +1,11 @@ -using FluentValidation; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.Commands; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; -namespace Ordering.API.Application.Validations +public class CancelOrderCommandValidator : AbstractValidator { - public class CancelOrderCommandValidator : AbstractValidator + public CancelOrderCommandValidator(ILogger logger) { - public CancelOrderCommandValidator(ILogger logger) - { - RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); + RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); - logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); - } + logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs index d3b09233a..eb61082e7 100644 --- a/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs +++ b/src/Services/Ordering/Ordering.API/Application/Validations/CreateOrderCommandValidator.cs @@ -1,40 +1,33 @@ -using FluentValidation; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; + using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; -namespace Ordering.API.Application.Validations +public class CreateOrderCommandValidator : AbstractValidator { - public class CreateOrderCommandValidator : AbstractValidator + public CreateOrderCommandValidator(ILogger logger) { - public CreateOrderCommandValidator(ILogger logger) - { - RuleFor(command => command.City).NotEmpty(); - RuleFor(command => command.Street).NotEmpty(); - RuleFor(command => command.State).NotEmpty(); - RuleFor(command => command.Country).NotEmpty(); - RuleFor(command => command.ZipCode).NotEmpty(); - RuleFor(command => command.CardNumber).NotEmpty().Length(12, 19); - RuleFor(command => command.CardHolderName).NotEmpty(); - RuleFor(command => command.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date"); - RuleFor(command => command.CardSecurityNumber).NotEmpty().Length(3); - RuleFor(command => command.CardTypeId).NotEmpty(); - RuleFor(command => command.OrderItems).Must(ContainOrderItems).WithMessage("No order items found"); + RuleFor(command => command.City).NotEmpty(); + RuleFor(command => command.Street).NotEmpty(); + RuleFor(command => command.State).NotEmpty(); + RuleFor(command => command.Country).NotEmpty(); + RuleFor(command => command.ZipCode).NotEmpty(); + RuleFor(command => command.CardNumber).NotEmpty().Length(12, 19); + RuleFor(command => command.CardHolderName).NotEmpty(); + RuleFor(command => command.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date"); + RuleFor(command => command.CardSecurityNumber).NotEmpty().Length(3); + RuleFor(command => command.CardTypeId).NotEmpty(); + RuleFor(command => command.OrderItems).Must(ContainOrderItems).WithMessage("No order items found"); - logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); - } + logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); + } - private bool BeValidExpirationDate(DateTime dateTime) - { - return dateTime >= DateTime.UtcNow; - } + private bool BeValidExpirationDate(DateTime dateTime) + { + return dateTime >= DateTime.UtcNow; + } - private bool ContainOrderItems(IEnumerable orderItems) - { - return orderItems.Any(); - } + private bool ContainOrderItems(IEnumerable orderItems) + { + return orderItems.Any(); } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/IdentifiedCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/IdentifiedCommandValidator.cs index ea928475d..bde2d771f 100644 --- a/src/Services/Ordering/Ordering.API/Application/Validations/IdentifiedCommandValidator.cs +++ b/src/Services/Ordering/Ordering.API/Application/Validations/IdentifiedCommandValidator.cs @@ -1,16 +1,11 @@ -using FluentValidation; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.Extensions.Logging; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; -namespace Ordering.API.Application.Validations +public class IdentifiedCommandValidator : AbstractValidator> { - public class IdentifiedCommandValidator : AbstractValidator> + public IdentifiedCommandValidator(ILogger logger) { - public IdentifiedCommandValidator(ILogger logger) - { - RuleFor(command => command.Id).NotEmpty(); + RuleFor(command => command.Id).NotEmpty(); - logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); - } + logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); } } diff --git a/src/Services/Ordering/Ordering.API/Application/Validations/ShipOrderCommandValidator.cs b/src/Services/Ordering/Ordering.API/Application/Validations/ShipOrderCommandValidator.cs index fbc294534..0341c6028 100644 --- a/src/Services/Ordering/Ordering.API/Application/Validations/ShipOrderCommandValidator.cs +++ b/src/Services/Ordering/Ordering.API/Application/Validations/ShipOrderCommandValidator.cs @@ -1,16 +1,11 @@ -using FluentValidation; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.Commands; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; -namespace Ordering.API.Application.Validations +public class ShipOrderCommandValidator : AbstractValidator { - public class ShipOrderCommandValidator : AbstractValidator + public ShipOrderCommandValidator(ILogger logger) { - public ShipOrderCommandValidator(ILogger logger) - { - RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); + RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); - logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); - } + logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Controllers/HomeController.cs b/src/Services/Ordering/Ordering.API/Controllers/HomeController.cs index ba4620b23..601b7ab6d 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/HomeController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/HomeController.cs @@ -1,13 +1,10 @@ -using Microsoft.AspNetCore.Mvc; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers +public class HomeController : Controller { - public class HomeController : Controller + // GET: // + public IActionResult Index() { - // GET: // - public IActionResult Index() - { - return new RedirectResult("~/swagger"); - } + return new RedirectResult("~/swagger"); } } diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index e31568d98..2cfd4063f 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -1,153 +1,143 @@ -using MediatR; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; + using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; -using Microsoft.Extensions.Logging; -using Ordering.API.Application.Commands; -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers + +[Route("api/v1/[controller]")] +[Authorize] +[ApiController] +public class OrdersController : ControllerBase { - [Route("api/v1/[controller]")] - [Authorize] - [ApiController] - public class OrdersController : ControllerBase + private readonly IMediator _mediator; + private readonly IOrderQueries _orderQueries; + private readonly IIdentityService _identityService; + private readonly ILogger _logger; + + public OrdersController( + IMediator mediator, + IOrderQueries orderQueries, + IIdentityService identityService, + ILogger logger) { - private readonly IMediator _mediator; - private readonly IOrderQueries _orderQueries; - private readonly IIdentityService _identityService; - private readonly ILogger _logger; - - public OrdersController( - IMediator mediator, - IOrderQueries orderQueries, - IIdentityService identityService, - ILogger logger) - { - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - _orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries)); - _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - [Route("cancel")] - [HttpPut] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public async Task CancelOrderAsync([FromBody] CancelOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) - { - bool commandResult = false; - - if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) - { - var requestCancelOrder = new IdentifiedCommand(command, guid); + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + _orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries)); + _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - requestCancelOrder.GetGenericTypeName(), - nameof(requestCancelOrder.Command.OrderNumber), - requestCancelOrder.Command.OrderNumber, - requestCancelOrder); + [Route("cancel")] + [HttpPut] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public async Task CancelOrderAsync([FromBody] CancelOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) + { + bool commandResult = false; - commandResult = await _mediator.Send(requestCancelOrder); - } + if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) + { + var requestCancelOrder = new IdentifiedCommand(command, guid); - if (!commandResult) - { - return BadRequest(); - } + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + requestCancelOrder.GetGenericTypeName(), + nameof(requestCancelOrder.Command.OrderNumber), + requestCancelOrder.Command.OrderNumber, + requestCancelOrder); - return Ok(); + commandResult = await _mediator.Send(requestCancelOrder); } - [Route("ship")] - [HttpPut] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public async Task ShipOrderAsync([FromBody] ShipOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) + if (!commandResult) { - bool commandResult = false; + return BadRequest(); + } - if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) - { - var requestShipOrder = new IdentifiedCommand(command, guid); + return Ok(); + } - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - requestShipOrder.GetGenericTypeName(), - nameof(requestShipOrder.Command.OrderNumber), - requestShipOrder.Command.OrderNumber, - requestShipOrder); + [Route("ship")] + [HttpPut] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public async Task ShipOrderAsync([FromBody] ShipOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) + { + bool commandResult = false; - commandResult = await _mediator.Send(requestShipOrder); - } + if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) + { + var requestShipOrder = new IdentifiedCommand(command, guid); - if (!commandResult) - { - return BadRequest(); - } + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + requestShipOrder.GetGenericTypeName(), + nameof(requestShipOrder.Command.OrderNumber), + requestShipOrder.Command.OrderNumber, + requestShipOrder); - return Ok(); + commandResult = await _mediator.Send(requestShipOrder); } - [Route("{orderId:int}")] - [HttpGet] - [ProducesResponseType(typeof(Order), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task GetOrderAsync(int orderId) + if (!commandResult) { - try - { - //Todo: It's good idea to take advantage of GetOrderByIdQuery and handle by GetCustomerByIdQueryHandler - //var order customer = await _mediator.Send(new GetOrderByIdQuery(orderId)); - var order = await _orderQueries.GetOrderAsync(orderId); - - return Ok(order); - } - catch - { - return NotFound(); - } + return BadRequest(); } - [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task>> GetOrdersAsync() + return Ok(); + } + + [Route("{orderId:int}")] + [HttpGet] + [ProducesResponseType(typeof(Order), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public async Task GetOrderAsync(int orderId) + { + try { - var userid = _identityService.GetUserIdentity(); - var orders = await _orderQueries.GetOrdersFromUserAsync(Guid.Parse(userid)); + //Todo: It's good idea to take advantage of GetOrderByIdQuery and handle by GetCustomerByIdQueryHandler + //var order customer = await _mediator.Send(new GetOrderByIdQuery(orderId)); + var order = await _orderQueries.GetOrderAsync(orderId); - return Ok(orders); + return Ok(order); } - - [Route("cardtypes")] - [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task>> GetCardTypesAsync() + catch { - var cardTypes = await _orderQueries.GetCardTypesAsync(); - - return Ok(cardTypes); + return NotFound(); } + } - [Route("draft")] - [HttpPost] - public async Task> CreateOrderDraftFromBasketDataAsync([FromBody] CreateOrderDraftCommand createOrderDraftCommand) - { - _logger.LogInformation( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - createOrderDraftCommand.GetGenericTypeName(), - nameof(createOrderDraftCommand.BuyerId), - createOrderDraftCommand.BuyerId, - createOrderDraftCommand); + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task>> GetOrdersAsync() + { + var userid = _identityService.GetUserIdentity(); + var orders = await _orderQueries.GetOrdersFromUserAsync(Guid.Parse(userid)); - return await _mediator.Send(createOrderDraftCommand); - } + return Ok(orders); + } + + [Route("cardtypes")] + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task>> GetCardTypesAsync() + { + var cardTypes = await _orderQueries.GetCardTypesAsync(); + + return Ok(cardTypes); + } + + [Route("draft")] + [HttpPost] + public async Task> CreateOrderDraftFromBasketDataAsync([FromBody] CreateOrderDraftCommand createOrderDraftCommand) + { + _logger.LogInformation( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + createOrderDraftCommand.GetGenericTypeName(), + nameof(createOrderDraftCommand.BuyerId), + createOrderDraftCommand.BuyerId, + createOrderDraftCommand); + + return await _mediator.Send(createOrderDraftCommand); } } diff --git a/src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs b/src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs index bbbc679ba..0f6122a13 100644 --- a/src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs +++ b/src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs @@ -1,28 +1,27 @@ -using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; + +using System.Collections.Generic; using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; -namespace Ordering.API.Application.Models +public static class BasketItemExtensions { - public static class BasketItemExtensions + public static IEnumerable ToOrderItemsDTO(this IEnumerable basketItems) { - public static IEnumerable ToOrderItemsDTO(this IEnumerable basketItems) + foreach (var item in basketItems) { - foreach (var item in basketItems) - { - yield return item.ToOrderItemDTO(); - } + yield return item.ToOrderItemDTO(); } + } - public static OrderItemDTO ToOrderItemDTO(this BasketItem item) + public static OrderItemDTO ToOrderItemDTO(this BasketItem item) + { + return new OrderItemDTO() { - return new OrderItemDTO() - { - ProductId = item.ProductId, - ProductName = item.ProductName, - PictureUrl = item.PictureUrl, - UnitPrice = item.UnitPrice, - Units = item.Quantity - }; - } + ProductId = item.ProductId, + ProductName = item.ProductName, + PictureUrl = item.PictureUrl, + UnitPrice = item.UnitPrice, + Units = item.Quantity + }; } } diff --git a/src/Services/Ordering/Ordering.API/Extensions/LinqSelectExtensions.cs b/src/Services/Ordering/Ordering.API/Extensions/LinqSelectExtensions.cs index 4efe08c07..c36385d6c 100644 --- a/src/Services/Ordering/Ordering.API/Extensions/LinqSelectExtensions.cs +++ b/src/Services/Ordering/Ordering.API/Extensions/LinqSelectExtensions.cs @@ -1,50 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Extensions; -namespace Ordering.API.Extensions +public static class LinqSelectExtensions { - public static class LinqSelectExtensions + public static IEnumerable> SelectTry(this IEnumerable enumerable, Func selector) { - public static IEnumerable> SelectTry(this IEnumerable enumerable, Func selector) + foreach (TSource element in enumerable) { - foreach (TSource element in enumerable) + SelectTryResult returnedValue; + try { - SelectTryResult returnedValue; - try - { - returnedValue = new SelectTryResult(element, selector(element), null); - } - catch (Exception ex) - { - returnedValue = new SelectTryResult(element, default(TResult), ex); - } - yield return returnedValue; + returnedValue = new SelectTryResult(element, selector(element), null); } + catch (Exception ex) + { + returnedValue = new SelectTryResult(element, default(TResult), ex); + } + yield return returnedValue; } + } - public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) - { - return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); - } + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); + } - public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) - { - return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); - } + public static IEnumerable OnCaughtException(this IEnumerable> enumerable, Func exceptionHandler) + { + return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); + } - public class SelectTryResult + public class SelectTryResult + { + internal SelectTryResult(TSource source, TResult result, Exception exception) { - internal SelectTryResult(TSource source, TResult result, Exception exception) - { - Source = source; - Result = result; - CaughtException = exception; - } - - public TSource Source { get; private set; } - public TResult Result { get; private set; } - public Exception CaughtException { get; private set; } + Source = source; + Result = result; + CaughtException = exception; } + + public TSource Source { get; private set; } + public TResult Result { get; private set; } + public Exception CaughtException { get; private set; } } } diff --git a/src/Services/Ordering/Ordering.API/GlobalUsings.cs b/src/Services/Ordering/Ordering.API/GlobalUsings.cs new file mode 100644 index 000000000..2af47f2ab --- /dev/null +++ b/src/Services/Ordering/Ordering.API/GlobalUsings.cs @@ -0,0 +1,85 @@ +global using ApiModels = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; +global using AppCommand = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +global using Autofac.Extensions.DependencyInjection; +global using Autofac; +global using Azure.Core; +global using Azure.Identity; +global using Dapper; +global using FluentValidation; +global using Google.Protobuf.Collections; +global using Grpc.Core; +global using HealthChecks.UI.Client; +global using MediatR; +global using Microsoft.AspNetCore.Authorization; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc.Authorization; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Server.Kestrel.Core; +global using Microsoft.AspNetCore; +global using Microsoft.Azure.ServiceBus; +global using Microsoft.EntityFrameworkCore.Design; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Extensions; +global using GrpcOrdering; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure; +global using Microsoft.eShopOnContainers.Services.Ordering.API; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Exceptions; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Microsoft.OpenApi.Models; +global using Polly.Retry; +global using Polly; +global using RabbitMQ.Client; +global using Serilog.Context; +global using Serilog; +global using Swashbuckle.AspNetCore.SwaggerGen; +global using System.Collections.Generic; +global using System.Data.Common; +global using System.Data.SqlClient; +global using System.IdentityModel.Tokens.Jwt; +global using System.IO; +global using System.Linq; +global using System.Net; +global using System.Reflection; +global using System.Runtime.Serialization; +global using System.Threading.Tasks; +global using System.Threading; +global using System; diff --git a/src/Services/Ordering/Ordering.API/Grpc/OrderingService.cs b/src/Services/Ordering/Ordering.API/Grpc/OrderingService.cs index 3ae6c1733..969a09d46 100644 --- a/src/Services/Ordering/Ordering.API/Grpc/OrderingService.cs +++ b/src/Services/Ordering/Ordering.API/Grpc/OrderingService.cs @@ -1,90 +1,78 @@ -using Google.Protobuf.Collections; -using Grpc.Core; -using MediatR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ApiModels = Ordering.API.Application.Models; -using AppCommand = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +namespace GrpcOrdering; -namespace GrpcOrdering +public class OrderingService : OrderingGrpc.OrderingGrpcBase { - public class OrderingService : OrderingGrpc.OrderingGrpcBase - { - private readonly IMediator _mediator; - private readonly ILogger _logger; + private readonly IMediator _mediator; + private readonly ILogger _logger; - public OrderingService(IMediator mediator, ILogger logger) - { - _mediator = mediator; - _logger = logger; - } - - public override async Task CreateOrderDraftFromBasketData(CreateOrderDraftCommand createOrderDraftCommand, ServerCallContext context) - { - _logger.LogInformation("Begin grpc call from method {Method} for ordering get order draft {CreateOrderDraftCommand}", context.Method, createOrderDraftCommand); - _logger.LogTrace( - "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", - createOrderDraftCommand.GetGenericTypeName(), - nameof(createOrderDraftCommand.BuyerId), - createOrderDraftCommand.BuyerId, - createOrderDraftCommand); + public OrderingService(IMediator mediator, ILogger logger) + { + _mediator = mediator; + _logger = logger; + } - var command = new AppCommand.CreateOrderDraftCommand( - createOrderDraftCommand.BuyerId, - this.MapBasketItems(createOrderDraftCommand.Items)); + public override async Task CreateOrderDraftFromBasketData(CreateOrderDraftCommand createOrderDraftCommand, ServerCallContext context) + { + _logger.LogInformation("Begin grpc call from method {Method} for ordering get order draft {CreateOrderDraftCommand}", context.Method, createOrderDraftCommand); + _logger.LogTrace( + "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", + createOrderDraftCommand.GetGenericTypeName(), + nameof(createOrderDraftCommand.BuyerId), + createOrderDraftCommand.BuyerId, + createOrderDraftCommand); + var command = new AppCommand.CreateOrderDraftCommand( + createOrderDraftCommand.BuyerId, + this.MapBasketItems(createOrderDraftCommand.Items)); - var data = await _mediator.Send(command); - if (data != null) - { - context.Status = new Status(StatusCode.OK, $" ordering get order draft {createOrderDraftCommand} do exist"); + var data = await _mediator.Send(command); - return this.MapResponse(data); - } - else - { - context.Status = new Status(StatusCode.NotFound, $" ordering get order draft {createOrderDraftCommand} do not exist"); - } + if (data != null) + { + context.Status = new Status(StatusCode.OK, $" ordering get order draft {createOrderDraftCommand} do exist"); - return new OrderDraftDTO(); + return this.MapResponse(data); } + else + { + context.Status = new Status(StatusCode.NotFound, $" ordering get order draft {createOrderDraftCommand} do not exist"); + } + + return new OrderDraftDTO(); + } - public OrderDraftDTO MapResponse(AppCommand.OrderDraftDTO order) + public OrderDraftDTO MapResponse(AppCommand.OrderDraftDTO order) + { + var result = new OrderDraftDTO() { - var result = new OrderDraftDTO() - { - Total = (double)order.Total, - }; + Total = (double)order.Total, + }; - order.OrderItems.ToList().ForEach(i => result.OrderItems.Add(new OrderItemDTO() - { - Discount = (double)i.Discount, - PictureUrl = i.PictureUrl, - ProductId = i.ProductId, - ProductName = i.ProductName, - UnitPrice = (double)i.UnitPrice, - Units = i.Units, - })); + order.OrderItems.ToList().ForEach(i => result.OrderItems.Add(new OrderItemDTO() + { + Discount = (double)i.Discount, + PictureUrl = i.PictureUrl, + ProductId = i.ProductId, + ProductName = i.ProductName, + UnitPrice = (double)i.UnitPrice, + Units = i.Units, + })); - return result; - } + return result; + } - public IEnumerable MapBasketItems(RepeatedField items) + public IEnumerable MapBasketItems(RepeatedField items) + { + return items.Select(x => new ApiModels.BasketItem() { - return items.Select(x => new ApiModels.BasketItem() - { - Id = x.Id, - ProductId = x.ProductId, - ProductName = x.ProductName, - UnitPrice = (decimal)x.UnitPrice, - OldUnitPrice = (decimal)x.OldUnitPrice, - Quantity = x.Quantity, - PictureUrl = x.PictureUrl, - }); - } + Id = x.Id, + ProductId = x.ProductId, + ProductName = x.ProductName, + UnitPrice = (decimal)x.UnitPrice, + OldUnitPrice = (decimal)x.OldUnitPrice, + Quantity = x.Quantity, + PictureUrl = x.PictureUrl, + }); } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs b/src/Services/Ordering/Ordering.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs index 55cb33c99..d886bf371 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs @@ -1,14 +1,10 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults -{ - using AspNetCore.Http; - using Microsoft.AspNetCore.Mvc; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; - public class InternalServerErrorObjectResult : ObjectResult +public class InternalServerErrorObjectResult : ObjectResult +{ + public InternalServerErrorObjectResult(object error) + : base(error) { - public InternalServerErrorObjectResult(object error) - : base(error) - { - StatusCode = StatusCodes.Status500InternalServerError; - } + StatusCode = StatusCodes.Status500InternalServerError; } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Auth/AuthorizationHeaderParameterOperationFilter.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Auth/AuthorizationHeaderParameterOperationFilter.cs index bc7916188..dbf8cf97c 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Auth/AuthorizationHeaderParameterOperationFilter.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Auth/AuthorizationHeaderParameterOperationFilter.cs @@ -1,34 +1,27 @@ -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Auth; -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Auth +public class AuthorizationHeaderParameterOperationFilter : IOperationFilter { - public class AuthorizationHeaderParameterOperationFilter : IOperationFilter + public void Apply(OpenApiOperation operation, OperationFilterContext context) { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors; - var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter); - var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter); + var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors; + var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter); + var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter); - if (isAuthorized && !allowAnonymous) - { - if (operation.Parameters == null) - operation.Parameters = new List(); + if (isAuthorized && !allowAnonymous) + { + if (operation.Parameters == null) + operation.Parameters = new List(); - operation.Parameters.Add(new OpenApiParameter - { - Name = "Authorization", - In = ParameterLocation.Header, - Description = "access token", - Required = true - }); - } + operation.Parameters.Add(new OpenApiParameter + { + Name = "Authorization", + In = ParameterLocation.Header, + Description = "access token", + Required = true + }); } - } + } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs index 1d8f45353..1ab775042 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs @@ -1,50 +1,38 @@ -using Autofac; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; -using System.Reflection; - -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules -{ +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules; - public class ApplicationModule - : Autofac.Module - { +public class ApplicationModule + : Autofac.Module +{ - public string QueriesConnectionString { get; } + public string QueriesConnectionString { get; } - public ApplicationModule(string qconstr) - { - QueriesConnectionString = qconstr; + public ApplicationModule(string qconstr) + { + QueriesConnectionString = qconstr; - } + } - protected override void Load(ContainerBuilder builder) - { + protected override void Load(ContainerBuilder builder) + { - builder.Register(c => new OrderQueries(QueriesConnectionString)) - .As() - .InstancePerLifetimeScope(); + builder.Register(c => new OrderQueries(QueriesConnectionString)) + .As() + .InstancePerLifetimeScope(); - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); + builder.RegisterType() + .As() + .InstancePerLifetimeScope(); - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); + builder.RegisterType() + .As() + .InstancePerLifetimeScope(); - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); + builder.RegisterType() + .As() + .InstancePerLifetimeScope(); - builder.RegisterAssemblyTypes(typeof(CreateOrderCommandHandler).GetTypeInfo().Assembly) - .AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); + builder.RegisterAssemblyTypes(typeof(CreateOrderCommandHandler).GetTypeInfo().Assembly) + .AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); - } } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs index 91e9a5149..2d59f1730 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs @@ -1,47 +1,36 @@ -using Autofac; -using FluentValidation; -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Ordering.API.Application.Behaviors; -using Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; -using Ordering.API.Application.Validations; -using System.Linq; -using System.Reflection; - -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules; + +public class MediatorModule : Autofac.Module { - public class MediatorModule : Autofac.Module + protected override void Load(ContainerBuilder builder) { - protected override void Load(ContainerBuilder builder) - { - builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly) - .AsImplementedInterfaces(); + builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly) + .AsImplementedInterfaces(); - // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands - builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly) - .AsClosedTypesOf(typeof(IRequestHandler<,>)); + // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands + builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly) + .AsClosedTypesOf(typeof(IRequestHandler<,>)); - // Register the DomainEventHandler classes (they implement INotificationHandler<>) in assembly holding the Domain Events - builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) - .AsClosedTypesOf(typeof(INotificationHandler<>)); + // Register the DomainEventHandler classes (they implement INotificationHandler<>) in assembly holding the Domain Events + builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) + .AsClosedTypesOf(typeof(INotificationHandler<>)); - // Register the Command's Validators (Validators based on FluentValidation library) - builder - .RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly) - .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) - .AsImplementedInterfaces(); + // Register the Command's Validators (Validators based on FluentValidation library) + builder + .RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly) + .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) + .AsImplementedInterfaces(); - builder.Register(context => - { - var componentContext = context.Resolve(); - return t => { object o; return componentContext.TryResolve(t, out o) ? o : null; }; - }); + builder.Register(context => + { + var componentContext = context.Resolve(); + return t => { object o; return componentContext.TryResolve(t, out o) ? o : null; }; + }); - builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>)); - builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); - builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>)); + builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>)); + builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); + builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>)); - } } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Factories/OrderingDbContextFactory.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Factories/OrderingDbContextFactory.cs index 56ec898af..9f05e610c 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Factories/OrderingDbContextFactory.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Factories/OrderingDbContextFactory.cs @@ -1,10 +1,4 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using Microsoft.Extensions.Configuration; -using System.IO; - -namespace Ordering.API.Infrastructure.Factories +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Factories { public class OrderingDbContextFactory : IDesignTimeDbContextFactory { diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs index 34b897a40..cdc4b76b1 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/AuthorizeCheckOperationFilter.cs @@ -1,36 +1,29 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters; -namespace Ordering.API.Infrastructure.Filters +public class AuthorizeCheckOperationFilter : IOperationFilter { - public class AuthorizeCheckOperationFilter : IOperationFilter + public void Apply(OpenApiOperation operation, OperationFilterContext context) { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - // Check for authorize attribute - var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || - context.MethodInfo.GetCustomAttributes(true).OfType().Any(); + // Check for authorize attribute + var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || + context.MethodInfo.GetCustomAttributes(true).OfType().Any(); - if (!hasAuthorize) return; + if (!hasAuthorize) return; - operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); - operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); + operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); + operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); - var oAuthScheme = new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }; + var oAuthScheme = new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } + }; - operation.Security = new List + operation.Security = new List + { + new OpenApiSecurityRequirement { - new OpenApiSecurityRequirement - { - [ oAuthScheme ] = new [] { "orderingapi" } - } - }; - } + [ oAuthScheme ] = new [] { "orderingapi" } + } + }; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 8b7587744..eef48c502 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -1,71 +1,60 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters; + +public class HttpGlobalExceptionFilter : IExceptionFilter { - using AspNetCore.Mvc; - using global::Ordering.Domain.Exceptions; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Mvc.Filters; - using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; - using System.Net; + private readonly IWebHostEnvironment env; + private readonly ILogger logger; - public class HttpGlobalExceptionFilter : IExceptionFilter + public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) { - private readonly IWebHostEnvironment env; - private readonly ILogger logger; + this.env = env; + this.logger = logger; + } - public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) - { - this.env = env; - this.logger = logger; - } + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); - public void OnException(ExceptionContext context) + if (context.Exception.GetType() == typeof(OrderingDomainException)) { - logger.LogError(new EventId(context.Exception.HResult), - context.Exception, - context.Exception.Message); - - if (context.Exception.GetType() == typeof(OrderingDomainException)) + var problemDetails = new ValidationProblemDetails() { - var problemDetails = new ValidationProblemDetails() - { - Instance = context.HttpContext.Request.Path, - Status = StatusCodes.Status400BadRequest, - Detail = "Please refer to the errors property for additional details." - }; + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; - problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); + problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); - context.Result = new BadRequestObjectResult(problemDetails); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; - } - else + context.Result = new BadRequestObjectResult(problemDetails); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse { - var json = new JsonErrorResponse - { - Messages = new[] { "An error occur.Try it again." } - }; - - if (env.IsDevelopment()) - { - json.DeveloperMessage = context.Exception; - } + Messages = new[] { "An error occur.Try it again." } + }; - // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 - // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information - context.Result = new InternalServerErrorObjectResult(json); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + if (env.IsDevelopment()) + { + json.DeveloperMessage = context.Exception; } - context.ExceptionHandled = true; + + // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 + // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } + context.ExceptionHandled = true; + } - private class JsonErrorResponse - { - public string[] Messages { get; set; } + private class JsonErrorResponse + { + public string[] Messages { get; set; } - public object DeveloperMessage { get; set; } - } + public object DeveloperMessage { get; set; } } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs b/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs index 35b79e505..3c0aca009 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs @@ -1,191 +1,174 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure; + +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; + +public class OrderingContextSeed { - using global::Ordering.API.Extensions; - using Microsoft.AspNetCore.Hosting; - using Microsoft.EntityFrameworkCore; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Options; - using Ordering.Infrastructure; - using Polly; - using Polly.Retry; - using System; - using System.Collections.Generic; - using System.Data.SqlClient; - using System.IO; - using System.Linq; - using System.Threading.Tasks; - - public class OrderingContextSeed + public async Task SeedAsync(OrderingContext context, IWebHostEnvironment env, IOptions settings, ILogger logger) { - public async Task SeedAsync(OrderingContext context, IWebHostEnvironment env, IOptions settings, ILogger logger) + var policy = CreatePolicy(logger, nameof(OrderingContextSeed)); + + await policy.ExecuteAsync(async () => { - var policy = CreatePolicy(logger, nameof(OrderingContextSeed)); - await policy.ExecuteAsync(async () => - { + var useCustomizationData = settings.Value + .UseCustomizationData; - var useCustomizationData = settings.Value - .UseCustomizationData; + var contentRootPath = env.ContentRootPath; - var contentRootPath = env.ContentRootPath; + using (context) + { + context.Database.Migrate(); - using (context) + if (!context.CardTypes.Any()) { - context.Database.Migrate(); - - if (!context.CardTypes.Any()) - { - context.CardTypes.AddRange(useCustomizationData - ? GetCardTypesFromFile(contentRootPath, logger) - : GetPredefinedCardTypes()); - - await context.SaveChangesAsync(); - } - - if (!context.OrderStatus.Any()) - { - context.OrderStatus.AddRange(useCustomizationData - ? GetOrderStatusFromFile(contentRootPath, logger) - : GetPredefinedOrderStatus()); - } + context.CardTypes.AddRange(useCustomizationData + ? GetCardTypesFromFile(contentRootPath, logger) + : GetPredefinedCardTypes()); await context.SaveChangesAsync(); } - }); - } - private IEnumerable GetCardTypesFromFile(string contentRootPath, ILogger log) - { - string csvFileCardTypes = Path.Combine(contentRootPath, "Setup", "CardTypes.csv"); + if (!context.OrderStatus.Any()) + { + context.OrderStatus.AddRange(useCustomizationData + ? GetOrderStatusFromFile(contentRootPath, logger) + : GetPredefinedOrderStatus()); + } - if (!File.Exists(csvFileCardTypes)) - { - return GetPredefinedCardTypes(); + await context.SaveChangesAsync(); } + }); + } - string[] csvheaders; - try - { - string[] requiredHeaders = { "CardType" }; - csvheaders = GetHeaders(requiredHeaders, csvFileCardTypes); - } - catch (Exception ex) - { - log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); - return GetPredefinedCardTypes(); - } + private IEnumerable GetCardTypesFromFile(string contentRootPath, ILogger log) + { + string csvFileCardTypes = Path.Combine(contentRootPath, "Setup", "CardTypes.csv"); - int id = 1; - return File.ReadAllLines(csvFileCardTypes) - .Skip(1) // skip header column - .SelectTry(x => CreateCardType(x, ref id)) - .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) - .Where(x => x != null); + if (!File.Exists(csvFileCardTypes)) + { + return GetPredefinedCardTypes(); } - private CardType CreateCardType(string value, ref int id) + string[] csvheaders; + try { - if (String.IsNullOrEmpty(value)) - { - throw new Exception("Orderstatus is null or empty"); - } - - return new CardType(id++, value.Trim('"').Trim()); + string[] requiredHeaders = { "CardType" }; + csvheaders = GetHeaders(requiredHeaders, csvFileCardTypes); } - - private IEnumerable GetPredefinedCardTypes() + catch (Exception ex) { - return Enumeration.GetAll(); + log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); + return GetPredefinedCardTypes(); } - private IEnumerable GetOrderStatusFromFile(string contentRootPath, ILogger log) + int id = 1; + return File.ReadAllLines(csvFileCardTypes) + .Skip(1) // skip header column + .SelectTry(x => CreateCardType(x, ref id)) + .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) + .Where(x => x != null); + } + + private CardType CreateCardType(string value, ref int id) + { + if (String.IsNullOrEmpty(value)) { - string csvFileOrderStatus = Path.Combine(contentRootPath, "Setup", "OrderStatus.csv"); + throw new Exception("Orderstatus is null or empty"); + } - if (!File.Exists(csvFileOrderStatus)) - { - return GetPredefinedOrderStatus(); - } + return new CardType(id++, value.Trim('"').Trim()); + } - string[] csvheaders; - try - { - string[] requiredHeaders = { "OrderStatus" }; - csvheaders = GetHeaders(requiredHeaders, csvFileOrderStatus); - } - catch (Exception ex) - { - log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); - return GetPredefinedOrderStatus(); - } + private IEnumerable GetPredefinedCardTypes() + { + return Enumeration.GetAll(); + } - int id = 1; - return File.ReadAllLines(csvFileOrderStatus) - .Skip(1) // skip header row - .SelectTry(x => CreateOrderStatus(x, ref id)) - .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) - .Where(x => x != null); - } + private IEnumerable GetOrderStatusFromFile(string contentRootPath, ILogger log) + { + string csvFileOrderStatus = Path.Combine(contentRootPath, "Setup", "OrderStatus.csv"); - private OrderStatus CreateOrderStatus(string value, ref int id) + if (!File.Exists(csvFileOrderStatus)) { - if (String.IsNullOrEmpty(value)) - { - throw new Exception("Orderstatus is null or empty"); - } + return GetPredefinedOrderStatus(); + } - return new OrderStatus(id++, value.Trim('"').Trim().ToLowerInvariant()); + string[] csvheaders; + try + { + string[] requiredHeaders = { "OrderStatus" }; + csvheaders = GetHeaders(requiredHeaders, csvFileOrderStatus); + } + catch (Exception ex) + { + log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); + return GetPredefinedOrderStatus(); } - private IEnumerable GetPredefinedOrderStatus() + int id = 1; + return File.ReadAllLines(csvFileOrderStatus) + .Skip(1) // skip header row + .SelectTry(x => CreateOrderStatus(x, ref id)) + .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) + .Where(x => x != null); + } + + private OrderStatus CreateOrderStatus(string value, ref int id) + { + if (String.IsNullOrEmpty(value)) { - return new List() - { - OrderStatus.Submitted, - OrderStatus.AwaitingValidation, - OrderStatus.StockConfirmed, - OrderStatus.Paid, - OrderStatus.Shipped, - OrderStatus.Cancelled - }; + throw new Exception("Orderstatus is null or empty"); } - private string[] GetHeaders(string[] requiredHeaders, string csvfile) + return new OrderStatus(id++, value.Trim('"').Trim().ToLowerInvariant()); + } + + private IEnumerable GetPredefinedOrderStatus() + { + return new List() { - string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); + OrderStatus.Submitted, + OrderStatus.AwaitingValidation, + OrderStatus.StockConfirmed, + OrderStatus.Paid, + OrderStatus.Shipped, + OrderStatus.Cancelled + }; + } - if (csvheaders.Count() != requiredHeaders.Count()) - { - throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); - } + private string[] GetHeaders(string[] requiredHeaders, string csvfile) + { + string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); - foreach (var requiredHeader in requiredHeaders) + if (csvheaders.Count() != requiredHeaders.Count()) + { + throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); + } + + foreach (var requiredHeader in requiredHeaders) + { + if (!csvheaders.Contains(requiredHeader)) { - if (!csvheaders.Contains(requiredHeader)) - { - throw new Exception($"does not contain required header '{requiredHeader}'"); - } + throw new Exception($"does not contain required header '{requiredHeader}'"); } - - return csvheaders; } + return csvheaders; + } - private AsyncRetryPolicy CreatePolicy(ILogger logger, string prefix, int retries = 3) - { - return Policy.Handle(). - WaitAndRetryAsync( - retryCount: retries, - sleepDurationProvider: retry => TimeSpan.FromSeconds(5), - onRetry: (exception, timeSpan, retry, ctx) => - { - logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); - } - ); - } + + private AsyncRetryPolicy CreatePolicy(ILogger logger, string prefix, int retries = 3) + { + return Policy.Handle(). + WaitAndRetryAsync( + retryCount: retries, + sleepDurationProvider: retry => TimeSpan.FromSeconds(5), + onRetry: (exception, timeSpan, retry, ctx) => + { + logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); + } + ); } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs index e51452d7d..3b055f0ab 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IIdentityService.cs @@ -1,9 +1,9 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; + +public interface IIdentityService { - public interface IIdentityService - { - string GetUserIdentity(); + string GetUserIdentity(); - string GetUserName(); - } + string GetUserName(); } + diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs index 4dab338c4..0849805e2 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Services/IdentityService.cs @@ -1,26 +1,21 @@ - -using Microsoft.AspNetCore.Http; -using System; +namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; -namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services +public class IdentityService : IIdentityService { - public class IdentityService : IIdentityService - { - private IHttpContextAccessor _context; + private IHttpContextAccessor _context; - public IdentityService(IHttpContextAccessor context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - } + public IdentityService(IHttpContextAccessor context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } - public string GetUserIdentity() - { - return _context.HttpContext.User.FindFirst("sub").Value; - } + public string GetUserIdentity() + { + return _context.HttpContext.User.FindFirst("sub").Value; + } - public string GetUserName() - { - return _context.HttpContext.User.Identity.Name; - } + public string GetUserName() + { + return _context.HttpContext.User.Identity.Name; } } diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index 3b545494f..f6daa8385 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -37,9 +37,9 @@ - + - + @@ -51,22 +51,23 @@ - - - - + + + + - - + + - - - - - - - + + + + + + + + diff --git a/src/Services/Ordering/Ordering.API/OrderingSettings.cs b/src/Services/Ordering/Ordering.API/OrderingSettings.cs index 7459bfd1c..93cc94b92 100644 --- a/src/Services/Ordering/Ordering.API/OrderingSettings.cs +++ b/src/Services/Ordering/Ordering.API/OrderingSettings.cs @@ -1,15 +1,14 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API +namespace Microsoft.eShopOnContainers.Services.Ordering.API; + +public class OrderingSettings { - public class OrderingSettings - { - public bool UseCustomizationData { get; set; } + public bool UseCustomizationData { get; set; } - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } - public string EventBusConnection { get; set; } + public string EventBusConnection { get; set; } - public int GracePeriodTime { get; set; } + public int GracePeriodTime { get; set; } - public int CheckUpdateTime { get; set; } - } + public int CheckUpdateTime { get; set; } } diff --git a/src/Services/Ordering/Ordering.API/Program.cs b/src/Services/Ordering/Ordering.API/Program.cs index 52eb1a38b..0d4c00677 100644 --- a/src/Services/Ordering/Ordering.API/Program.cs +++ b/src/Services/Ordering/Ordering.API/Program.cs @@ -1,22 +1,4 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.eShopOnContainers.Services.Ordering.API; -using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Serilog; -using System; -using System.IO; -using System.Net; -using Azure.Identity; -using Azure.Core; - -var configuration = GetConfiguration(); +var configuration = GetConfiguration(); Log.Logger = CreateSerilogLogger(configuration); diff --git a/src/Services/Ordering/Ordering.API/Startup.cs b/src/Services/Ordering/Ordering.API/Startup.cs index 5813a0578..ab02b27c0 100644 --- a/src/Services/Ordering/Ordering.API/Startup.cs +++ b/src/Services/Ordering/Ordering.API/Startup.cs @@ -1,429 +1,389 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.API +namespace Microsoft.eShopOnContainers.Services.Ordering.API; + +public class Startup { - using AspNetCore.Http; - using Autofac; - using Autofac.Extensions.DependencyInjection; - using global::Ordering.API.Application.IntegrationEvents; - using global::Ordering.API.Application.IntegrationEvents.Events; - using global::Ordering.API.Infrastructure.Filters; - using GrpcOrdering; - using HealthChecks.UI.Client; - using Infrastructure.AutofacModules; - using Infrastructure.Filters; - using Infrastructure.Services; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Diagnostics.HealthChecks; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Azure.ServiceBus; - using Microsoft.EntityFrameworkCore; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; - using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; - using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; - using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Diagnostics.HealthChecks; - using Microsoft.Extensions.Logging; - using Microsoft.OpenApi.Models; - using Ordering.Infrastructure; - using RabbitMQ.Client; - using System; - using System.Collections.Generic; - using System.Data.Common; - using System.IdentityModel.Tokens.Jwt; - using System.IO; - using System.Reflection; - - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - public virtual IServiceProvider ConfigureServices(IServiceCollection services) - { - services - .AddGrpc(options => - { - options.EnableDetailedErrors = true; - }) - .Services - .AddApplicationInsights(Configuration) - .AddCustomMvc() - .AddHealthChecks(Configuration) - .AddCustomDbContext(Configuration) - .AddCustomSwagger(Configuration) - .AddCustomIntegrations(Configuration) - .AddCustomConfiguration(Configuration) - .AddEventBus(Configuration) - .AddCustomAuthentication(Configuration); - //configure autofac - - var container = new ContainerBuilder(); - container.Populate(services); - - container.RegisterModule(new MediatorModule()); - container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); - - return new AutofacServiceProvider(container.Build()); - } + public virtual IServiceProvider ConfigureServices(IServiceCollection services) + { + services + .AddGrpc(options => + { + options.EnableDetailedErrors = true; + }) + .Services + .AddApplicationInsights(Configuration) + .AddCustomMvc() + .AddHealthChecks(Configuration) + .AddCustomDbContext(Configuration) + .AddCustomSwagger(Configuration) + .AddCustomIntegrations(Configuration) + .AddCustomConfiguration(Configuration) + .AddEventBus(Configuration) + .AddCustomAuthentication(Configuration); + //configure autofac + + var container = new ContainerBuilder(); + container.Populate(services); + + container.RegisterModule(new MediatorModule()); + container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); + + return new AutofacServiceProvider(container.Build()); + } - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + { + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + + var pathBase = Configuration["PATH_BASE"]; + if (!string.IsNullOrEmpty(pathBase)) { - //loggerFactory.AddAzureWebAppDiagnostics(); - //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); + app.UsePathBase(pathBase); + } - var pathBase = Configuration["PATH_BASE"]; - if (!string.IsNullOrEmpty(pathBase)) + app.UseSwagger() + .UseSwaggerUI(c => { - loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); - app.UsePathBase(pathBase); - } - - app.UseSwagger() - .UseSwaggerUI(c => - { - c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1"); - c.OAuthClientId("orderingswaggerui"); - c.OAuthAppName("Ordering Swagger UI"); - }); - - app.UseRouting(); - app.UseCors("CorsPolicy"); - ConfigureAuth(app); - - app.UseEndpoints(endpoints => + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1"); + c.OAuthClientId("orderingswaggerui"); + c.OAuthAppName("Ordering Swagger UI"); + }); + + app.UseRouting(); + app.UseCors("CorsPolicy"); + ConfigureAuth(app); + + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + endpoints.MapGet("/_proto/", async ctx => { - endpoints.MapGrpcService(); - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - endpoints.MapGet("/_proto/", async ctx => + ctx.Response.ContentType = "text/plain"; + using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read); + using var sr = new StreamReader(fs); + while (!sr.EndOfStream) { - ctx.Response.ContentType = "text/plain"; - using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read); - using var sr = new StreamReader(fs); - while (!sr.EndOfStream) + var line = await sr.ReadLineAsync(); + if (line != "/* >>" || line != "<< */") { - var line = await sr.ReadLineAsync(); - if (line != "/* >>" || line != "<< */") - { - await ctx.Response.WriteAsync(line); - } + await ctx.Response.WriteAsync(line); } - }); - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); + } + }); + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions + { + Predicate = r => r.Name.Contains("self") }); + }); - ConfigureEventBus(app); - } + ConfigureEventBus(app); + } - private void ConfigureEventBus(IApplicationBuilder app) - { - var eventBus = app.ApplicationServices.GetRequiredService(); - - eventBus.Subscribe>(); - eventBus.Subscribe>(); - eventBus.Subscribe>(); - eventBus.Subscribe>(); - eventBus.Subscribe>(); - eventBus.Subscribe>(); - } + private void ConfigureEventBus(IApplicationBuilder app) + { + var eventBus = app.ApplicationServices.GetRequiredService(); + + eventBus.Subscribe>(); + eventBus.Subscribe>(); + eventBus.Subscribe>(); + eventBus.Subscribe>(); + eventBus.Subscribe>(); + eventBus.Subscribe>(); + } - protected virtual void ConfigureAuth(IApplicationBuilder app) - { - app.UseAuthentication(); - app.UseAuthorization(); - } + protected virtual void ConfigureAuth(IApplicationBuilder app) + { + app.UseAuthentication(); + app.UseAuthorization(); } +} - static class CustomExtensionsMethods +static class CustomExtensionsMethods +{ + public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) - { - services.AddApplicationInsightsTelemetry(configuration); - services.AddApplicationInsightsKubernetesEnricher(); + services.AddApplicationInsightsTelemetry(configuration); + services.AddApplicationInsightsKubernetesEnricher(); - return services; - } + return services; + } - public static IServiceCollection AddCustomMvc(this IServiceCollection services) - { - // Add framework services. - services.AddControllers(options => - { - options.Filters.Add(typeof(HttpGlobalExceptionFilter)); - }) - // Added for functional tests - .AddApplicationPart(typeof(OrdersController).Assembly) - .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true) - .SetCompatibilityVersion(CompatibilityVersion.Version_3_0); - - services.AddCors(options => + public static IServiceCollection AddCustomMvc(this IServiceCollection services) + { + // Add framework services. + services.AddControllers(options => { - options.AddPolicy("CorsPolicy", - builder => builder - .SetIsOriginAllowed((host) => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); - }); + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }) + // Added for functional tests + .AddApplicationPart(typeof(OrdersController).Assembly) + .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true) + .SetCompatibilityVersion(CompatibilityVersion.Version_3_0); + + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder + .SetIsOriginAllowed((host) => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + return services; + } - return services; - } + public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); - public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) - { - var hcBuilder = services.AddHealthChecks(); + hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); - hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); + hcBuilder + .AddSqlServer( + configuration["ConnectionString"], + name: "OrderingDB-check", + tags: new string[] { "orderingdb" }); + if (configuration.GetValue("AzureServiceBusEnabled")) + { hcBuilder - .AddSqlServer( - configuration["ConnectionString"], - name: "OrderingDB-check", - tags: new string[] { "orderingdb" }); - - if (configuration.GetValue("AzureServiceBusEnabled")) - { - hcBuilder - .AddAzureServiceBusTopic( - configuration["EventBusConnection"], - topicName: "eshop_event_bus", - name: "ordering-servicebus-check", - tags: new string[] { "servicebus" }); - } - else - { - hcBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", - name: "ordering-rabbitmqbus-check", - tags: new string[] { "rabbitmqbus" }); - } - - return services; + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "ordering-servicebus-check", + tags: new string[] { "servicebus" }); } - - public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) + else { - services.AddDbContext(options => - { - options.UseSqlServer(configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); - }, - ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) - ); - - services.AddDbContext(options => - { - options.UseSqlServer(configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); - }); - - return services; + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "ordering-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); } - public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) + return services; + } + + public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) + { + services.AddDbContext(options => + { + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }, + ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) + ); + + services.AddDbContext(options => { - services.AddSwaggerGen(options => + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }); + + return services; + } + + public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) + { + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo { - options.DescribeAllEnumsAsStrings(); - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "eShopOnContainers - Ordering HTTP API", - Version = "v1", - Description = "The Ordering Service HTTP API" - }); - options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + Title = "eShopOnContainers - Ordering HTTP API", + Version = "v1", + Description = "The Ordering Service HTTP API" + }); + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OAuth2, + Flows = new OpenApiOAuthFlows() { - Type = SecuritySchemeType.OAuth2, - Flows = new OpenApiOAuthFlows() + Implicit = new OpenApiOAuthFlow() { - Implicit = new OpenApiOAuthFlow() + AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), + TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), + Scopes = new Dictionary() { - AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), - TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), - Scopes = new Dictionary() - { - { "orders", "Ordering API" } - } + { "orders", "Ordering API" } } } - }); - - options.OperationFilter(); + } }); - return services; - } + options.OperationFilter(); + }); - public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) - { - services.AddSingleton(); - services.AddTransient(); - services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); + return services; + } - services.AddTransient(); + public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); + services.AddTransient(); + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); - if (configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusConnectionString = configuration["EventBusConnection"]; - var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - var subscriptionClientName = configuration["SubscriptionClientName"]; - - return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); - }); - } - else + services.AddTransient(); + + if (configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => { - services.AddSingleton(sp => - { - var logger = sp.GetRequiredService>(); + var serviceBusConnectionString = configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); + var subscriptionClientName = configuration["SubscriptionClientName"]; + return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); + }); + } + else + { + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); - var factory = new ConnectionFactory() - { - HostName = configuration["EventBusConnection"], - DispatchConsumersAsync = true - }; - if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) - { - factory.UserName = configuration["EventBusUserName"]; - } + var factory = new ConnectionFactory() + { + HostName = configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; - if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) - { - factory.Password = configuration["EventBusPassword"]; - } + if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) + { + factory.UserName = configuration["EventBusUserName"]; + } - var retryCount = 5; - if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(configuration["EventBusRetryCount"]); - } + if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) + { + factory.Password = configuration["EventBusPassword"]; + } - return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); - }); - } + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } - return services; + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); + }); } - public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) + return services; + } + + public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) + { + services.AddOptions(); + services.Configure(configuration); + services.Configure(options => { - services.AddOptions(); - services.Configure(configuration); - services.Configure(options => + options.InvalidModelStateResponseFactory = context => { - options.InvalidModelStateResponseFactory = context => + var problemDetails = new ValidationProblemDetails(context.ModelState) { - var problemDetails = new ValidationProblemDetails(context.ModelState) - { - Instance = context.HttpContext.Request.Path, - Status = StatusCodes.Status400BadRequest, - Detail = "Please refer to the errors property for additional details." - }; + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; - return new BadRequestObjectResult(problemDetails) - { - ContentTypes = { "application/problem+json", "application/problem+xml" } - }; + return new BadRequestObjectResult(problemDetails) + { + ContentTypes = { "application/problem+json", "application/problem+xml" } }; - }); + }; + }); - return services; - } + return services; + } - public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) + { + if (configuration.GetValue("AzureServiceBusEnabled")) { - if (configuration.GetValue("AzureServiceBusEnabled")) + services.AddSingleton(sp => { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope); - }); - } - else + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, iLifetimeScope); + }); + } + else + { + services.AddSingleton(sp => { - services.AddSingleton(sp => + var subscriptionClientName = configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) { - var subscriptionClientName = configuration["SubscriptionClientName"]; - var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - var retryCount = 5; - if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(configuration["EventBusRetryCount"]); - } + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); - }); - } + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } - services.AddSingleton(); + services.AddSingleton(); - return services; - } + return services; + } - public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) - { - // prevent from mapping "sub" claim to nameidentifier. - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + { + // prevent from mapping "sub" claim to nameidentifier. + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); - var identityUrl = configuration.GetValue("IdentityUrl"); + var identityUrl = configuration.GetValue("IdentityUrl"); - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; - }).AddJwtBearer(options => - { - options.Authority = identityUrl; - options.RequireHttpsMetadata = false; - options.Audience = "orders"; - }); + }).AddJwtBearer(options => + { + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "orders"; + }); - return services; - } + return services; } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs index a3de10052..e959227bf 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs @@ -1,55 +1,48 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using Ordering.Domain.Events; -using System; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate +public class Buyer + : Entity, IAggregateRoot { - public class Buyer - : Entity, IAggregateRoot - { - public string IdentityGuid { get; private set; } + public string IdentityGuid { get; private set; } - public string Name { get; private set; } + public string Name { get; private set; } - private List _paymentMethods; + private List _paymentMethods; - public IEnumerable PaymentMethods => _paymentMethods.AsReadOnly(); + public IEnumerable PaymentMethods => _paymentMethods.AsReadOnly(); - protected Buyer() - { + protected Buyer() + { - _paymentMethods = new List(); - } + _paymentMethods = new List(); + } - public Buyer(string identity, string name) : this() - { - IdentityGuid = !string.IsNullOrWhiteSpace(identity) ? identity : throw new ArgumentNullException(nameof(identity)); - Name = !string.IsNullOrWhiteSpace(name) ? name : throw new ArgumentNullException(nameof(name)); - } + public Buyer(string identity, string name) : this() + { + IdentityGuid = !string.IsNullOrWhiteSpace(identity) ? identity : throw new ArgumentNullException(nameof(identity)); + Name = !string.IsNullOrWhiteSpace(name) ? name : throw new ArgumentNullException(nameof(name)); + } - public PaymentMethod VerifyOrAddPaymentMethod( - int cardTypeId, string alias, string cardNumber, - string securityNumber, string cardHolderName, DateTime expiration, int orderId) - { - var existingPayment = _paymentMethods - .SingleOrDefault(p => p.IsEqualTo(cardTypeId, cardNumber, expiration)); + public PaymentMethod VerifyOrAddPaymentMethod( + int cardTypeId, string alias, string cardNumber, + string securityNumber, string cardHolderName, DateTime expiration, int orderId) + { + var existingPayment = _paymentMethods + .SingleOrDefault(p => p.IsEqualTo(cardTypeId, cardNumber, expiration)); - if (existingPayment != null) - { - AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, existingPayment, orderId)); + if (existingPayment != null) + { + AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, existingPayment, orderId)); - return existingPayment; - } + return existingPayment; + } - var payment = new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration); + var payment = new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration); - _paymentMethods.Add(payment); + _paymentMethods.Add(payment); - AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, payment, orderId)); + AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, payment, orderId)); - return payment; - } + return payment; } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/CardType.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/CardType.cs index 333aaec50..52074c6d9 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/CardType.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/CardType.cs @@ -1,21 +1,20 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; + +/// +/// Card type class should be marked as abstract with protected constructor to encapsulate known enum types +/// this is currently not possible as OrderingContextSeed uses this constructor to load cardTypes from csv file +/// +public class CardType + : Enumeration { - /// - /// Card type class should be marked as abstract with protected constructor to encapsulate known enum types - /// this is currently not possible as OrderingContextSeed uses this constructor to load cardTypes from csv file - /// - public class CardType - : Enumeration - { - public static CardType Amex = new(1, nameof(Amex)); - public static CardType Visa = new(2, nameof(Visa)); - public static CardType MasterCard = new(3, nameof(MasterCard)); + public static CardType Amex = new(1, nameof(Amex)); + public static CardType Visa = new(2, nameof(Visa)); + public static CardType MasterCard = new(3, nameof(MasterCard)); - public CardType(int id, string name) - : base(id, name) - { - } + public CardType(int id, string name) + : base(id, name) + { } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs index 197f725f6..a241e7dbd 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/IBuyerRepository.cs @@ -1,16 +1,13 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate -{ - //This is just the RepositoryContracts or Interface defined at the Domain Layer - //as requisite for the Buyer Aggregate +//This is just the RepositoryContracts or Interface defined at the Domain Layer +//as requisite for the Buyer Aggregate - public interface IBuyerRepository : IRepository - { - Buyer Add(Buyer buyer); - Buyer Update(Buyer buyer); - Task FindAsync(string BuyerIdentityGuid); - Task FindByIdAsync(string id); - } +public interface IBuyerRepository : IRepository +{ + Buyer Add(Buyer buyer); + Buyer Update(Buyer buyer); + Task FindAsync(string BuyerIdentityGuid); + Task FindByIdAsync(string id); } + diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs index bd245d1a8..e4e490488 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/PaymentMethod.cs @@ -1,46 +1,41 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using Ordering.Domain.Exceptions; -using System; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate +public class PaymentMethod + : Entity { - public class PaymentMethod - : Entity - { - private string _alias; - private string _cardNumber; - private string _securityNumber; - private string _cardHolderName; - private DateTime _expiration; - - private int _cardTypeId; - public CardType CardType { get; private set; } + private string _alias; + private string _cardNumber; + private string _securityNumber; + private string _cardHolderName; + private DateTime _expiration; + private int _cardTypeId; + public CardType CardType { get; private set; } - protected PaymentMethod() { } - public PaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration) - { + protected PaymentMethod() { } - _cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new OrderingDomainException(nameof(cardNumber)); - _securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new OrderingDomainException(nameof(securityNumber)); - _cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new OrderingDomainException(nameof(cardHolderName)); - - if (expiration < DateTime.UtcNow) - { - throw new OrderingDomainException(nameof(expiration)); - } + public PaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration) + { - _alias = alias; - _expiration = expiration; - _cardTypeId = cardTypeId; - } + _cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new OrderingDomainException(nameof(cardNumber)); + _securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new OrderingDomainException(nameof(securityNumber)); + _cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new OrderingDomainException(nameof(cardHolderName)); - public bool IsEqualTo(int cardTypeId, string cardNumber, DateTime expiration) + if (expiration < DateTime.UtcNow) { - return _cardTypeId == cardTypeId - && _cardNumber == cardNumber - && _expiration == expiration; + throw new OrderingDomainException(nameof(expiration)); } + + _alias = alias; + _expiration = expiration; + _cardTypeId = cardTypeId; + } + + public bool IsEqualTo(int cardTypeId, string cardNumber, DateTime expiration) + { + return _cardTypeId == cardTypeId + && _cardNumber == cardNumber + && _expiration == expiration; } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs index e2c78094d..c58f81b16 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Address.cs @@ -1,36 +1,33 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; -using System; -using System.Collections.Generic; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + +public class Address : ValueObject { - public class Address : ValueObject - { - public String Street { get; private set; } - public String City { get; private set; } - public String State { get; private set; } - public String Country { get; private set; } - public String ZipCode { get; private set; } + public String Street { get; private set; } + public String City { get; private set; } + public String State { get; private set; } + public String Country { get; private set; } + public String ZipCode { get; private set; } - public Address() { } + public Address() { } - public Address(string street, string city, string state, string country, string zipcode) - { - Street = street; - City = city; - State = state; - Country = country; - ZipCode = zipcode; - } + public Address(string street, string city, string state, string country, string zipcode) + { + Street = street; + City = city; + State = state; + Country = country; + ZipCode = zipcode; + } - protected override IEnumerable GetEqualityComponents() - { - // Using a yield return statement to return each element one at a time - yield return Street; - yield return City; - yield return State; - yield return Country; - yield return ZipCode; - } + protected override IEnumerable GetEqualityComponents() + { + // Using a yield return statement to return each element one at a time + yield return Street; + yield return City; + yield return State; + yield return Country; + yield return ZipCode; } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs index a4e298cd7..af72ed88f 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs @@ -1,17 +1,13 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate -{ - //This is just the RepositoryContracts or Interface defined at the Domain Layer - //as requisite for the Order Aggregate +//This is just the RepositoryContracts or Interface defined at the Domain Layer +//as requisite for the Order Aggregate - public interface IOrderRepository : IRepository - { - Order Add(Order order); +public interface IOrderRepository : IRepository +{ + Order Add(Order order); - void Update(Order order); + void Update(Order order); - Task GetAsync(int orderId); - } + Task GetAsync(int orderId); } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs index 897a08592..81a67324b 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs @@ -1,202 +1,195 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using Ordering.Domain.Events; -using Ordering.Domain.Exceptions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate +using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; + +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + +public class Order + : Entity, IAggregateRoot { - public class Order - : Entity, IAggregateRoot - { - // DDD Patterns comment - // Using private fields, allowed since EF Core 1.1, is a much better encapsulation - // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections) - private DateTime _orderDate; + // DDD Patterns comment + // Using private fields, allowed since EF Core 1.1, is a much better encapsulation + // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections) + private DateTime _orderDate; - // Address is a Value Object pattern example persisted as EF Core 2.0 owned entity - public Address Address { get; private set; } + // Address is a Value Object pattern example persisted as EF Core 2.0 owned entity + public Address Address { get; private set; } - public int? GetBuyerId => _buyerId; - private int? _buyerId; + public int? GetBuyerId => _buyerId; + private int? _buyerId; - public OrderStatus OrderStatus { get; private set; } - private int _orderStatusId; + public OrderStatus OrderStatus { get; private set; } + private int _orderStatusId; - private string _description; + private string _description; - // Draft orders have this set to true. Currently we don't check anywhere the draft status of an Order, but we could do it if needed - private bool _isDraft; + // Draft orders have this set to true. Currently we don't check anywhere the draft status of an Order, but we could do it if needed + private bool _isDraft; - // DDD Patterns comment - // Using a private collection field, better for DDD Aggregate's encapsulation - // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, - // but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour. - private readonly List _orderItems; - public IReadOnlyCollection OrderItems => _orderItems; + // DDD Patterns comment + // Using a private collection field, better for DDD Aggregate's encapsulation + // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, + // but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour. + private readonly List _orderItems; + public IReadOnlyCollection OrderItems => _orderItems; - private int? _paymentMethodId; + private int? _paymentMethodId; - public static Order NewDraft() - { - var order = new Order(); - order._isDraft = true; - return order; - } + public static Order NewDraft() + { + var order = new Order(); + order._isDraft = true; + return order; + } - protected Order() - { - _orderItems = new List(); - _isDraft = false; - } + protected Order() + { + _orderItems = new List(); + _isDraft = false; + } - public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber, - string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this() - { - _buyerId = buyerId; - _paymentMethodId = paymentMethodId; - _orderStatusId = OrderStatus.Submitted.Id; - _orderDate = DateTime.UtcNow; - Address = address; - - // Add the OrderStarterDomainEvent to the domain events collection - // to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ] - AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber, - cardSecurityNumber, cardHolderName, cardExpiration); - } + public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber, + string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this() + { + _buyerId = buyerId; + _paymentMethodId = paymentMethodId; + _orderStatusId = OrderStatus.Submitted.Id; + _orderDate = DateTime.UtcNow; + Address = address; + + // Add the OrderStarterDomainEvent to the domain events collection + // to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ] + AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber, + cardSecurityNumber, cardHolderName, cardExpiration); + } - // DDD Patterns comment - // This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order, - // so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot - // in order to maintain consistency between the whole Aggregate. - public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1) + // DDD Patterns comment + // This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order, + // so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot + // in order to maintain consistency between the whole Aggregate. + public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1) + { + var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId) + .SingleOrDefault(); + + if (existingOrderForProduct != null) { - var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId) - .SingleOrDefault(); + //if previous line exist modify it with higher discount and units.. - if (existingOrderForProduct != null) + if (discount > existingOrderForProduct.GetCurrentDiscount()) { - //if previous line exist modify it with higher discount and units.. - - if (discount > existingOrderForProduct.GetCurrentDiscount()) - { - existingOrderForProduct.SetNewDiscount(discount); - } - - existingOrderForProduct.AddUnits(units); + existingOrderForProduct.SetNewDiscount(discount); } - else - { - //add validated new order item - var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); - _orderItems.Add(orderItem); - } + existingOrderForProduct.AddUnits(units); } - - public void SetPaymentId(int id) + else { - _paymentMethodId = id; - } + //add validated new order item - public void SetBuyerId(int id) - { - _buyerId = id; + var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); + _orderItems.Add(orderItem); } + } + + public void SetPaymentId(int id) + { + _paymentMethodId = id; + } - public void SetAwaitingValidationStatus() + public void SetBuyerId(int id) + { + _buyerId = id; + } + + public void SetAwaitingValidationStatus() + { + if (_orderStatusId == OrderStatus.Submitted.Id) { - if (_orderStatusId == OrderStatus.Submitted.Id) - { - AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems)); - _orderStatusId = OrderStatus.AwaitingValidation.Id; - } + AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems)); + _orderStatusId = OrderStatus.AwaitingValidation.Id; } + } - public void SetStockConfirmedStatus() + public void SetStockConfirmedStatus() + { + if (_orderStatusId == OrderStatus.AwaitingValidation.Id) { - if (_orderStatusId == OrderStatus.AwaitingValidation.Id) - { - AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id)); + AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id)); - _orderStatusId = OrderStatus.StockConfirmed.Id; - _description = "All the items were confirmed with available stock."; - } + _orderStatusId = OrderStatus.StockConfirmed.Id; + _description = "All the items were confirmed with available stock."; } + } - public void SetPaidStatus() + public void SetPaidStatus() + { + if (_orderStatusId == OrderStatus.StockConfirmed.Id) { - if (_orderStatusId == OrderStatus.StockConfirmed.Id) - { - AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems)); + AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems)); - _orderStatusId = OrderStatus.Paid.Id; - _description = "The payment was performed at a simulated \"American Bank checking bank account ending on XX35071\""; - } + _orderStatusId = OrderStatus.Paid.Id; + _description = "The payment was performed at a simulated \"American Bank checking bank account ending on XX35071\""; } + } - public void SetShippedStatus() + public void SetShippedStatus() + { + if (_orderStatusId != OrderStatus.Paid.Id) { - if (_orderStatusId != OrderStatus.Paid.Id) - { - StatusChangeException(OrderStatus.Shipped); - } - - _orderStatusId = OrderStatus.Shipped.Id; - _description = "The order was shipped."; - AddDomainEvent(new OrderShippedDomainEvent(this)); + StatusChangeException(OrderStatus.Shipped); } - public void SetCancelledStatus() - { - if (_orderStatusId == OrderStatus.Paid.Id || - _orderStatusId == OrderStatus.Shipped.Id) - { - StatusChangeException(OrderStatus.Cancelled); - } + _orderStatusId = OrderStatus.Shipped.Id; + _description = "The order was shipped."; + AddDomainEvent(new OrderShippedDomainEvent(this)); + } - _orderStatusId = OrderStatus.Cancelled.Id; - _description = $"The order was cancelled."; - AddDomainEvent(new OrderCancelledDomainEvent(this)); + public void SetCancelledStatus() + { + if (_orderStatusId == OrderStatus.Paid.Id || + _orderStatusId == OrderStatus.Shipped.Id) + { + StatusChangeException(OrderStatus.Cancelled); } - public void SetCancelledStatusWhenStockIsRejected(IEnumerable orderStockRejectedItems) + _orderStatusId = OrderStatus.Cancelled.Id; + _description = $"The order was cancelled."; + AddDomainEvent(new OrderCancelledDomainEvent(this)); + } + + public void SetCancelledStatusWhenStockIsRejected(IEnumerable orderStockRejectedItems) + { + if (_orderStatusId == OrderStatus.AwaitingValidation.Id) { - if (_orderStatusId == OrderStatus.AwaitingValidation.Id) - { - _orderStatusId = OrderStatus.Cancelled.Id; + _orderStatusId = OrderStatus.Cancelled.Id; - var itemsStockRejectedProductNames = OrderItems - .Where(c => orderStockRejectedItems.Contains(c.ProductId)) - .Select(c => c.GetOrderItemProductName()); + var itemsStockRejectedProductNames = OrderItems + .Where(c => orderStockRejectedItems.Contains(c.ProductId)) + .Select(c => c.GetOrderItemProductName()); - var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames); - _description = $"The product items don't have stock: ({itemsStockRejectedDescription})."; - } + var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames); + _description = $"The product items don't have stock: ({itemsStockRejectedDescription})."; } + } - private void AddOrderStartedDomainEvent(string userId, string userName, int cardTypeId, string cardNumber, - string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) - { - var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, userName, cardTypeId, - cardNumber, cardSecurityNumber, - cardHolderName, cardExpiration); + private void AddOrderStartedDomainEvent(string userId, string userName, int cardTypeId, string cardNumber, + string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) + { + var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, userName, cardTypeId, + cardNumber, cardSecurityNumber, + cardHolderName, cardExpiration); - this.AddDomainEvent(orderStartedDomainEvent); - } + this.AddDomainEvent(orderStartedDomainEvent); + } - private void StatusChangeException(OrderStatus orderStatusToChange) - { - throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}."); - } + private void StatusChangeException(OrderStatus orderStatusToChange) + { + throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}."); + } - public decimal GetTotal() - { - return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice()); - } + public decimal GetTotal() + { + return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice()); } } - diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs index 9fbec8225..0b2d95002 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs @@ -1,82 +1,78 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using Ordering.Domain.Exceptions; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate +public class OrderItem + : Entity { - public class OrderItem - : Entity - { - // DDD Patterns comment - // Using private fields, allowed since EF Core 1.1, is a much better encapsulation - // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections) - private string _productName; - private string _pictureUrl; - private decimal _unitPrice; - private decimal _discount; - private int _units; + // DDD Patterns comment + // Using private fields, allowed since EF Core 1.1, is a much better encapsulation + // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections) + private string _productName; + private string _pictureUrl; + private decimal _unitPrice; + private decimal _discount; + private int _units; - public int ProductId { get; private set; } + public int ProductId { get; private set; } - protected OrderItem() { } + protected OrderItem() { } - public OrderItem(int productId, string productName, decimal unitPrice, decimal discount, string PictureUrl, int units = 1) + public OrderItem(int productId, string productName, decimal unitPrice, decimal discount, string PictureUrl, int units = 1) + { + if (units <= 0) { - if (units <= 0) - { - throw new OrderingDomainException("Invalid number of units"); - } - - if ((unitPrice * units) < discount) - { - throw new OrderingDomainException("The total of order item is lower than applied discount"); - } - - ProductId = productId; - - _productName = productName; - _unitPrice = unitPrice; - _discount = discount; - _units = units; - _pictureUrl = PictureUrl; + throw new OrderingDomainException("Invalid number of units"); } - public string GetPictureUri() => _pictureUrl; - - public decimal GetCurrentDiscount() + if ((unitPrice * units) < discount) { - return _discount; + throw new OrderingDomainException("The total of order item is lower than applied discount"); } - public int GetUnits() - { - return _units; - } + ProductId = productId; - public decimal GetUnitPrice() - { - return _unitPrice; - } + _productName = productName; + _unitPrice = unitPrice; + _discount = discount; + _units = units; + _pictureUrl = PictureUrl; + } - public string GetOrderItemProductName() => _productName; + public string GetPictureUri() => _pictureUrl; - public void SetNewDiscount(decimal discount) - { - if (discount < 0) - { - throw new OrderingDomainException("Discount is not valid"); - } + public decimal GetCurrentDiscount() + { + return _discount; + } - _discount = discount; - } + public int GetUnits() + { + return _units; + } + + public decimal GetUnitPrice() + { + return _unitPrice; + } + + public string GetOrderItemProductName() => _productName; - public void AddUnits(int units) + public void SetNewDiscount(decimal discount) + { + if (discount < 0) { - if (units < 0) - { - throw new OrderingDomainException("Invalid units"); - } + throw new OrderingDomainException("Discount is not valid"); + } + + _discount = discount; + } - _units += units; + public void AddUnits(int units) + { + if (units < 0) + { + throw new OrderingDomainException("Invalid units"); } + + _units += units; } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs index 19e62311a..aae09bc6a 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs @@ -1,52 +1,47 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate +using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; + +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + +public class OrderStatus + : Enumeration { - using global::Ordering.Domain.Exceptions; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; - using System; - using System.Collections.Generic; - using System.Linq; - - public class OrderStatus - : Enumeration + public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant()); + public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant()); + public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant()); + public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant()); + public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant()); + public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant()); + + public OrderStatus(int id, string name) + : base(id, name) { - public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant()); - public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant()); - public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant()); - public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant()); - public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant()); - public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant()); - - public OrderStatus(int id, string name) - : base(id, name) - { - } - - public static IEnumerable List() => - new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled }; + } - public static OrderStatus FromName(string name) - { - var state = List() - .SingleOrDefault(s => String.Equals(s.Name, name, StringComparison.CurrentCultureIgnoreCase)); + public static IEnumerable List() => + new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled }; - if (state == null) - { - throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); - } + public static OrderStatus FromName(string name) + { + var state = List() + .SingleOrDefault(s => String.Equals(s.Name, name, StringComparison.CurrentCultureIgnoreCase)); - return state; + if (state == null) + { + throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); } - public static OrderStatus From(int id) - { - var state = List().SingleOrDefault(s => s.Id == id); + return state; + } - if (state == null) - { - throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); - } + public static OrderStatus From(int id) + { + var state = List().SingleOrDefault(s => s.Id == id); - return state; + if (state == null) + { + throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}"); } + + return state; } } diff --git a/src/Services/Ordering/Ordering.Domain/Events/BuyerPaymentMethodVerifiedDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/BuyerPaymentMethodVerifiedDomainEvent.cs index 70adeefad..61acf898f 100644 --- a/src/Services/Ordering/Ordering.Domain/Events/BuyerPaymentMethodVerifiedDomainEvent.cs +++ b/src/Services/Ordering/Ordering.Domain/Events/BuyerPaymentMethodVerifiedDomainEvent.cs @@ -1,20 +1,16 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; -namespace Ordering.Domain.Events +public class BuyerAndPaymentMethodVerifiedDomainEvent + : INotification { - public class BuyerAndPaymentMethodVerifiedDomainEvent - : INotification - { - public Buyer Buyer { get; private set; } - public PaymentMethod Payment { get; private set; } - public int OrderId { get; private set; } + public Buyer Buyer { get; private set; } + public PaymentMethod Payment { get; private set; } + public int OrderId { get; private set; } - public BuyerAndPaymentMethodVerifiedDomainEvent(Buyer buyer, PaymentMethod payment, int orderId) - { - Buyer = buyer; - Payment = payment; - OrderId = orderId; - } + public BuyerAndPaymentMethodVerifiedDomainEvent(Buyer buyer, PaymentMethod payment, int orderId) + { + Buyer = buyer; + Payment = payment; + OrderId = orderId; } } diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderCancelledDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderCancelledDomainEvent.cs index 144527fbf..be0e34fe0 100644 --- a/src/Services/Ordering/Ordering.Domain/Events/OrderCancelledDomainEvent.cs +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderCancelledDomainEvent.cs @@ -1,15 +1,12 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; -namespace Ordering.Domain.Events +public class OrderCancelledDomainEvent : INotification { - public class OrderCancelledDomainEvent : INotification - { - public Order Order { get; } + public Order Order { get; } - public OrderCancelledDomainEvent(Order order) - { - Order = order; - } + public OrderCancelledDomainEvent(Order order) + { + Order = order; } } + diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderShippedDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderShippedDomainEvent.cs index 53fcef510..1b704ef77 100644 --- a/src/Services/Ordering/Ordering.Domain/Events/OrderShippedDomainEvent.cs +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderShippedDomainEvent.cs @@ -1,15 +1,11 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; -namespace Ordering.Domain.Events +public class OrderShippedDomainEvent : INotification { - public class OrderShippedDomainEvent : INotification - { - public Order Order { get; } + public Order Order { get; } - public OrderShippedDomainEvent(Order order) - { - Order = order; - } + public OrderShippedDomainEvent(Order order) + { + Order = order; } } diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderStartedDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderStartedDomainEvent.cs index ed779d4bc..8cc6a39e3 100644 --- a/src/Services/Ordering/Ordering.Domain/Events/OrderStartedDomainEvent.cs +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderStartedDomainEvent.cs @@ -1,8 +1,5 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using System; - -namespace Ordering.Domain.Events + +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events { /// /// Event used when an order is created diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingValidationDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingValidationDomainEvent.cs index b4dd7aa82..677ef321b 100644 --- a/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingValidationDomainEvent.cs +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingValidationDomainEvent.cs @@ -1,23 +1,18 @@ -namespace Ordering.Domain.Events +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; + +/// +/// Event used when the grace period order is confirmed +/// +public class OrderStatusChangedToAwaitingValidationDomainEvent + : INotification { - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; - using System.Collections.Generic; + public int OrderId { get; } + public IEnumerable OrderItems { get; } - /// - /// Event used when the grace period order is confirmed - /// - public class OrderStatusChangedToAwaitingValidationDomainEvent - : INotification + public OrderStatusChangedToAwaitingValidationDomainEvent(int orderId, + IEnumerable orderItems) { - public int OrderId { get; } - public IEnumerable OrderItems { get; } - - public OrderStatusChangedToAwaitingValidationDomainEvent(int orderId, - IEnumerable orderItems) - { - OrderId = orderId; - OrderItems = orderItems; - } + OrderId = orderId; + OrderItems = orderItems; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs index f315bab60..0ef176564 100644 --- a/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs @@ -1,23 +1,18 @@ -namespace Ordering.Domain.Events +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; + +/// +/// Event used when the order is paid +/// +public class OrderStatusChangedToPaidDomainEvent + : INotification { - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; - using System.Collections.Generic; + public int OrderId { get; } + public IEnumerable OrderItems { get; } - /// - /// Event used when the order is paid - /// - public class OrderStatusChangedToPaidDomainEvent - : INotification + public OrderStatusChangedToPaidDomainEvent(int orderId, + IEnumerable orderItems) { - public int OrderId { get; } - public IEnumerable OrderItems { get; } - - public OrderStatusChangedToPaidDomainEvent(int orderId, - IEnumerable orderItems) - { - OrderId = orderId; - OrderItems = orderItems; - } + OrderId = orderId; + OrderItems = orderItems; } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToStockConfirmedDomainEvent.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToStockConfirmedDomainEvent.cs index b16bebbcc..e897141ad 100644 --- a/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToStockConfirmedDomainEvent.cs +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToStockConfirmedDomainEvent.cs @@ -1,16 +1,13 @@ -namespace Ordering.Domain.Events -{ - using MediatR; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; - /// - /// Event used when the order stock items are confirmed - /// - public class OrderStatusChangedToStockConfirmedDomainEvent - : INotification - { - public int OrderId { get; } +/// +/// Event used when the order stock items are confirmed +/// +public class OrderStatusChangedToStockConfirmedDomainEvent + : INotification +{ + public int OrderId { get; } - public OrderStatusChangedToStockConfirmedDomainEvent(int orderId) - => OrderId = orderId; - } -} \ No newline at end of file + public OrderStatusChangedToStockConfirmedDomainEvent(int orderId) + => OrderId = orderId; +} diff --git a/src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs b/src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs index 80c2262ca..fd31522fd 100644 --- a/src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs +++ b/src/Services/Ordering/Ordering.Domain/Exceptions/OrderingDomainException.cs @@ -1,21 +1,18 @@ -using System; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Exceptions; -namespace Ordering.Domain.Exceptions +/// +/// Exception type for domain exceptions +/// +public class OrderingDomainException : Exception { - /// - /// Exception type for domain exceptions - /// - public class OrderingDomainException : Exception - { - public OrderingDomainException() - { } + public OrderingDomainException() + { } - public OrderingDomainException(string message) - : base(message) - { } + public OrderingDomainException(string message) + : base(message) + { } - public OrderingDomainException(string message, Exception innerException) - : base(message, innerException) - { } - } + public OrderingDomainException(string message, Exception innerException) + : base(message, innerException) + { } } diff --git a/src/Services/Ordering/Ordering.Domain/GlobalUsings.cs b/src/Services/Ordering/Ordering.Domain/GlobalUsings.cs new file mode 100644 index 000000000..bc11cd71b --- /dev/null +++ b/src/Services/Ordering/Ordering.Domain/GlobalUsings.cs @@ -0,0 +1,13 @@ +global using global::Microsoft.eShopOnContainers.Services.Ordering.Domain.Exceptions; +global using MediatR; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Exceptions; +global using System.Collections.Generic; +global using System.Linq; +global using System.Reflection; +global using System.Threading.Tasks; +global using System.Threading; +global using System; \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs index f58df286b..a8e5c23ad 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs @@ -1,92 +1,87 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork -{ - using MediatR; - using System; - using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; - public abstract class Entity +public abstract class Entity +{ + int? _requestedHashCode; + int _Id; + public virtual int Id { - int? _requestedHashCode; - int _Id; - public virtual int Id + get { - get - { - return _Id; - } - protected set - { - _Id = value; - } + return _Id; } - - private List _domainEvents; - public IReadOnlyCollection DomainEvents => _domainEvents?.AsReadOnly(); - - public void AddDomainEvent(INotification eventItem) + protected set { - _domainEvents = _domainEvents ?? new List(); - _domainEvents.Add(eventItem); + _Id = value; } + } - public void RemoveDomainEvent(INotification eventItem) - { - _domainEvents?.Remove(eventItem); - } + private List _domainEvents; + public IReadOnlyCollection DomainEvents => _domainEvents?.AsReadOnly(); - public void ClearDomainEvents() - { - _domainEvents?.Clear(); - } + public void AddDomainEvent(INotification eventItem) + { + _domainEvents = _domainEvents ?? new List(); + _domainEvents.Add(eventItem); + } - public bool IsTransient() - { - return this.Id == default(Int32); - } + public void RemoveDomainEvent(INotification eventItem) + { + _domainEvents?.Remove(eventItem); + } - public override bool Equals(object obj) - { - if (obj == null || !(obj is Entity)) - return false; + public void ClearDomainEvents() + { + _domainEvents?.Clear(); + } - if (Object.ReferenceEquals(this, obj)) - return true; + public bool IsTransient() + { + return this.Id == default(Int32); + } - if (this.GetType() != obj.GetType()) - return false; + public override bool Equals(object obj) + { + if (obj == null || !(obj is Entity)) + return false; - Entity item = (Entity)obj; + if (Object.ReferenceEquals(this, obj)) + return true; - if (item.IsTransient() || this.IsTransient()) - return false; - else - return item.Id == this.Id; - } + if (this.GetType() != obj.GetType()) + return false; - public override int GetHashCode() - { - if (!IsTransient()) - { - if (!_requestedHashCode.HasValue) - _requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx) + Entity item = (Entity)obj; - return _requestedHashCode.Value; - } - else - return base.GetHashCode(); + if (item.IsTransient() || this.IsTransient()) + return false; + else + return item.Id == this.Id; + } - } - public static bool operator ==(Entity left, Entity right) + public override int GetHashCode() + { + if (!IsTransient()) { - if (Object.Equals(left, null)) - return (Object.Equals(right, null)) ? true : false; - else - return left.Equals(right); - } + if (!_requestedHashCode.HasValue) + _requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx) - public static bool operator !=(Entity left, Entity right) - { - return !(left == right); + return _requestedHashCode.Value; } + else + return base.GetHashCode(); + + } + public static bool operator ==(Entity left, Entity right) + { + if (Object.Equals(left, null)) + return (Object.Equals(right, null)) ? true : false; + else + return left.Equals(right); + } + + public static bool operator !=(Entity left, Entity right) + { + return !(left == right); } } diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/Enumeration.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/Enumeration.cs index 731dd8c86..8d313149d 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/Enumeration.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/Enumeration.cs @@ -1,70 +1,64 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork +public abstract class Enumeration : IComparable { - public abstract class Enumeration : IComparable - { - public string Name { get; private set; } + public string Name { get; private set; } - public int Id { get; private set; } + public int Id { get; private set; } - protected Enumeration(int id, string name) => (Id, Name) = (id, name); + protected Enumeration(int id, string name) => (Id, Name) = (id, name); - public override string ToString() => Name; + public override string ToString() => Name; - public static IEnumerable GetAll() where T : Enumeration => - typeof(T).GetFields(BindingFlags.Public | - BindingFlags.Static | - BindingFlags.DeclaredOnly) - .Select(f => f.GetValue(null)) - .Cast(); + public static IEnumerable GetAll() where T : Enumeration => + typeof(T).GetFields(BindingFlags.Public | + BindingFlags.Static | + BindingFlags.DeclaredOnly) + .Select(f => f.GetValue(null)) + .Cast(); - public override bool Equals(object obj) + public override bool Equals(object obj) + { + if (obj is not Enumeration otherValue) { - if (obj is not Enumeration otherValue) - { - return false; - } - - var typeMatches = GetType().Equals(obj.GetType()); - var valueMatches = Id.Equals(otherValue.Id); - - return typeMatches && valueMatches; + return false; } - public override int GetHashCode() => Id.GetHashCode(); + var typeMatches = GetType().Equals(obj.GetType()); + var valueMatches = Id.Equals(otherValue.Id); - public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue) - { - var absoluteDifference = Math.Abs(firstValue.Id - secondValue.Id); - return absoluteDifference; - } + return typeMatches && valueMatches; + } - public static T FromValue(int value) where T : Enumeration - { - var matchingItem = Parse(value, "value", item => item.Id == value); - return matchingItem; - } + public override int GetHashCode() => Id.GetHashCode(); - public static T FromDisplayName(string displayName) where T : Enumeration - { - var matchingItem = Parse(displayName, "display name", item => item.Name == displayName); - return matchingItem; - } + public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue) + { + var absoluteDifference = Math.Abs(firstValue.Id - secondValue.Id); + return absoluteDifference; + } - private static T Parse(K value, string description, Func predicate) where T : Enumeration - { - var matchingItem = GetAll().FirstOrDefault(predicate); + public static T FromValue(int value) where T : Enumeration + { + var matchingItem = Parse(value, "value", item => item.Id == value); + return matchingItem; + } - if (matchingItem == null) - throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}"); + public static T FromDisplayName(string displayName) where T : Enumeration + { + var matchingItem = Parse(displayName, "display name", item => item.Name == displayName); + return matchingItem; + } - return matchingItem; - } + private static T Parse(K value, string description, Func predicate) where T : Enumeration + { + var matchingItem = GetAll().FirstOrDefault(predicate); - public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id); + if (matchingItem == null) + throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}"); + + return matchingItem; } + + public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id); } diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRoot.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRoot.cs index 47a8dc7b5..9af3a0437 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRoot.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/IAggregateRoot.cs @@ -1,6 +1,5 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork -{ +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; + +public interface IAggregateRoot { } - public interface IAggregateRoot { } -} diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs index 3657ec092..f9b84a7f0 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/IRepository.cs @@ -1,7 +1,6 @@ -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; + +public interface IRepository where T : IAggregateRoot { - public interface IRepository where T : IAggregateRoot - { - IUnitOfWork UnitOfWork { get; } - } + IUnitOfWork UnitOfWork { get; } } diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs index a6cfca7c6..b9c121655 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs @@ -1,12 +1,7 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork +public interface IUnitOfWork : IDisposable { - public interface IUnitOfWork : IDisposable - { - Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); - Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)); - } + Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); + Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)); } diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs index 5f36900ee..8111088f8 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs @@ -1,48 +1,44 @@ -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; -namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork +public abstract class ValueObject { - public abstract class ValueObject + protected static bool EqualOperator(ValueObject left, ValueObject right) { - protected static bool EqualOperator(ValueObject left, ValueObject right) + if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null)) { - if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null)) - { - return false; - } - return ReferenceEquals(left, null) || left.Equals(right); + return false; } + return ReferenceEquals(left, null) || left.Equals(right); + } - protected static bool NotEqualOperator(ValueObject left, ValueObject right) - { - return !(EqualOperator(left, right)); - } + protected static bool NotEqualOperator(ValueObject left, ValueObject right) + { + return !(EqualOperator(left, right)); + } - protected abstract IEnumerable GetEqualityComponents(); + protected abstract IEnumerable GetEqualityComponents(); - public override bool Equals(object obj) + public override bool Equals(object obj) + { + if (obj == null || obj.GetType() != GetType()) { - if (obj == null || obj.GetType() != GetType()) - { - return false; - } + return false; + } - var other = (ValueObject)obj; + var other = (ValueObject)obj; - return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); - } + return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); + } - public override int GetHashCode() - { - return GetEqualityComponents() - .Select(x => x != null ? x.GetHashCode() : 0) - .Aggregate((x, y) => x ^ y); - } + public override int GetHashCode() + { + return GetEqualityComponents() + .Select(x => x != null ? x.GetHashCode() : 0) + .Aggregate((x, y) => x ^ y); + } - public ValueObject GetCopy() - { - return this.MemberwiseClone() as ValueObject; - } + public ValueObject GetCopy() + { + return this.MemberwiseClone() as ValueObject; } } diff --git a/src/Services/Ordering/Ordering.FunctionalTests/AutoAuthorizeMiddleware.cs b/src/Services/Ordering/Ordering.FunctionalTests/AutoAuthorizeMiddleware.cs index a1f77b75d..8062dd5d0 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/AutoAuthorizeMiddleware.cs +++ b/src/Services/Ordering/Ordering.FunctionalTests/AutoAuthorizeMiddleware.cs @@ -1,31 +1,26 @@ -using Microsoft.AspNetCore.Http; -using System.Security.Claims; -using System.Threading.Tasks; +namespace Ordering.FunctionalTests; -namespace Ordering.FunctionalTests +class AutoAuthorizeMiddleware { - class AutoAuthorizeMiddleware - { - public const string IDENTITY_ID = "9e3163b9-1ae6-4652-9dc6-7898ab7b7a00"; + public const string IDENTITY_ID = "9e3163b9-1ae6-4652-9dc6-7898ab7b7a00"; - private readonly RequestDelegate _next; + private readonly RequestDelegate _next; - public AutoAuthorizeMiddleware(RequestDelegate rd) - { - _next = rd; - } + public AutoAuthorizeMiddleware(RequestDelegate rd) + { + _next = rd; + } - public async Task Invoke(HttpContext httpContext) - { - var identity = new ClaimsIdentity("cookies"); + public async Task Invoke(HttpContext httpContext) + { + var identity = new ClaimsIdentity("cookies"); - identity.AddClaim(new Claim("sub", IDENTITY_ID)); - identity.AddClaim(new Claim("unique_name", IDENTITY_ID)); - identity.AddClaim(new Claim(ClaimTypes.Name, IDENTITY_ID)); + identity.AddClaim(new Claim("sub", IDENTITY_ID)); + identity.AddClaim(new Claim("unique_name", IDENTITY_ID)); + identity.AddClaim(new Claim(ClaimTypes.Name, IDENTITY_ID)); - httpContext.User.AddIdentity(identity); + httpContext.User.AddIdentity(identity); - await _next.Invoke(httpContext); - } + await _next.Invoke(httpContext); } } diff --git a/src/Services/Ordering/Ordering.FunctionalTests/GlobalUsings.cs b/src/Services/Ordering/Ordering.FunctionalTests/GlobalUsings.cs new file mode 100644 index 000000000..cee4b6733 --- /dev/null +++ b/src/Services/Ordering/Ordering.FunctionalTests/GlobalUsings.cs @@ -0,0 +1,19 @@ +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Routing; +global using Microsoft.AspNetCore.TestHost; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure; +global using Microsoft.eShopOnContainers.Services.Ordering.API; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using System.IO; +global using System.Net.Http; +global using System.Reflection; +global using System.Security.Claims; +global using System.Threading.Tasks; +global using System; diff --git a/src/Services/Ordering/Ordering.FunctionalTests/HttpClientExtensions.cs b/src/Services/Ordering/Ordering.FunctionalTests/HttpClientExtensions.cs index da2577b24..a4a0dd9c2 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/HttpClientExtensions.cs +++ b/src/Services/Ordering/Ordering.FunctionalTests/HttpClientExtensions.cs @@ -1,16 +1,11 @@ -using Microsoft.AspNetCore.TestHost; -using System; -using System.Net.Http; +namespace Ordering.FunctionalTests; -namespace Ordering.FunctionalTests +static class HttpClientExtensions { - static class HttpClientExtensions + public static HttpClient CreateIdempotentClient(this TestServer server) { - public static HttpClient CreateIdempotentClient(this TestServer server) - { - var client = server.CreateClient(); - client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString()); - return client; - } + var client = server.CreateClient(); + client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString()); + return client; } } diff --git a/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj b/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj index fdfce11a3..74c75a731 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj +++ b/src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj @@ -17,9 +17,9 @@ - + - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs index 962ce21c1..d03d54380 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs +++ b/src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs @@ -1,65 +1,51 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; -using Microsoft.eShopOnContainers.Services.Ordering.API; -using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System.IO; -using System.Reflection; +namespace Ordering.FunctionalTests; -namespace Ordering.FunctionalTests +public class OrderingScenarioBase { - public class OrderingScenarioBase + public TestServer CreateServer() { - public TestServer CreateServer() - { - var path = Assembly.GetAssembly(typeof(OrderingScenarioBase)) - .Location; + var path = Assembly.GetAssembly(typeof(OrderingScenarioBase)) + .Location; - var hostBuilder = new WebHostBuilder() - .UseContentRoot(Path.GetDirectoryName(path)) - .ConfigureAppConfiguration(cb => - { - cb.AddJsonFile("appsettings.json", optional: false) - .AddEnvironmentVariables(); - }).UseStartup(); + var hostBuilder = new WebHostBuilder() + .UseContentRoot(Path.GetDirectoryName(path)) + .ConfigureAppConfiguration(cb => + { + cb.AddJsonFile("appsettings.json", optional: false) + .AddEnvironmentVariables(); + }).UseStartup(); - var testServer = new TestServer(hostBuilder); + var testServer = new TestServer(hostBuilder); - testServer.Host - .MigrateDbContext((context, services) => - { - var env = services.GetService(); - var settings = services.GetService>(); - var logger = services.GetService>(); + testServer.Host + .MigrateDbContext((context, services) => + { + var env = services.GetService(); + var settings = services.GetService>(); + var logger = services.GetService>(); - new OrderingContextSeed() - .SeedAsync(context, env, settings, logger) - .Wait(); - }) - .MigrateDbContext((_, __) => { }); + new OrderingContextSeed() + .SeedAsync(context, env, settings, logger) + .Wait(); + }) + .MigrateDbContext((_, __) => { }); - return testServer; - } - - public static class Get - { - public static string Orders = "api/v1/orders"; + return testServer; + } - public static string OrderBy(int id) - { - return $"api/v1/orders/{id}"; - } - } + public static class Get + { + public static string Orders = "api/v1/orders"; - public static class Put + public static string OrderBy(int id) { - public static string CancelOrder = "api/v1/orders/cancel"; - public static string ShipOrder = "api/v1/orders/ship"; + return $"api/v1/orders/{id}"; } } + + public static class Put + { + public static string CancelOrder = "api/v1/orders/cancel"; + public static string ShipOrder = "api/v1/orders/ship"; + } } diff --git a/src/Services/Ordering/Ordering.FunctionalTests/OrderingTestStartup.cs b/src/Services/Ordering/Ordering.FunctionalTests/OrderingTestStartup.cs index 7980d5bff..7367042de 100644 --- a/src/Services/Ordering/Ordering.FunctionalTests/OrderingTestStartup.cs +++ b/src/Services/Ordering/Ordering.FunctionalTests/OrderingTestStartup.cs @@ -1,35 +1,27 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.eShopOnContainers.Services.Ordering.API; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System; +namespace Ordering.FunctionalTests; -namespace Ordering.FunctionalTests +public class OrderingTestsStartup : Startup { - public class OrderingTestsStartup : Startup + public OrderingTestsStartup(IConfiguration env) : base(env) { - public OrderingTestsStartup(IConfiguration env) : base(env) - { - } + } - public override IServiceProvider ConfigureServices(IServiceCollection services) + public override IServiceProvider ConfigureServices(IServiceCollection services) + { + // Added to avoid the Authorize data annotation in test environment. + // Property "SuppressCheckForUnhandledSecurityMetadata" in appsettings.json + services.Configure(Configuration); + return base.ConfigureServices(services); + } + protected override void ConfigureAuth(IApplicationBuilder app) + { + if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant()) { - // Added to avoid the Authorize data annotation in test environment. - // Property "SuppressCheckForUnhandledSecurityMetadata" in appsettings.json - services.Configure(Configuration); - return base.ConfigureServices(services); + app.UseMiddleware(); } - protected override void ConfigureAuth(IApplicationBuilder app) + else { - if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant()) - { - app.UseMiddleware(); - } - else - { - base.ConfigureAuth(app); - } + base.ConfigureAuth(app); } } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTypeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTypeConfiguration.cs index f5bdea756..06ea7bcf6 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTypeConfiguration.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/BuyerEntityTypeConfiguration.cs @@ -1,41 +1,35 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; -namespace Ordering.Infrastructure.EntityConfigurations +class BuyerEntityTypeConfiguration + : IEntityTypeConfiguration { - class BuyerEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder buyerConfiguration) { - public void Configure(EntityTypeBuilder buyerConfiguration) - { - buyerConfiguration.ToTable("buyers", OrderingContext.DEFAULT_SCHEMA); + buyerConfiguration.ToTable("buyers", OrderingContext.DEFAULT_SCHEMA); - buyerConfiguration.HasKey(b => b.Id); + buyerConfiguration.HasKey(b => b.Id); - buyerConfiguration.Ignore(b => b.DomainEvents); + buyerConfiguration.Ignore(b => b.DomainEvents); - buyerConfiguration.Property(b => b.Id) - .UseHiLo("buyerseq", OrderingContext.DEFAULT_SCHEMA); + buyerConfiguration.Property(b => b.Id) + .UseHiLo("buyerseq", OrderingContext.DEFAULT_SCHEMA); - buyerConfiguration.Property(b => b.IdentityGuid) - .HasMaxLength(200) - .IsRequired(); + buyerConfiguration.Property(b => b.IdentityGuid) + .HasMaxLength(200) + .IsRequired(); - buyerConfiguration.HasIndex("IdentityGuid") - .IsUnique(true); + buyerConfiguration.HasIndex("IdentityGuid") + .IsUnique(true); - buyerConfiguration.Property(b => b.Name); + buyerConfiguration.Property(b => b.Name); - buyerConfiguration.HasMany(b => b.PaymentMethods) - .WithOne() - .HasForeignKey("BuyerId") - .OnDelete(DeleteBehavior.Cascade); + buyerConfiguration.HasMany(b => b.PaymentMethods) + .WithOne() + .HasForeignKey("BuyerId") + .OnDelete(DeleteBehavior.Cascade); - var navigation = buyerConfiguration.Metadata.FindNavigation(nameof(Buyer.PaymentMethods)); + var navigation = buyerConfiguration.Metadata.FindNavigation(nameof(Buyer.PaymentMethods)); - navigation.SetPropertyAccessMode(PropertyAccessMode.Field); - } + navigation.SetPropertyAccessMode(PropertyAccessMode.Field); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/CardTypeEntityTypeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/CardTypeEntityTypeConfiguration.cs index cdac780a1..c305c045b 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/CardTypeEntityTypeConfiguration.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/CardTypeEntityTypeConfiguration.cs @@ -1,27 +1,21 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; -namespace Ordering.Infrastructure.EntityConfigurations +class CardTypeEntityTypeConfiguration + : IEntityTypeConfiguration { - class CardTypeEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder cardTypesConfiguration) { - public void Configure(EntityTypeBuilder cardTypesConfiguration) - { - cardTypesConfiguration.ToTable("cardtypes", OrderingContext.DEFAULT_SCHEMA); + cardTypesConfiguration.ToTable("cardtypes", OrderingContext.DEFAULT_SCHEMA); - cardTypesConfiguration.HasKey(ct => ct.Id); + cardTypesConfiguration.HasKey(ct => ct.Id); - cardTypesConfiguration.Property(ct => ct.Id) - .HasDefaultValue(1) - .ValueGeneratedNever() - .IsRequired(); + cardTypesConfiguration.Property(ct => ct.Id) + .HasDefaultValue(1) + .ValueGeneratedNever() + .IsRequired(); - cardTypesConfiguration.Property(ct => ct.Name) - .HasMaxLength(200) - .IsRequired(); - } + cardTypesConfiguration.Property(ct => ct.Name) + .HasMaxLength(200) + .IsRequired(); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/ClientRequestEntityTypeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/ClientRequestEntityTypeConfiguration.cs index 5a5f8547e..17ee92e02 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/ClientRequestEntityTypeConfiguration.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/ClientRequestEntityTypeConfiguration.cs @@ -1,19 +1,13 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; -namespace Ordering.Infrastructure.EntityConfigurations +class ClientRequestEntityTypeConfiguration + : IEntityTypeConfiguration { - class ClientRequestEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder requestConfiguration) { - public void Configure(EntityTypeBuilder requestConfiguration) - { - requestConfiguration.ToTable("requests", OrderingContext.DEFAULT_SCHEMA); - requestConfiguration.HasKey(cr => cr.Id); - requestConfiguration.Property(cr => cr.Name).IsRequired(); - requestConfiguration.Property(cr => cr.Time).IsRequired(); - } + requestConfiguration.ToTable("requests", OrderingContext.DEFAULT_SCHEMA); + requestConfiguration.HasKey(cr => cr.Id); + requestConfiguration.Property(cr => cr.Name).IsRequired(); + requestConfiguration.Property(cr => cr.Time).IsRequired(); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderEntityTypeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderEntityTypeConfiguration.cs index 4e32763eb..e8fe7d97b 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderEntityTypeConfiguration.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderEntityTypeConfiguration.cs @@ -1,86 +1,78 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using System; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; -namespace Ordering.Infrastructure.EntityConfigurations +class OrderEntityTypeConfiguration : IEntityTypeConfiguration { - class OrderEntityTypeConfiguration : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder orderConfiguration) { - public void Configure(EntityTypeBuilder orderConfiguration) - { - orderConfiguration.ToTable("orders", OrderingContext.DEFAULT_SCHEMA); + orderConfiguration.ToTable("orders", OrderingContext.DEFAULT_SCHEMA); - orderConfiguration.HasKey(o => o.Id); + orderConfiguration.HasKey(o => o.Id); - orderConfiguration.Ignore(b => b.DomainEvents); + orderConfiguration.Ignore(b => b.DomainEvents); - orderConfiguration.Property(o => o.Id) - .UseHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA); + orderConfiguration.Property(o => o.Id) + .UseHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA); - //Address value object persisted as owned entity type supported since EF Core 2.0 - orderConfiguration - .OwnsOne(o => o.Address, a => - { - // Explicit configuration of the shadow key property in the owned type - // as a workaround for a documented issue in EF Core 5: https://github.com/dotnet/efcore/issues/20740 - a.Property("OrderId") - .UseHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA); - a.WithOwner(); - }); + //Address value object persisted as owned entity type supported since EF Core 2.0 + orderConfiguration + .OwnsOne(o => o.Address, a => + { + // Explicit configuration of the shadow key property in the owned type + // as a workaround for a documented issue in EF Core 5: https://github.com/dotnet/efcore/issues/20740 + a.Property("OrderId") + .UseHiLo("orderseq", OrderingContext.DEFAULT_SCHEMA); + a.WithOwner(); + }); - orderConfiguration - .Property("_buyerId") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("BuyerId") - .IsRequired(false); + orderConfiguration + .Property("_buyerId") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("BuyerId") + .IsRequired(false); - orderConfiguration - .Property("_orderDate") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("OrderDate") - .IsRequired(); + orderConfiguration + .Property("_orderDate") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("OrderDate") + .IsRequired(); - orderConfiguration - .Property("_orderStatusId") - // .HasField("_orderStatusId") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("OrderStatusId") - .IsRequired(); + orderConfiguration + .Property("_orderStatusId") + // .HasField("_orderStatusId") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("OrderStatusId") + .IsRequired(); - orderConfiguration - .Property("_paymentMethodId") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("PaymentMethodId") - .IsRequired(false); + orderConfiguration + .Property("_paymentMethodId") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("PaymentMethodId") + .IsRequired(false); - orderConfiguration.Property("Description").IsRequired(false); + orderConfiguration.Property("Description").IsRequired(false); - var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems)); + var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems)); - // DDD Patterns comment: - //Set as field (New since EF 1.1) to access the OrderItem collection property through its field - navigation.SetPropertyAccessMode(PropertyAccessMode.Field); + // DDD Patterns comment: + //Set as field (New since EF 1.1) to access the OrderItem collection property through its field + navigation.SetPropertyAccessMode(PropertyAccessMode.Field); - orderConfiguration.HasOne() - .WithMany() - // .HasForeignKey("PaymentMethodId") - .HasForeignKey("_paymentMethodId") - .IsRequired(false) - .OnDelete(DeleteBehavior.Restrict); + orderConfiguration.HasOne() + .WithMany() + // .HasForeignKey("PaymentMethodId") + .HasForeignKey("_paymentMethodId") + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); - orderConfiguration.HasOne() - .WithMany() - .IsRequired(false) - // .HasForeignKey("BuyerId"); - .HasForeignKey("_buyerId"); + orderConfiguration.HasOne() + .WithMany() + .IsRequired(false) + // .HasForeignKey("BuyerId"); + .HasForeignKey("_buyerId"); - orderConfiguration.HasOne(o => o.OrderStatus) - .WithMany() - // .HasForeignKey("OrderStatusId"); - .HasForeignKey("_orderStatusId"); - } + orderConfiguration.HasOne(o => o.OrderStatus) + .WithMany() + // .HasForeignKey("OrderStatusId"); + .HasForeignKey("_orderStatusId"); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderItemEntityTypeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderItemEntityTypeConfiguration.cs index 9fd5734ed..04e2bbec5 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderItemEntityTypeConfiguration.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderItemEntityTypeConfiguration.cs @@ -1,59 +1,53 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; -namespace Ordering.Infrastructure.EntityConfigurations +class OrderItemEntityTypeConfiguration + : IEntityTypeConfiguration { - class OrderItemEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder orderItemConfiguration) { - public void Configure(EntityTypeBuilder orderItemConfiguration) - { - orderItemConfiguration.ToTable("orderItems", OrderingContext.DEFAULT_SCHEMA); - - orderItemConfiguration.HasKey(o => o.Id); - - orderItemConfiguration.Ignore(b => b.DomainEvents); - - orderItemConfiguration.Property(o => o.Id) - .UseHiLo("orderitemseq"); - - orderItemConfiguration.Property("OrderId") - .IsRequired(); - - orderItemConfiguration - .Property("_discount") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("Discount") - .IsRequired(); - - orderItemConfiguration.Property("ProductId") - .IsRequired(); - - orderItemConfiguration - .Property("_productName") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("ProductName") - .IsRequired(); - - orderItemConfiguration - .Property("_unitPrice") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("UnitPrice") - .IsRequired(); - - orderItemConfiguration - .Property("_units") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("Units") - .IsRequired(); - - orderItemConfiguration - .Property("_pictureUrl") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("PictureUrl") - .IsRequired(false); - } + orderItemConfiguration.ToTable("orderItems", OrderingContext.DEFAULT_SCHEMA); + + orderItemConfiguration.HasKey(o => o.Id); + + orderItemConfiguration.Ignore(b => b.DomainEvents); + + orderItemConfiguration.Property(o => o.Id) + .UseHiLo("orderitemseq"); + + orderItemConfiguration.Property("OrderId") + .IsRequired(); + + orderItemConfiguration + .Property("_discount") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("Discount") + .IsRequired(); + + orderItemConfiguration.Property("ProductId") + .IsRequired(); + + orderItemConfiguration + .Property("_productName") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("ProductName") + .IsRequired(); + + orderItemConfiguration + .Property("_unitPrice") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("UnitPrice") + .IsRequired(); + + orderItemConfiguration + .Property("_units") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("Units") + .IsRequired(); + + orderItemConfiguration + .Property("_pictureUrl") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("PictureUrl") + .IsRequired(false); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderStatusEntityTypeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderStatusEntityTypeConfiguration.cs index f968d9011..c99eb6bbe 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderStatusEntityTypeConfiguration.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/OrderStatusEntityTypeConfiguration.cs @@ -1,27 +1,21 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; -namespace Ordering.Infrastructure.EntityConfigurations +class OrderStatusEntityTypeConfiguration + : IEntityTypeConfiguration { - class OrderStatusEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder orderStatusConfiguration) { - public void Configure(EntityTypeBuilder orderStatusConfiguration) - { - orderStatusConfiguration.ToTable("orderstatus", OrderingContext.DEFAULT_SCHEMA); + orderStatusConfiguration.ToTable("orderstatus", OrderingContext.DEFAULT_SCHEMA); - orderStatusConfiguration.HasKey(o => o.Id); + orderStatusConfiguration.HasKey(o => o.Id); - orderStatusConfiguration.Property(o => o.Id) - .HasDefaultValue(1) - .ValueGeneratedNever() - .IsRequired(); + orderStatusConfiguration.Property(o => o.Id) + .HasDefaultValue(1) + .ValueGeneratedNever() + .IsRequired(); - orderStatusConfiguration.Property(o => o.Name) - .HasMaxLength(200) - .IsRequired(); - } + orderStatusConfiguration.Property(o => o.Name) + .HasMaxLength(200) + .IsRequired(); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/PaymentMethodEntityTypeConfiguration.cs b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/PaymentMethodEntityTypeConfiguration.cs index 52fdf5f24..9083bcfcb 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/PaymentMethodEntityTypeConfiguration.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/EntityConfigurations/PaymentMethodEntityTypeConfiguration.cs @@ -1,65 +1,58 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using System; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; -namespace Ordering.Infrastructure.EntityConfigurations +class PaymentMethodEntityTypeConfiguration + : IEntityTypeConfiguration { - class PaymentMethodEntityTypeConfiguration - : IEntityTypeConfiguration + public void Configure(EntityTypeBuilder paymentConfiguration) { - public void Configure(EntityTypeBuilder paymentConfiguration) - { - paymentConfiguration.ToTable("paymentmethods", OrderingContext.DEFAULT_SCHEMA); - - paymentConfiguration.HasKey(b => b.Id); - - paymentConfiguration.Ignore(b => b.DomainEvents); - - paymentConfiguration.Property(b => b.Id) - .UseHiLo("paymentseq", OrderingContext.DEFAULT_SCHEMA); - - paymentConfiguration.Property("BuyerId") - .IsRequired(); - - paymentConfiguration - .Property("_cardHolderName") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("CardHolderName") - .HasMaxLength(200) - .IsRequired(); - - paymentConfiguration - .Property("_alias") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("Alias") - .HasMaxLength(200) - .IsRequired(); - - paymentConfiguration - .Property("_cardNumber") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("CardNumber") - .HasMaxLength(25) - .IsRequired(); - - paymentConfiguration - .Property("_expiration") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("Expiration") - .HasMaxLength(25) - .IsRequired(); - - paymentConfiguration - .Property("_cardTypeId") - .UsePropertyAccessMode(PropertyAccessMode.Field) - .HasColumnName("CardTypeId") - .IsRequired(); - - paymentConfiguration.HasOne(p => p.CardType) - .WithMany() - .HasForeignKey("_cardTypeId"); - } + paymentConfiguration.ToTable("paymentmethods", OrderingContext.DEFAULT_SCHEMA); + + paymentConfiguration.HasKey(b => b.Id); + + paymentConfiguration.Ignore(b => b.DomainEvents); + + paymentConfiguration.Property(b => b.Id) + .UseHiLo("paymentseq", OrderingContext.DEFAULT_SCHEMA); + + paymentConfiguration.Property("BuyerId") + .IsRequired(); + + paymentConfiguration + .Property("_cardHolderName") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("CardHolderName") + .HasMaxLength(200) + .IsRequired(); + + paymentConfiguration + .Property("_alias") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("Alias") + .HasMaxLength(200) + .IsRequired(); + + paymentConfiguration + .Property("_cardNumber") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("CardNumber") + .HasMaxLength(25) + .IsRequired(); + + paymentConfiguration + .Property("_expiration") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("Expiration") + .HasMaxLength(25) + .IsRequired(); + + paymentConfiguration + .Property("_cardTypeId") + .UsePropertyAccessMode(PropertyAccessMode.Field) + .HasColumnName("CardTypeId") + .IsRequired(); + + paymentConfiguration.HasOne(p => p.CardType) + .WithMany() + .HasForeignKey("_cardTypeId"); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/GlobalUsings.cs b/src/Services/Ordering/Ordering.Infrastructure/GlobalUsings.cs new file mode 100644 index 000000000..1d0d69df5 --- /dev/null +++ b/src/Services/Ordering/Ordering.Infrastructure/GlobalUsings.cs @@ -0,0 +1,17 @@ +global using MediatR; +global using Microsoft.EntityFrameworkCore.Design; +global using Microsoft.EntityFrameworkCore.Metadata.Builders; +global using Microsoft.EntityFrameworkCore.Storage; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Exceptions; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.EntityConfigurations; +global using System.Data; +global using System.Linq; +global using System.Threading.Tasks; +global using System.Threading; +global using System; diff --git a/src/Services/Ordering/Ordering.Infrastructure/Idempotency/ClientRequest.cs b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/ClientRequest.cs index 7ca49fa41..96002ce1b 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Idempotency/ClientRequest.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/ClientRequest.cs @@ -1,11 +1,8 @@ -using System; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency +public class ClientRequest { - public class ClientRequest - { - public Guid Id { get; set; } - public string Name { get; set; } - public DateTime Time { get; set; } - } + public Guid Id { get; set; } + public string Name { get; set; } + public DateTime Time { get; set; } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/Idempotency/IRequestManager.cs b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/IRequestManager.cs index d38c23e09..7adb31fe2 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Idempotency/IRequestManager.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/IRequestManager.cs @@ -1,12 +1,8 @@ -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency +public interface IRequestManager { - public interface IRequestManager - { - Task ExistAsync(Guid id); + Task ExistAsync(Guid id); - Task CreateRequestForCommandAsync(Guid id); - } + Task CreateRequestForCommandAsync(Guid id); } diff --git a/src/Services/Ordering/Ordering.Infrastructure/Idempotency/RequestManager.cs b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/RequestManager.cs index f2ae2d876..bb053eeaf 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Idempotency/RequestManager.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Idempotency/RequestManager.cs @@ -1,43 +1,38 @@ -using Ordering.Domain.Exceptions; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency +public class RequestManager : IRequestManager { - public class RequestManager : IRequestManager - { - private readonly OrderingContext _context; + private readonly OrderingContext _context; - public RequestManager(OrderingContext context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - } + public RequestManager(OrderingContext context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } - public async Task ExistAsync(Guid id) - { - var request = await _context. - FindAsync(id); + public async Task ExistAsync(Guid id) + { + var request = await _context. + FindAsync(id); - return request != null; - } + return request != null; + } - public async Task CreateRequestForCommandAsync(Guid id) - { - var exists = await ExistAsync(id); + public async Task CreateRequestForCommandAsync(Guid id) + { + var exists = await ExistAsync(id); - var request = exists ? - throw new OrderingDomainException($"Request with {id} already exists") : - new ClientRequest() - { - Id = id, - Name = typeof(T).Name, - Time = DateTime.UtcNow - }; + var request = exists ? + throw new OrderingDomainException($"Request with {id} already exists") : + new ClientRequest() + { + Id = id, + Name = typeof(T).Name, + Time = DateTime.UtcNow + }; - _context.Add(request); + _context.Add(request); - await _context.SaveChangesAsync(); - } + await _context.SaveChangesAsync(); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs b/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs index 8d0ea1d63..571280aba 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs @@ -1,28 +1,21 @@ -using MediatR; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; -namespace Ordering.Infrastructure +static class MediatorExtension { - static class MediatorExtension + public static async Task DispatchDomainEventsAsync(this IMediator mediator, OrderingContext ctx) { - public static async Task DispatchDomainEventsAsync(this IMediator mediator, OrderingContext ctx) - { - var domainEntities = ctx.ChangeTracker - .Entries() - .Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any()); + var domainEntities = ctx.ChangeTracker + .Entries() + .Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any()); - var domainEvents = domainEntities - .SelectMany(x => x.Entity.DomainEvents) - .ToList(); + var domainEvents = domainEntities + .SelectMany(x => x.Entity.DomainEvents) + .ToList(); - domainEntities.ToList() - .ForEach(entity => entity.Entity.ClearDomainEvents()); + domainEntities.ToList() + .ForEach(entity => entity.Entity.ClearDomainEvents()); - foreach (var domainEvent in domainEvents) - await mediator.Publish(domainEvent); - } + foreach (var domainEvent in domainEvents) + await mediator.Publish(domainEvent); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj b/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj index cdd6b9bd9..31edf4ba0 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj +++ b/src/Services/Ordering/Ordering.Infrastructure/Ordering.Infrastructure.csproj @@ -11,9 +11,9 @@ - - - + + + diff --git a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs index f6e6d4a02..0ca4ef7cf 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs @@ -1,156 +1,141 @@ -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using Ordering.Infrastructure; -using Ordering.Infrastructure.EntityConfigurations; -using System; -using System.Data; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; + +public class OrderingContext : DbContext, IUnitOfWork { - public class OrderingContext : DbContext, IUnitOfWork + public const string DEFAULT_SCHEMA = "ordering"; + public DbSet Orders { get; set; } + public DbSet OrderItems { get; set; } + public DbSet Payments { get; set; } + public DbSet Buyers { get; set; } + public DbSet CardTypes { get; set; } + public DbSet OrderStatus { get; set; } + + private readonly IMediator _mediator; + private IDbContextTransaction _currentTransaction; + + public OrderingContext(DbContextOptions options) : base(options) { } + + public IDbContextTransaction GetCurrentTransaction() => _currentTransaction; + + public bool HasActiveTransaction => _currentTransaction != null; + + public OrderingContext(DbContextOptions options, IMediator mediator) : base(options) { - public const string DEFAULT_SCHEMA = "ordering"; - public DbSet Orders { get; set; } - public DbSet OrderItems { get; set; } - public DbSet Payments { get; set; } - public DbSet Buyers { get; set; } - public DbSet CardTypes { get; set; } - public DbSet OrderStatus { get; set; } + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - private readonly IMediator _mediator; - private IDbContextTransaction _currentTransaction; - public OrderingContext(DbContextOptions options) : base(options) { } + System.Diagnostics.Debug.WriteLine("OrderingContext::ctor ->" + this.GetHashCode()); + } - public IDbContextTransaction GetCurrentTransaction() => _currentTransaction; + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfiguration(new ClientRequestEntityTypeConfiguration()); + modelBuilder.ApplyConfiguration(new PaymentMethodEntityTypeConfiguration()); + modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration()); + modelBuilder.ApplyConfiguration(new OrderItemEntityTypeConfiguration()); + modelBuilder.ApplyConfiguration(new CardTypeEntityTypeConfiguration()); + modelBuilder.ApplyConfiguration(new OrderStatusEntityTypeConfiguration()); + modelBuilder.ApplyConfiguration(new BuyerEntityTypeConfiguration()); + } - public bool HasActiveTransaction => _currentTransaction != null; + public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + // Dispatch Domain Events collection. + // Choices: + // A) Right BEFORE committing data (EF SaveChanges) into the DB will make a single transaction including + // side effects from the domain event handlers which are using the same DbContext with "InstancePerLifetimeScope" or "scoped" lifetime + // B) Right AFTER committing data (EF SaveChanges) into the DB will make multiple transactions. + // You will need to handle eventual consistency and compensatory actions in case of failures in any of the Handlers. + await _mediator.DispatchDomainEventsAsync(this); + + // After executing this line all the changes (from the Command Handler and Domain Event Handlers) + // performed through the DbContext will be committed + var result = await base.SaveChangesAsync(cancellationToken); + + return true; + } - public OrderingContext(DbContextOptions options, IMediator mediator) : base(options) - { - _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); + public async Task BeginTransactionAsync() + { + if (_currentTransaction != null) return null; + _currentTransaction = await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted); - System.Diagnostics.Debug.WriteLine("OrderingContext::ctor ->" + this.GetHashCode()); - } + return _currentTransaction; + } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.ApplyConfiguration(new ClientRequestEntityTypeConfiguration()); - modelBuilder.ApplyConfiguration(new PaymentMethodEntityTypeConfiguration()); - modelBuilder.ApplyConfiguration(new OrderEntityTypeConfiguration()); - modelBuilder.ApplyConfiguration(new OrderItemEntityTypeConfiguration()); - modelBuilder.ApplyConfiguration(new CardTypeEntityTypeConfiguration()); - modelBuilder.ApplyConfiguration(new OrderStatusEntityTypeConfiguration()); - modelBuilder.ApplyConfiguration(new BuyerEntityTypeConfiguration()); - } + public async Task CommitTransactionAsync(IDbContextTransaction transaction) + { + if (transaction == null) throw new ArgumentNullException(nameof(transaction)); + if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current"); - public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)) + try { - // Dispatch Domain Events collection. - // Choices: - // A) Right BEFORE committing data (EF SaveChanges) into the DB will make a single transaction including - // side effects from the domain event handlers which are using the same DbContext with "InstancePerLifetimeScope" or "scoped" lifetime - // B) Right AFTER committing data (EF SaveChanges) into the DB will make multiple transactions. - // You will need to handle eventual consistency and compensatory actions in case of failures in any of the Handlers. - await _mediator.DispatchDomainEventsAsync(this); - - // After executing this line all the changes (from the Command Handler and Domain Event Handlers) - // performed through the DbContext will be committed - var result = await base.SaveChangesAsync(cancellationToken); - - return true; + await SaveChangesAsync(); + transaction.Commit(); } - - public async Task BeginTransactionAsync() + catch { - if (_currentTransaction != null) return null; - - _currentTransaction = await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted); - - return _currentTransaction; + RollbackTransaction(); + throw; } - - public async Task CommitTransactionAsync(IDbContextTransaction transaction) + finally { - if (transaction == null) throw new ArgumentNullException(nameof(transaction)); - if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current"); - - try - { - await SaveChangesAsync(); - transaction.Commit(); - } - catch - { - RollbackTransaction(); - throw; - } - finally + if (_currentTransaction != null) { - if (_currentTransaction != null) - { - _currentTransaction.Dispose(); - _currentTransaction = null; - } + _currentTransaction.Dispose(); + _currentTransaction = null; } } + } - public void RollbackTransaction() + public void RollbackTransaction() + { + try { - try - { - _currentTransaction?.Rollback(); - } - finally + _currentTransaction?.Rollback(); + } + finally + { + if (_currentTransaction != null) { - if (_currentTransaction != null) - { - _currentTransaction.Dispose(); - _currentTransaction = null; - } + _currentTransaction.Dispose(); + _currentTransaction = null; } } } +} - public class OrderingContextDesignFactory : IDesignTimeDbContextFactory +public class OrderingContextDesignFactory : IDesignTimeDbContextFactory +{ + public OrderingContext CreateDbContext(string[] args) { - public OrderingContext CreateDbContext(string[] args) - { - var optionsBuilder = new DbContextOptionsBuilder() - .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;Integrated Security=true"); + var optionsBuilder = new DbContextOptionsBuilder() + .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.OrderingDb;Integrated Security=true"); - return new OrderingContext(optionsBuilder.Options, new NoMediator()); - } + return new OrderingContext(optionsBuilder.Options, new NoMediator()); + } - class NoMediator : IMediator + class NoMediator : IMediator + { + public Task Publish(TNotification notification, CancellationToken cancellationToken = default(CancellationToken)) where TNotification : INotification { - public Task Publish(TNotification notification, CancellationToken cancellationToken = default(CancellationToken)) where TNotification : INotification - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task Publish(object notification, CancellationToken cancellationToken = default) - { - return Task.CompletedTask; - } + public Task Publish(object notification, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } - public Task Send(IRequest request, CancellationToken cancellationToken = default(CancellationToken)) - { - return Task.FromResult(default(TResponse)); - } + public Task Send(IRequest request, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(default(TResponse)); + } - public Task Send(object request, CancellationToken cancellationToken = default) - { - return Task.FromResult(default(object)); - } + public Task Send(object request, CancellationToken cancellationToken = default) + { + return Task.FromResult(default(object)); } } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs b/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs index 2e7b41bd6..615320842 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Repositories/BuyerRepository.cs @@ -1,68 +1,60 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using System; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories +public class BuyerRepository + : IBuyerRepository { - public class BuyerRepository - : IBuyerRepository + private readonly OrderingContext _context; + public IUnitOfWork UnitOfWork { - private readonly OrderingContext _context; - public IUnitOfWork UnitOfWork + get { - get - { - return _context; - } - } - - public BuyerRepository(OrderingContext context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); + return _context; } + } - public Buyer Add(Buyer buyer) - { - if (buyer.IsTransient()) - { - return _context.Buyers - .Add(buyer) - .Entity; - } - else - { - return buyer; - } - } + public BuyerRepository(OrderingContext context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } - public Buyer Update(Buyer buyer) + public Buyer Add(Buyer buyer) + { + if (buyer.IsTransient()) { return _context.Buyers - .Update(buyer) - .Entity; + .Add(buyer) + .Entity; } - - public async Task FindAsync(string identity) + else { - var buyer = await _context.Buyers - .Include(b => b.PaymentMethods) - .Where(b => b.IdentityGuid == identity) - .SingleOrDefaultAsync(); - return buyer; } + } - public async Task FindByIdAsync(string id) - { - var buyer = await _context.Buyers - .Include(b => b.PaymentMethods) - .Where(b => b.Id == int.Parse(id)) - .SingleOrDefaultAsync(); + public Buyer Update(Buyer buyer) + { + return _context.Buyers + .Update(buyer) + .Entity; + } - return buyer; - } + public async Task FindAsync(string identity) + { + var buyer = await _context.Buyers + .Include(b => b.PaymentMethods) + .Where(b => b.IdentityGuid == identity) + .SingleOrDefaultAsync(); + + return buyer; + } + + public async Task FindByIdAsync(string id) + { + var buyer = await _context.Buyers + .Include(b => b.PaymentMethods) + .Where(b => b.Id == int.Parse(id)) + .SingleOrDefaultAsync(); + + return buyer; } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs b/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs index 040256cf6..9040de569 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs @@ -1,63 +1,55 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories +namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; + +public class OrderRepository + : IOrderRepository { - public class OrderRepository - : IOrderRepository - { - private readonly OrderingContext _context; + private readonly OrderingContext _context; - public IUnitOfWork UnitOfWork + public IUnitOfWork UnitOfWork + { + get { - get - { - return _context; - } + return _context; } + } - public OrderRepository(OrderingContext context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - } + public OrderRepository(OrderingContext context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } - public Order Add(Order order) - { - return _context.Orders.Add(order).Entity; + public Order Add(Order order) + { + return _context.Orders.Add(order).Entity; - } + } - public async Task GetAsync(int orderId) - { - var order = await _context - .Orders - .Include(x => x.Address) - .FirstOrDefaultAsync(o => o.Id == orderId); - if (order == null) - { - order = _context + public async Task GetAsync(int orderId) + { + var order = await _context .Orders - .Local - .FirstOrDefault(o => o.Id == orderId); - } - if (order != null) - { - await _context.Entry(order) - .Collection(i => i.OrderItems).LoadAsync(); - await _context.Entry(order) - .Reference(i => i.OrderStatus).LoadAsync(); - } - - return order; + .Include(x => x.Address) + .FirstOrDefaultAsync(o => o.Id == orderId); + if (order == null) + { + order = _context + .Orders + .Local + .FirstOrDefault(o => o.Id == orderId); } - - public void Update(Order order) + if (order != null) { - _context.Entry(order).State = EntityState.Modified; + await _context.Entry(order) + .Collection(i => i.OrderItems).LoadAsync(); + await _context.Entry(order) + .Reference(i => i.OrderStatus).LoadAsync(); } + + return order; + } + + public void Update(Order order) + { + _context.Entry(order).State = EntityState.Modified; } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/AutofacModules/ApplicationModule.cs b/src/Services/Ordering/Ordering.SignalrHub/AutofacModules/ApplicationModule.cs index e7b09f636..9d28154ea 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/AutofacModules/ApplicationModule.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/AutofacModules/ApplicationModule.cs @@ -1,26 +1,20 @@ -using Autofac; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Ordering.SignalrHub.IntegrationEvents; -using System.Reflection; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.AutofacModules; -namespace Ordering.SignalrHub.AutofacModules +public class ApplicationModule + : Autofac.Module { - public class ApplicationModule - : Autofac.Module - { - public string QueriesConnectionString { get; } + public string QueriesConnectionString { get; } - public ApplicationModule() - { - } + public ApplicationModule() + { + } - protected override void Load(ContainerBuilder builder) - { + protected override void Load(ContainerBuilder builder) + { - builder.RegisterAssemblyTypes(typeof(OrderStatusChangedToAwaitingValidationIntegrationEvent).GetTypeInfo().Assembly) - .AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); + builder.RegisterAssemblyTypes(typeof(OrderStatusChangedToAwaitingValidationIntegrationEvent).GetTypeInfo().Assembly) + .AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); - } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs b/src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs new file mode 100644 index 000000000..0a1b3457e --- /dev/null +++ b/src/Services/Ordering/Ordering.SignalrHub/GlobalUsings.cs @@ -0,0 +1,35 @@ +global using Autofac.Extensions.DependencyInjection; +global using Autofac; +global using HealthChecks.UI.Client; +global using Microsoft.AspNetCore.Authentication.JwtBearer; +global using Microsoft.AspNetCore.Authorization; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.SignalR; +global using Microsoft.AspNetCore; +global using Microsoft.Azure.ServiceBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; +global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Logging; +global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.AutofacModules; +global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; +global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; +global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents; +global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub; +global using RabbitMQ.Client; +global using Serilog.Context; +global using Serilog; +global using System.IdentityModel.Tokens.Jwt; +global using System.IO; +global using System.Reflection; +global using System.Threading.Tasks; +global using System; diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs index 17e2efa90..341b781ab 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs @@ -1,36 +1,28 @@ -using Microsoft.AspNetCore.SignalR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using Serilog.Context; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents; -namespace Ordering.SignalrHub.IntegrationEvents +public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : IIntegrationEventHandler { - public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IHubContext _hubContext; - private readonly ILogger _logger; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToAwaitingValidationIntegrationEventHandler( - IHubContext hubContext, - ILogger logger) - { - _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public OrderStatusChangedToAwaitingValidationIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); - } + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs index 829ec39b8..aba5091b9 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToCancelledIntegrationEventHandler.cs @@ -1,37 +1,28 @@ -using Microsoft.AspNetCore.SignalR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using Ordering.SignalrHub.IntegrationEvents.Events; -using Serilog.Context; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; -namespace Ordering.SignalrHub.IntegrationEvents.EventHandling +public class OrderStatusChangedToCancelledIntegrationEventHandler : IIntegrationEventHandler { - public class OrderStatusChangedToCancelledIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IHubContext _hubContext; - private readonly ILogger _logger; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToCancelledIntegrationEventHandler( - IHubContext hubContext, - ILogger logger) - { - _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public OrderStatusChangedToCancelledIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStatusChangedToCancelledIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToCancelledIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); - } + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); } } } \ No newline at end of file diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs index 836e02d3c..03fc04356 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs @@ -1,37 +1,28 @@ -using Microsoft.AspNetCore.SignalR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using Ordering.SignalrHub.IntegrationEvents.Events; -using Serilog.Context; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; -namespace Ordering.SignalrHub.IntegrationEvents.EventHandling +public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler { - public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IHubContext _hubContext; - private readonly ILogger _logger; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToPaidIntegrationEventHandler( - IHubContext hubContext, - ILogger logger) - { - _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public OrderStatusChangedToPaidIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); - } + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs index 40664c268..663f7ae3a 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToShippedIntegrationEventHandler.cs @@ -1,37 +1,28 @@ -using Microsoft.AspNetCore.SignalR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using Ordering.SignalrHub.IntegrationEvents.Events; -using Serilog.Context; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; -namespace Ordering.SignalrHub.IntegrationEvents.EventHandling +public class OrderStatusChangedToShippedIntegrationEventHandler : IIntegrationEventHandler { - public class OrderStatusChangedToShippedIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IHubContext _hubContext; - private readonly ILogger _logger; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToShippedIntegrationEventHandler( - IHubContext hubContext, - ILogger logger) - { - _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public OrderStatusChangedToShippedIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStatusChangedToShippedIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToShippedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); - } + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs index 3ffddfff9..aa662065d 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs @@ -1,38 +1,29 @@ -using Microsoft.AspNetCore.SignalR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using Ordering.SignalrHub.IntegrationEvents.Events; -using Serilog.Context; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; -namespace Ordering.SignalrHub.IntegrationEvents.EventHandling +public class OrderStatusChangedToStockConfirmedIntegrationEventHandler : + IIntegrationEventHandler { - public class OrderStatusChangedToStockConfirmedIntegrationEventHandler : - IIntegrationEventHandler - { - private readonly IHubContext _hubContext; - private readonly ILogger _logger; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToStockConfirmedIntegrationEventHandler( - IHubContext hubContext, - ILogger logger) - { - _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public OrderStatusChangedToStockConfirmedIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStatusChangedToStockConfirmedIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToStockConfirmedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); - } + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); } } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs index e9109939e..0f8042013 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/EventHandling/OrderStatusChangedToSubmittedIntegrationEventHandler.cs @@ -1,38 +1,29 @@ -using Microsoft.AspNetCore.SignalR; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using Ordering.SignalrHub.IntegrationEvents.Events; -using Serilog.Context; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; -namespace Ordering.SignalrHub.IntegrationEvents.EventHandling +public class OrderStatusChangedToSubmittedIntegrationEventHandler : + IIntegrationEventHandler { - public class OrderStatusChangedToSubmittedIntegrationEventHandler : - IIntegrationEventHandler - { - private readonly IHubContext _hubContext; - private readonly ILogger _logger; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; - public OrderStatusChangedToSubmittedIntegrationEventHandler( - IHubContext hubContext, - ILogger logger) - { - _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public OrderStatusChangedToSubmittedIntegrationEventHandler( + IHubContext hubContext, + ILogger logger) + { + _hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStatusChangedToSubmittedIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToSubmittedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await _hubContext.Clients - .Group(@event.BuyerName) - .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); - } + await _hubContext.Clients + .Group(@event.BuyerName) + .SendAsync("UpdatedOrderState", new { OrderId = @event.OrderId, Status = @event.OrderStatus }); } } -} \ No newline at end of file +} diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs index dc1727951..3afcf8f36 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs @@ -1,18 +1,16 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents; -namespace Ordering.SignalrHub.IntegrationEvents +public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } + diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs index 1ea290082..5df74ac78 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToCancelledIntegrationEvent.cs @@ -1,18 +1,16 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; -namespace Ordering.SignalrHub.IntegrationEvents.Events +public record OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } + diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs index e3bf88190..63246835d 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToPaidIntegrationEvent.cs @@ -1,19 +1,16 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; -namespace Ordering.SignalrHub.IntegrationEvents.Events +public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToPaidIntegrationEvent(int orderId, - string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToPaidIntegrationEvent(int orderId, + string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs index 436b5a95a..9306d198e 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToShippedIntegrationEvent.cs @@ -1,18 +1,16 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; -namespace Ordering.SignalrHub.IntegrationEvents.Events +public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } + diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs index 137967ff6..896ef34cb 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs @@ -1,18 +1,15 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; -namespace Ordering.SignalrHub.IntegrationEvents.Events +public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToSubmittedIntegrationEvent.cs b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToSubmittedIntegrationEvent.cs index 0d2de4abe..23f01044e 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToSubmittedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/IntegrationEvents/Events/OrderStatusChangedToSubmittedIntegrationEvent.cs @@ -1,18 +1,15 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; -namespace Ordering.SignalrHub.IntegrationEvents.Events +public record OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public string OrderStatus { get; } - public string BuyerName { get; } + public int OrderId { get; } + public string OrderStatus { get; } + public string BuyerName { get; } - public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs b/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs index 7696a4da1..9f545bba4 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs @@ -1,24 +1,18 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.SignalR; -using System; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub; -namespace Ordering.SignalrHub +[Authorize] +public class NotificationsHub : Hub { - [Authorize] - public class NotificationsHub : Hub - { - public override async Task OnConnectedAsync() - { - await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); - await base.OnConnectedAsync(); - } + public override async Task OnConnectedAsync() + { + await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); + await base.OnConnectedAsync(); + } - public override async Task OnDisconnectedAsync(Exception ex) - { - await Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.Identity.Name); - await base.OnDisconnectedAsync(ex); - } + public override async Task OnDisconnectedAsync(Exception ex) + { + await Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.Identity.Name); + await base.OnDisconnectedAsync(ex); } } diff --git a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj index 69bae2155..7392259ed 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj +++ b/src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj @@ -13,26 +13,26 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/src/Services/Ordering/Ordering.SignalrHub/Program.cs b/src/Services/Ordering/Ordering.SignalrHub/Program.cs index d73e1da2a..e472539fd 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Program.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Program.cs @@ -1,12 +1,4 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Serilog; -using System; -using System.IO; -using Ordering.SignalrHub; - -var configuration = GetConfiguration(); +var configuration = GetConfiguration(); Log.Logger = CreateSerilogLogger(configuration); diff --git a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs index a4a5d08c7..944e82b52 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Startup.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/Startup.cs @@ -1,274 +1,249 @@ -using Autofac; -using Autofac.Extensions.DependencyInjection; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.Azure.ServiceBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; -using Ordering.SignalrHub.AutofacModules; -using Ordering.SignalrHub.IntegrationEvents; -using Ordering.SignalrHub.IntegrationEvents.EventHandling; -using Ordering.SignalrHub.IntegrationEvents.Events; -using RabbitMQ.Client; -using System; -using System.IdentityModel.Tokens.Jwt; -using System.Threading.Tasks; - -namespace Ordering.SignalrHub +namespace Microsoft.eShopOnContainers.Services.Ordering.SignalrHub; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } + + public IConfiguration Configuration { get; } - public IConfiguration Configuration { get; } + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services + .AddCustomHealthCheck(Configuration) + .AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder + .AllowAnyMethod() + .AllowAnyHeader() + .SetIsOriginAllowed((host) => true) + .AllowCredentials()); + }); - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public IServiceProvider ConfigureServices(IServiceCollection services) + if (Configuration.GetValue("IsClusterEnv") == bool.TrueString) { services - .AddCustomHealthCheck(Configuration) - .AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder - .AllowAnyMethod() - .AllowAnyHeader() - .SetIsOriginAllowed((host) => true) - .AllowCredentials()); - }); - - if (Configuration.GetValue("IsClusterEnv") == bool.TrueString) - { - services - .AddSignalR() - .AddStackExchangeRedis(Configuration["SignalrStoreConnectionString"]); - } - else - { - services.AddSignalR(); - } + .AddSignalR() + .AddStackExchangeRedis(Configuration["SignalrStoreConnectionString"]); + } + else + { + services.AddSignalR(); + } - if (Configuration.GetValue("AzureServiceBusEnabled")) + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => { - services.AddSingleton(sp => - { - var serviceBusConnectionString = Configuration["EventBusConnection"]; - var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - var subscriptionClientName = Configuration["SubscriptionClientName"]; + var subscriptionClientName = Configuration["SubscriptionClientName"]; - return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); - }); - } - else + return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); + }); + } + else + { + services.AddSingleton(sp => { - services.AddSingleton(sp => + var logger = sp.GetRequiredService>(); + + + var factory = new ConnectionFactory() { - var logger = sp.GetRequiredService>(); + HostName = Configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; + if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) + { + factory.UserName = Configuration["EventBusUserName"]; + } - var factory = new ConnectionFactory() - { - HostName = Configuration["EventBusConnection"], - DispatchConsumersAsync = true - }; + if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) + { + factory.Password = Configuration["EventBusPassword"]; + } - if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) - { - factory.UserName = Configuration["EventBusUserName"]; - } + var retryCount = 5; + if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(Configuration["EventBusRetryCount"]); + } - if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) - { - factory.Password = Configuration["EventBusPassword"]; - } + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); + }); + } - var retryCount = 5; - if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(Configuration["EventBusRetryCount"]); - } + ConfigureAuthService(services); - return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); - }); - } + RegisterEventBus(services); - ConfigureAuthService(services); + services.AddOptions(); - RegisterEventBus(services); + //configure autofac + var container = new ContainerBuilder(); + container.RegisterModule(new ApplicationModule()); + container.Populate(services); - services.AddOptions(); + return new AutofacServiceProvider(container.Build()); + } - //configure autofac - var container = new ContainerBuilder(); - container.RegisterModule(new ApplicationModule()); - container.Populate(services); + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + //loggerFactory.AddConsole(Configuration.GetSection("Logging")); + //loggerFactory.AddDebug(); + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); - return new AutofacServiceProvider(container.Build()); - } + var pathBase = Configuration["PATH_BASE"]; - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + if (!string.IsNullOrEmpty(pathBase)) { - //loggerFactory.AddConsole(Configuration.GetSection("Logging")); - //loggerFactory.AddDebug(); - //loggerFactory.AddAzureWebAppDiagnostics(); - //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); + app.UsePathBase(pathBase); + } - var pathBase = Configuration["PATH_BASE"]; + app.UseRouting(); + app.UseCors("CorsPolicy"); + app.UseAuthentication(); + app.UseAuthorization(); - if (!string.IsNullOrEmpty(pathBase)) + app.UseEndpoints(endpoints => + { + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() { - loggerFactory.CreateLogger().LogDebug("Using PATH BASE '{pathBase}'", pathBase); - app.UsePathBase(pathBase); - } - - app.UseRouting(); - app.UseCors("CorsPolicy"); - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions { - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); - endpoints.MapHub("/hub/notificationhub"); + Predicate = r => r.Name.Contains("self") }); + endpoints.MapHub("/hub/notificationhub"); + }); - ConfigureEventBus(app); - } + ConfigureEventBus(app); + } - private void ConfigureEventBus(IApplicationBuilder app) - { - var eventBus = app.ApplicationServices.GetRequiredService(); - - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - } + private void ConfigureEventBus(IApplicationBuilder app) + { + var eventBus = app.ApplicationServices.GetRequiredService(); + + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + } - private void ConfigureAuthService(IServiceCollection services) - { - // prevent from mapping "sub" claim to nameidentifier. - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + private void ConfigureAuthService(IServiceCollection services) + { + // prevent from mapping "sub" claim to nameidentifier. + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); - var identityUrl = Configuration.GetValue("IdentityUrl"); + var identityUrl = Configuration.GetValue("IdentityUrl"); - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }).AddJwtBearer(options => + }).AddJwtBearer(options => + { + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "orders.signalrhub"; + options.Events = new JwtBearerEvents { - options.Authority = identityUrl; - options.RequireHttpsMetadata = false; - options.Audience = "orders.signalrhub"; - options.Events = new JwtBearerEvents + OnMessageReceived = context => { - OnMessageReceived = context => + var accessToken = context.Request.Query["access_token"]; + + var path = context.HttpContext.Request.Path; + if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hub/notificationhub"))) { - var accessToken = context.Request.Query["access_token"]; - - var path = context.HttpContext.Request.Path; - if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hub/notificationhub"))) - { - context.Token = accessToken; - } - return Task.CompletedTask; + context.Token = accessToken; } - }; - }); - } + return Task.CompletedTask; + } + }; + }); + } - private void RegisterEventBus(IServiceCollection services) + private void RegisterEventBus(IServiceCollection services) + { + if (Configuration.GetValue("AzureServiceBusEnabled")) { - if (Configuration.GetValue("AzureServiceBusEnabled")) + services.AddSingleton(sp => { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope); - }); - } - else + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, iLifetimeScope); + }); + } + else + { + services.AddSingleton(sp => { - services.AddSingleton(sp => + var subscriptionClientName = Configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) { - var subscriptionClientName = Configuration["SubscriptionClientName"]; - var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - var retryCount = 5; - if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(Configuration["EventBusRetryCount"]); - } + retryCount = int.Parse(Configuration["EventBusRetryCount"]); + } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); - }); - } - - services.AddSingleton(); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); } + + services.AddSingleton(); } +} - public static class CustomExtensionMethods +public static class CustomExtensionMethods +{ + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) - { - var hcBuilder = services.AddHealthChecks(); + var hcBuilder = services.AddHealthChecks(); - hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); + hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); - if (configuration.GetValue("AzureServiceBusEnabled")) - { - hcBuilder - .AddAzureServiceBusTopic( - configuration["EventBusConnection"], - topicName: "eshop_event_bus", - name: "signalr-servicebus-check", - tags: new string[] { "servicebus" }); - } - else - { - hcBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", - name: "signalr-rabbitmqbus-check", - tags: new string[] { "rabbitmqbus" }); - } - - return services; + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "signalr-servicebus-check", + tags: new string[] { "servicebus" }); } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "signalr-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; } } diff --git a/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs b/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs index 62818115d..1b7d38864 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs @@ -1,91 +1,79 @@ -using System; +namespace UnitTest.Ordering.Application; -namespace UnitTest.Ordering.Application +public class IdentifiedCommandHandlerTest { - using global::Ordering.API.Application.Models; - using MediatR; - using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; - using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; - using Microsoft.Extensions.Logging; - using Moq; - using System.Collections.Generic; - using System.Threading.Tasks; - using Xunit; - public class IdentifiedCommandHandlerTest - { - private readonly Mock _requestManager; - private readonly Mock _mediator; - private readonly Mock>> _loggerMock; + private readonly Mock _requestManager; + private readonly Mock _mediator; + private readonly Mock>> _loggerMock; - public IdentifiedCommandHandlerTest() - { - _requestManager = new Mock(); - _mediator = new Mock(); - _loggerMock = new Mock>>(); - } + public IdentifiedCommandHandlerTest() + { + _requestManager = new Mock(); + _mediator = new Mock(); + _loggerMock = new Mock>>(); + } - [Fact] - public async Task Handler_sends_command_when_order_no_exists() - { - // Arrange - var fakeGuid = Guid.NewGuid(); - var fakeOrderCmd = new IdentifiedCommand(FakeOrderRequest(), fakeGuid); + [Fact] + public async Task Handler_sends_command_when_order_no_exists() + { + // Arrange + var fakeGuid = Guid.NewGuid(); + var fakeOrderCmd = new IdentifiedCommand(FakeOrderRequest(), fakeGuid); - _requestManager.Setup(x => x.ExistAsync(It.IsAny())) - .Returns(Task.FromResult(false)); + _requestManager.Setup(x => x.ExistAsync(It.IsAny())) + .Returns(Task.FromResult(false)); - _mediator.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) - .Returns(Task.FromResult(true)); + _mediator.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) + .Returns(Task.FromResult(true)); - //Act - var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object); - var cltToken = new System.Threading.CancellationToken(); - var result = await handler.Handle(fakeOrderCmd, cltToken); + //Act + var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object); + var cltToken = new System.Threading.CancellationToken(); + var result = await handler.Handle(fakeOrderCmd, cltToken); - //Assert - Assert.True(result); - _mediator.Verify(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken)), Times.Once()); - } + //Assert + Assert.True(result); + _mediator.Verify(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken)), Times.Once()); + } - [Fact] - public async Task Handler_sends_no_command_when_order_already_exists() - { - // Arrange - var fakeGuid = Guid.NewGuid(); - var fakeOrderCmd = new IdentifiedCommand(FakeOrderRequest(), fakeGuid); + [Fact] + public async Task Handler_sends_no_command_when_order_already_exists() + { + // Arrange + var fakeGuid = Guid.NewGuid(); + var fakeOrderCmd = new IdentifiedCommand(FakeOrderRequest(), fakeGuid); - _requestManager.Setup(x => x.ExistAsync(It.IsAny())) - .Returns(Task.FromResult(true)); + _requestManager.Setup(x => x.ExistAsync(It.IsAny())) + .Returns(Task.FromResult(true)); - _mediator.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) - .Returns(Task.FromResult(true)); + _mediator.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) + .Returns(Task.FromResult(true)); - //Act - var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object); - var cltToken = new System.Threading.CancellationToken(); - var result = await handler.Handle(fakeOrderCmd, cltToken); + //Act + var handler = new IdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object); + var cltToken = new System.Threading.CancellationToken(); + var result = await handler.Handle(fakeOrderCmd, cltToken); - //Assert - Assert.False(result); - _mediator.Verify(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken)), Times.Never()); - } + //Assert + Assert.False(result); + _mediator.Verify(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken)), Times.Never()); + } - private CreateOrderCommand FakeOrderRequest(Dictionary args = null) - { - return new CreateOrderCommand( - new List(), - userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null, - userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null, - city: args != null && args.ContainsKey("city") ? (string)args["city"] : null, - street: args != null && args.ContainsKey("street") ? (string)args["street"] : null, - state: args != null && args.ContainsKey("state") ? (string)args["state"] : null, - country: args != null && args.ContainsKey("country") ? (string)args["country"] : null, - zipcode: args != null && args.ContainsKey("zipcode") ? (string)args["zipcode"] : null, - cardNumber: args != null && args.ContainsKey("cardNumber") ? (string)args["cardNumber"] : "1234", - cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue, - cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123", - cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX", - cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0); - } + private CreateOrderCommand FakeOrderRequest(Dictionary args = null) + { + return new CreateOrderCommand( + new List(), + userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null, + userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null, + city: args != null && args.ContainsKey("city") ? (string)args["city"] : null, + street: args != null && args.ContainsKey("street") ? (string)args["street"] : null, + state: args != null && args.ContainsKey("state") ? (string)args["state"] : null, + country: args != null && args.ContainsKey("country") ? (string)args["country"] : null, + zipcode: args != null && args.ContainsKey("zipcode") ? (string)args["zipcode"] : null, + cardNumber: args != null && args.ContainsKey("cardNumber") ? (string)args["cardNumber"] : "1234", + cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue, + cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123", + cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX", + cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0); } } diff --git a/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs b/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs index 78d7dbed7..184a5f5d2 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Application/NewOrderCommandHandlerTest.cs @@ -1,97 +1,83 @@ -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; -using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Moq; -using System; -using System.Threading; -using System.Threading.Tasks; +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents; + +namespace UnitTest.Ordering.Application; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -namespace UnitTest.Ordering.Application +public class NewOrderRequestHandlerTest { - using global::Ordering.API.Application.IntegrationEvents; - using global::Ordering.API.Application.Models; - using MediatR; - using Microsoft.Extensions.Logging; - using System.Collections.Generic; - using Xunit; - - public class NewOrderRequestHandlerTest + private readonly Mock _orderRepositoryMock; + private readonly Mock _identityServiceMock; + private readonly Mock _mediator; + private readonly Mock _orderingIntegrationEventService; + + public NewOrderRequestHandlerTest() + { + + _orderRepositoryMock = new Mock(); + _identityServiceMock = new Mock(); + _orderingIntegrationEventService = new Mock(); + _mediator = new Mock(); + } + + [Fact] + public async Task Handle_return_false_if_order_is_not_persisted() + { + var buyerId = "1234"; + + var fakeOrderCmd = FakeOrderRequestWithBuyer(new Dictionary + { ["cardExpiration"] = DateTime.Now.AddYears(1) }); + + _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) + .Returns(Task.FromResult(FakeOrder())); + + _orderRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + .Returns(Task.FromResult(1)); + + _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); + + var LoggerMock = new Mock>(); + //Act + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderingIntegrationEventService.Object, _orderRepositoryMock.Object, _identityServiceMock.Object, LoggerMock.Object); + var cltToken = new System.Threading.CancellationToken(); + var result = await handler.Handle(fakeOrderCmd, cltToken); + + //Assert + Assert.False(result); + } + + [Fact] + public void Handle_throws_exception_when_no_buyerId() + { + //Assert + Assert.Throws(() => new Buyer(string.Empty, string.Empty)); + } + + private Buyer FakeBuyer() + { + return new Buyer(Guid.NewGuid().ToString(), "1"); + } + + private Order FakeOrder() + { + return new Order("1", "fakeName", new Address("street", "city", "state", "country", "zipcode"), 1, "12", "111", "fakeName", DateTime.Now.AddYears(1)); + } + + private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary args = null) { - private readonly Mock _orderRepositoryMock; - private readonly Mock _identityServiceMock; - private readonly Mock _mediator; - private readonly Mock _orderingIntegrationEventService; - - public NewOrderRequestHandlerTest() - { - - _orderRepositoryMock = new Mock(); - _identityServiceMock = new Mock(); - _orderingIntegrationEventService = new Mock(); - _mediator = new Mock(); - } - - [Fact] - public async Task Handle_return_false_if_order_is_not_persisted() - { - var buyerId = "1234"; - - var fakeOrderCmd = FakeOrderRequestWithBuyer(new Dictionary - { ["cardExpiration"] = DateTime.Now.AddYears(1) }); - - _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) - .Returns(Task.FromResult(FakeOrder())); - - _orderRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) - .Returns(Task.FromResult(1)); - - _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); - - var LoggerMock = new Mock>(); - //Act - var handler = new CreateOrderCommandHandler(_mediator.Object, _orderingIntegrationEventService.Object, _orderRepositoryMock.Object, _identityServiceMock.Object, LoggerMock.Object); - var cltToken = new System.Threading.CancellationToken(); - var result = await handler.Handle(fakeOrderCmd, cltToken); - - //Assert - Assert.False(result); - } - - [Fact] - public void Handle_throws_exception_when_no_buyerId() - { - //Assert - Assert.Throws(() => new Buyer(string.Empty, string.Empty)); - } - - private Buyer FakeBuyer() - { - return new Buyer(Guid.NewGuid().ToString(), "1"); - } - - private Order FakeOrder() - { - return new Order("1", "fakeName", new Address("street", "city", "state", "country", "zipcode"), 1, "12", "111", "fakeName", DateTime.Now.AddYears(1)); - } - - private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary args = null) - { - return new CreateOrderCommand( - new List(), - userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null, - userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null, - city: args != null && args.ContainsKey("city") ? (string)args["city"] : null, - street: args != null && args.ContainsKey("street") ? (string)args["street"] : null, - state: args != null && args.ContainsKey("state") ? (string)args["state"] : null, - country: args != null && args.ContainsKey("country") ? (string)args["country"] : null, - zipcode: args != null && args.ContainsKey("zipcode") ? (string)args["zipcode"] : null, - cardNumber: args != null && args.ContainsKey("cardNumber") ? (string)args["cardNumber"] : "1234", - cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue, - cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123", - cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX", - cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0); - } + return new CreateOrderCommand( + new List(), + userId: args != null && args.ContainsKey("userId") ? (string)args["userId"] : null, + userName: args != null && args.ContainsKey("userName") ? (string)args["userName"] : null, + city: args != null && args.ContainsKey("city") ? (string)args["city"] : null, + street: args != null && args.ContainsKey("street") ? (string)args["street"] : null, + state: args != null && args.ContainsKey("state") ? (string)args["state"] : null, + country: args != null && args.ContainsKey("country") ? (string)args["country"] : null, + zipcode: args != null && args.ContainsKey("zipcode") ? (string)args["zipcode"] : null, + cardNumber: args != null && args.ContainsKey("cardNumber") ? (string)args["cardNumber"] : "1234", + cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue, + cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123", + cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX", + cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0); } } diff --git a/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs b/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs index 26281e110..cfa14a3ec 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs @@ -1,148 +1,134 @@ -using MediatR; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +namespace UnitTest.Ordering.Application; + using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; -using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; -using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; -using Microsoft.Extensions.Logging; -using Moq; -using Ordering.API.Application.Commands; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace UnitTest.Ordering.Application + +public class OrdersWebApiTest { - public class OrdersWebApiTest + private readonly Mock _mediatorMock; + private readonly Mock _orderQueriesMock; + private readonly Mock _identityServiceMock; + private readonly Mock> _loggerMock; + + public OrdersWebApiTest() + { + _mediatorMock = new Mock(); + _orderQueriesMock = new Mock(); + _identityServiceMock = new Mock(); + _loggerMock = new Mock>(); + } + + [Fact] + public async Task Cancel_order_with_requestId_success() + { + //Arrange + _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(CancellationToken))) + .Returns(Task.FromResult(true)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), Guid.NewGuid().ToString()) as OkResult; + + //Assert + Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK); + + } + + [Fact] + public async Task Cancel_order_bad_request() + { + //Arrange + _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(CancellationToken))) + .Returns(Task.FromResult(true)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), String.Empty) as BadRequestResult; + + //Assert + Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.BadRequest); + } + + [Fact] + public async Task Ship_order_with_requestId_success() + { + //Arrange + _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) + .Returns(Task.FromResult(true)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), Guid.NewGuid().ToString()) as OkResult; + + //Assert + Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK); + + } + + [Fact] + public async Task Ship_order_bad_request() + { + //Arrange + _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) + .Returns(Task.FromResult(true)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), String.Empty) as BadRequestResult; + + //Assert + Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.BadRequest); + } + + [Fact] + public async Task Get_orders_success() + { + //Arrange + var fakeDynamicResult = Enumerable.Empty(); + + _identityServiceMock.Setup(x => x.GetUserIdentity()) + .Returns(Guid.NewGuid().ToString()); + + _orderQueriesMock.Setup(x => x.GetOrdersFromUserAsync(Guid.NewGuid())) + .Returns(Task.FromResult(fakeDynamicResult)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.GetOrdersAsync(); + + //Assert + Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); + } + + [Fact] + public async Task Get_order_success() { - private readonly Mock _mediatorMock; - private readonly Mock _orderQueriesMock; - private readonly Mock _identityServiceMock; - private readonly Mock> _loggerMock; - - public OrdersWebApiTest() - { - _mediatorMock = new Mock(); - _orderQueriesMock = new Mock(); - _identityServiceMock = new Mock(); - _loggerMock = new Mock>(); - } - - [Fact] - public async Task Cancel_order_with_requestId_success() - { - //Arrange - _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(CancellationToken))) - .Returns(Task.FromResult(true)); - - //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); - var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), Guid.NewGuid().ToString()) as OkResult; - - //Assert - Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK); - - } - - [Fact] - public async Task Cancel_order_bad_request() - { - //Arrange - _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(CancellationToken))) - .Returns(Task.FromResult(true)); - - //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); - var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), String.Empty) as BadRequestResult; - - //Assert - Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.BadRequest); - } - - [Fact] - public async Task Ship_order_with_requestId_success() - { - //Arrange - _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) - .Returns(Task.FromResult(true)); - - //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); - var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), Guid.NewGuid().ToString()) as OkResult; - - //Assert - Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK); - - } - - [Fact] - public async Task Ship_order_bad_request() - { - //Arrange - _mediatorMock.Setup(x => x.Send(It.IsAny>(), default(System.Threading.CancellationToken))) - .Returns(Task.FromResult(true)); - - //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); - var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), String.Empty) as BadRequestResult; - - //Assert - Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.BadRequest); - } - - [Fact] - public async Task Get_orders_success() - { - //Arrange - var fakeDynamicResult = Enumerable.Empty(); - - _identityServiceMock.Setup(x => x.GetUserIdentity()) - .Returns(Guid.NewGuid().ToString()); - - _orderQueriesMock.Setup(x => x.GetOrdersFromUserAsync(Guid.NewGuid())) - .Returns(Task.FromResult(fakeDynamicResult)); - - //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); - var actionResult = await orderController.GetOrdersAsync(); - - //Assert - Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); - } - - [Fact] - public async Task Get_order_success() - { - //Arrange - var fakeOrderId = 123; - var fakeDynamicResult = new Order(); - _orderQueriesMock.Setup(x => x.GetOrderAsync(It.IsAny())) - .Returns(Task.FromResult(fakeDynamicResult)); - - //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); - var actionResult = await orderController.GetOrderAsync(fakeOrderId) as OkObjectResult; - - //Assert - Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK); - } - - [Fact] - public async Task Get_cardTypes_success() - { - //Arrange - var fakeDynamicResult = Enumerable.Empty(); - _orderQueriesMock.Setup(x => x.GetCardTypesAsync()) - .Returns(Task.FromResult(fakeDynamicResult)); - - //Act - var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); - var actionResult = await orderController.GetCardTypesAsync(); - - //Assert - Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); - } + //Arrange + var fakeOrderId = 123; + var fakeDynamicResult = new Order(); + _orderQueriesMock.Setup(x => x.GetOrderAsync(It.IsAny())) + .Returns(Task.FromResult(fakeDynamicResult)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.GetOrderAsync(fakeOrderId) as OkObjectResult; + + //Assert + Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK); + } + + [Fact] + public async Task Get_cardTypes_success() + { + //Arrange + var fakeDynamicResult = Enumerable.Empty(); + _orderQueriesMock.Setup(x => x.GetCardTypesAsync()) + .Returns(Task.FromResult(fakeDynamicResult)); + + //Act + var orderController = new OrdersController(_mediatorMock.Object, _orderQueriesMock.Object, _identityServiceMock.Object, _loggerMock.Object); + var actionResult = await orderController.GetCardTypesAsync(); + + //Assert + Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK); } } diff --git a/src/Services/Ordering/Ordering.UnitTests/Builders.cs b/src/Services/Ordering/Ordering.UnitTests/Builders.cs index 2e9415274..8ecd66cbd 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Builders.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Builders.cs @@ -1,48 +1,46 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using System; +namespace UnitTest.Ordering; -namespace UnitTest.Ordering +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; + +public class AddressBuilder { - public class AddressBuilder + public Address Build() { - public Address Build() - { - return new Address("street", "city", "state", "country", "zipcode"); - } + return new Address("street", "city", "state", "country", "zipcode"); } +} - public class OrderBuilder - { - private readonly Order order; +public class OrderBuilder +{ + private readonly Order order; - public OrderBuilder(Address address) - { - order = new Order( - "userId", - "fakeName", - address, - cardTypeId: 5, - cardNumber: "12", - cardSecurityNumber: "123", - cardHolderName: "name", - cardExpiration: DateTime.UtcNow); - } + public OrderBuilder(Address address) + { + order = new Order( + "userId", + "fakeName", + address, + cardTypeId: 5, + cardNumber: "12", + cardSecurityNumber: "123", + cardHolderName: "name", + cardExpiration: DateTime.UtcNow); + } - public OrderBuilder AddOne( - int productId, - string productName, - decimal unitPrice, - decimal discount, - string pictureUrl, - int units = 1) - { - order.AddOrderItem(productId, productName, unitPrice, discount, pictureUrl, units); - return this; - } + public OrderBuilder AddOne( + int productId, + string productName, + decimal unitPrice, + decimal discount, + string pictureUrl, + int units = 1) + { + order.AddOrderItem(productId, productName, unitPrice, discount, pictureUrl, units); + return this; + } - public Order Build() - { - return order; - } + public Order Build() + { + return order; } } diff --git a/src/Services/Ordering/Ordering.UnitTests/Domain/BuyerAggregateTest.cs b/src/Services/Ordering/Ordering.UnitTests/Domain/BuyerAggregateTest.cs index 7f3832059..e8132a15f 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Domain/BuyerAggregateTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Domain/BuyerAggregateTest.cs @@ -1,9 +1,4 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; -using Ordering.Domain.Exceptions; -using System; -using Xunit; - -public class BuyerAggregateTest +public class BuyerAggregateTest { public BuyerAggregateTest() { } diff --git a/src/Services/Ordering/Ordering.UnitTests/Domain/OrderAggregateTest.cs b/src/Services/Ordering/Ordering.UnitTests/Domain/OrderAggregateTest.cs index 105d64079..676c05fd8 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Domain/OrderAggregateTest.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Domain/OrderAggregateTest.cs @@ -1,10 +1,6 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; -using Ordering.Domain.Events; -using Ordering.Domain.Exceptions; -using System; -using UnitTest.Ordering; -using Xunit; +namespace Ordering.UnitTests.Domain; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; public class OrderAggregateTest { public OrderAggregateTest() diff --git a/src/Services/Ordering/Ordering.UnitTests/Domain/SeedWork/ValueObjectTests.cs b/src/Services/Ordering/Ordering.UnitTests/Domain/SeedWork/ValueObjectTests.cs index bbfa340d2..d4811062f 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Domain/SeedWork/ValueObjectTests.cs +++ b/src/Services/Ordering/Ordering.UnitTests/Domain/SeedWork/ValueObjectTests.cs @@ -1,189 +1,182 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; -using System; -using System.Collections.Generic; -using System.Linq; -using Xunit; +namespace Ordering.UnitTests.Domain.SeedWork; -namespace Ordering.UnitTests.Domain.SeedWork +public class ValueObjectTests { - public class ValueObjectTests - { - public ValueObjectTests() - { } + public ValueObjectTests() + { } - [Theory] - [MemberData(nameof(EqualValueObjects))] - public void Equals_EqualValueObjects_ReturnsTrue(ValueObject instanceA, ValueObject instanceB, string reason) - { - // Act - var result = EqualityComparer.Default.Equals(instanceA, instanceB); + [Theory] + [MemberData(nameof(EqualValueObjects))] + public void Equals_EqualValueObjects_ReturnsTrue(ValueObject instanceA, ValueObject instanceB, string reason) + { + // Act + var result = EqualityComparer.Default.Equals(instanceA, instanceB); - // Assert - Assert.True(result, reason); - } + // Assert + Assert.True(result, reason); + } - [Theory] - [MemberData(nameof(NonEqualValueObjects))] - public void Equals_NonEqualValueObjects_ReturnsFalse(ValueObject instanceA, ValueObject instanceB, string reason) - { - // Act - var result = EqualityComparer.Default.Equals(instanceA, instanceB); + [Theory] + [MemberData(nameof(NonEqualValueObjects))] + public void Equals_NonEqualValueObjects_ReturnsFalse(ValueObject instanceA, ValueObject instanceB, string reason) + { + // Act + var result = EqualityComparer.Default.Equals(instanceA, instanceB); - // Assert - Assert.False(result, reason); - } + // Assert + Assert.False(result, reason); + } - private static readonly ValueObject APrettyValueObject = new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")); + private static readonly ValueObject APrettyValueObject = new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")); - public static readonly TheoryData EqualValueObjects = new TheoryData + public static readonly TheoryData EqualValueObjects = new TheoryData + { { - { - null, - null, - "they should be equal because they are both null" - }, - { - APrettyValueObject, - APrettyValueObject, - "they should be equal because they are the same object" - }, - { - new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")), - new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")), - "they should be equal because they have equal members" - }, - { - new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3"), notAnEqualityComponent: "xpto"), - new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3"), notAnEqualityComponent: "xpto2"), - "they should be equal because all equality components are equal, even though an additional member was set" - }, - { - new ValueObjectB(1, "2", 1, 2, 3 ), - new ValueObjectB(1, "2", 1, 2, 3 ), - "they should be equal because all equality components are equal, including the 'C' list" - } - }; + null, + null, + "they should be equal because they are both null" + }, + { + APrettyValueObject, + APrettyValueObject, + "they should be equal because they are the same object" + }, + { + new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")), + new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3")), + "they should be equal because they have equal members" + }, + { + new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3"), notAnEqualityComponent: "xpto"), + new ValueObjectA(1, "2", Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), new ComplexObject(2, "3"), notAnEqualityComponent: "xpto2"), + "they should be equal because all equality components are equal, even though an additional member was set" + }, + { + new ValueObjectB(1, "2", 1, 2, 3 ), + new ValueObjectB(1, "2", 1, 2, 3 ), + "they should be equal because all equality components are equal, including the 'C' list" + } + }; - public static readonly TheoryData NonEqualValueObjects = new TheoryData + public static readonly TheoryData NonEqualValueObjects = new TheoryData + { { - { - new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), - new ValueObjectA(a: 2, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), - "they should not be equal because the 'A' member on ValueObjectA is different among them" - }, - { - new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), - new ValueObjectA(a: 1, b: null, c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), - "they should not be equal because the 'B' member on ValueObjectA is different among them" - }, - { - new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 2, b: "3")), - new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 3, b: "3")), - "they should not be equal because the 'A' member on ValueObjectA's 'D' member is different among them" - }, - { - new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 2, b: "3")), - new ValueObjectB(a: 1, b: "2"), - "they should not be equal because they are not of the same type" - }, - { - new ValueObjectB(1, "2", 1, 2, 3 ), - new ValueObjectB(1, "2", 1, 2, 3, 4 ), - "they should be not be equal because the 'C' list contains one additional value" - }, - { - new ValueObjectB(1, "2", 1, 2, 3, 5 ), - new ValueObjectB(1, "2", 1, 2, 3 ), - "they should be not be equal because the 'C' list contains one additional value" - }, - { - new ValueObjectB(1, "2", 1, 2, 3, 5 ), - new ValueObjectB(1, "2", 1, 2, 3, 4 ), - "they should be not be equal because the 'C' lists are not equal" - } + new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), + new ValueObjectA(a: 2, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), + "they should not be equal because the 'A' member on ValueObjectA is different among them" + }, + { + new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), + new ValueObjectA(a: 1, b: null, c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(2, "3")), + "they should not be equal because the 'B' member on ValueObjectA is different among them" + }, + { + new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 2, b: "3")), + new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 3, b: "3")), + "they should not be equal because the 'A' member on ValueObjectA's 'D' member is different among them" + }, + { + new ValueObjectA(a: 1, b: "2", c: Guid.Parse("97ea43f0-6fef-4fb7-8c67-9114a7ff6ec0"), d: new ComplexObject(a: 2, b: "3")), + new ValueObjectB(a: 1, b: "2"), + "they should not be equal because they are not of the same type" + }, + { + new ValueObjectB(1, "2", 1, 2, 3 ), + new ValueObjectB(1, "2", 1, 2, 3, 4 ), + "they should be not be equal because the 'C' list contains one additional value" + }, + { + new ValueObjectB(1, "2", 1, 2, 3, 5 ), + new ValueObjectB(1, "2", 1, 2, 3 ), + "they should be not be equal because the 'C' list contains one additional value" + }, + { + new ValueObjectB(1, "2", 1, 2, 3, 5 ), + new ValueObjectB(1, "2", 1, 2, 3, 4 ), + "they should be not be equal because the 'C' lists are not equal" + } - }; + }; - private class ValueObjectA : ValueObject + private class ValueObjectA : ValueObject + { + public ValueObjectA(int a, string b, Guid c, ComplexObject d, string notAnEqualityComponent = null) { - public ValueObjectA(int a, string b, Guid c, ComplexObject d, string notAnEqualityComponent = null) - { - A = a; - B = b; - C = c; - D = d; - NotAnEqualityComponent = notAnEqualityComponent; - } + A = a; + B = b; + C = c; + D = d; + NotAnEqualityComponent = notAnEqualityComponent; + } - public int A { get; } - public string B { get; } - public Guid C { get; } - public ComplexObject D { get; } - public string NotAnEqualityComponent { get; } + public int A { get; } + public string B { get; } + public Guid C { get; } + public ComplexObject D { get; } + public string NotAnEqualityComponent { get; } - protected override IEnumerable GetEqualityComponents() - { - yield return A; - yield return B; - yield return C; - yield return D; - } + protected override IEnumerable GetEqualityComponents() + { + yield return A; + yield return B; + yield return C; + yield return D; } + } - private class ValueObjectB : ValueObject + private class ValueObjectB : ValueObject + { + public ValueObjectB(int a, string b, params int[] c) { - public ValueObjectB(int a, string b, params int[] c) - { - A = a; - B = b; - C = c.ToList(); - } + A = a; + B = b; + C = c.ToList(); + } - public int A { get; } - public string B { get; } + public int A { get; } + public string B { get; } - public List C { get; } + public List C { get; } - protected override IEnumerable GetEqualityComponents() - { - yield return A; - yield return B; + protected override IEnumerable GetEqualityComponents() + { + yield return A; + yield return B; - foreach (var c in C) - { - yield return c; - } + foreach (var c in C) + { + yield return c; } } + } - private class ComplexObject : IEquatable + private class ComplexObject : IEquatable + { + public ComplexObject(int a, string b) { - public ComplexObject(int a, string b) - { - A = a; - B = b; - } + A = a; + B = b; + } - public int A { get; set; } + public int A { get; set; } - public string B { get; set; } + public string B { get; set; } - public override bool Equals(object obj) - { - return Equals(obj as ComplexObject); - } + public override bool Equals(object obj) + { + return Equals(obj as ComplexObject); + } - public bool Equals(ComplexObject other) - { - return other != null && - A == other.A && - B == other.B; - } + public bool Equals(ComplexObject other) + { + return other != null && + A == other.A && + B == other.B; + } - public override int GetHashCode() - { - return HashCode.Combine(A, B); - } + public override int GetHashCode() + { + return HashCode.Combine(A, B); } } } diff --git a/src/Services/Ordering/Ordering.UnitTests/GlobalUsings.cs b/src/Services/Ordering/Ordering.UnitTests/GlobalUsings.cs new file mode 100644 index 000000000..9fa77aef7 --- /dev/null +++ b/src/Services/Ordering/Ordering.UnitTests/GlobalUsings.cs @@ -0,0 +1,23 @@ +global using System; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; +global using MediatR; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +global using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +global using Microsoft.Extensions.Logging; +global using Moq; +global using System.Collections.Generic; +global using System.Threading.Tasks; +global using Xunit; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +global using System.Threading; +global using global::Ordering.API.Application.IntegrationEvents; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; +global using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; +global using System.Linq; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Exceptions; +global using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; +global using UnitTest.Ordering; diff --git a/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj b/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj index 53093af39..45666c1f6 100644 --- a/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj +++ b/src/Services/Ordering/Ordering.UnitTests/Ordering.UnitTests.csproj @@ -9,7 +9,7 @@ - + all diff --git a/src/Services/Payment/Payment.API/GlobalUsings.cs b/src/Services/Payment/Payment.API/GlobalUsings.cs new file mode 100644 index 000000000..13c1bf6e2 --- /dev/null +++ b/src/Services/Payment/Payment.API/GlobalUsings.cs @@ -0,0 +1,21 @@ +global using Autofac.Extensions.DependencyInjection; +global using Autofac; +global using Azure.Core; +global using Azure.Identity; +global using HealthChecks.UI.Client; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.AspNetCore; +global using Microsoft.Azure.ServiceBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +global using Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.Events; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Options; +global using Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.EventHandling; +global using Microsoft.eShopOnContainers.Payment.API; +global using RabbitMQ.Client; +global using Serilog.Context; +global using Serilog; \ No newline at end of file diff --git a/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs b/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs index 5921529b2..ce9aa4d73 100644 --- a/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs +++ b/src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs @@ -1,61 +1,52 @@ -namespace Payment.API.IntegrationEvents.EventHandling +namespace Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.EventHandling; + +public class OrderStatusChangedToStockConfirmedIntegrationEventHandler : + IIntegrationEventHandler { - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Options; - using Payment.API.IntegrationEvents.Events; - using Serilog.Context; - using System.Threading.Tasks; - - public class OrderStatusChangedToStockConfirmedIntegrationEventHandler : - IIntegrationEventHandler + private readonly IEventBus _eventBus; + private readonly PaymentSettings _settings; + private readonly ILogger _logger; + + public OrderStatusChangedToStockConfirmedIntegrationEventHandler( + IEventBus eventBus, + IOptionsSnapshot settings, + ILogger logger) { - private readonly IEventBus _eventBus; - private readonly PaymentSettings _settings; - private readonly ILogger _logger; - - public OrderStatusChangedToStockConfirmedIntegrationEventHandler( - IEventBus eventBus, - IOptionsSnapshot settings, - ILogger logger) - { - _eventBus = eventBus; - _settings = settings.Value; - _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + _eventBus = eventBus; + _settings = settings.Value; + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); - _logger.LogTrace("PaymentSettings: {@PaymentSettings}", _settings); - } + _logger.LogTrace("PaymentSettings: {@PaymentSettings}", _settings); + } - public async Task Handle(OrderStatusChangedToStockConfirmedIntegrationEvent @event) + public async Task Handle(OrderStatusChangedToStockConfirmedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - IntegrationEvent orderPaymentIntegrationEvent; + IntegrationEvent orderPaymentIntegrationEvent; - //Business feature comment: - // When OrderStatusChangedToStockConfirmed Integration Event is handled. - // Here we're simulating that we'd be performing the payment against any payment gateway - // Instead of a real payment we just take the env. var to simulate the payment - // The payment can be successful or it can fail + //Business feature comment: + // When OrderStatusChangedToStockConfirmed Integration Event is handled. + // Here we're simulating that we'd be performing the payment against any payment gateway + // Instead of a real payment we just take the env. var to simulate the payment + // The payment can be successful or it can fail - if (_settings.PaymentSucceeded) - { - orderPaymentIntegrationEvent = new OrderPaymentSucceededIntegrationEvent(@event.OrderId); - } - else - { - orderPaymentIntegrationEvent = new OrderPaymentFailedIntegrationEvent(@event.OrderId); - } + if (_settings.PaymentSucceeded) + { + orderPaymentIntegrationEvent = new OrderPaymentSucceededIntegrationEvent(@event.OrderId); + } + else + { + orderPaymentIntegrationEvent = new OrderPaymentFailedIntegrationEvent(@event.OrderId); + } - _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", orderPaymentIntegrationEvent.Id, Program.AppName, orderPaymentIntegrationEvent); + _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", orderPaymentIntegrationEvent.Id, Program.AppName, orderPaymentIntegrationEvent); - _eventBus.Publish(orderPaymentIntegrationEvent); + _eventBus.Publish(orderPaymentIntegrationEvent); - await Task.CompletedTask; - } + await Task.CompletedTask; } } -} \ No newline at end of file +} diff --git a/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent.cs b/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent.cs index 953e5dbaa..23c713059 100644 --- a/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent.cs +++ b/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent.cs @@ -1,11 +1,8 @@ -namespace Payment.API.IntegrationEvents.Events -{ - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.Events; - public record OrderPaymentFailedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderPaymentFailedIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId; - } -} \ No newline at end of file + public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId; +} diff --git a/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs b/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs index 317ef8554..7e6e26600 100644 --- a/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs +++ b/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentSucceededIntegrationEvent.cs @@ -1,11 +1,8 @@ -namespace Payment.API.IntegrationEvents.Events -{ - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.Events; - public record OrderPaymentSucceededIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } +public record OrderPaymentSucceededIntegrationEvent : IntegrationEvent +{ + public int OrderId { get; } - public OrderPaymentSucceededIntegrationEvent(int orderId) => OrderId = orderId; - } -} \ No newline at end of file + public OrderPaymentSucceededIntegrationEvent(int orderId) => OrderId = orderId; +} diff --git a/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs b/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs index caa3295d5..adac9f2bd 100644 --- a/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs +++ b/src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs @@ -1,12 +1,9 @@ -namespace Payment.API.IntegrationEvents.Events +namespace Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.Events; + +public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent { - using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; + public int OrderId { get; } - public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - - public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId) - => OrderId = orderId; - } -} \ No newline at end of file + public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId) + => OrderId = orderId; +} diff --git a/src/Services/Payment/Payment.API/PaymentSettings.cs b/src/Services/Payment/Payment.API/PaymentSettings.cs index fdb8a29cc..ad2501dec 100644 --- a/src/Services/Payment/Payment.API/PaymentSettings.cs +++ b/src/Services/Payment/Payment.API/PaymentSettings.cs @@ -1,8 +1,8 @@ -namespace Payment.API +namespace Microsoft.eShopOnContainers.Payment.API; + +public class PaymentSettings { - public class PaymentSettings - { - public bool PaymentSucceeded { get; set; } - public string EventBusConnection { get; set; } - } + public bool PaymentSucceeded { get; set; } + public string EventBusConnection { get; set; } } + diff --git a/src/Services/Payment/Payment.API/Program.cs b/src/Services/Payment/Payment.API/Program.cs index 268099957..a369170c1 100644 --- a/src/Services/Payment/Payment.API/Program.cs +++ b/src/Services/Payment/Payment.API/Program.cs @@ -1,16 +1,4 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Payment.API; -using Serilog; -using System; -using System.IO; -using Azure.Identity; -using Azure.Core; - -var configuration = GetConfiguration(); +var configuration = GetConfiguration(); Log.Logger = CreateSerilogLogger(configuration); diff --git a/src/Services/Payment/Payment.API/Startup.cs b/src/Services/Payment/Payment.API/Startup.cs index a39d3b97d..cb4f3fd7a 100644 --- a/src/Services/Payment/Payment.API/Startup.cs +++ b/src/Services/Payment/Payment.API/Startup.cs @@ -1,198 +1,178 @@ -using Autofac; -using Autofac.Extensions.DependencyInjection; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.Azure.ServiceBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; -using Payment.API.IntegrationEvents.EventHandling; -using Payment.API.IntegrationEvents.Events; -using RabbitMQ.Client; -using System; - -namespace Payment.API +namespace Microsoft.eShopOnContainers.Payment.API; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public IServiceProvider ConfigureServices(IServiceCollection services) - { - services.AddCustomHealthCheck(Configuration); - services.Configure(Configuration); + // This method gets called by the runtime. Use this method to add services to the container. + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddCustomHealthCheck(Configuration); + services.Configure(Configuration); - RegisterAppInsights(services); + RegisterAppInsights(services); - if (Configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusConnectionString = Configuration["EventBusConnection"]; - var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - var subscriptionClientName = Configuration["SubscriptionClientName"]; - - return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); - }); - } - else + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => { - services.AddSingleton(sp => - { - var logger = sp.GetRequiredService>(); - var factory = new ConnectionFactory() - { - HostName = Configuration["EventBusConnection"], - DispatchConsumersAsync = true - }; - - if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) - { - factory.UserName = Configuration["EventBusUserName"]; - } - - if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) - { - factory.Password = Configuration["EventBusPassword"]; - } - - var retryCount = 5; - if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(Configuration["EventBusRetryCount"]); - } - - return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); - }); - } - - RegisterEventBus(services); - - var container = new ContainerBuilder(); - container.Populate(services); - return new AutofacServiceProvider(container.Build()); - } + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); + var subscriptionClientName = Configuration["SubscriptionClientName"]; - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); + }); + } + else { - //loggerFactory.AddAzureWebAppDiagnostics(); - //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); - - var pathBase = Configuration["PATH_BASE"]; - if (!string.IsNullOrEmpty(pathBase)) + services.AddSingleton(sp => { - app.UsePathBase(pathBase); - } + var logger = sp.GetRequiredService>(); + var factory = new ConnectionFactory() + { + HostName = Configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; - ConfigureEventBus(app); + if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) + { + factory.UserName = Configuration["EventBusUserName"]; + } - app.UseRouting(); - app.UseEndpoints(endpoints => - { - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() + if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions + factory.Password = Configuration["EventBusPassword"]; + } + + var retryCount = 5; + if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) { - Predicate = r => r.Name.Contains("self") - }); + retryCount = int.Parse(Configuration["EventBusRetryCount"]); + } + + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); }); } - private void RegisterAppInsights(IServiceCollection services) + RegisterEventBus(services); + + var container = new ContainerBuilder(); + container.Populate(services); + return new AutofacServiceProvider(container.Build()); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + + var pathBase = Configuration["PATH_BASE"]; + if (!string.IsNullOrEmpty(pathBase)) { - services.AddApplicationInsightsTelemetry(Configuration); - services.AddApplicationInsightsKubernetesEnricher(); + app.UsePathBase(pathBase); } - private void RegisterEventBus(IServiceCollection services) + ConfigureEventBus(app); + + app.UseRouting(); + app.UseEndpoints(endpoints => { - if (Configuration.GetValue("AzureServiceBusEnabled")) + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope); - }); - } - else + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions { - services.AddSingleton(sp => - { - var subscriptionClientName = Configuration["SubscriptionClientName"]; - var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - var retryCount = 5; - if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(Configuration["EventBusRetryCount"]); - } - - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); - }); - } - - services.AddTransient(); - services.AddSingleton(); - } + Predicate = r => r.Name.Contains("self") + }); + }); + } + + private void RegisterAppInsights(IServiceCollection services) + { + services.AddApplicationInsightsTelemetry(Configuration); + services.AddApplicationInsightsKubernetesEnricher(); + } + + private void RegisterEventBus(IServiceCollection services) + { + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); - private void ConfigureEventBus(IApplicationBuilder app) + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, iLifetimeScope); + }); + } + else { - var eventBus = app.ApplicationServices.GetRequiredService(); - eventBus.Subscribe(); + services.AddSingleton(sp => + { + var subscriptionClientName = Configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(Configuration["EventBusRetryCount"]); + } + + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); } + + services.AddTransient(); + services.AddSingleton(); } - public static class CustomExtensionMethods + private void ConfigureEventBus(IApplicationBuilder app) { - public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) - { - var hcBuilder = services.AddHealthChecks(); + var eventBus = app.ApplicationServices.GetRequiredService(); + eventBus.Subscribe(); + } +} + +public static class CustomExtensionMethods +{ + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var hcBuilder = services.AddHealthChecks(); - hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); + hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); - if (configuration.GetValue("AzureServiceBusEnabled")) - { - hcBuilder - .AddAzureServiceBusTopic( - configuration["EventBusConnection"], - topicName: "eshop_event_bus", - name: "payment-servicebus-check", - tags: new string[] { "servicebus" }); - } - else - { - hcBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", - name: "payment-rabbitmqbus-check", - tags: new string[] { "rabbitmqbus" }); - } - - return services; + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "payment-servicebus-check", + tags: new string[] { "servicebus" }); } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "payment-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); + } + + return services; } -} \ No newline at end of file +} diff --git a/src/Services/Webhooks/Webhooks.API/Controllers/HomeController.cs b/src/Services/Webhooks/Webhooks.API/Controllers/HomeController.cs index 01c33916f..2a7951ec3 100644 --- a/src/Services/Webhooks/Webhooks.API/Controllers/HomeController.cs +++ b/src/Services/Webhooks/Webhooks.API/Controllers/HomeController.cs @@ -1,15 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +namespace Webhooks.API.Controllers; -namespace Webhooks.API.Controllers +public class HomeController : Controller { - - public class HomeController : Controller + // GET: // + public IActionResult Index() { - // GET: // - public IActionResult Index() - { - return new RedirectResult("~/swagger"); - } - + return new RedirectResult("~/swagger"); } + } diff --git a/src/Services/Webhooks/Webhooks.API/Controllers/WebhookSubscriptionRequest.cs b/src/Services/Webhooks/Webhooks.API/Controllers/WebhookSubscriptionRequest.cs index 2a4fa5cfd..069cd22af 100644 --- a/src/Services/Webhooks/Webhooks.API/Controllers/WebhookSubscriptionRequest.cs +++ b/src/Services/Webhooks/Webhooks.API/Controllers/WebhookSubscriptionRequest.cs @@ -1,35 +1,29 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Webhooks.API.Model; +namespace Webhooks.API.Controllers; -namespace Webhooks.API.Controllers +public class WebhookSubscriptionRequest : IValidatableObject { - public class WebhookSubscriptionRequest : IValidatableObject - { - public string Url { get; set; } - public string Token { get; set; } - public string Event { get; set; } - public string GrantUrl { get; set; } + public string Url { get; set; } + public string Token { get; set; } + public string Event { get; set; } + public string GrantUrl { get; set; } - public IEnumerable Validate(ValidationContext validationContext) + public IEnumerable Validate(ValidationContext validationContext) + { + if (!Uri.IsWellFormedUriString(GrantUrl, UriKind.Absolute)) { - if (!Uri.IsWellFormedUriString(GrantUrl, UriKind.Absolute)) - { - yield return new ValidationResult("GrantUrl is not valid", new[] { nameof(GrantUrl) }); - } - - if (!Uri.IsWellFormedUriString(Url, UriKind.Absolute)) - { - yield return new ValidationResult("Url is not valid", new[] { nameof(Url) }); - } + yield return new ValidationResult("GrantUrl is not valid", new[] { nameof(GrantUrl) }); + } - var isOk = Enum.TryParse(Event, ignoreCase: true, result: out WebhookType whtype); - if (!isOk) - { - yield return new ValidationResult($"{Event} is invalid event name", new[] { nameof(Event) }); - } + if (!Uri.IsWellFormedUriString(Url, UriKind.Absolute)) + { + yield return new ValidationResult("Url is not valid", new[] { nameof(Url) }); } + var isOk = Enum.TryParse(Event, ignoreCase: true, result: out WebhookType whtype); + if (!isOk) + { + yield return new ValidationResult($"{Event} is invalid event name", new[] { nameof(Event) }); + } } + } diff --git a/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs b/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs index 075b220dc..ed5eed705 100644 --- a/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs +++ b/src/Services/Webhooks/Webhooks.API/Controllers/WebhooksController.cs @@ -1,115 +1,100 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using Webhooks.API.Infrastructure; -using Webhooks.API.Model; -using Webhooks.API.Services; +namespace Webhooks.API.Controllers; -namespace Webhooks.API.Controllers +[Route("api/v1/[controller]")] +[ApiController] +public class WebhooksController : ControllerBase { - [Route("api/v1/[controller]")] - [ApiController] - public class WebhooksController : ControllerBase + private readonly WebhooksContext _dbContext; + private readonly IIdentityService _identityService; + private readonly IGrantUrlTesterService _grantUrlTester; + + public WebhooksController(WebhooksContext dbContext, IIdentityService identityService, IGrantUrlTesterService grantUrlTester) { - private readonly WebhooksContext _dbContext; - private readonly IIdentityService _identityService; - private readonly IGrantUrlTesterService _grantUrlTester; + _dbContext = dbContext; + _identityService = identityService; + _grantUrlTester = grantUrlTester; + } - public WebhooksController(WebhooksContext dbContext, IIdentityService identityService, IGrantUrlTesterService grantUrlTester) - { - _dbContext = dbContext; - _identityService = identityService; - _grantUrlTester = grantUrlTester; - } + [Authorize] + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task ListByUser() + { + var userId = _identityService.GetUserIdentity(); + var data = await _dbContext.Subscriptions.Where(s => s.UserId == userId).ToListAsync(); + return Ok(data); + } - [Authorize] - [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task ListByUser() + [Authorize] + [HttpGet("{id:int}")] + [ProducesResponseType(typeof(WebhookSubscription), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public async Task GetByUserAndId(int id) + { + var userId = _identityService.GetUserIdentity(); + var subscription = await _dbContext.Subscriptions.SingleOrDefaultAsync(s => s.Id == id && s.UserId == userId); + if (subscription != null) { - var userId = _identityService.GetUserIdentity(); - var data = await _dbContext.Subscriptions.Where(s => s.UserId == userId).ToListAsync(); - return Ok(data); + return Ok(subscription); } + return NotFound($"Subscriptions {id} not found"); + } - [Authorize] - [HttpGet("{id:int}")] - [ProducesResponseType(typeof(WebhookSubscription), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task GetByUserAndId(int id) + [Authorize] + [HttpPost] + [ProducesResponseType((int)HttpStatusCode.Created)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType(418)] + public async Task SubscribeWebhook(WebhookSubscriptionRequest request) + { + if (!ModelState.IsValid) { - var userId = _identityService.GetUserIdentity(); - var subscription = await _dbContext.Subscriptions.SingleOrDefaultAsync(s => s.Id == id && s.UserId == userId); - if (subscription != null) - { - return Ok(subscription); - } - return NotFound($"Subscriptions {id} not found"); + return ValidationProblem(ModelState); } - [Authorize] - [HttpPost] - [ProducesResponseType((int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType(418)] - public async Task SubscribeWebhook(WebhookSubscriptionRequest request) - { - if (!ModelState.IsValid) - { - return ValidationProblem(ModelState); - } + var userId = _identityService.GetUserIdentity(); - var userId = _identityService.GetUserIdentity(); + var grantOk = await _grantUrlTester.TestGrantUrl(request.Url, request.GrantUrl, request.Token ?? string.Empty); - var grantOk = await _grantUrlTester.TestGrantUrl(request.Url, request.GrantUrl, request.Token ?? string.Empty); - - if (grantOk) - { - var subscription = new WebhookSubscription() - { - Date = DateTime.UtcNow, - DestUrl = request.Url, - Token = request.Token, - Type = Enum.Parse(request.Event, ignoreCase: true), - UserId = _identityService.GetUserIdentity() - }; - - _dbContext.Add(subscription); - await _dbContext.SaveChangesAsync(); - return CreatedAtAction("GetByUserAndId", new { id = subscription.Id }, subscription); - } - else + if (grantOk) + { + var subscription = new WebhookSubscription() { - return StatusCode(418, "Grant url can't be validated"); - } + Date = DateTime.UtcNow, + DestUrl = request.Url, + Token = request.Token, + Type = Enum.Parse(request.Event, ignoreCase: true), + UserId = _identityService.GetUserIdentity() + }; + + _dbContext.Add(subscription); + await _dbContext.SaveChangesAsync(); + return CreatedAtAction("GetByUserAndId", new { id = subscription.Id }, subscription); } - - [Authorize] - [HttpDelete("{id:int}")] - [ProducesResponseType((int)HttpStatusCode.Accepted)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - public async Task UnsubscribeWebhook(int id) + else { - var userId = _identityService.GetUserIdentity(); - var subscription = await _dbContext.Subscriptions.SingleOrDefaultAsync(s => s.Id == id && s.UserId == userId); + return StatusCode(418, "Grant url can't be validated"); + } + } - if (subscription != null) - { - _dbContext.Remove(subscription); - await _dbContext.SaveChangesAsync(); - return Accepted(); - } + [Authorize] + [HttpDelete("{id:int}")] + [ProducesResponseType((int)HttpStatusCode.Accepted)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public async Task UnsubscribeWebhook(int id) + { + var userId = _identityService.GetUserIdentity(); + var subscription = await _dbContext.Subscriptions.SingleOrDefaultAsync(s => s.Id == id && s.UserId == userId); - return NotFound($"Subscriptions {id} not found"); + if (subscription != null) + { + _dbContext.Remove(subscription); + await _dbContext.SaveChangesAsync(); + return Accepted(); } + return NotFound($"Subscriptions {id} not found"); } - } diff --git a/src/Services/Webhooks/Webhooks.API/Exceptions/WebhooksDomainException.cs b/src/Services/Webhooks/Webhooks.API/Exceptions/WebhooksDomainException.cs index 6307a6296..94f28a525 100644 --- a/src/Services/Webhooks/Webhooks.API/Exceptions/WebhooksDomainException.cs +++ b/src/Services/Webhooks/Webhooks.API/Exceptions/WebhooksDomainException.cs @@ -1,8 +1,5 @@ -using System; +namespace Webhooks.API.Exceptions; -namespace Webhooks.API.Exceptions +public class WebhooksDomainException : Exception { - public class WebhooksDomainException : Exception - { - } } diff --git a/src/Services/Webhooks/Webhooks.API/GlobalUsings.cs b/src/Services/Webhooks/Webhooks.API/GlobalUsings.cs new file mode 100644 index 000000000..5481264c7 --- /dev/null +++ b/src/Services/Webhooks/Webhooks.API/GlobalUsings.cs @@ -0,0 +1,50 @@ +global using Autofac.Extensions.DependencyInjection; +global using Autofac; +global using Devspaces.Support; +global using HealthChecks.UI.Client; +global using Microsoft.AspNetCore.Authentication.JwtBearer; +global using Microsoft.AspNetCore.Authorization; +global using Microsoft.AspNetCore.Builder; +global using Microsoft.AspNetCore.Diagnostics.HealthChecks; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore; +global using Microsoft.Azure.ServiceBus; +global using Microsoft.EntityFrameworkCore.Design; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; +global using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; +global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Diagnostics.HealthChecks; +global using Microsoft.Extensions.Hosting; +global using Microsoft.Extensions.Logging; +global using Microsoft.OpenApi.Models; +global using RabbitMQ.Client; +global using Swashbuckle.AspNetCore.SwaggerGen; +global using System.Collections.Generic; +global using System.ComponentModel.DataAnnotations; +global using System.Data.Common; +global using System.IdentityModel.Tokens.Jwt; +global using System.Linq; +global using System.Net.Http; +global using System.Net; +global using System.Reflection; +global using System.Text.Json; +global using System.Text; +global using System.Threading.Tasks; +global using System.Threading; +global using System; +global using Webhooks.API.Exceptions; +global using Webhooks.API.Infrastructure.ActionResult; +global using Webhooks.API.Infrastructure; +global using Webhooks.API.IntegrationEvents; +global using Webhooks.API.Model; +global using Webhooks.API.Services; +global using Webhooks.API; diff --git a/src/Services/Webhooks/Webhooks.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs b/src/Services/Webhooks/Webhooks.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs index 9876cc68b..d71e8b55f 100644 --- a/src/Services/Webhooks/Webhooks.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs +++ b/src/Services/Webhooks/Webhooks.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs @@ -1,13 +1,9 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; +namespace Webhooks.API.Infrastructure.ActionResult; -namespace Webhooks.API.Infrastructure.ActionResult +class InternalServerErrorObjectResult : ObjectResult { - class InternalServerErrorObjectResult : ObjectResult + public InternalServerErrorObjectResult(object error) : base(error) { - public InternalServerErrorObjectResult(object error) : base(error) - { - StatusCode = StatusCodes.Status500InternalServerError; - } + StatusCode = StatusCodes.Status500InternalServerError; } } diff --git a/src/Services/Webhooks/Webhooks.API/Infrastructure/AuthorizeCheckOperationFilter.cs b/src/Services/Webhooks/Webhooks.API/Infrastructure/AuthorizeCheckOperationFilter.cs index 487a59981..ea9667263 100644 --- a/src/Services/Webhooks/Webhooks.API/Infrastructure/AuthorizeCheckOperationFilter.cs +++ b/src/Services/Webhooks/Webhooks.API/Infrastructure/AuthorizeCheckOperationFilter.cs @@ -1,36 +1,29 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.Linq; +namespace Webhooks.API.Infrastructure; -namespace Webhooks.API.Infrastructure +public class AuthorizeCheckOperationFilter : IOperationFilter { - public class AuthorizeCheckOperationFilter : IOperationFilter + public void Apply(OpenApiOperation operation, OperationFilterContext context) { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - // Check for authorize attribute - var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || - context.MethodInfo.GetCustomAttributes(true).OfType().Any(); + // Check for authorize attribute + var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || + context.MethodInfo.GetCustomAttributes(true).OfType().Any(); - if (!hasAuthorize) return; + if (!hasAuthorize) return; - operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); - operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); + operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); + operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); - var oAuthScheme = new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }; + var oAuthScheme = new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } + }; - operation.Security = new List + operation.Security = new List + { + new OpenApiSecurityRequirement { - new OpenApiSecurityRequirement - { - [ oAuthScheme ] = new [] { "webhooksapi" } - } - }; - } + [ oAuthScheme ] = new [] { "webhooksapi" } + } + }; } } diff --git a/src/Services/Webhooks/Webhooks.API/Infrastructure/HttpGlobalExceptionFilter.cs b/src/Services/Webhooks/Webhooks.API/Infrastructure/HttpGlobalExceptionFilter.cs index 7bb818879..a1ebf5c4c 100644 --- a/src/Services/Webhooks/Webhooks.API/Infrastructure/HttpGlobalExceptionFilter.cs +++ b/src/Services/Webhooks/Webhooks.API/Infrastructure/HttpGlobalExceptionFilter.cs @@ -1,69 +1,58 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System.Net; -using Webhooks.API.Exceptions; -using Webhooks.API.Infrastructure.ActionResult; +namespace Webhooks.API.Infrastructure; -namespace Webhooks.API.Infrastructure +public class HttpGlobalExceptionFilter : IExceptionFilter { - public class HttpGlobalExceptionFilter : IExceptionFilter + private readonly IWebHostEnvironment env; + private readonly ILogger logger; + + public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) { - private readonly IWebHostEnvironment env; - private readonly ILogger logger; + this.env = env; + this.logger = logger; + } - public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) - { - this.env = env; - this.logger = logger; - } + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); - public void OnException(ExceptionContext context) + if (context.Exception.GetType() == typeof(WebhooksDomainException)) { - logger.LogError(new EventId(context.Exception.HResult), - context.Exception, - context.Exception.Message); - - if (context.Exception.GetType() == typeof(WebhooksDomainException)) + var problemDetails = new ValidationProblemDetails() { - var problemDetails = new ValidationProblemDetails() - { - Instance = context.HttpContext.Request.Path, - Status = StatusCodes.Status400BadRequest, - Detail = "Please refer to the errors property for additional details." - }; + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." + }; - problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); + problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); - context.Result = new BadRequestObjectResult(problemDetails); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; - } - else + context.Result = new BadRequestObjectResult(problemDetails); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse { - var json = new JsonErrorResponse - { - Messages = new[] { "An error ocurred." } - }; - - if (env.IsDevelopment()) - { - json.DeveloperMeesage = context.Exception; - } + Messages = new[] { "An error ocurred." } + }; - context.Result = new InternalServerErrorObjectResult(json); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + if (env.IsDevelopment()) + { + json.DeveloperMeesage = context.Exception; } - context.ExceptionHandled = true; + + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } + context.ExceptionHandled = true; + } - private class JsonErrorResponse - { - public string[] Messages { get; set; } + private class JsonErrorResponse + { + public string[] Messages { get; set; } - public object DeveloperMeesage { get; set; } - } + public object DeveloperMeesage { get; set; } } } diff --git a/src/Services/Webhooks/Webhooks.API/Infrastructure/WebhooksContext.cs b/src/Services/Webhooks/Webhooks.API/Infrastructure/WebhooksContext.cs index f5b9fbf34..b98a27784 100644 --- a/src/Services/Webhooks/Webhooks.API/Infrastructure/WebhooksContext.cs +++ b/src/Services/Webhooks/Webhooks.API/Infrastructure/WebhooksContext.cs @@ -1,26 +1,21 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Webhooks.API.Model; +namespace Webhooks.API.Infrastructure; -namespace Webhooks.API.Infrastructure +public class WebhooksContext : DbContext { - public class WebhooksContext : DbContext - { - public WebhooksContext(DbContextOptions options) : base(options) - { - } - public DbSet Subscriptions { get; set; } + public WebhooksContext(DbContextOptions options) : base(options) + { } + public DbSet Subscriptions { get; set; } +} - public class WebhooksContextDesignFactory : IDesignTimeDbContextFactory +public class WebhooksContextDesignFactory : IDesignTimeDbContextFactory +{ + public WebhooksContext CreateDbContext(string[] args) { - public WebhooksContext CreateDbContext(string[] args) - { - var optionsBuilder = new DbContextOptionsBuilder() - .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;Integrated Security=true"); + var optionsBuilder = new DbContextOptionsBuilder() + .UseSqlServer("Server=.;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;Integrated Security=true"); - return new WebhooksContext(optionsBuilder.Options); - } + return new WebhooksContext(optionsBuilder.Options); } } diff --git a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEvent.cs b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEvent.cs index 0f14dd83e..76c458dc0 100644 --- a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEvent.cs +++ b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEvent.cs @@ -1,30 +1,26 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System.Collections.Generic; +namespace Webhooks.API.IntegrationEvents; -namespace Webhooks.API.IntegrationEvents +public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent - { - public int OrderId { get; } - public IEnumerable OrderStockItems { get; } + public int OrderId { get; } + public IEnumerable OrderStockItems { get; } - public OrderStatusChangedToPaidIntegrationEvent(int orderId, - IEnumerable orderStockItems) - { - OrderId = orderId; - OrderStockItems = orderStockItems; - } + public OrderStatusChangedToPaidIntegrationEvent(int orderId, + IEnumerable orderStockItems) + { + OrderId = orderId; + OrderStockItems = orderStockItems; } +} - public record OrderStockItem - { - public int ProductId { get; } - public int Units { get; } +public record OrderStockItem +{ + public int ProductId { get; } + public int Units { get; } - public OrderStockItem(int productId, int units) - { - ProductId = productId; - Units = units; - } + public OrderStockItem(int productId, int units) + { + ProductId = productId; + Units = units; } } diff --git a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEventHandler.cs b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEventHandler.cs index 338ffa7d3..f799fb1c7 100644 --- a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEventHandler.cs +++ b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToPaidIntegrationEventHandler.cs @@ -1,30 +1,22 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; -using Webhooks.API.Model; -using Webhooks.API.Services; +namespace Webhooks.API.IntegrationEvents; -namespace Webhooks.API.IntegrationEvents +public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler { - public class OrderStatusChangedToPaidIntegrationEventHandler : IIntegrationEventHandler + private readonly IWebhooksRetriever _retriever; + private readonly IWebhooksSender _sender; + private readonly ILogger _logger; + public OrderStatusChangedToPaidIntegrationEventHandler(IWebhooksRetriever retriever, IWebhooksSender sender, ILogger logger) { - private readonly IWebhooksRetriever _retriever; - private readonly IWebhooksSender _sender; - private readonly ILogger _logger; - public OrderStatusChangedToPaidIntegrationEventHandler(IWebhooksRetriever retriever, IWebhooksSender sender, ILogger logger) - { - _retriever = retriever; - _sender = sender; - _logger = logger; - } + _retriever = retriever; + _sender = sender; + _logger = logger; + } - public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) - { - var subscriptions = await _retriever.GetSubscriptionsOfType(WebhookType.OrderPaid); - _logger.LogInformation("Received OrderStatusChangedToShippedIntegrationEvent and got {SubscriptionsCount} subscriptions to process", subscriptions.Count()); - var whook = new WebhookData(WebhookType.OrderPaid, @event); - await _sender.SendAll(subscriptions, whook); - } + public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event) + { + var subscriptions = await _retriever.GetSubscriptionsOfType(WebhookType.OrderPaid); + _logger.LogInformation("Received OrderStatusChangedToShippedIntegrationEvent and got {SubscriptionsCount} subscriptions to process", subscriptions.Count()); + var whook = new WebhookData(WebhookType.OrderPaid, @event); + await _sender.SendAll(subscriptions, whook); } } diff --git a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEvent.cs b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEvent.cs index 2b03abacf..bb20534a6 100644 --- a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEvent.cs +++ b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEvent.cs @@ -1,18 +1,15 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Webhooks.API.IntegrationEvents; -namespace Webhooks.API.IntegrationEvents +public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent { - public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent - { - public int OrderId { get; private init; } - public string OrderStatus { get; private init; } - public string BuyerName { get; private init; } + public int OrderId { get; private init; } + public string OrderStatus { get; private init; } + public string BuyerName { get; private init; } - public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) - { - OrderId = orderId; - OrderStatus = orderStatus; - BuyerName = buyerName; - } + public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) + { + OrderId = orderId; + OrderStatus = orderStatus; + BuyerName = buyerName; } } diff --git a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEventHandler.cs b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEventHandler.cs index 7f04e2db2..29b21be27 100644 --- a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEventHandler.cs +++ b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/OrderStatusChangedToShippedIntegrationEventHandler.cs @@ -1,30 +1,22 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; -using Webhooks.API.Model; -using Webhooks.API.Services; +namespace Webhooks.API.IntegrationEvents; -namespace Webhooks.API.IntegrationEvents +public class OrderStatusChangedToShippedIntegrationEventHandler : IIntegrationEventHandler { - public class OrderStatusChangedToShippedIntegrationEventHandler : IIntegrationEventHandler + private readonly IWebhooksRetriever _retriever; + private readonly IWebhooksSender _sender; + private readonly ILogger _logger; + public OrderStatusChangedToShippedIntegrationEventHandler(IWebhooksRetriever retriever, IWebhooksSender sender, ILogger logger) { - private readonly IWebhooksRetriever _retriever; - private readonly IWebhooksSender _sender; - private readonly ILogger _logger; - public OrderStatusChangedToShippedIntegrationEventHandler(IWebhooksRetriever retriever, IWebhooksSender sender, ILogger logger) - { - _retriever = retriever; - _sender = sender; - _logger = logger; - } + _retriever = retriever; + _sender = sender; + _logger = logger; + } - public async Task Handle(OrderStatusChangedToShippedIntegrationEvent @event) - { - var subscriptions = await _retriever.GetSubscriptionsOfType(WebhookType.OrderShipped); - _logger.LogInformation("Received OrderStatusChangedToShippedIntegrationEvent and got {SubscriptionCount} subscriptions to process", subscriptions.Count()); - var whook = new WebhookData(WebhookType.OrderShipped, @event); - await _sender.SendAll(subscriptions, whook); - } + public async Task Handle(OrderStatusChangedToShippedIntegrationEvent @event) + { + var subscriptions = await _retriever.GetSubscriptionsOfType(WebhookType.OrderShipped); + _logger.LogInformation("Received OrderStatusChangedToShippedIntegrationEvent and got {SubscriptionCount} subscriptions to process", subscriptions.Count()); + var whook = new WebhookData(WebhookType.OrderShipped, @event); + await _sender.SendAll(subscriptions, whook); } } diff --git a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEvent.cs b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEvent.cs index 8fb860007..0dca66700 100644 --- a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEvent.cs +++ b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEvent.cs @@ -1,20 +1,17 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Webhooks.API.IntegrationEvents; -namespace Webhooks.API.IntegrationEvents +public record ProductPriceChangedIntegrationEvent : IntegrationEvent { - public record ProductPriceChangedIntegrationEvent : IntegrationEvent - { - public int ProductId { get; private init; } + public int ProductId { get; private init; } - public decimal NewPrice { get; private init; } + public decimal NewPrice { get; private init; } - public decimal OldPrice { get; private init; } + public decimal OldPrice { get; private init; } - public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice) - { - ProductId = productId; - NewPrice = newPrice; - OldPrice = oldPrice; - } + public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice) + { + ProductId = productId; + NewPrice = newPrice; + OldPrice = oldPrice; } } diff --git a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEventHandler.cs b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEventHandler.cs index 044ce3bd3..1172d0820 100644 --- a/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEventHandler.cs +++ b/src/Services/Webhooks/Webhooks.API/IntegrationEvents/ProductPriceChangedIntegrationEventHandler.cs @@ -1,13 +1,9 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using System.Threading.Tasks; +namespace Webhooks.API.IntegrationEvents; -namespace Webhooks.API.IntegrationEvents +public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler { - public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler + public async Task Handle(ProductPriceChangedIntegrationEvent @event) { - public async Task Handle(ProductPriceChangedIntegrationEvent @event) - { - int i = 0; - } + int i = 0; } } diff --git a/src/Services/Webhooks/Webhooks.API/Model/WebhookData.cs b/src/Services/Webhooks/Webhooks.API/Model/WebhookData.cs index ad4455f99..a76a8609d 100644 --- a/src/Services/Webhooks/Webhooks.API/Model/WebhookData.cs +++ b/src/Services/Webhooks/Webhooks.API/Model/WebhookData.cs @@ -1,21 +1,17 @@ -using System; -using System.Text.Json; +namespace Webhooks.API.Model; -namespace Webhooks.API.Model +public class WebhookData { - public class WebhookData - { - public DateTime When { get; } + public DateTime When { get; } - public string Payload { get; } + public string Payload { get; } - public string Type { get; } + public string Type { get; } - public WebhookData(WebhookType hookType, object data) - { - When = DateTime.UtcNow; - Type = hookType.ToString(); - Payload = JsonSerializer.Serialize(data); - } + public WebhookData(WebhookType hookType, object data) + { + When = DateTime.UtcNow; + Type = hookType.ToString(); + Payload = JsonSerializer.Serialize(data); } } diff --git a/src/Services/Webhooks/Webhooks.API/Model/WebhookSubscription.cs b/src/Services/Webhooks/Webhooks.API/Model/WebhookSubscription.cs index 72d726834..1095b5c65 100644 --- a/src/Services/Webhooks/Webhooks.API/Model/WebhookSubscription.cs +++ b/src/Services/Webhooks/Webhooks.API/Model/WebhookSubscription.cs @@ -1,15 +1,12 @@ -using System; +namespace Webhooks.API.Model; -namespace Webhooks.API.Model +public class WebhookSubscription { - public class WebhookSubscription - { - public int Id { get; set; } + public int Id { get; set; } - public WebhookType Type { get; set; } - public DateTime Date { get; set; } - public string DestUrl { get; set; } - public string Token { get; set; } - public string UserId { get; set; } - } + public WebhookType Type { get; set; } + public DateTime Date { get; set; } + public string DestUrl { get; set; } + public string Token { get; set; } + public string UserId { get; set; } } diff --git a/src/Services/Webhooks/Webhooks.API/Model/WebhookType.cs b/src/Services/Webhooks/Webhooks.API/Model/WebhookType.cs index cda3d7316..2e02fc47f 100644 --- a/src/Services/Webhooks/Webhooks.API/Model/WebhookType.cs +++ b/src/Services/Webhooks/Webhooks.API/Model/WebhookType.cs @@ -1,9 +1,8 @@ -namespace Webhooks.API.Model +namespace Webhooks.API.Model; + +public enum WebhookType { - public enum WebhookType - { - CatalogItemPriceChange = 1, - OrderShipped = 2, - OrderPaid = 3 - } + CatalogItemPriceChange = 1, + OrderShipped = 2, + OrderPaid = 3 } diff --git a/src/Services/Webhooks/Webhooks.API/Program.cs b/src/Services/Webhooks/Webhooks.API/Program.cs index 85ab12997..027814057 100644 --- a/src/Services/Webhooks/Webhooks.API/Program.cs +++ b/src/Services/Webhooks/Webhooks.API/Program.cs @@ -1,11 +1,4 @@ -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Webhooks.API; -using Webhooks.API.Infrastructure; - -CreateWebHostBuilder(args).Build() +CreateWebHostBuilder(args).Build() .MigrateDbContext((_, __) => { }) .Run(); diff --git a/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs b/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs index fef390dda..ea75ac343 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/GrantUrlTesterService.cs @@ -1,57 +1,50 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; +namespace Webhooks.API.Services; -namespace Webhooks.API.Services +class GrantUrlTesterService : IGrantUrlTesterService { - class GrantUrlTesterService : IGrantUrlTesterService + private readonly IHttpClientFactory _clientFactory; + private readonly ILogger _logger; + public GrantUrlTesterService(IHttpClientFactory factory, ILogger logger) { - private readonly IHttpClientFactory _clientFactory; - private readonly ILogger _logger; - public GrantUrlTesterService(IHttpClientFactory factory, ILogger logger) - { - _clientFactory = factory; - _logger = logger; - } + _clientFactory = factory; + _logger = logger; + } - public async Task TestGrantUrl(string urlHook, string url, string token) + public async Task TestGrantUrl(string urlHook, string url, string token) + { + if (!CheckSameOrigin(urlHook, url)) { - if (!CheckSameOrigin(urlHook, url)) - { - _logger.LogWarning("Url of the hook ({UrlHook} and the grant url ({Url} do not belong to same origin)", urlHook, url); - return false; - } + _logger.LogWarning("Url of the hook ({UrlHook} and the grant url ({Url} do not belong to same origin)", urlHook, url); + return false; + } - var client = _clientFactory.CreateClient("GrantClient"); - var msg = new HttpRequestMessage(HttpMethod.Options, url); - msg.Headers.Add("X-eshop-whtoken", token); - _logger.LogInformation("Sending the OPTIONS message to {Url} with token \"{Token}\"", url, token ?? string.Empty); - try - { - var response = await client.SendAsync(msg); - var tokenReceived = response.Headers.TryGetValues("X-eshop-whtoken", out var tokenValues) ? tokenValues.FirstOrDefault() : null; - var tokenExpected = string.IsNullOrWhiteSpace(token) ? null : token; - _logger.LogInformation("Response code is {StatusCode} for url {Url} and token in header was {TokenReceived} (expected token was {TokenExpected})", response.StatusCode, url, tokenReceived, tokenExpected); - return response.IsSuccessStatusCode && tokenReceived == tokenExpected; - } - catch (Exception ex) - { - _logger.LogWarning("Exception {TypeName} when sending OPTIONS request. Url can't be granted.", ex.GetType().Name); - return false; - } + var client = _clientFactory.CreateClient("GrantClient"); + var msg = new HttpRequestMessage(HttpMethod.Options, url); + msg.Headers.Add("X-eshop-whtoken", token); + _logger.LogInformation("Sending the OPTIONS message to {Url} with token \"{Token}\"", url, token ?? string.Empty); + try + { + var response = await client.SendAsync(msg); + var tokenReceived = response.Headers.TryGetValues("X-eshop-whtoken", out var tokenValues) ? tokenValues.FirstOrDefault() : null; + var tokenExpected = string.IsNullOrWhiteSpace(token) ? null : token; + _logger.LogInformation("Response code is {StatusCode} for url {Url} and token in header was {TokenReceived} (expected token was {TokenExpected})", response.StatusCode, url, tokenReceived, tokenExpected); + return response.IsSuccessStatusCode && tokenReceived == tokenExpected; } - - private bool CheckSameOrigin(string urlHook, string url) + catch (Exception ex) { - var firstUrl = new Uri(urlHook, UriKind.Absolute); - var secondUrl = new Uri(url, UriKind.Absolute); - - return firstUrl.Scheme == secondUrl.Scheme && - firstUrl.Port == secondUrl.Port && - firstUrl.Host == firstUrl.Host; + _logger.LogWarning("Exception {TypeName} when sending OPTIONS request. Url can't be granted.", ex.GetType().Name); + return false; } } + + private bool CheckSameOrigin(string urlHook, string url) + { + var firstUrl = new Uri(urlHook, UriKind.Absolute); + var secondUrl = new Uri(url, UriKind.Absolute); + + return firstUrl.Scheme == secondUrl.Scheme && + firstUrl.Port == secondUrl.Port && + firstUrl.Host == firstUrl.Host; + } } diff --git a/src/Services/Webhooks/Webhooks.API/Services/IGrantUrlTesterService.cs b/src/Services/Webhooks/Webhooks.API/Services/IGrantUrlTesterService.cs index c69e41934..c4ccf764f 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/IGrantUrlTesterService.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/IGrantUrlTesterService.cs @@ -1,9 +1,6 @@ -using System.Threading.Tasks; +namespace Webhooks.API.Services; -namespace Webhooks.API.Services +public interface IGrantUrlTesterService { - public interface IGrantUrlTesterService - { - Task TestGrantUrl(string urlHook, string url, string token); - } + Task TestGrantUrl(string urlHook, string url, string token); } diff --git a/src/Services/Webhooks/Webhooks.API/Services/IIdentityService.cs b/src/Services/Webhooks/Webhooks.API/Services/IIdentityService.cs index 678ae1b97..97d2a0b67 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/IIdentityService.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/IIdentityService.cs @@ -1,7 +1,6 @@ -namespace Webhooks.API.Services +namespace Webhooks.API.Services; + +public interface IIdentityService { - public interface IIdentityService - { - string GetUserIdentity(); - } + string GetUserIdentity(); } diff --git a/src/Services/Webhooks/Webhooks.API/Services/IWebhooksRetriever.cs b/src/Services/Webhooks/Webhooks.API/Services/IWebhooksRetriever.cs index 0ae20f03b..20e6439b6 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/IWebhooksRetriever.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/IWebhooksRetriever.cs @@ -1,12 +1,7 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Webhooks.API.Model; +namespace Webhooks.API.Services; -namespace Webhooks.API.Services +public interface IWebhooksRetriever { - public interface IWebhooksRetriever - { - Task> GetSubscriptionsOfType(WebhookType type); - } + Task> GetSubscriptionsOfType(WebhookType type); } diff --git a/src/Services/Webhooks/Webhooks.API/Services/IWebhooksSender.cs b/src/Services/Webhooks/Webhooks.API/Services/IWebhooksSender.cs index a0a412ef8..2bfb4955d 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/IWebhooksSender.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/IWebhooksSender.cs @@ -1,11 +1,6 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Webhooks.API.Model; +namespace Webhooks.API.Services; -namespace Webhooks.API.Services +public interface IWebhooksSender { - public interface IWebhooksSender - { - Task SendAll(IEnumerable receivers, WebhookData data); - } + Task SendAll(IEnumerable receivers, WebhookData data); } diff --git a/src/Services/Webhooks/Webhooks.API/Services/IdentityService.cs b/src/Services/Webhooks/Webhooks.API/Services/IdentityService.cs index be1d54570..cd821dc74 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/IdentityService.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/IdentityService.cs @@ -1,21 +1,16 @@ - -using Microsoft.AspNetCore.Http; -using System; +namespace Webhooks.API.Services; -namespace Webhooks.API.Services +public class IdentityService : IIdentityService { - public class IdentityService : IIdentityService - { - private IHttpContextAccessor _context; + private IHttpContextAccessor _context; - public IdentityService(IHttpContextAccessor context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - } + public IdentityService(IHttpContextAccessor context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } - public string GetUserIdentity() - { - return _context.HttpContext.User.FindFirst("sub").Value; - } + public string GetUserIdentity() + { + return _context.HttpContext.User.FindFirst("sub").Value; } } diff --git a/src/Services/Webhooks/Webhooks.API/Services/WebhooksRetriever.cs b/src/Services/Webhooks/Webhooks.API/Services/WebhooksRetriever.cs index 1f7fb3f1c..d34ddc5a0 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/WebhooksRetriever.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/WebhooksRetriever.cs @@ -1,23 +1,15 @@ -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Webhooks.API.Infrastructure; -using Webhooks.API.Model; +namespace Webhooks.API.Services; -namespace Webhooks.API.Services +public class WebhooksRetriever : IWebhooksRetriever { - public class WebhooksRetriever : IWebhooksRetriever + private readonly WebhooksContext _db; + public WebhooksRetriever(WebhooksContext db) { - private readonly WebhooksContext _db; - public WebhooksRetriever(WebhooksContext db) - { - _db = db; - } - public async Task> GetSubscriptionsOfType(WebhookType type) - { - var data = await _db.Subscriptions.Where(s => s.Type == type).ToListAsync(); - return data; - } + _db = db; + } + public async Task> GetSubscriptionsOfType(WebhookType type) + { + var data = await _db.Subscriptions.Where(s => s.Type == type).ToListAsync(); + return data; } } diff --git a/src/Services/Webhooks/Webhooks.API/Services/WebhooksSender.cs b/src/Services/Webhooks/Webhooks.API/Services/WebhooksSender.cs index 9009cdcda..625662a93 100644 --- a/src/Services/Webhooks/Webhooks.API/Services/WebhooksSender.cs +++ b/src/Services/Webhooks/Webhooks.API/Services/WebhooksSender.cs @@ -1,49 +1,38 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Webhooks.API.Model; +namespace Webhooks.API.Services; -namespace Webhooks.API.Services +public class WebhooksSender : IWebhooksSender { - public class WebhooksSender : IWebhooksSender + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + public WebhooksSender(IHttpClientFactory httpClientFactory, ILogger logger) { - private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger _logger; - public WebhooksSender(IHttpClientFactory httpClientFactory, ILogger logger) - { - _httpClientFactory = httpClientFactory; - _logger = logger; - } + _httpClientFactory = httpClientFactory; + _logger = logger; + } - public async Task SendAll(IEnumerable receivers, WebhookData data) - { - var client = _httpClientFactory.CreateClient(); - var json = JsonSerializer.Serialize(data); - var tasks = receivers.Select(r => OnSendData(r, json, client)); - await Task.WhenAll(tasks.ToArray()); - } + public async Task SendAll(IEnumerable receivers, WebhookData data) + { + var client = _httpClientFactory.CreateClient(); + var json = JsonSerializer.Serialize(data); + var tasks = receivers.Select(r => OnSendData(r, json, client)); + await Task.WhenAll(tasks.ToArray()); + } - private Task OnSendData(WebhookSubscription subs, string jsonData, HttpClient client) + private Task OnSendData(WebhookSubscription subs, string jsonData, HttpClient client) + { + var request = new HttpRequestMessage() { - var request = new HttpRequestMessage() - { - RequestUri = new Uri(subs.DestUrl, UriKind.Absolute), - Method = HttpMethod.Post, - Content = new StringContent(jsonData, Encoding.UTF8, "application/json") - }; + RequestUri = new Uri(subs.DestUrl, UriKind.Absolute), + Method = HttpMethod.Post, + Content = new StringContent(jsonData, Encoding.UTF8, "application/json") + }; - if (!string.IsNullOrWhiteSpace(subs.Token)) - { - request.Headers.Add("X-eshop-whtoken", subs.Token); - } - _logger.LogDebug("Sending hook to {DestUrl} of type {Type}", subs.Type.ToString(), subs.Type.ToString()); - return client.SendAsync(request); + if (!string.IsNullOrWhiteSpace(subs.Token)) + { + request.Headers.Add("X-eshop-whtoken", subs.Token); } - + _logger.LogDebug("Sending hook to {DestUrl} of type {Type}", subs.Type.ToString(), subs.Type.ToString()); + return client.SendAsync(request); } + } diff --git a/src/Services/Webhooks/Webhooks.API/Startup.cs b/src/Services/Webhooks/Webhooks.API/Startup.cs index 4744e9d24..274a4a9d9 100644 --- a/src/Services/Webhooks/Webhooks.API/Startup.cs +++ b/src/Services/Webhooks/Webhooks.API/Startup.cs @@ -1,350 +1,318 @@ -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Devspaces.Support; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.AspNetCore.Http; -using Microsoft.Azure.ServiceBus; -using Microsoft.EntityFrameworkCore; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; -using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; -using RabbitMQ.Client; -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.IdentityModel.Tokens.Jwt; -using System.Reflection; -using System.Threading; -using Webhooks.API.Infrastructure; -using Webhooks.API.IntegrationEvents; -using Webhooks.API.Services; - -namespace Webhooks.API +namespace Webhooks.API; + +public class Startup { - public class Startup + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) { - public IConfiguration Configuration { get; } + Configuration = configuration; + } - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services + .AddAppInsight(Configuration) + .AddCustomRouting(Configuration) + .AddCustomDbContext(Configuration) + .AddSwagger(Configuration) + .AddCustomHealthCheck(Configuration) + .AddDevspaces() + .AddHttpClientServices(Configuration) + .AddIntegrationServices(Configuration) + .AddEventBus(Configuration) + .AddCustomAuthentication(Configuration) + .AddSingleton() + .AddTransient() + .AddTransient() + .AddTransient() + .AddTransient(); + + var container = new ContainerBuilder(); + container.Populate(services); + return new AutofacServiceProvider(container.Build()); + } - public IServiceProvider ConfigureServices(IServiceCollection services) + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + + var pathBase = Configuration["PATH_BASE"]; + + if (!string.IsNullOrEmpty(pathBase)) { - services - .AddAppInsight(Configuration) - .AddCustomRouting(Configuration) - .AddCustomDbContext(Configuration) - .AddSwagger(Configuration) - .AddCustomHealthCheck(Configuration) - .AddDevspaces() - .AddHttpClientServices(Configuration) - .AddIntegrationServices(Configuration) - .AddEventBus(Configuration) - .AddCustomAuthentication(Configuration) - .AddSingleton() - .AddTransient() - .AddTransient() - .AddTransient() - .AddTransient(); - - var container = new ContainerBuilder(); - container.Populate(services); - return new AutofacServiceProvider(container.Build()); + loggerFactory.CreateLogger("init").LogDebug("Using PATH BASE '{PathBase}'", pathBase); + app.UsePathBase(pathBase); } - public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) - { - loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); - var pathBase = Configuration["PATH_BASE"]; - if (!string.IsNullOrEmpty(pathBase)) + app.UseRouting(); + app.UseCors("CorsPolicy"); + ConfigureAuth(app); + + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() { - loggerFactory.CreateLogger("init").LogDebug("Using PATH BASE '{PathBase}'", pathBase); - app.UsePathBase(pathBase); - } + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions + { + Predicate = r => r.Name.Contains("self") + }); + }); + app.UseSwagger() + .UseSwaggerUI(c => + { + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Webhooks.API V1"); + c.OAuthClientId("webhooksswaggerui"); + c.OAuthAppName("WebHooks Service Swagger UI"); + }); + ConfigureEventBus(app); + } - app.UseRouting(); - app.UseCors("CorsPolicy"); - ConfigureAuth(app); + protected virtual void ConfigureAuth(IApplicationBuilder app) + { + app.UseAuthentication(); + app.UseAuthorization(); + } - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); - }); + protected virtual void ConfigureEventBus(IApplicationBuilder app) + { + var eventBus = app.ApplicationServices.GetRequiredService(); + eventBus.Subscribe(); + eventBus.Subscribe(); + eventBus.Subscribe(); + } +} - app.UseSwagger() - .UseSwaggerUI(c => - { - c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Webhooks.API V1"); - c.OAuthClientId("webhooksswaggerui"); - c.OAuthAppName("WebHooks Service Swagger UI"); - }); +static class CustomExtensionMethods +{ + public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) + { + services.AddApplicationInsightsTelemetry(configuration); + services.AddApplicationInsightsKubernetesEnricher(); - ConfigureEventBus(app); - } + return services; + } - protected virtual void ConfigureAuth(IApplicationBuilder app) + public static IServiceCollection AddCustomRouting(this IServiceCollection services, IConfiguration configuration) + { + services.AddControllers(options => { - app.UseAuthentication(); - app.UseAuthorization(); - } + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }); - protected virtual void ConfigureEventBus(IApplicationBuilder app) + services.AddCors(options => { - var eventBus = app.ApplicationServices.GetRequiredService(); - eventBus.Subscribe(); - eventBus.Subscribe(); - eventBus.Subscribe(); - } + options.AddPolicy("CorsPolicy", + builder => builder + .SetIsOriginAllowed((host) => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + return services; } - static class CustomExtensionMethods + public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) + services.AddEntityFrameworkSqlServer() + .AddDbContext(options => { - services.AddApplicationInsightsTelemetry(configuration); - services.AddApplicationInsightsKubernetesEnricher(); - - return services; - } + options.UseSqlServer(configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + }); + + return services; + } - public static IServiceCollection AddCustomRouting(this IServiceCollection services, IConfiguration configuration) + public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) + { + services.AddSwaggerGen(options => { - services.AddControllers(options => + options.DescribeAllEnumsAsStrings(); + options.SwaggerDoc("v1", new OpenApiInfo { - options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + Title = "eShopOnContainers - Webhooks HTTP API", + Version = "v1", + Description = "The Webhooks Microservice HTTP API. This is a simple webhooks CRUD registration entrypoint" }); - services.AddCors(options => + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { - options.AddPolicy("CorsPolicy", - builder => builder - .SetIsOriginAllowed((host) => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); + Type = SecuritySchemeType.OAuth2, + Flows = new OpenApiOAuthFlows() + { + Implicit = new OpenApiOAuthFlow() + { + AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), + TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), + Scopes = new Dictionary() + { + { "webhooks", "Webhooks API" } + } + } + } }); - return services; - } + options.OperationFilter(); + }); - public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) + return services; + } + public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) + { + if (configuration.GetValue("AzureServiceBusEnabled")) { - services.AddEntityFrameworkSqlServer() - .AddDbContext(options => + services.AddSingleton(sp => { - options.UseSqlServer(configuration["ConnectionString"], - sqlServerOptionsAction: sqlOptions => - { - sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency - sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); - }); + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, iLifetimeScope); }); - return services; } - - public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) + else { - services.AddSwaggerGen(options => + services.AddSingleton(sp => { - options.DescribeAllEnumsAsStrings(); - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "eShopOnContainers - Webhooks HTTP API", - Version = "v1", - Description = "The Webhooks Microservice HTTP API. This is a simple webhooks CRUD registration entrypoint" - }); - - options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + var subscriptionClientName = configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) { - Type = SecuritySchemeType.OAuth2, - Flows = new OpenApiOAuthFlows() - { - Implicit = new OpenApiOAuthFlow() - { - AuthorizationUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), - TokenUrl = new Uri($"{configuration.GetValue("IdentityUrlExternal")}/connect/token"), - Scopes = new Dictionary() - { - { "webhooks", "Webhooks API" } - } - } - } - }); + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } - options.OperationFilter(); + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); }); - - return services; } - public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) - { - if (configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope); - }); - } - else - { - services.AddSingleton(sp => - { - var subscriptionClientName = configuration["SubscriptionClientName"]; - var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - var retryCount = 5; - if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(configuration["EventBusRetryCount"]); - } + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + return services; + } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); - }); - } + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) + { + var accountName = configuration.GetValue("AzureStorageAccountName"); + var accountKey = configuration.GetValue("AzureStorageAccountKey"); - services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - return services; - } + var hcBuilder = services.AddHealthChecks(); - public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) - { - var accountName = configuration.GetValue("AzureStorageAccountName"); - var accountKey = configuration.GetValue("AzureStorageAccountKey"); + hcBuilder + .AddCheck("self", () => HealthCheckResult.Healthy()) + .AddSqlServer( + configuration["ConnectionString"], + name: "WebhooksApiDb-check", + tags: new string[] { "webhooksdb" }); - var hcBuilder = services.AddHealthChecks(); + return services; + } - hcBuilder - .AddCheck("self", () => HealthCheckResult.Healthy()) - .AddSqlServer( - configuration["ConnectionString"], - name: "WebhooksApiDb-check", - tags: new string[] { "webhooksdb" }); + public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration) + { + services.AddSingleton(); + services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(Timeout.InfiniteTimeSpan); + //add http client services + services.AddHttpClient("GrantClient") + .SetHandlerLifetime(TimeSpan.FromMinutes(5)) + .AddDevspacesSupport(); + return services; + } - return services; - } + public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) + { + services.AddTransient>( + sp => (DbConnection c) => new IntegrationEventLogService(c)); - public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration) + if (configuration.GetValue("AzureServiceBusEnabled")) { - services.AddSingleton(); - services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(Timeout.InfiniteTimeSpan); - //add http client services - services.AddHttpClient("GrantClient") - .SetHandlerLifetime(TimeSpan.FromMinutes(5)) - .AddDevspacesSupport(); - return services; + services.AddSingleton(sp => + { + var serviceBusConnection = new ServiceBusConnectionStringBuilder(configuration["EventBusConnection"]); + var subscriptionClientName = configuration["SubscriptionClientName"]; + return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); + }); } - - public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) + else { - services.AddTransient>( - sp => (DbConnection c) => new IntegrationEventLogService(c)); - - if (configuration.GetValue("AzureServiceBusEnabled")) + services.AddSingleton(sp => { - services.AddSingleton(sp => + var logger = sp.GetRequiredService>(); + + var factory = new ConnectionFactory() { - var serviceBusConnection = new ServiceBusConnectionStringBuilder(configuration["EventBusConnection"]); - var subscriptionClientName = configuration["SubscriptionClientName"]; - return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); - }); - } - else - { - services.AddSingleton(sp => + HostName = configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; + + if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) { - var logger = sp.GetRequiredService>(); + factory.UserName = configuration["EventBusUserName"]; + } - var factory = new ConnectionFactory() - { - HostName = configuration["EventBusConnection"], - DispatchConsumersAsync = true - }; + if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) + { + factory.Password = configuration["EventBusPassword"]; + } - if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) - { - factory.UserName = configuration["EventBusUserName"]; - } + var retryCount = 5; + if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(configuration["EventBusRetryCount"]); + } - if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) - { - factory.Password = configuration["EventBusPassword"]; - } + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); + }); + } - var retryCount = 5; - if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(configuration["EventBusRetryCount"]); - } + return services; + } - return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); - }); - } + public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + { + // prevent from mapping "sub" claim to nameidentifier. + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); - return services; - } + var identityUrl = configuration.GetValue("IdentityUrl"); - public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) + services.AddAuthentication(options => { - // prevent from mapping "sub" claim to nameidentifier. - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); - - var identityUrl = configuration.GetValue("IdentityUrl"); + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - - }).AddJwtBearer(options => - { - options.Authority = identityUrl; - options.RequireHttpsMetadata = false; - options.Audience = "webhooks"; - }); + }).AddJwtBearer(options => + { + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "webhooks"; + }); - return services; - } + return services; } -} \ No newline at end of file +}