Compare commits

...

12 Commits

Author SHA1 Message Date
  David Fowler 70036a5e3c Fixed remaining tests 1 year ago
  David Fowler 2b0889f684 Fix ordering and basked scenarios 1 year ago
  David Fowler 2736eae13e Fixed catalog functional tests 1 year ago
  David Fowler f493d900d8 Remove whitespace 1 year ago
  David Fowler 207172b200 Make more tests pass 1 year ago
  David Fowler a4ae332c31 Make tests work 1 year ago
  Reuben Bond 146652ca6a BAD MISC - playing with tests 1 year ago
  Reuben Bond 1e4de70295 Fix IncludeScopes 1 year ago
  Reuben Bond b4432d50d6 Remove gRPC generated code from global usings etc 1 year ago
  Reuben Bond cfef616eb6 Fix IncludeScopes setting in appsettings.json for WebSPA 1 year ago
  Reuben Bond 8c6351b1c4 Remove accidentally committed keys and update .gitignore to prevent them being added again 1 year ago
  Reuben Bond 27aaed0a00 Fix formatting 1 year ago
116 changed files with 8906 additions and 7305 deletions
Split View
  1. +1
    -0
      .gitignore
  2. +1
    -2
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/GlobalUsings.cs
  3. +1
    -1
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs
  4. +5
    -5
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs
  5. +2
    -2
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs
  6. +2
    -1
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.json
  7. +2
    -1
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.localhost.json
  8. +1
    -2
      src/ApiGateways/Web.Bff.Shopping/aggregator/GlobalUsings.cs
  9. +2
    -3
      src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs
  10. +1
    -1
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs
  11. +5
    -5
      src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderingService.cs
  12. +2
    -1
      src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.Development.json
  13. +2
    -1
      src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.json
  14. +1
    -1
      src/BuildingBlocks/EventBus/EventBus.Tests/InMemory_SubscriptionManager_Tests.cs
  15. +1
    -1
      src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs
  16. +2
    -2
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
  17. +1
    -1
      src/BuildingBlocks/EventBus/EventBusServiceBus/DefaultServiceBusPersisterConnection.cs
  18. +2
    -2
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs
  19. +11
    -11
      src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs
  20. +0
    -6
      src/Services/Basket/Basket.API/Basket.API.csproj
  21. +1
    -1
      src/Services/Basket/Basket.API/Controllers/BasketController.cs
  22. +0
    -11
      src/Services/Basket/Basket.API/Controllers/HomeController.cs
  23. +104
    -1
      src/Services/Basket/Basket.API/CustomExtensionMethods.cs
  24. +86
    -161
      src/Services/Basket/Basket.API/Program.cs
  25. +0
    -6
      src/Services/Basket/Basket.API/TestHttpResponseTrailersFeature.cs
  26. +0
    -17
      src/Services/Basket/Basket.API/web.config
  27. +1
    -0
      src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs
  28. +36
    -14
      src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs
  29. +0
    -13
      src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs
  30. +0
    -4
      src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj
  31. +9
    -7
      src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs
  32. +2
    -7
      src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs
  33. +3
    -1
      src/Services/Basket/Basket.FunctionalTests/appsettings.json
  34. +3
    -5
      src/Services/Catalog/Catalog.API/Catalog.API.csproj
  35. +11
    -11
      src/Services/Catalog/Catalog.API/Extensions/WebHostExtensions.cs
  36. +1
    -1
      src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs
  37. +1
    -1
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs
  38. +1
    -1
      src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs
  39. +120
    -64
      src/Services/Catalog/Catalog.API/Program.cs
  40. +17
    -17
      src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs
  41. +1
    -1
      src/Services/Identity/Identity.API/IWebHostExtensions.cs
  42. +1
    -1
      src/Services/Identity/Identity.API/ProgramExtensions.cs
  43. +2
    -2
      src/Services/Identity/Identity.API/Quickstart/Account/AccountController.cs
  44. +0
    -1
      src/Services/Identity/Identity.API/keys/is-signing-key-10C452043E09C5A22FC8D97669868B8F.json
  45. +0
    -1
      src/Services/Identity/Identity.API/keys/is-signing-key-1417AE0BAF26F393609EF30FA355D06C.json
  46. +0
    -1
      src/Services/Identity/Identity.API/keys/is-signing-key-22178AEDB80CD0A41DF6874FE93F794D.json
  47. +0
    -1
      src/Services/Identity/Identity.API/keys/is-signing-key-3139DF3FF07C8E3881CFA9743F89A787.json
  48. +0
    -1
      src/Services/Identity/Identity.API/keys/is-signing-key-870FA7120249C21C30ADE458B07918C1.json
  49. +0
    -1
      src/Services/Identity/Identity.API/keys/is-signing-key-B0B34AEB91E2638F61EB9A5543F3940F.json
  50. +0
    -1
      src/Services/Identity/Identity.API/keys/is-signing-key-CFE19FEED1112F0740C7CEAAE490834F.json
  51. +4
    -4
      src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehavior.cs
  52. +1
    -16
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs
  53. +0
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs
  54. +16
    -2
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs
  55. +4
    -6
      src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs
  56. +2
    -2
      src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs
  57. +2
    -2
      src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs
  58. +1
    -1
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelledDomainEventHandler.cs
  59. +1
    -1
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShippedDomainEventHandler.cs
  60. +2
    -2
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs
  61. +6
    -6
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStatusChangedToPaidDomainEventHandler.cs
  62. +1
    -1
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStatusChangedToStockConfirmedDomainEventHandler.cs
  63. +1
    -1
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/SendEmailToCustomerWhenOrderStartedDomainEventHandler.cs
  64. +1
    -1
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs
  65. +3
    -3
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs
  66. +1
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSucceededIntegrationEventHandler.cs
  67. +1
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs
  68. +1
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/GracePeriodConfirmedIntegrationEvent.cs
  69. +1
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs
  70. +1
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs
  71. +5
    -5
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs
  72. +1
    -1
      src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs
  73. +2
    -1
      src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs
  74. +3
    -4
      src/Services/Ordering/Ordering.API/GlobalUsings.cs
  75. +6
    -2
      src/Services/Ordering/Ordering.API/Grpc/OrderingService.cs
  76. +0
    -18
      src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs
  77. +0
    -20
      src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs
  78. +1
    -1
      src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextDesignTimeFactory.cs
  79. +1
    -1
      src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs
  80. +2
    -1
      src/Services/Ordering/Ordering.API/Ordering.API.csproj
  81. +99
    -42
      src/Services/Ordering/Ordering.API/Program.cs
  82. +1
    -1
      src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs
  83. +4
    -2
      src/Services/Ordering/Ordering.BackgroundTasks/Services/GracePeriodManagerService.cs
  84. +1
    -1
      src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs
  85. +2
    -2
      src/Services/Ordering/Ordering.Domain/SeedWork/Enumeration.cs
  86. +0
    -11
      src/Services/Ordering/Ordering.FunctionalTests/HttpClientExtensions.cs
  87. +2
    -7
      src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj
  88. +43
    -24
      src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs
  89. +12
    -6
      src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs
  90. +1
    -2
      src/Services/Ordering/Ordering.SignalrHub/Program.cs
  91. +2
    -2
      src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs
  92. +4
    -4
      src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs
  93. +1
    -1
      src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs
  94. +1
    -1
      src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs
  95. +1
    -23
      src/Services/Payment/Payment.API/Program.cs
  96. +3
    -1
      src/Services/Payment/Payment.API/appsettings.Development.json
  97. +1
    -1
      src/Services/Webhooks/Webhooks.API/Infrastructure/HttpGlobalExceptionFilter.cs
  98. +5
    -4
      src/Services/Webhooks/Webhooks.API/Program.cs
  99. +36
    -36
      src/Services/Webhooks/Webhooks.API/Startup.cs
  100. +8
    -7
      src/Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj

+ 1
- 0
.gitignore View File

@ -282,3 +282,4 @@ src/**/app.yaml
src/**/inf.yaml
.angular/
/src/Services/Identity/Identity.API/keys/*.json

+ 1
- 2
src/ApiGateways/Mobile.Bff.Shopping/aggregator/GlobalUsings.cs View File

@ -2,7 +2,6 @@
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;
@ -38,4 +37,4 @@ global using System.Text.Json;
global using System.Threading.Tasks;
global using System.Threading;
global using System;
global using Microsoft.IdentityModel.Tokens;
global using Microsoft.IdentityModel.Tokens;

+ 1
- 1
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs View File

@ -1,4 +1,4 @@
await BuildWebHost(args).RunAsync();
await BuildWebHost(args).RunAsync();
IWebHost BuildWebHost(string[] args) =>
WebHost
.CreateDefaultBuilder(args)


+ 5
- 5
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs View File

@ -2,10 +2,10 @@
public class OrderingService : IOrderingService
{
private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
private readonly GrpcOrdering.OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
private readonly ILogger<OrderingService> _logger;
public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
public OrderingService(GrpcOrdering.OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
{
_orderingGrpcClient = orderingGrpcClient;
_logger = logger;
@ -48,14 +48,14 @@ public class OrderingService : IOrderingService
return data;
}
private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
private GrpcOrdering.CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
{
var command = new CreateOrderDraftCommand
var command = new GrpcOrdering.CreateOrderDraftCommand
{
BuyerId = basketData.BuyerId,
};
basketData.Items.ForEach(i => command.Items.Add(new BasketItem
basketData.Items.ForEach(i => command.Items.Add(new GrpcOrdering.BasketItem
{
Id = i.Id,
OldUnitPrice = (double)i.OldUnitPrice,


+ 2
- 2
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs View File

@ -44,7 +44,7 @@ public class Startup
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Purchase BFF V1");
c.OAuthClientId("mobileshoppingaggswaggerui");
c.OAuthClientSecret(string.Empty);
@ -198,7 +198,7 @@ public static class ServiceCollectionExtensions
services.AddScoped<IOrderingService, OrderingService>();
services.AddGrpcClient<OrderingGrpc.OrderingGrpcClient>((services, options) =>
services.AddGrpcClient<GrpcOrdering.OrderingGrpc.OrderingGrpcClient>((services, options) =>
{
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
options.Address = new Uri(orderingApi);


+ 2
- 1
src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.json View File

@ -1,12 +1,13 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}


+ 2
- 1
src/ApiGateways/Mobile.Bff.Shopping/aggregator/appsettings.localhost.json View File

@ -11,13 +11,14 @@
"IdentityUrlExternal": "http://localhost:5105",
"IdentityUrl": "http://localhost:5105",
"Logging": {
"IncludeScopes": false,
"Debug": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug"
}
},
"Console": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug"
}


+ 1
- 2
src/ApiGateways/Web.Bff.Shopping/aggregator/GlobalUsings.cs View File

@ -2,7 +2,6 @@
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;
global using Microsoft.AspNetCore.Authorization;
@ -38,4 +37,4 @@ global using System.Threading.Tasks;
global using System.Threading;
global using System;
global using Microsoft.IdentityModel.Tokens;
global using Serilog.Context;
global using Serilog.Context;

+ 2
- 3
src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs View File

@ -1,5 +1,4 @@
var appName = "Web.Shopping.HttpAggregator";
var builder = WebApplication.CreateBuilder(args);
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration));
builder.Services.AddHealthChecks()
@ -197,7 +196,7 @@ public static class ServiceCollectionExtensions
services.AddScoped<IOrderingService, OrderingService>();
services.AddGrpcClient<OrderingGrpc.OrderingGrpcClient>((services, options) =>
services.AddGrpcClient<GrpcOrdering.OrderingGrpc.OrderingGrpcClient>((services, options) =>
{
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
options.Address = new Uri(orderingApi);


+ 1
- 1
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs View File

@ -10,7 +10,7 @@ public class BasketService : IBasketService
_basketClient = basketClient;
_logger = logger;
}
public async Task<BasketData> GetByIdAsync(string id)
{
_logger.LogDebug("grpc client created, request = {@id}", id);


+ 5
- 5
src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderingService.cs View File

@ -2,10 +2,10 @@
public class OrderingService : IOrderingService
{
private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
private readonly GrpcOrdering.OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
private readonly ILogger<OrderingService> _logger;
public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
public OrderingService(GrpcOrdering.OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
{
_orderingGrpcClient = orderingGrpcClient;
_logger = logger;
@ -48,14 +48,14 @@ public class OrderingService : IOrderingService
return data;
}
private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
private GrpcOrdering.CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
{
var command = new CreateOrderDraftCommand
var command = new GrpcOrdering.CreateOrderDraftCommand
{
BuyerId = basketData.BuyerId,
};
basketData.Items.ForEach(i => command.Items.Add(new BasketItem
basketData.Items.ForEach(i => command.Items.Add(new GrpcOrdering.BasketItem
{
Id = i.Id,
OldUnitPrice = (double)i.OldUnitPrice,


+ 2
- 1
src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.Development.json View File

@ -1,12 +1,13 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug"
}
},
"Console": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug"
}


+ 2
- 1
src/ApiGateways/Web.Bff.Shopping/aggregator/appsettings.json View File

@ -1,12 +1,13 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}


+ 1
- 1
src/BuildingBlocks/EventBus/EventBus.Tests/InMemory_SubscriptionManager_Tests.cs View File

@ -17,7 +17,7 @@ namespace EventBus.Tests
public void After_One_Event_Subscription_Should_Contain_The_Event()
{
var manager = new InMemoryEventBusSubscriptionsManager();
manager.AddSubscription<TestIntegrationEvent,TestIntegrationEventHandler>();
manager.AddSubscription<TestIntegrationEvent, TestIntegrationEventHandler>();
Assert.True(manager.HasSubscriptionsForEvent<TestIntegrationEvent>());
}


+ 1
- 1
src/BuildingBlocks/EventBus/EventBus/Events/IntegrationEvent.cs View File

@ -1,7 +1,7 @@
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public record IntegrationEvent
{
{
public IntegrationEvent()
{
Id = Guid.NewGuid();


+ 2
- 2
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs View File

@ -80,7 +80,7 @@ public class EventBusRabbitMQ : IEventBus, IDisposable
var properties = channel.CreateBasicProperties();
properties.DeliveryMode = 2; // persistent
_logger.LogTrace("Publishing event to RabbitMQ: {EventId}", @event.Id);
_logger.LogTrace("Publishing event to RabbitMQ: {EventId}", @event.Id);
channel.BasicPublish(
exchange: BROKER_NAME,
@ -123,7 +123,7 @@ public class EventBusRabbitMQ : IEventBus, IDisposable
{
_persistentConnection.TryConnect();
}
_consumerChannel.QueueBind(queue: _queueName,
exchange: BROKER_NAME,
routingKey: eventName);


+ 1
- 1
src/BuildingBlocks/EventBus/EventBusServiceBus/DefaultServiceBusPersisterConnection.cs View File

@ -27,7 +27,7 @@ public class DefaultServiceBusPersisterConnection : IServiceBusPersisterConnecti
}
}
public ServiceBusAdministrationClient AdministrationClient =>
public ServiceBusAdministrationClient AdministrationClient =>
_subscriptionClient;
public ServiceBusClient CreateModel()


+ 2
- 2
src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs View File

@ -7,7 +7,7 @@ public class IntegrationEventLogEntry
{
EventId = @event.Id;
CreationTime = @event.CreationDate;
EventTypeName = @event.GetType().FullName;
EventTypeName = @event.GetType().FullName;
Content = JsonSerializer.Serialize(@event, @event.GetType(), new JsonSerializerOptions
{
WriteIndented = true
@ -29,7 +29,7 @@ public class IntegrationEventLogEntry
public string TransactionId { get; private set; }
public IntegrationEventLogEntry DeserializeJsonContent(Type type)
{
{
IntegrationEvent = JsonSerializer.Deserialize(Content, type, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) as IntegrationEvent;
return this;
}


+ 11
- 11
src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs View File

@ -10,21 +10,21 @@ namespace Microsoft.AspNetCore.Hosting
{
public static class IWebHostExtensions
{
public static bool IsInKubernetes(this IWebHost webHost)
public static bool IsInKubernetes(this IServiceProvider services)
{
var cfg = webHost.Services.GetService<IConfiguration>();
var cfg = services.GetService<IConfiguration>();
var orchestratorType = cfg.GetValue<string>("OrchestratorType");
return orchestratorType?.ToUpper() == "K8S";
}
public static IWebHost MigrateDbContext<TContext>(this IWebHost webHost, Action<TContext, IServiceProvider> seeder) where TContext : DbContext
public static IServiceProvider MigrateDbContext<TContext>(this IServiceProvider services, Action<TContext, IServiceProvider> seeder) where TContext : DbContext
{
var underK8s = webHost.IsInKubernetes();
var underK8s = services.IsInKubernetes();
using var scope = webHost.Services.CreateScope();
var services = scope.ServiceProvider;
var logger = services.GetRequiredService<ILogger<TContext>>();
var context = services.GetService<TContext>();
using var scope = services.CreateScope();
var scopeServices = scope.ServiceProvider;
var logger = scopeServices.GetRequiredService<ILogger<TContext>>();
var context = scopeServices.GetService<TContext>();
try
{
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Hosting
if (underK8s)
{
InvokeSeeder(seeder, context, services);
InvokeSeeder(seeder, context, scopeServices);
}
else
{
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Hosting
//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));
retry.Execute(() => InvokeSeeder(seeder, context, scopeServices));
}
logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name);
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Hosting
}
}
return webHost;
return services;
}
private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services)


+ 0
- 6
src/Services/Basket/Basket.API/Basket.API.csproj View File

@ -7,12 +7,6 @@
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
<ItemGroup>
<Content Update="web.config">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />


+ 1
- 1
src/Services/Basket/Basket.API/Controllers/BasketController.cs View File

@ -56,7 +56,7 @@ public class BasketController : ControllerBase
return BadRequest();
}
var userName = this.HttpContext.User.FindFirst(x => x.Type == ClaimTypes.Name).Value;
var userName = User.FindFirst(x => x.Type == ClaimTypes.Name).Value;
var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, userName, basketCheckout.City, basketCheckout.Street,
basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName,


+ 0
- 11
src/Services/Basket/Basket.API/Controllers/HomeController.cs View File

@ -1,11 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers;
public class HomeController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return new RedirectResult("~/swagger");
}
}

+ 104
- 1
src/Services/Basket/Basket.API/CustomExtensionMethods.cs View File

@ -2,6 +2,32 @@
public static class CustomExtensionMethods
{
public static ConfigurationManager AddKeyVault(this ConfigurationManager configuration)
{
if (configuration.GetValue("UseVault", false))
{
var credential = new ClientSecretCredential(
configuration["Vault:TenantId"],
configuration["Vault:ClientId"],
configuration["Vault:ClientSecret"]);
configuration.AddAzureKeyVault(new Uri($"https://{configuration["Vault:Name"]}.vault.azure.net/"), credential);
}
return configuration;
}
public static IServiceCollection AddRedis(this IServiceCollection services)
{
return services.AddSingleton(sp =>
{
var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true);
return ConnectionMultiplexer.Connect(configuration);
});
}
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
{
var hcBuilder = services.AddHealthChecks();
@ -34,4 +60,81 @@ public static class CustomExtensionMethods
return services;
}
}
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
if (configuration.GetValue("AzureServiceBusEnabled", false))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var serviceBusConnectionString = configuration["EventBusConnection"];
return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
});
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubscriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubscriptionsManager, sp, subscriptionName);
});
}
else
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
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);
});
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var subscriptionClientName = configuration["SubscriptionClientName"];
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubscriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubscriptionsManager, subscriptionClientName, retryCount);
});
}
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddTransient<ProductPriceChangedIntegrationEventHandler>();
services.AddTransient<OrderStartedIntegrationEventHandler>();
return services;
}
}

+ 86
- 161
src/Services/Basket/Basket.API/Program.cs View File

@ -1,46 +1,39 @@
using Autofac.Core;
using Microsoft.Azure.Amqp.Framing;
using Microsoft.Extensions.Configuration;
var appName = "Basket.API";
var builder = WebApplication.CreateBuilder(new WebApplicationOptions {
Args = args,
ApplicationName = typeof(Program).Assembly.FullName,
ContentRootPath = Directory.GetCurrentDirectory()
});
if (builder.Configuration.GetValue<bool>("UseVault", false)) {
TokenCredential credential = new ClientSecretCredential(
builder.Configuration["Vault:TenantId"],
builder.Configuration["Vault:ClientId"],
builder.Configuration["Vault:ClientSecret"]);
builder.Configuration.AddAzureKeyVault(new Uri($"https://{builder.Configuration["Vault:Name"]}.vault.azure.net/"), credential);
}
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddKeyVault();
builder.Services.AddGrpc(options => {
builder.Services.AddGrpc(options =>
{
options.EnableDetailedErrors = true;
});
builder.Services.AddApplicationInsightsTelemetry(builder.Configuration);
builder.Services.AddApplicationInsightsKubernetesEnricher();
builder.Services.AddControllers(options => {
builder.Services.AddControllers(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
options.Filters.Add(typeof(ValidateModelStateFilter));
});
}) // Added for functional tests
.AddApplicationPart(typeof(BasketController).Assembly)
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
builder.Services.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new OpenApiInfo {
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "eShopOnContainers - Basket HTTP API",
Version = "v1",
Description = "The Basket Service HTTP API"
});
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme {
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows() {
Implicit = new OpenApiOAuthFlow() {
AuthorizationUrl = new Uri($"{builder.Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"),
TokenUrl = new Uri($"{builder.Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"),
Flows = new OpenApiOAuthFlows()
{
Implicit = new OpenApiOAuthFlow()
{
AuthorizationUrl = new Uri($"{builder.Configuration["IdentityUrlExternal"]}/connect/authorize"),
TokenUrl = new Uri($"{builder.Configuration["IdentityUrlExternal"]}/connect/token"),
Scopes = new Dictionary<string, string>() { { "basket", "Basket API" } }
}
}
@ -52,16 +45,20 @@ builder.Services.AddSwaggerGen(options => {
// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
var identityUrl = builder.Configuration.GetValue<string>("IdentityUrl");
var identityUrl = builder.Configuration["IdentityUrl"];
builder.Services.AddAuthentication("Bearer").AddJwtBearer(options => {
builder.Services.AddAuthentication().AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "basket";
options.TokenValidationParameters.ValidateAudience = false;
});
builder.Services.AddAuthorization(options => {
options.AddPolicy("ApiScope", policy => {
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "basket");
});
@ -71,131 +68,92 @@ builder.Services.AddCustomHealthCheck(builder.Configuration);
builder.Services.Configure<BasketSettings>(builder.Configuration);
builder.Services.AddSingleton<ConnectionMultiplexer>(sp => {
var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true);
return ConnectionMultiplexer.Connect(configuration);
});
builder.Services.AddRedis();
if (builder.Configuration.GetValue<bool>("AzureServiceBusEnabled")) {
builder.Services.AddSingleton<IServiceBusPersisterConnection>(sp => {
var serviceBusConnectionString = builder.Configuration["EventBusConnection"];
builder.Services.AddEventBus(builder.Configuration);
return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
});
}
else {
builder.Services.AddSingleton<IRabbitMQPersistentConnection>(sp => {
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
builder.Services.AddHttpContextAccessor();
var factory = new ConnectionFactory() {
HostName = builder.Configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(builder.Configuration["EventBusUserName"])) {
factory.UserName = builder.Configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(builder.Configuration["EventBusPassword"])) {
factory.Password = builder.Configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(builder.Configuration["EventBusRetryCount"])) {
retryCount = int.Parse(builder.Configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
builder.Services.RegisterEventBus(builder.Configuration);
builder.Services.AddCors(options => {
options.AddPolicy("CorsPolicy",
builder => builder
.SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddTransient<IBasketRepository, RedisBasketRepository>();
builder.Services.AddTransient<IIdentityService, IdentityService>();
builder.Services.AddOptions();
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory());
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.Configuration.AddEnvironmentVariables();
builder.WebHost.UseKestrel(options => {
builder.WebHost.UseKestrel(options =>
{
var ports = GetDefinedPorts(builder.Configuration);
options.Listen(IPAddress.Any, ports.httpPort, listenOptions => {
options.Listen(IPAddress.Any, ports.httpPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions => {
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
});
builder.WebHost.CaptureStartupErrors(false);
builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration));
builder.WebHost.UseFailing(options => {
builder.WebHost.UseFailing(options =>
{
options.ConfigPath = "/Failing";
options.NotFilteredPaths.AddRange(new[] { "/hc", "/liveness" });
});
var app = builder.Build();
app.MapGet("hello", () => "hello");
if (app.Environment.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
else {
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
var pathBase = app.Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase)) {
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
}
app.UseSwagger()
.UseSwaggerUI(setup => {
setup.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Basket.API V1");
setup.OAuthClientId("basketswaggerui");
setup.OAuthAppName("Basket Swagger UI");
});
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles();
app.UseSwagger();
app.UseSwaggerUI(setup =>
{
setup.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Basket.API V1");
setup.OAuthClientId("basketswaggerui");
setup.OAuthAppName("Basket Swagger UI");
});
app.MapGrpcService<BasketService>();
app.MapDefaultControllerRoute();
app.MapControllers();
app.MapGet("/_proto/", async ctx => {
app.MapGet("/_proto/", async ctx =>
{
ctx.Response.ContentType = "text/plain";
using var fs = new FileStream(Path.Combine(app.Environment.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read);
using var sr = new StreamReader(fs);
while (!sr.EndOfStream) {
while (!sr.EndOfStream)
{
var line = await sr.ReadLineAsync();
if (line != "/* >>" || line != "<< */") {
if (line != "/* >>" || line != "<< */")
{
await ctx.Response.WriteAsync(line);
}
}
});
app.MapHealthChecks("/hc", new HealthCheckOptions() {
app.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.MapHealthChecks("/liveness", new HealthCheckOptions {
app.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
ConfigureEventBus(app);
try {
try
{
Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName);
@ -204,15 +162,18 @@ try {
return 0;
}
catch (Exception ex) {
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName);
return 1;
}
finally {
finally
{
Log.CloseAndFlush();
}
Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) {
Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
var logstashUrl = configuration["Serilog:LogstashgUrl"];
return new LoggerConfiguration()
@ -226,59 +187,23 @@ Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) {
.CreateLogger();
}
(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config) {
(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config)
{
var grpcPort = config.GetValue("GRPC_PORT", 5001);
var port = config.GetValue("PORT", 80);
return (port, grpcPort);
}
void ConfigureEventBus(IApplicationBuilder app) {
void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>();
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>();
}
public partial class Program {
public static string Namespace = typeof(Program).Assembly.GetName().Name;
public partial class Program
{
private static string Namespace = typeof(Program).Assembly.GetName().Name;
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
}
public static class CustomExtensionMethods {
public static IServiceCollection RegisterEventBus(this IServiceCollection services, IConfiguration configuration) {
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) {
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => {
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubscriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubscriptionsManager, sp, subscriptionName);
});
}
else {
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => {
var subscriptionClientName = configuration["SubscriptionClientName"];
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubscriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) {
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubscriptionsManager, subscriptionClientName, retryCount);
});
}
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddTransient<ProductPriceChangedIntegrationEventHandler>();
services.AddTransient<OrderStartedIntegrationEventHandler>();
return services;
}
}

+ 0
- 6
src/Services/Basket/Basket.API/TestHttpResponseTrailersFeature.cs View File

@ -1,6 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Basket.API;
internal class TestHttpResponseTrailersFeature : IHttpResponseTrailersFeature
{
public IHeaderDictionary Trailers { get; set; }
}

+ 0
- 17
src/Services/Basket/Basket.API/web.config View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" hostingModel="InProcess">
<environmentVariables>
<environmentVariable name="COMPLUS_ForceENC" value="1" />
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</configuration>

+ 1
- 0
src/Services/Basket/Basket.FunctionalTests/Base/AutoAuthorizeMiddleware.cs View File

@ -18,6 +18,7 @@ class AutoAuthorizeMiddleware
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("scope", "basket"));
httpContext.User.AddIdentity(identity);


+ 36
- 14
src/Services/Basket/Basket.FunctionalTests/Base/BasketScenarioBase.cs View File

@ -1,23 +1,15 @@
namespace Basket.FunctionalTests.Base;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Hosting;
public class BasketScenarioBase
namespace Basket.FunctionalTests.Base;
public class BasketScenarioBase : WebApplicationFactory<Program>
{
private const string ApiUrlBase = "api/v1/basket";
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();
});
return new TestServer(hostBuilder);
return Server;
}
public static class Get
@ -33,4 +25,34 @@ public class BasketScenarioBase
public static string Basket = $"{ApiUrlBase}/";
public static string CheckoutOrder = $"{ApiUrlBase}/checkout";
}
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.AddSingleton<IStartupFilter, AuthStartupFilter>();
});
builder.ConfigureAppConfiguration(c =>
{
var directory = Path.GetDirectoryName(typeof(BasketScenarioBase).Assembly.Location)!;
c.AddJsonFile(Path.Combine(directory, "appsettings.json"), optional: false);
});
return base.CreateHost(builder);
}
private class AuthStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseMiddleware<AutoAuthorizeMiddleware>();
next(app);
};
}
}
}

+ 0
- 13
src/Services/Basket/Basket.FunctionalTests/Base/HttpClientExtensions.cs View File

@ -1,13 +0,0 @@
namespace Basket.FunctionalTests.Base;
static class HttpClientExtensions
{
public static HttpClient CreateIdempotentClient(this TestServer server)
{
var client = server.CreateClient();
client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString());
return client;
}
}

+ 0
- 4
src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj View File

@ -6,10 +6,6 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>


+ 9
- 7
src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs View File

@ -1,15 +1,15 @@
namespace Basket.FunctionalTests;
public class BasketScenarios
: BasketScenarioBase
public class BasketScenarios :
BasketScenarioBase
{
[Fact]
public async Task Post_basket_and_response_ok_status_code()
{
using var server = CreateServer();
var content = new StringContent(BuildBasket(), UTF8Encoding.UTF8, "application/json");
var response = await server.CreateClient()
.PostAsync(Post.Basket, content);
var uri = "/api/v1/basket/";
var response = await server.CreateClient().PostAsync(uri, content);
response.EnsureSuccessStatusCode();
}
@ -20,7 +20,6 @@ public class BasketScenarios
using var server = CreateServer();
var response = await server.CreateClient()
.GetAsync(Get.GetBasket(1));
response.EnsureSuccessStatusCode();
}
@ -33,9 +32,12 @@ public class BasketScenarios
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")
{
Headers = { { "x-requestid", Guid.NewGuid().ToString() } }
};
var response = await server.CreateIdempotentClient()
var response = await server.CreateClient()
.PostAsync(Post.CheckoutOrder, contentCheckout);
response.EnsureSuccessStatusCode();


+ 2
- 7
src/Services/Basket/Basket.FunctionalTests/RedisBasketRepositoryTests.cs View File

@ -9,8 +9,7 @@ namespace Basket.FunctionalTests
[Fact]
public async Task UpdateBasket_return_and_add_basket()
{
using var server = CreateServer();
var redis = server.Host.Services.GetRequiredService<ConnectionMultiplexer>();
var redis = Services.GetRequiredService<ConnectionMultiplexer>();
var redisBasketRepository = BuildBasketRepository(redis);
@ -22,16 +21,12 @@ namespace Basket.FunctionalTests
Assert.NotNull(basket);
Assert.Single(basket.Items);
}
[Fact]
public async Task Delete_Basket_return_null()
{
using var server = CreateServer();
var redis = server.Host.Services.GetRequiredService<ConnectionMultiplexer>();
var redis = Services.GetRequiredService<ConnectionMultiplexer>();
var redisBasketRepository = BuildBasketRepository(redis);


+ 3
- 1
src/Services/Basket/Basket.FunctionalTests/appsettings.json View File

@ -1,6 +1,8 @@
{
"Logging": {
"IncludeScopes": false,
"Console": {
"IncludeScopes": false
},
"LogLevel": {
"Default": "Debug",
"System": "Information",


+ 3
- 5
src/Services/Catalog/Catalog.API/Catalog.API.csproj View File

@ -14,7 +14,7 @@
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
@ -25,10 +25,8 @@
<Content Include="Setup\**\*;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Remove="Setup\Catalogitems - Copy.zip" />
<None Remove="Setup\Catalogitems - Copy.zip" />
<Compile Include="IntegrationEvents\EventHandling\AnyFutureIntegrationEventHandler.cs.txt" />
<Content Update="web.config;">
<None Include="IntegrationEvents\EventHandling\AnyFutureIntegrationEventHandler.cs.txt" />
<Content Update="web.config">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>


+ 11
- 11
src/Services/Catalog/Catalog.API/Extensions/WebHostExtensions.cs View File

@ -2,23 +2,23 @@
public static class WebHostExtensions
{
public static bool IsInKubernetes(this IWebHost host)
public static bool IsInKubernetes(this IServiceProvider services)
{
var cfg = host.Services.GetService<IConfiguration>();
var cfg = services.GetService<IConfiguration>();
var orchestratorType = cfg.GetValue<string>("OrchestratorType");
return orchestratorType?.ToUpper() == "K8S";
}
public static IWebHost MigrateDbContext<TContext>(this IWebHost host, Action<TContext, IServiceProvider> seeder) where TContext : DbContext
public static IServiceProvider MigrateDbContext<TContext>(this IServiceProvider services, Action<TContext, IServiceProvider> seeder) where TContext : DbContext
{
var underK8s = host.IsInKubernetes();
var underK8s = services.IsInKubernetes();
using var scope = host.Services.CreateScope();
var services = scope.ServiceProvider;
using var scope = services.CreateScope();
var scopedServices = scope.ServiceProvider;
var logger = services.GetRequiredService<ILogger<TContext>>();
var logger = scopedServices.GetRequiredService<ILogger<TContext>>();
var context = services.GetService<TContext>();
var context = scopedServices.GetService<TContext>();
try
{
@ -26,7 +26,7 @@ public static class WebHostExtensions
if (underK8s)
{
InvokeSeeder(seeder, context, services);
InvokeSeeder(seeder, context, scopedServices);
}
else
{
@ -42,7 +42,7 @@ public static class WebHostExtensions
//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));
retry.Execute(() => InvokeSeeder(seeder, context, scopedServices));
}
logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name);
@ -56,7 +56,7 @@ public static class WebHostExtensions
}
}
return host;
return services;
}
private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services)


+ 1
- 1
src/Services/Catalog/Catalog.API/Grpc/CatalogService.cs View File

@ -9,7 +9,7 @@ public class CatalogService : CatalogBase
private readonly CatalogContext _catalogContext;
private readonly CatalogSettings _settings;
private readonly ILogger _logger;
public CatalogService(CatalogContext dbContext, IOptions<CatalogSettings> settings, ILogger<CatalogService> logger)
{
_settings = settings.Value;


+ 1
- 1
src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs View File

@ -316,7 +316,7 @@ public class CatalogContextSeed
if (csvheaders.Count() < requiredHeaders.Count())
{
throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is bigger then csv header count '{csvheaders.Count()}' ");
throw new Exception($"requiredHeader count '{requiredHeaders.Count()}' is bigger then csv header count '{csvheaders.Count()}' ");
}
if (optionalHeaders != null)


+ 1
- 1
src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling;
public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler :
IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent>
{


+ 120
- 64
src/Services/Catalog/Catalog.API/Program.cs View File

@ -1,11 +1,12 @@
var appName = "Catalog.API";
var builder = WebApplication.CreateBuilder(new WebApplicationOptions {
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ApplicationName = typeof(Program).Assembly.FullName,
ContentRootPath = Directory.GetCurrentDirectory(),
WebRootPath = "Pics",
});
if (builder.Configuration.GetValue<bool>("UseVault", false)) {
if (builder.Configuration.GetValue<bool>("UseVault", false))
{
TokenCredential credential = new ClientSecretCredential(
builder.Configuration["Vault:TenantId"],
builder.Configuration["Vault:ClientId"],
@ -13,12 +14,15 @@ if (builder.Configuration.GetValue<bool>("UseVault", false)) {
//builder.AddAzureKeyVault(new Uri($"https://{builder.Configuration["Vault:Name"]}.vault.azure.net/"), credential);
}
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.WebHost.UseKestrel(options => {
builder.WebHost.UseKestrel(options =>
{
var ports = GetDefinedPorts(builder.Configuration);
options.Listen(IPAddress.Any, ports.httpPort, listenOptions => {
options.Listen(IPAddress.Any, ports.httpPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions => {
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
@ -35,20 +39,24 @@ builder.Services.AddGrpc().Services
var app = builder.Build();
if (app.Environment.IsDevelopment()) {
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else {
else
{
app.UseExceptionHandler("/Home/Error");
}
var pathBase = app.Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase)) {
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
}
app.UseSwagger()
.UseSwaggerUI(c => {
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Catalog.API V1");
});
@ -56,39 +64,45 @@ app.UseRouting();
app.UseCors("CorsPolicy");
app.MapDefaultControllerRoute();
app.MapControllers();
app.UseFileServer(new FileServerOptions {
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Pics")),
RequestPath = "/pics"
});
app.UseStaticFiles(new StaticFileOptions {
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Pics")),
RequestPath = "/pics"
});
app.MapGet("/_proto/", async ctx => {
app.MapGet("/_proto/", async ctx =>
{
ctx.Response.ContentType = "text/plain";
using var fs = new FileStream(Path.Combine(app.Environment.ContentRootPath, "Proto", "catalog.proto"), FileMode.Open, FileAccess.Read);
using var sr = new StreamReader(fs);
while (!sr.EndOfStream) {
while (!sr.EndOfStream)
{
var line = await sr.ReadLineAsync();
if (line != "/* >>" || line != "<< */") {
if (line != "/* >>" || line != "<< */")
{
await ctx.Response.WriteAsync(line);
}
}
});
app.MapGrpcService<CatalogService>();
app.MapHealthChecks("/hc", new HealthCheckOptions() {
app.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.MapHealthChecks("/liveness", new HealthCheckOptions {
app.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
ConfigureEventBus(app);
try {
try
{
Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName);
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<CatalogContext>();
@ -100,50 +114,60 @@ try {
await new CatalogContextSeed().SeedAsync(context, env, settings, logger);
var integEventContext = scope.ServiceProvider.GetRequiredService<IntegrationEventLogContext>();
await integEventContext.Database.MigrateAsync();
app.Logger.LogInformation("Starting web host ({ApplicationName})...", appName);
app.Logger.LogInformation("Starting web host ({ApplicationName})...", AppName);
await app.RunAsync();
return 0;
}
catch (Exception ex) {
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName);
return 1;
}
finally {
finally
{
Log.CloseAndFlush();
}
void ConfigureEventBus(IApplicationBuilder app) {
void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<OrderStatusChangedToAwaitingValidationIntegrationEvent, OrderStatusChangedToAwaitingValidationIntegrationEventHandler>();
eventBus.Subscribe<OrderStatusChangedToPaidIntegrationEvent, OrderStatusChangedToPaidIntegrationEventHandler>();
}
(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config) {
(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config)
{
var grpcPort = config.GetValue("GRPC_PORT", 81);
var port = config.GetValue("PORT", 80);
return (port, grpcPort);
}
public partial class Program {
public partial class Program
{
public static string Namespace = typeof(Program).Assembly.GetName().Name;
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
}
public static class CustomExtensionMethods {
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) {
public static class CustomExtensionMethods
{
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration)
{
services.AddApplicationInsightsTelemetry(configuration);
services.AddApplicationInsightsKubernetesEnricher();
return services;
}
public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration) {
services.AddControllers(options => {
public static IServiceCollection AddCustomMVC(this IServiceCollection services, IConfiguration configuration)
{
services.AddControllers(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
})
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
services.AddCors(options => {
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.SetIsOriginAllowed((host) => true)
@ -155,7 +179,8 @@ public static class CustomExtensionMethods {
return services;
}
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) {
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration)
{
var accountName = configuration.GetValue<string>("AzureStorageAccountName");
var accountKey = configuration.GetValue<string>("AzureStorageAccountKey");
@ -168,7 +193,8 @@ public static class CustomExtensionMethods {
name: "CatalogDB-check",
tags: new string[] { "catalogdb" });
if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey)) {
if (!string.IsNullOrEmpty(accountName) && !string.IsNullOrEmpty(accountKey))
{
hcBuilder
.AddAzureBlobStorage(
$"DefaultEndpointsProtocol=https;AccountName={accountName};AccountKey={accountKey};EndpointSuffix=core.windows.net",
@ -176,7 +202,8 @@ public static class CustomExtensionMethods {
tags: new string[] { "catalogstorage" });
}
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) {
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
hcBuilder
.AddAzureServiceBusTopic(
configuration["EventBusConnection"],
@ -184,7 +211,8 @@ public static class CustomExtensionMethods {
name: "catalog-servicebus-check",
tags: new string[] { "servicebus" });
}
else {
else
{
hcBuilder
.AddRabbitMQ(
$"amqp://{configuration["EventBusConnection"]}",
@ -195,20 +223,25 @@ public static class CustomExtensionMethods {
return services;
}
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) {
public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddEntityFrameworkSqlServer()
.AddDbContext<CatalogContext>(options => {
.AddDbContext<CatalogContext>(options =>
{
options.UseSqlServer(configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions => {
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Program).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<IntegrationEventLogContext>(options => {
services.AddDbContext<IntegrationEventLogContext>(options =>
{
options.UseSqlServer(configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions => {
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Program).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);
@ -218,17 +251,22 @@ public static class CustomExtensionMethods {
return services;
}
public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration) {
public static IServiceCollection AddCustomOptions(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<CatalogSettings>(configuration);
services.Configure<ApiBehaviorOptions>(options => {
options.InvalidModelStateResponseFactory = context => {
var problemDetails = new ValidationProblemDetails(context.ModelState) {
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
var problemDetails = new ValidationProblemDetails(context.ModelState)
{
Instance = context.HttpContext.Request.Path,
Status = StatusCodes.Status400BadRequest,
Detail = "Please refer to the errors property for additional details."
};
return new BadRequestObjectResult(problemDetails) {
return new BadRequestObjectResult(problemDetails)
{
ContentTypes = { "application/problem+json", "application/problem+xml" }
};
};
@ -237,9 +275,12 @@ public static class CustomExtensionMethods {
return services;
}
public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration) {
services.AddSwaggerGen(options => {
options.SwaggerDoc("v1", new OpenApiInfo {
public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration)
{
services.AddSwaggerGen(options =>
{
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"
@ -250,40 +291,49 @@ public static class CustomExtensionMethods {
}
public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration) {
public static IServiceCollection AddIntegrationServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
services.AddTransient<ICatalogIntegrationEventService, CatalogIntegrationEventService>();
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) {
services.AddSingleton<IServiceBusPersisterConnection>(sp => {
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var settings = sp.GetRequiredService<IOptions<CatalogSettings>>().Value;
var serviceBusConnection = settings.EventBusConnection;
return new DefaultServiceBusPersisterConnection(serviceBusConnection);
});
}
else {
services.AddSingleton<IRabbitMQPersistentConnection>(sp => {
else
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var settings = sp.GetRequiredService<IOptions<CatalogSettings>>().Value;
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory() {
var factory = new ConnectionFactory()
{
HostName = configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) {
if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{
factory.UserName = configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) {
if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{
factory.Password = configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) {
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
@ -294,9 +344,12 @@ public static class CustomExtensionMethods {
return services;
}
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) {
if (configuration.GetValue<bool>("AzureServiceBusEnabled")) {
services.AddSingleton<IEventBus, EventBusServiceBus>(sp => {
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
@ -307,15 +360,18 @@ public static class CustomExtensionMethods {
});
}
else {
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => {
else
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var subscriptionClientName = configuration["SubscriptionClientName"];
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) {
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
@ -329,4 +385,4 @@ public static class CustomExtensionMethods {
return services;
}
}
}

+ 17
- 17
src/Services/Catalog/Catalog.FunctionalTests/CatalogScenarioBase.cs View File

@ -1,24 +1,12 @@
using Microsoft.AspNetCore.Mvc.Testing;
namespace Catalog.FunctionalTests;
public class CatalogScenariosBase
public class CatalogScenariosBase : WebApplicationFactory<Program>
{
public TestServer CreateServer()
{
var path = Assembly.GetAssembly(typeof(CatalogScenariosBase))
.Location;
var hostBuilder = new WebHostBuilder()
.UseContentRoot(Path.GetDirectoryName(path))
.ConfigureAppConfiguration(cb =>
{
cb.AddJsonFile("appsettings.json", optional: false)
.AddEnvironmentVariables();
});
var testServer = new TestServer(hostBuilder);
testServer.Host
Services
.MigrateDbContext<CatalogContext>((context, services) =>
{
var env = services.GetService<IWebHostEnvironment>();
@ -31,7 +19,19 @@ public class CatalogScenariosBase
})
.MigrateDbContext<IntegrationEventLogContext>((_, __) => { });
return testServer;
return Server;
}
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureAppConfiguration(c =>
{
var directory = Path.GetDirectoryName(typeof(CatalogScenariosBase).Assembly.Location)!;
c.AddJsonFile(Path.Combine(directory, "appsettings.json"), optional: false);
});
return base.CreateHost(builder);
}
public static class Get


+ 1
- 1
src/Services/Identity/Identity.API/IWebHostExtensions.cs View File

@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Hosting
}
}
return webHost;
return webHost;
}
private static void InvokeSeeder<TContext>(Action<TContext, IServiceProvider> seeder, TContext context, IServiceProvider services)


+ 1
- 1
src/Services/Identity/Identity.API/ProgramExtensions.cs View File

@ -9,7 +9,7 @@ public static class ProgramExtensions
public static void AddCustomConfiguration(this WebApplicationBuilder builder)
{
builder.Configuration.AddConfiguration(GetConfiguration()).Build();
}
public static void AddCustomSerilog(this WebApplicationBuilder builder)


+ 2
- 2
src/Services/Identity/Identity.API/Quickstart/Account/AccountController.cs View File

@ -127,7 +127,7 @@ namespace IdentityServerHost.Quickstart.UI
}
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.Client.ClientId));
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId));
ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
}
@ -139,7 +139,7 @@ namespace IdentityServerHost.Quickstart.UI
return View(vm);
}
/// <summary>
/// Show logout page
/// </summary>


+ 0
- 1
src/Services/Identity/Identity.API/keys/is-signing-key-10C452043E09C5A22FC8D97669868B8F.json View File

@ -1 +0,0 @@
{"Version":1,"Id":"10C452043E09C5A22FC8D97669868B8F","Created":"2022-11-30T12:03:40.1630635Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8JmnWj_BpdBDujWYr1NmSawi1hoBHaFG7R1_Hu0pSObE9SiTNg1Wm83zVA3-oCwbntipelQt_gpZ3KH-NMBYdleYt4Gzhpvf4uchFNFzvdnx1X9jpWQi9WCmUm3cVNfzNG2eIKzrVLAoiaWLuDDG9XT-u2ojWIuKRKH7zEBMGrqCDQiVHZlBuqH_qzpWUQn-qKYEkFRcm05OmMYXJA0AquXimdOl1V_BcagZOpNVzG3C5t7lZTsTrm7FpD5zMdyZxL2VJJjRy5fUZbIQIjbQdTGYJaROhj-Sc4h2MgyrMNYJ-TQZKci_5ZoN-GJfjdaB-3UHDk_uFrVk9WpAYEWw0rw-I05tMu8vVxlE2jAnEGaVWEqS0f_Bz53MkDQb2YEleekTwaLJM1JZF791NTaG7oDXHSetSeo7UngHt3D9Ls2FHnZwV-B5mvzZju_-U7inwsoKXjZeqzQfQMs2IsIgiXJJkS20PObmosEbq7LkT97qRrinGicY0pA2zIcDmLiywYzWpsP6Rjw7epDaDlPu2J9BZ93Ni-slfTkNdn2SvAfE2dsJmV7Pz5tMEE-0mBlSMbcrs8TuVQayYPm2dm-sZLCv-iQu0rrvGcJ-lLPliby69du5oFU6WKpo52X4Du9tcM-5prBLWRVt_gcq16zBY_f8ieNnq6b0C_9chaztCevTQAouiSk7ni3zjJo3RuG1wPUts2tLT4sjY6IK2Zbhsvi-negegn91cFA0MzdOaD2Ca2GWN2L2kRrDWioVz_P4ztVFIU3SrgjW6stOGNreRvOB8txUCexJuUP0rb85pOa8rxPCkk1Iw8BjjRdirhvU3bvqhml6i2_iqNTAxpx89A6LRUuOYvYiwJersfhOF9F-FfOVGNxxfm1AKKnUBDfgLWDAw1r8PBeV6huAqUSIwaN5PcQJcdsfER9FeP9_CJqDgNGaL-psm2NQDaVV2Vx4m3GqWQhKMlk90zee5XNe1wobBS0XW_GQYieWWhH5t0pyzJi0QvpFyMAz1mcGXQs-TiHoU142FlFQojIu3_Ynf2RHOOByWDM9krMKMn6ZTnLWgBXnWtMefXqYNEhlACtP8lIXBxcbI2wFeBgRmaQA2DtEEjlymZ4RTaiQEuVC6B_wtTTVVFx3CL-TM62Xs-FOWTYafzKqGsHKzVWnB3fZqjDh4DNwb_A4mkfxoyb2UI9W4P7BJmbV5tOktYudVAFLt3XL7uNhBYLDSOjL_n8bixTg-sLUyO1H5-IxYQJciZdAro4yGO7r40FTYpZfTyvlr3RqFMDhBEPu_nSx9lPKrgpei0DmqYWw_cHJ3mPR1HDUE_i8Prlyzsyf9F-pYV4HMyupetXeXxMAIULnB52SE7uYfSUZHC25Ffo_tif8zMPo2ZKiMpc42BZ0cBptkPKUCvkR-Cq3pFnqXkKADWh1LLBP09hZtOjXqkFdsIFZq5KfRE6UVIyMJ91L-OSHOnggdjScFJzPlgEteUbydKSlPEBkicIs-uXXe8Tk8KZg2CfUU2WnziP0-VgymbTomKR5xz36kJqMjP2VYp6Xy-t3joQEqPE50ntT0AYUFJb6NZt5stHQz76-WecPnA6ZSA3aJec7NlLrxwM6BMGDyBZpbkgWtWFJdGWz6m9j4RKuyZBnyOXzYZU8FDNpDu6FVEymUkYxEhUnpIl7PZWuiZGADwj9-Bpzg0To8jMJ-jgx8w3RDzb5DoM6-jh0IJ5vecn8lSfRzHcOu48CLY5mTuHFr93YKiQCK06DS8VY3kI1b8irtdvmY-IvUCyCJlpzBYh8vFrS2PdJYsRVWuGJraxMiHYP_zH5r4Q4z27p6vt0Rbm0S4Fm1I9z4Hmaa4igF1mnktuTMizyxKWTsh3DjbsB_6W_lNU62RqJAt_IGr69MIphVY6ZOcbLx2I8p0z4yWpuYJey2BmRQPQeCZ-Tb9HtHESxTBFxupYul8619OJWA7RfLl-xv43X81OZnaxE9wLVFs5uEowKoEOzl_K5nxOw4igCiWYWufaFMMrZSu-0ni9RVSwytOPiEbGV0XOcVnB_lHDz1ymvDI4u1rfOVkehwB_wFBeRJRSEymzoD-bgRox-a1qLJNhOsdm9WUdZF3MHbmmA8NpyqU5qOTTqCvY-FXdJHL_OHmq1nDVHCJvcwLqUfPFMfFAi0MZQEhRndeQWYGdRGOhmvtwbRX-Hryz2HKXber1ixUvY3-XCxjzSyZdn845UzpKBdsgwAwY-LFzlgNQGEKHzsZUl8MSeqx3iO2QwDwC5eV4gNWBC5QBw0NAloBmdnQ84sSWydPViOYW1CinUQAnZsW2Z-0Xw0FP4fbS4HvulNBMIxE_mfJsHny4YZu_463zGR0SrV2hEFSiXuwSx27K6xSyPRB26__t8w37AeFYXtavqr9rB5GXnEJRQkW-i5lDnhtA93uRjfNzVhUpcTK-ROm3h7b4UrUTaK_IMLXxoYEjOFm8-cw1ZGHu3","DataProtected":true}

+ 0
- 1
src/Services/Identity/Identity.API/keys/is-signing-key-1417AE0BAF26F393609EF30FA355D06C.json View File

@ -1 +0,0 @@
{"Version":1,"Id":"1417AE0BAF26F393609EF30FA355D06C","Created":"2022-11-30T15:12:36.3392917Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8Etya4tS5j5DiLTWt2Gh6S_0SY7Shff7x3C-zhStPviP80hTMRjXNVXCumUQnwoKrJ48OLZhHuU7wgSxqXaAT3hk35KvO_KOhz5pV_sWLPOogHC-hpQTIzSEbXzyfEIkliqVgW3AfpLbuDTZY4Hj8hu-OM0waYEjCQkIv-PrftxX7JU4KCT1rxZBUrDJN_mKCQEGWy0LGLVsLV8dKyLMSvJsIz3HJh4sngzist-XVaPxat2eMPqVOJEWkbUf6VqwJOnssR-ZOS9b63YZPhihUcz6J0T5Y2aWIyZfZwSkuh8DiL9_wzKSRTmplaY1tzeq_-FhbkgwtLX9VDbeesx_HGL_Pxj5SG5aprfZ7DN2FBr70m7olb-bEl8jOm0w7ot0QGwxHft53Nf9piLEyQp4hsReq-PNwCirzFNkQ5aM6Kwvwitkop_Ab8gpzfwiEw-uVyPN_gpkrt099MQrdtljc9gXHKuXr4321pgcbwO6Ua-amRcF_9uYSCVBdIhwmpaQAgMmI-0uI-5Ph7H23ZV2zR1epClkiR2t5RWOtXNgLqoupXyBjDWrse3al-CsdnkofmtY0bqr2ZdNd_EKFxqn7Gf5-dNuPJEboDUdwvjUpWYIzMlq6_3q9KP012SbimkqMgz3-3jDBw_xe43DvZd2CnWFTDQSuD0m7DTf2GVL0DlasQNYQgO1T7xN7hgScsXmzhJNUx7iz2ze5BMqWV3vw6P6ny4sj_QA4KAi0zxl7sPgPo2p-65MWlbZy646ZYlm-jDD9dfORdo9nmURg3ITYNVUREtVg4IcEzPVDBMnV-NEf3T3vHD1V49yVhovA9oAHj4uTye-bbLUIhoLPCZ5GfR8AEHE4IhL0JyjuaWilp1qNEXAhziwZUELenYAf74wEzMvSSobEWL5Hq-ApMdRePBjjeGrIobS6KuE7b3HVsYwJt9pWvmVCYZ-nQCDAOIFcgH5kifkHu6NEi23Jk7a5hmgq1kVJXr5xcli9B-bHu1mO1kl0gXsqJcGpfetcw8bEtwO8F_6kdkmW3RXLa3rvrnkigPJJtn4sm_3ee1dqFYarJlXHPugwuyzfEPXNiT1HCcDhQYlrYxfKSzmlhjthGn14W2ZYtb3YVCXUFReA8V20rs4ec7VTdjuKCPgA8bk3xKevr6aETF9I65hbI0pIFQ9_cJHkl9a3BZL-X5qSH2s3_rvPupVuP6mu-Y1pUEGp4p5XjZc0CqsLm-W6MsUKmBgeCiokmUGfs5P_Riv9ZuvlHyYcwj2qmgf4MDkmhThfisook1nrPomO1lByHMRHx8rwbVQQXHPKMI32WZxfpRQiuAERonMvLBd2P2UOutqe6t7qy_gbeM0WNEcKb08UowSxGUFnEH6Vk1Dx9Vzye2tw7ej-EsT39hudhe_59nd9IYvx1o547N1cI_ASDVn3ibVQU_BWLrSK-HIqGaA52F_hthPGpMjVBO8xtLXHiyxqI-gh9iLq0vpvMeFQjyLSmFjKQifMEVaATAF9h2TJTc0yqkIVN9y77LvkUDoFvi96zGSYDyIf6rtiF_E1GnAwu-6Le16bj9npI-FcNmh-DRIvA6LepbSXs27rVMjQdK6dOPDavu5Y9IM5bg0pndKek0orZzkbaFwxTxmM08AhKlyN6PPiIqcZ7i456FQDhqMTUEeBEShsOJy4573e0GA-J1sHvzVQYiKDlzvgYD-zh79OxnJXlq22BNrWsQ6pqXfXgB5UIvhUTrmcrUtv3uw6GcAyl76ySgfs3Hb9Mpk0aeUdI-xyMuxxJvz1w1NffcuXPsxf3VosZO8vkofjVSO8lGtzarScy-abFGD9UA-S7xGvz0rqRSAEnpxe4AZ5eBfs_VmTy21RYJqxPbrYWcjrRdQjxERhND70wiuLkF1pivUQ-bohP3w0EfV0fJoSoH9HthQ9_aY2ASJnv82BX_lPRdEgYVh1RYWziVGB_6WWkch8BLrUzXezJZcfQHGuEQYzVB17RQIhCqFo7GexmHkFB40punvvS1gUZOQLiqSu8UvJ27F5KNLpkgDy8q0pDWewRGmZ5J09aMlJE6e08u_gPrA9G52SWXvdkX8CRvPn-xOLsBfb1_84QBhB1PuSKeptNLjgMufTdfUkGrL_b3fTo4AgLz7VuKrAMNYjwCTLr4e6towu-JJ08Fr3c8igbWApP6yNiVbE1W1mX66jaRfyw3jiVAP2ytEH_0kLOE3BDFkepIopwGtkQ2ultqFHxr_-oSadSpP3g4dLt1GfQ7fjzYkYAkstXxgYgGGe7Q-mwQM1dR8RnE0jFvQnTKi-Oc_pj0h91HgRK_ASQrqrgOf9JN6McbVlE3VbEKvFfOKP8ukFa6i8n7tk1H0B8bX5iy4m_b3i3MbQmQ16gkZ2ECW29-bA4vWz6-R3LZ3kf2hHAJzv8U6cG71KLVgWSBxIyRJE-cSiBPxtM1hGT1qcJvpAVWvowkcO9Anng3lrfgnGaYh","DataProtected":true}

+ 0
- 1
src/Services/Identity/Identity.API/keys/is-signing-key-22178AEDB80CD0A41DF6874FE93F794D.json View File

@ -1 +0,0 @@
{"Version":1,"Id":"22178AEDB80CD0A41DF6874FE93F794D","Created":"2022-11-29T07:52:44.7571677Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8E4RSuU_s2BGnQ_I0J1Xz9YiYIkBDFNHHGo4A20gtVeWqoNIQNxtP1p0JacgjeZkGl9NlE2-7qD02Vs9NIZ_IfOHW71XkfBIuIp5JvnwMi-X42Qw1F6UTQKNlYNvk1Fh27a6tM-mQgxysayCdDLlJWoRJYKDPDkUO03rZVkWDD2r6QNcMNtYhjM7oxy2yiw479PIeIldPOk73cPqp_k4oMPTRWwS04dX8fpQNA_3d2PoV1sw8-Hbmspiv3PO-umUGU2OFcN243cP9aaGXDoR-RgEbCn225WuHAWTe3tf39zVDtbAWDnRTNEX_jCNBItSaFOdMerDKD28PXTnWXuoMKyZ1_8ETZk0CDMAMVktqOatYrx0JHisG8vJbHtzxIvQrZL1YqoAN_qraVLgTl99WIZv56rYSb3RbNSzqB7yNn1Y_YAobXVbqJCaqqVG6uEaQxQ4WRI78sit630anIlKP7vccOtJH_bspQlPisNVrpCWiGo0D0IzNsZXLNPkc9kUbJKGc3xgE5H-z9kbk3sOecVH_iQy4b6fJ0IrDGz9fIhu2UI0-_lUE6sQ0g-NAA9kW68vD6PVSytiag7tFVV0olRPhaiBkSRuNGKjqsPmlPtkpzVxuTxSj19YxoC200cFARZq68qjxZ7ugdHQGlqd6bKFjj6NWCfG3Sbub1HpTkPnEUIR6ic8EfUrbu18MlOh-7bnIJkIuz0Nr5zZ9tPgiJCdGPjNo47z4i-kPWiNhS9k1sJqg60dgCT3onC2Uft4F4dnQpIKb28Fli9qkTDOg9gObEr9W19tMJvEPAhWJSl6HSmIJ-TElrW7nNCdS-H7PIlGWIvP6f0CCzrlgtsfMh8TK0B-Vpv5ky9D67L03IA7VfrhHss5mEkoz2s5-yefn2Wo5uMTxy53O4GIS8j3lF0ufUIBjGqR5q6IyEQUyU79OA5A7A3gJx78Cb-OEK3GixhYwjXYOw1YcguMZo_rGsq-EnjPoVBuZfdwyEIZ847gVwDzFsh0FHZIi2vVxXZ3tNGjJI2TXxNx7nyXuBxERgCPI3C5OeK2Uy8e3ooZCqPICKS9xEnICZteEcSYkRFmvPX6O1pZMKWoiyzGe-bhuBiotTB3yGIVlkhMyqNni2hQHoR5b1CX4YaGBMT_fdtW4uhvYLhLnGR69aal2ffIhiFJMfB_ByI8sALzgsn5jW-zOAmtiEOaPs1O9FqPf1x3Wk2YuoyujgHjHi9SSUz1hAJRmsyCmcYsMdGjMd-wnk83dsdMMeNANTKJHlZP5-__XA8emO-sloHrCiHpALX-JB5RQ1BGVD-dPiTlM0MYeTLdt7y5HRKSAbsOCnnF1vzV8y8NkUgpKVLTPRsJaoNYqik4Wpoa2d2ez-gXrG9S-pAByXHyTHiVDVNyqUTDMI0s8Fm_6_QwJTkgGEV6dR6r89Rzq70FFjdKB5RygRLhRdF2Be8blyExHI3HQg8_S9fcZ3zCybjatlNuNCgNZTTelC1UzWSEql3HqeqiMWIi9Kr8GNl59OIGm2lm9KNoyvcFHlQVQ33lw4akd5Z7aYu_HcdpBhMcEr9CuN6yQPhbCDOVhsMkqzXGzpwC2ezFnALAkbj9P44vRU9p0FEKFDNn_pQimVTLQJoU5Taf1n70CxYcPgM9BU3JPL9VUb9rDS3bHA7XQoO6878k6PkLbJ6GcfIJitaRZL5TkAUJUywH-a5xADVo-1_k3ADJYhOTbwxsYU96Kuhw8FOIToBH8CtLnqJLoJ8iwVPjB9LSsHPhmR7Rsi-x7Oi5JeugLctOvBn1sq7x_hHQKPVHA5LvtQ6Y719bOgPKLybQSCebe0ordUIsPAZMq-OvLj1sPS8PvfvZBDkyUA04avHm5kgT6RJF7ARUI1vMNLijm-_dxxrzY2bj6FHT340iFDURFePLdSM-Ctu3X-gLXKqPzQ6MBBQS0pyC3GkuX7J_GhPOZ-yXvRJmvIEoDAVEvjUQc57Ud6nyS_C_ljLmIoHghJiTJxNOOjR5pucb9Vljxm_BGPZwMi6-bTbD454ESbZ1kyvOAG73PjnPoQSaEYyM3XLW3XgJ9R4dnYDk31zHpQebodqwcFsXzxV43yi7zPrJSMzaFnC3-uPQ7qH4xs9puX5RL4q4lSCUKAB82TMtCJ0xHwj4DYqsJQt9jUAz1IZbgbChq8d0EPe6y2yg38LZs50r6DD0f-VgF1fyup_1ORM1Ub6gRyWxIBZLyLFY6uLvzPhVQaBsDitK2ku-9ELv9frr0j-BPNNCBpNRQyJCbZwOcovJ3gn9w1TAJ4cXGxzJWRGVnJY0e_tJ5ZMwRrn4E6LwhKXK9N2lBU7-2RQxEU79sTRz12SV9v8Az51MlUHzjWZp8p2bttpkQKMeoIkIVu31t88Dn7MOZkwR5DQ8b1_tNV_3Nbqyc9OUUFTSrpfEJeU6VRrUilgvBq78lSov4q1tbKxp4OjhxdNv9NxfYCL11kTZVB_h","DataProtected":true}

+ 0
- 1
src/Services/Identity/Identity.API/keys/is-signing-key-3139DF3FF07C8E3881CFA9743F89A787.json View File

@ -1 +0,0 @@
{"Version":1,"Id":"3139DF3FF07C8E3881CFA9743F89A787","Created":"2022-12-13T11:30:03.9474661Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8LWRXnsATcxOsVa-h-Gywwx7S1URlnPkv5vJtTle9xG_ytf2YXKlRsyey8qFhmrfLZ9uixxUmHTLywyrhUV6-gdvwyirWsUU8qn3SNe4rlPgNYHESkjYwWHERmwrMQIjRell8Xnot5UU1tfIvdpX9AbruzjEuAtYg668fBiXv338XfZ-2DW2RlYxT2TsPAak5ZsGXuIojYCLC3S4uGRz8AhSoko_DSl2KXQqXsqJWTNqAmiEHkVtHeFzc_o2gJIbegzqi7l4F2jDxM0-gkkRZYk6AZlN25wYb90xYaM82bKbzEnqPhLEIkuM-3QTGnjLZIF9jFp1qftq6dDm0feovXXM3SQCsgQPvZ6rKHR_8f-Z072t8PiRxDTgVQKywQ5bCS3u3Mq89hOZf_o_IBNT4uO_hDBqv4eFhj_AAy1Kk2K_QJZhF94bPLsujkkoFjWlNXvm6-Layr9chiY7ZQGxb5QtJr03Y5CT7w6hO2LsdsYEyJLs4kWBdlILT5FjrB4LCVHJlVDGxPBNuGzZL75rSGDa38FCKsUhoBvu7T5XFTm37FaBYrgY7MASQrlHQjGPwyRrjGe-M2ZwTCrtd_MwAHJnJsLoYs6SLQLpQa4sII2qgjRr2UmfDlXP12E6wJrpWfSL6QV13CoCy4hEko4LAy4tG2Yz0HzsqFzPYL142UeJC5z-d9JNdJr9Ya8TqVc7biOJCdY47jeOF5wJpM_BWTzT584w5HRs_1LVNsmjm9l6w1v5I04uNxbpopfRS0B1J2NPS3bfpS2lGuQlyPsZJwNgkZNwxDwCQMa8cgwREnaKWXOeKNksWmS6-ve2M5DguPaKVYF30AyJ1ECqWLX-CLJScBym-ZwVM-2pdfKw3pucfXXUqsXK1lTZSWi7A8gFtnsUZFUC2lkltBT80xuw7f9ojIfoYZjGSwl1mwM5LN5IuwUyppToU7vct9GueMaBjNP5bTrWS1XwiU_oOJbjIx8gNdN-DWNG8-5VmdgXoaJDcd_ZRLaM2XpWcpB5R2MwodIe3HGdpieTDxdqh9Nd1TzQO3FivfxcW0K21CQC-xYaRsx8Ii3djek6k90C304VFRF_QLe4lTOIaAjxxDSFVKbn8tVOZyINRI3FRAXOOaVBoGR1Wsn7bXVGNGbM5eN6wCPOzE-g5_489Sq2MWgczq33_GHW8rGc840OXiqJVu7fL4LVIuG7hPzaVZD3Q-jCu3xG3rYmr2ophVP3HNhl7QDgtpHLYNAIdzZNXz7ROKJU7ws0uZq34plYnLhXEzNe8ntV1N3qCbv1lMk7RjfffZxRwE0OM0mbIC--SPqKG_Cm0cD2k04vS36N9mr1LCmDNiFQRcOqzqxgk8mJdl0Vna-IYycviWyUBs63y6n7bDU3RAifVTSVPUrrrAIMTT5uk4ZVLbO_tGKvKWaSsi7TMypJXd993tut8SPwCl8iNZS3qPwIUXx8IpStnAmJmqcWLZcldZa1bfOWqXW2s22xLNgexft7Xosp-gPNPsICPwtux_tXN-XbbgopGnWgrsyrXctm95OcfEUOVYna73ZA5cukdUbfftGVBitwe7DkG1Jb3MJLZbo2ykiqW71mLgDqvQ2KD_PHw410v-51jsXkUPNknyeSuHiRXomo2HqUf-y0xvx2S73v58yM39XadMJUMYrQlw9lnWBqCDcYoutAVakgWAXSPYELDX2BbmpZsa099h6HlKJptmSLqp2D3J7fHGGWZpdBr9hxVQ56TkMDUNCEd5W1Bc5ecT7b1R5u6IuM16A5aEGOa_phaGuqc9cUhD0UmRBDO6FE-LbfjCnzhjroAOYEujJloOcAYEAL1zx3wUHd_-0hVkmffPWC_Wu2uV8EyQQwlj8bVgCEz6R6bqxl8TN5993C2joikVDCcFYSi8RingP-ItGC5TVTxx0kWweImuBA9s6eoqUZ7TMLKOAQHTxjx5g8mBkLs94RVWhChIUif513Br0aJGxwjBvhN_NBWDFJSbP-l581YNCrmALfS4IX2jeV81bnOAAde53Yplaski3eoR2Z3daNdAJNVqOBe32gHf1eTwDCXrgqd5wYsXxW_YJ-P2kKN2CpuPE-so6tdoCLqFmXu4-3q3vGGE_nVvp-MnhHwLP50U6-h7_MDh105qlIxduM91AT7I3XPTQGrOeIv_eBrng3vULA_ohah-OzbPxpPmYSNFS8YUlGChhPgmMWHKnaYbirh-Df4rc6sK5OFEyRVlCzeKon8hFsmwIEEk7uAR2AysF1PJWjoObQkM-3vqJphkTBBYsZEmngjvQlV8TnGY6P0GRe5gNOaT0BZ71SIlKQY1iLq5NmzEGM1iIOEJI-JA3VHhXs2mw7WDKpsE_k5kbFDlgdachB778jMTSs8xWjESxyidPz-tjqvWaDg482VjoOVYBQOGyUuFRMKVJ9xZBE03nVLG29cKVCNJ_KylBUy0O4LkHskJRzrbZE_HlcoCafl1z1","DataProtected":true}

+ 0
- 1
src/Services/Identity/Identity.API/keys/is-signing-key-870FA7120249C21C30ADE458B07918C1.json View File

@ -1 +0,0 @@
{"Version":1,"Id":"870FA7120249C21C30ADE458B07918C1","Created":"2022-11-30T09:59:58.1299062Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8BCNHSqqa79Fqw5EoSNNrOMZDqq5p81RZsuqUc9iuvF_3nm9RHjc2lAsu-T8AjFPxhbWkSNnQAU7JGWpNh_TU1XK9-Skieb68wO2lUFauCBnoNf7R7JEpuw7zdjkInQZFOF1hBzhL0qKszqhM8-2gz2Q2V5c8Ng0C_2CzHNqCsyhTHBD8NgXfzpxdym7V8HZSS4SkJ3QpKLH-VMXRFssCM7PXEimNWBN2yky4IW5_fRgYgRkkCaXCU43ayGxLyBe_J6DzZ1fefO7ZBne2b8HaVVQaAEQDTlsFUxNqDLcAO-k6q3FoL9ZW2gm82a8ra1mSJTbEeAacNK5oE22urnovVF1GPwTVnnXMi_9eloBqyTM5xtTQPYbVlOkZl5pgRjKOY_Mc62Ix9p9hoH-gCfDHw2Q9X34JT1ymzBdYqtgTmWoY548-ZkX6v3AEpkhmsMdKMyNSdz-SJfasTqKkp4JkqGR-gXW17HOtxHmaOPg0ZRuQ4Dx_YHuQr98UQPMUktYL_kF_76FevNyfy5eyN6eawHNGwhXzn0YdyCjaRV1LXfD-4LqqJBhExUSyCtG0Pq0qjZ0ySAuKi72-0532XNRJ1gh7CxlfRdeLDID4ffs1cgmfODFv1k1JElYihqqGwXMwx-SfgpM9eo4j6jqd0kdAgj3n4ZsJKQMCDOGv84Gfhnw7dxlIL0e-D0sKKc6f7KagNFJCbt0LZ492IWzIPIGp5lXQwhrDPECjdNvBv0jhC6yMzZQZlVQXX6MYue_qzXb45TK23j8HjlgYPCip9eqH1NBA1MWkHykA6ZkdZ29eCCMuO-m8zB_Z_7YdEdzGcViK25T2q8qo_0BMzAzdrEJ3rOYOly7wzsoESn6aKr2U1b4qh-fljOwQrYqoSzuiu5l4QY1qVG4ZR3VMjV2lo3LEbMayA6Sis38LQy_ulUKGdOi4KeyNSlbL44wN48gDM86kNPkTM2kVoJc6RRZ5zptnGyXgAsuknHaF5EX48Ihxt2AFU_pVgKGnrx4XkYkxMEwbInoL19WgaKZyLAXjwtUG3FSOPdWswUqbcDNoetfY21Y9yWZsZ0rUTUa3x9rLzRP-H9NGbK_Yf8j75lpdx2sFUvJyyxZ2KEoq74bS-Zg203Q3jhz_RVJi8M3R0oRQHh2I-wUimdAHQ9JQu-M-VAtVLG6DVrY4vyhdv2JxIPyLrRTJPVhMDEqKqUcbm0VJE8MJOb3C6hdFrrzcM8AAkxGhYHh2vlJu0MAa8y7U0lHm8mcrNqsjPahAFvVMwUPkxaXFSYwJDdnBfODquWHt6reE9GwQw-ZFvigPGmY3SWMZiEBK-2Zkz9NglOKEgnojT6xcAbdaeEDadvY-Qriej8rMBZ3F-2IHYNMM4J2MkbexgloQgS6UWx7Nx1ln-pI-uHCDUmwsb5bl05Jma4_DXMsIjzinc2u-CvpEUXe1KmDddh7NYSrUv5RjaZfxgzjYCW3gSynFRGNfy57PaX7R7VV9pn3IRMKsbdWKlA957M2tDqmgkzC2M8KZAJRFmw_tCVk-swjPjyFmXUgsdLs0oX8OSAh4JX8DWvK42wHerh4gbjX_N7mYWh38e4-jMvyZ5Zdqf-2phj21z9uZla1mCsncjDDPJy7sVCrlJasgPLgtCD5j0lxp3wdAjvx2I4q6CKNcP8-MapHABe2TIgMmEf2VOJv42yrO99q49OwkopbyhMf-0-drAOo7Tw7eKHH7S174LfEKewCMmj2BVaM_I-rTgU28Be8pfbl6jTmj-9WCvuBbBiCtvhpeOApuMbJkc43As4QV031W9DEQpB520DWUdAHjvNkUMo0QDBLm7Zx4xzeO3Ei35Xw8_w9OjZBCZw3kalIbZAL9MlsyfFXczbZXMZnzkccPTwTK2SgS5lq1Ic07YB_uqY52CwYlpjbRP3ygM2mVCLTB921NhXKh9-Md4oJ9ieaDUNyNPch2MLQluKmyIH0sumgBkyn1k17IM4qN3EXRSsUiqa-FBU-PFa1gVIf_dhdnLI_GOPGEYBmZYTrk8J8HK2-wIi5shGDOB-WjBaAWbymdMmPhifRKiDKdOmPVArGptSyrM_5UvHF189Jl0ABxEWcsW6DScgw72GtcXxhDQligdcjanZHNVGEnfwFN604v68G42IC7clOVAno7cgaapHXXYSadEqVzP_Q4d-d-obG0v44yMu3WSdfhmJ8GC-8jS3b16q5_vTfyim5VFmLdG0PX1L9--QYop1Rns-64a5Dz8T8MmyrtgRXBAMfXC84R7N8I7SgRbsSo2pgMzRafCOpYWqfUlNcPjqQXSPj8oEt0iz0DBq9ui1dK0JiiOGOO9uOdswQP2r_cVlF-QvTszcyy1hn6QHCdsfqojSi70jmyhpBa5Vgba7nyP_qbJCtYiZwk7RLWT-jZ47BwZzEK73UXoAYCc8NT5HsIW8KFuManq8Mau7BLNj6WhXJOKmbP0oD88LMj3TeEvZ3","DataProtected":true}

+ 0
- 1
src/Services/Identity/Identity.API/keys/is-signing-key-B0B34AEB91E2638F61EB9A5543F3940F.json View File

@ -1 +0,0 @@
{"Version":1,"Id":"B0B34AEB91E2638F61EB9A5543F3940F","Created":"2022-11-30T11:21:15.586844Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8Fzd_R2KZDdMrJulCrd4GFTnFtf9LeKAfSemG0-9e385u01WZOrLNU5PPCNSRvcQKMERaUvLDcAsYIsqdKPJ14QGDgSTlxggwrR50kCv3494z5uCvWOvWqYc3KKzhR-7e-mXViY4m2oYHpGaVCITKXYwCRYxO-P0mSdr6D7W-846cfdst47_rjIxxeMajL1suYSYe_sz_nz8ooKW8ZH_kl5M9cI0waB2XFviIP3f08pz_eyneKqy7tCc3ruSNiUzfUBAF7fBHDcyPrE2h4AKaXG1PT-I6JOFVj9lAgv_1ACSqTVEjJiRWfFIfft3sQ-chSv1yxy7b8GvhIsKKEn-OqXCnYr5pFW7VPagt7pf9IQdivU7I4m4zSyW0bjIWXdCL3sXQAChiDpvRCFqFfsiH4lWmI4X8Aq8Tz1_LkbicHW5-ZxeZpvgPlg6t77xqFIxFtixqdc1XxifVwJR5GwrFcbcI9VpnlBomkBSUFMslW9NxGwSztgnYatkQuiXTzdqFoOr5d4YGPEHe_Oc3h9yDyUxgBdJK30sSx1Dwki94CeQNBS7PisGbu5q60MbsyFQ2S6kBogReQdURLW326NkgOBJ27jHk3ccx24WfFuuDzquE7xetxvnX4AV0oKOudMClsOv4Wr1aMm98p4VimUQkAneFImQszK1-zSQfxeElamoYy-4h1P2V_A5siSmfKpDs1q2ja3C62lBr0YSx822O-QF4XI0id6GOZiCc_9v52B_teTlgMq6L54EjqDM-h6ERkPvoqNFL0fWz3ktOBPCQm56YrSyyaXNKG2TwL4AMLsaQ9us5SXnJ7UpiY_7ls4SVm_h3btoQcokOiO247mYo4-BRq4pCsFHNBsCReiKlJH8G8fwrujWcu_U7NdVWgmUgmcsIh9mfommuOdBdVQdP8CYZM8od1NBIIifIoj51WuZXtb9Wr4QVIddEjAQrYnexJAozF9TeBVfjhDkIIA2-b8yBGqNl5VrF4bo3b3LLm1rbq5vzfKN6pKzs2sDGple6kZn_t0Ym5fmf8iWXLzMnRyasEDuXX0-Vg9WxiWyXzHsUY-SZEfEXs_WfuzBZb9NP1JvrB_w33cUfgl9Sa6RD56O5F1YAR3WxiCiWGnlSnqpaiiyTb4NVr8N0qQl9xOz9KCSaN8H2WKgCML2DdjW5_O8Zea2EPpSf1NGB1G-Nm-9HWAn8xDAei5x8kzTIxka6Jb8GO6Rdffk7wwuYMoB4ZYuiuL1q-rqvaU5GffQWMjGz-yH-WCivVFCosvJgk9340K0bDl97CULs43LF3nQFcmuLQoW-fasNYk5ZAXYA8bjn9eYm-hWbsfA402c3iZKKZVcLAU-jPAr44Z1P9PbT-xwooGTKKnZuR55l24T3p4hDatpazlObRd27kPtSLgbXPkNKbru9N8--JUhoEeFGC8E01gCn9256wlpIO1hk9a-P87t7zJWdYkl9Uo8Poz_Hfm9Uaml55BWSghfXlB99hULUmpnPwyOVJnRhGOrnr6ys_2l6bCW-gZlWS7oevQ_BuJBcCXVuRYQPbVtH9ZZSKF-S-uczbtfzhJfH29tCNsHzAukVHFiIwHqzhLHNu1gX5COGzYdN57p7DXGkJXGJUipczm2ZAfI9g-Kgvy6MaXScuwaobPobL5AH5WG1CclEb8kg_08rnQsDfvNhmsAJE7dN1GjpA46fP2bJ09T4hfNdmW-IZbeQaDMGc_8RpoADdUY81swXUgrL56UxoCFxqifXtskbzxF5Z1ad4qRYNlqU_zMbxjSZTsSizNja4pmsx1O2NAhvN31bSoA8PWpLLz37Lq-craDpXW6-mIySm3NQWxTLx2hsk4i29-dD6AjUX2aDLEng4JLKLWKLcH_ulkujNP0oXdkXcGikLqFwUKG8_lEWV_ROlXUVlmwNf9WaohIX8caCK2CF_tyEkCxEjZIBzw5xazq7_zNbI66rXfqBHxDKqrdM3UJYBNFMk2LLyEGD4TetGu4RzBDt2r4rXatSXQh9a662LYlCXxlElP8xfH7BudBa9KgV0FQOdHQ4sm9chHiT9JPQX6bHwf_imYHmTyMV3cIN7f7bACLji-5hFf7if8ANbhY7zer9-rQWR9Wy-vYUyUaSWCWRXHQC2eXL0XklBNnv3vK1nFOcaeC5R0GGULfMnBluyClu2Z67OCsAzQqi-_aacxQVN91SNmAfUbVCrPgBTbQY_1u64fEMc8ZcL52xwT0qgCSvBPCNDEJ8YDVVd8L8eLX0hg7rV8SgCnAl5qG74BCdkmhCMLB-OiwGZxZ9s94yGdjX9jA4D70uOgn5t9OhpboX3OoRK40Xged6zCBEhUd9MilD9o-BtEPeAHloKqMrvrVDE4l9R739pjhgOfvXqGIytXD-bKlMZAY7dnJ4vu4GjVvhHfzxTV3zu_nAV8j3n41_knCe2RyCm9j74YZejnRC15ADqItTDmTre8_iS6hTOrVO9FP","DataProtected":true}

+ 0
- 1
src/Services/Identity/Identity.API/keys/is-signing-key-CFE19FEED1112F0740C7CEAAE490834F.json View File

@ -1 +0,0 @@
{"Version":1,"Id":"CFE19FEED1112F0740C7CEAAE490834F","Created":"2022-11-29T07:38:58.7490696Z","Algorithm":"RS256","IsX509Certificate":false,"Data":"CfDJ8PyEV1YRvftHv5uH-VZQ5M7m2ypM-2X7o3A9EvjXNQQsFD50DwqX3UR8e4cDEITQrBowxyPPRXo_bTgEnifJjx1kdzA3q0L-FPJqCOjh6AaRqUg_TlnhvbEgG1HYoBZTAwvhi6g3h86RFPU4juwhOWHcD1vzCgW1vfJ9VFXXq3F-FSfbpdykS5bLn5hUdg3zgNWh7zZXOIFxgNkelR7lGMfF6hquTQMbCRhtDg0-N8O8m7M0JaNsB6_H6v-Z2RlYzH0R0B0Ka2fH3uPnudPrd1RIP4BONzMorJpDFZP9TF0DY9sJhkcHEhKDZlRlwI_Z-e0z8WeW183Gg_HSV23wO4B5R8ldXRSioqWyUuQfoYplUanuNeccblBe7hxQw9fhsanHGgbiuBjuypXarZmlVlbFO2zPKl46Vj8YZTxopOFOi4ALcNlUzGgPG5lwPjDfXEyAp7_ipMIW84UVfwqnPZFE1FASpFx1BPif-huoAaXw2GF4HJ2QVqfxkr_Gn8isrzBOT3fqmeZ1n0HNOc8VxCSdScbBSKFMnxNSgPQSexHGSKnXpvXF0OL6R8oqHoi8TNfYFitGE_qED6wCjQ77qMXOUVXB18kW7nJYqph2u1BH1PykRWaWDlnh3H7fKWRhO0ejaTJWl7oSLllYzi99yL5JvudDCjI0QdfbMz7o3SrdrZbIKQcbTPPgl_cfNneP4QA6MSlJFbG3bc3q8IUPhISrZHO_s7SsoX0BiP-AHcA7M_WrkAQYLkTMsBuO3x6N-bWjtTouqzHY7-h5ZYHqvk7Mg-jlgJjFjgKRDOJFiDorbf-5nvxjl3eLV8rjX7ML9KqjD-ZUjMsDt79lpjCi6B2EFqbiGT35I67hC6a7l8gPjwLKB7Oc8h4YggwyS1eLCq3CgmfMqlIAeAqLL7nVwDPReW-pirHix92q2tSkKCuz5UV5jphmDAnRFQmICaIinoSsBtjb1RlonM-yklDO-jdR5r0ZEFb8ZCssSojhfqEi5mcK3xJsyzM_Inzg71zAHCUl5cuBhbWekBeHiqb2p5ar9guCK_h7zmga1ZZTqfWAGzxHrHkHb6XiK_wLlwBjJEoGhgHfi0UzUECddK2DVwEuTsXhNDJ8CgB2oWNhIHgnlNxL_JA6_bY8BnGtcOKnEuaKpR6rXEZit2Jcr-1-Q8YJ29tVNubphYm2ZMrDTGdAVE8ODftey6BIN_HmBELf6a7x-Cd5DPrW46iVqlOuBqmjxjZ4HHTS08HHGSD4_xj2vNaWl8rIbE4BbDq9V_qyP548KLNbGqSIed38DDcbsRMIuac8po95Z3q6NUf2JR0mCOEizdrszZOW9sGVlO-WReaTkpNN8MaKYoUy6k84IxwPPv-Tov0yMdTN-jp7H2HaTOGDvEb72swcW39huoAbXwjZdrpFQjTD9_cnNrVdclDGN_yix_i8-vQDQlQ9Ronpk2NvAdnrmEzQ6w7D7PqAe5c4YJfn_0z45ERm9zBwCMCbw8lP_Yi3nwTbqGtFqAoXTSZ8vR5_UxQLqxsJy6U6gK4Es68-hnD-YR5p3CzBXxSEgV7_qvW2gly5VCB-1yoD8dTVInogMhOzDZD71gmpJURgKXX9WN92yq-YB24E8koOXdbxTvSblIVTxwDqQ7lb81y68HmZVSRLk3zhxbkEkjI1kkB8uzlArbXljN7LnxyXHU81DDeufVK5_myoIMA5NpSwEKxAkmCoF7p642qBwo90k0q0AvJVmoy8xpCRiJyLDR8wYHFsed3T99jvvvV7GyA4SG5Tt-ST5kKFKvOOwT_WrOg-ao5cz6Br8_YL2xW5dB231R4ZxfJlmrJY9WcuTQMZrWnYOOSj_5u2I4sgcnp7nrRF1l2xnieiym5ef23a-rSMBZi_leN6BAma6N1M7tu1N8cEw7wd_6FV6hqrcKFPGNm4HE5M-pmoWvzjBTo25rh_4atG5uEmDgmhmY6qiZc1eso__Fz29uJ4792kkpdGyPAcV8igzPMdKts1KiO9ThrDRH25Knwxa1tOWwezkeId-OuZRwC4LhGuZseww5TxzQtyN8mMWbLDPCmWya56F-i0jag4i-r2Z26VEmhpSywbACetljEsaVDJF2ZdIrIFJqjYLa9zhcKbD9GZPHZt0HKilXDp8DomCQHHWsIapwCiG6WLGOhVc0-0VJ28dBqIowyCQScefePKIIk7Oa0T4UzPXcXiXYNQktHGs9VrF2ux4De-SbxDRdJy1IRSH8_NQ4SFtUelO8IG27B_8Ywbonn9yRsoojmuS-fXPuWSJMHDjPcSYwJqQyxjfPjGCLsnQWRZCNT5Ft6cfcSDRZWoCqKK2qLfqcGs-FR71HM0G6ipAuVI_2IyfalS-fXqTPCncyFsOqp4BRhhxLFu8UxTZ5-HQyKH3fX75mZCD21d9RObyWv8g2T0HtEBLEf2eR8I8zCUcOf98asCoT2CU4AtOhVwqrE2u1qNKupc5Nvw","DataProtected":true}

src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs → src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehavior.cs View File

@ -2,15 +2,15 @@
using Microsoft.Extensions.Logging;
public class TransactionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
public class TransactionBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
private readonly ILogger<TransactionBehaviour<TRequest, TResponse>> _logger;
private readonly ILogger<TransactionBehavior<TRequest, TResponse>> _logger;
private readonly OrderingContext _dbContext;
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
public TransactionBehaviour(OrderingContext dbContext,
public TransactionBehavior(OrderingContext dbContext,
IOrderingIntegrationEventService orderingIntegrationEventService,
ILogger<TransactionBehaviour<TRequest, TResponse>> logger)
ILogger<TransactionBehavior<TRequest, TResponse>> logger)
{
_dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext));
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentException(nameof(orderingIntegrationEventService));

+ 1
- 16
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs View File

@ -10,6 +10,7 @@
// 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;
using Microsoft.eShopOnContainers.Services.Ordering.API.Extensions;
[DataContract]
public class CreateOrderCommand
@ -80,21 +81,5 @@ public class CreateOrderCommand
CardSecurityNumber = cardSecurityNumber;
CardTypeId = cardTypeId;
}
public record OrderItemDTO
{
public int ProductId { get; init; }
public string ProductName { get; init; }
public decimal UnitPrice { get; init; }
public decimal Discount { get; init; }
public int Units { get; init; }
public string PictureUrl { get; init; }
}
}

+ 0
- 1
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommand.cs View File

@ -3,7 +3,6 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models;
public class CreateOrderDraftCommand : IRequest<OrderDraftDTO>
{
public string BuyerId { get; private set; }
public IEnumerable<BasketItem> Items { get; private set; }


+ 16
- 2
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderDraftCommandHandler.cs View File

@ -1,6 +1,6 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
using Microsoft.eShopOnContainers.Services.Ordering.API.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
// Regular CommandHandler
@ -42,5 +42,19 @@ public record OrderDraftDTO
Total = order.GetTotal()
};
}
}
public record OrderItemDTO
{
public int ProductId { get; init; }
public string ProductName { get; init; }
}
public decimal UnitPrice { get; init; }
public decimal Discount { get; init; }
public int Units { get; init; }
public string PictureUrl { get; init; }
}

+ 4
- 6
src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs View File

@ -6,7 +6,7 @@
/// </summary>
/// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam>
/// <typeparam name="R">Return value of the inner command handler</typeparam>
public class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<T, R>, R>
public abstract class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<T, R>, R>
where T : IRequest<R>
{
private readonly IMediator _mediator;
@ -18,19 +18,17 @@ public class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<T, R>> logger)
{
ArgumentNullException.ThrowIfNull(logger);
_mediator = mediator;
_requestManager = requestManager;
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
_logger = logger;
}
/// <summary>
/// Creates the result value to return if a previous request was found
/// </summary>
/// <returns></returns>
protected virtual R CreateResultForDuplicateRequest()
{
return default(R);
}
protected abstract R CreateResultForDuplicateRequest();
/// <summary>
/// This method handles the command. It just ensures that no other request exists with the same ID, and if this is the case


+ 2
- 2
src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs View File

@ -34,9 +34,9 @@ public class SetStockConfirmedOrderStatusCommandHandler : IRequestHandler<SetSto
// Use for Idempotency in Command process
public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>
public class SetStockConfirmedOrderStatusIdentifiedCommandHandler : IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>
{
public SetStockConfirmedOrderStatusIdenfifiedCommandHandler(
public SetStockConfirmedOrderStatusIdentifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>> logger)


+ 2
- 2
src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs View File

@ -35,9 +35,9 @@ public class SetStockRejectedOrderStatusCommandHandler : IRequestHandler<SetStoc
// Use for Idempotency in Command process
public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>
public class SetStockRejectedOrderStatusIdentifiedCommandHandler : IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>
{
public SetStockRejectedOrderStatusIdenfifiedCommandHandler(
public SetStockRejectedOrderStatusIdentifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>> logger)


src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelledDomainEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderCancelled;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class OrderCancelledDomainEventHandler
: INotificationHandler<OrderCancelledDomainEvent>

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShippedDomainEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderShipped;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class OrderShippedDomainEventHandler
: INotificationHandler<OrderShippedDomainEvent>

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderGracePeriodConfirmed;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class OrderStatusChangedToAwaitingValidationDomainEventHandler
: INotificationHandler<OrderStatusChangedToAwaitingValidationDomainEvent>
{

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStatusChangedToPaidDomainEventHandler.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderPaid;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class OrderStatusChangedToPaidDomainEventHandler
: INotificationHandler<OrderStatusChangedToPaidDomainEvent>
{
@ -10,10 +10,10 @@ public class OrderStatusChangedToPaidDomainEventHandler
public OrderStatusChangedToPaidDomainEventHandler(
IOrderRepository orderRepository, ILoggerFactory logger,
IOrderRepository orderRepository,
ILoggerFactory logger,
IBuyerRepository buyerRepository,
IOrderingIntegrationEventService orderingIntegrationEventService
)
IOrderingIntegrationEventService orderingIntegrationEventService)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@ -24,7 +24,7 @@ public class OrderStatusChangedToPaidDomainEventHandler
public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken)
{
_logger.CreateLogger<OrderStatusChangedToPaidDomainEventHandler>()
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
.LogInformation("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);

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStatusChangedToStockConfirmedDomainEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class OrderStatusChangedToStockConfirmedDomainEventHandler
: INotificationHandler<OrderStatusChangedToStockConfirmedDomainEvent>

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/SendEmailToCustomerWhenOrderStartedDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/SendEmailToCustomerWhenOrderStartedDomainEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class SendEmailToCustomerWhenOrderStartedDomainEventHandler
//: IAsyncNotificationHandler<OrderStartedDomainEvent>

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler
: INotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler
: INotificationHandler<OrderStartedDomainEvent>
@ -22,9 +22,9 @@ public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler
public async Task Handle(OrderStartedDomainEvent orderStartedEvent, CancellationToken cancellationToken)
{
var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1;
var cardTypeId = orderStartedEvent.CardTypeId != 0 ? orderStartedEvent.CardTypeId : 1;
var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId);
bool buyerOriginallyExisted = (buyer == null) ? false : true;
var buyerOriginallyExisted = buyer == null ? false : true;
if (!buyerOriginallyExisted)
{

+ 1
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSucceededIntegrationEventHandler.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling;
public class OrderPaymentSucceededIntegrationEventHandler :
IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>
{


+ 1
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Ordering.API.Application.IntegrationEvents.EventHandling;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling;
public class OrderStockConfirmedIntegrationEventHandler :
IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>


+ 1
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/GracePeriodConfirmedIntegrationEvent.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
public record GracePeriodConfirmedIntegrationEvent : IntegrationEvent
{


+ 1
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStatusChangedToAwaitingValidationIntegrationEvent.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent
{
public int OrderId { get; }


+ 1
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockRejectedIntegrationEvent.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
public record OrderStockRejectedIntegrationEvent : IntegrationEvent
{


+ 5
- 5
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs View File

@ -1,15 +1,15 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
{
{
public string UserId { get; }
public string UserName { get; }
public string City { get; set; }
public string Street { get; set; }
public string State { get; set; }
public string Country { get; set; }


+ 1
- 1
src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
public class OrderQueries
: IOrderQueries
{


+ 2
- 1
src/Services/Ordering/Ordering.API/Extensions/BasketItemExtensions.cs View File

@ -1,6 +1,7 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Extensions;
using System.Collections.Generic;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models;
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
public static class BasketItemExtensions


+ 3
- 4
src/Services/Ordering/Ordering.API/GlobalUsings.cs View File

@ -32,16 +32,15 @@ global using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Se
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.Infrastructure.AutofacModules;
global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers;
global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents;
global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
global using Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling;
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;


+ 6
- 2
src/Services/Ordering/Ordering.API/Grpc/OrderingService.cs View File

@ -1,4 +1,9 @@
namespace GrpcOrdering;
using GrpcOrdering;
using OrderDraftDTO = GrpcOrdering.OrderDraftDTO;
using CreateOrderDraftCommand = GrpcOrdering.CreateOrderDraftCommand;
using BasketItem = GrpcOrdering.BasketItem;
using OrderItemDTO = GrpcOrdering.OrderItemDTO;
public class OrderingService : OrderingGrpc.OrderingGrpcBase
{
@ -25,7 +30,6 @@ public class OrderingService : OrderingGrpc.OrderingGrpcBase
createOrderDraftCommand.BuyerId,
this.MapBasketItems(createOrderDraftCommand.Items));
var data = await _mediator.Send(command);
if (data != null)


+ 0
- 18
src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/ApplicationModule.cs View File

@ -15,24 +15,6 @@ public class ApplicationModule
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new OrderQueries(QueriesConnectionString))
.As<IOrderQueries>()
.InstancePerLifetimeScope();
builder.RegisterType<BuyerRepository>()
.As<IBuyerRepository>()
.InstancePerLifetimeScope();
builder.RegisterType<OrderRepository>()
.As<IOrderRepository>()
.InstancePerLifetimeScope();
builder.RegisterType<RequestManager>()
.As<IRequestManager>()
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(typeof(CreateOrderCommandHandler).GetTypeInfo().Assembly)
.AsClosedTypesOf(typeof(IIntegrationEventHandler<>));
}
}

+ 0
- 20
src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs View File

@ -4,26 +4,6 @@ public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
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 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();
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>));
}
}

+ 1
- 1
src/Services/Ordering/Ordering.API/Infrastructure/IntegrationEventMigrations/IntegrationEventLogContextDesignTimeFactory.cs View File

@ -1,4 +1,4 @@
namespace Catalog.API.Infrastructure.IntegrationEventMigrations
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.IntegrationEventMigrations
{
public class IntegrationEventLogContextDesignTimeFactory : IDesignTimeDbContextFactory<IntegrationEventLogContext>
{


+ 1
- 1
src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs View File

@ -144,7 +144,7 @@ public class OrderingContextSeed
if (csvheaders.Count() != requiredHeaders.Count())
{
throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'");
throw new Exception($"requiredHeader count '{requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'");
}
foreach (var requiredHeader in requiredHeaders)


+ 2
- 1
src/Services/Ordering/Ordering.API/Ordering.API.csproj View File

@ -6,7 +6,8 @@
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<GenerateErrorForMissingTargetingPacks>false</GenerateErrorForMissingTargetingPacks>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
<RootNamespace>Microsoft.eShopOnContainers.Services.Ordering.API</RootNamespace>
</PropertyGroup>
<ItemGroup>


+ 99
- 42
src/Services/Ordering/Ordering.API/Program.cs View File

@ -1,15 +1,5 @@
using Autofac.Core;
using Microsoft.Azure.Amqp.Framing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
var builder = WebApplication.CreateBuilder(args);
var appName = "Ordering.API";
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ApplicationName = typeof(Program).Assembly.FullName,
ContentRootPath = Directory.GetCurrentDirectory()
});
if (builder.Configuration.GetValue<bool>("UseVault", false))
{
TokenCredential credential = new ClientSecretCredential(
@ -18,24 +8,22 @@ if (builder.Configuration.GetValue<bool>("UseVault", false))
builder.Configuration["Vault:ClientSecret"]);
builder.Configuration.AddAzureKeyVault(new Uri($"https://{builder.Configuration["Vault:Name"]}.vault.azure.net/"), credential);
}
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory());
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
builder.Configuration.AddEnvironmentVariables();
builder.WebHost.ConfigureKestrel(options =>
{
var ports = GetDefinedPorts(builder.Configuration);
options.Listen(IPAddress.Any, ports.httpPort, listenOptions =>
var httpPort = builder.Configuration.GetValue("PORT", 80);
options.Listen(IPAddress.Any, httpPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
options.Listen(IPAddress.Any, ports.grpcPort, listenOptions =>
var grpcPort = builder.Configuration.GetValue("GRPC_PORT", 5001);
options.Listen(IPAddress.Any, grpcPort, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
});
builder.WebHost.CaptureStartupErrors(false);
builder.Host.UseSerilog(CreateSerilogLogger(builder.Configuration));
builder.Services
.AddGrpc(options =>
@ -47,7 +35,7 @@ builder.Services
.AddCustomMvc()
.AddHealthChecks(builder.Configuration)
.AddCustomDbContext(builder.Configuration)
.AddCustomSwagger(builder.Configuration)
.AddCustomSwagger(builder.Configuration)
.AddCustomAuthentication(builder.Configuration)
.AddCustomAuthorization(builder.Configuration)
.AddCustomIntegrations(builder.Configuration)
@ -58,8 +46,81 @@ builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// Register your own things directly with Autofac here. Don't
// call builder.Populate(), that happens in AutofacServiceProviderFactory
// for you.
builder.Host.ConfigureContainer<ContainerBuilder>(conbuilder => conbuilder.RegisterModule(new MediatorModule()));
builder.Host.ConfigureContainer<ContainerBuilder>(conbuilder => conbuilder.RegisterModule(new ApplicationModule(builder.Configuration["ConnectionString"])));
var services = builder.Services;
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblyContaining(typeof(Program));
cfg.AddOpenBehavior(typeof(LoggingBehavior<,>));
cfg.AddOpenBehavior(typeof(ValidatorBehavior<,>));
cfg.AddOpenBehavior(typeof(TransactionBehavior<,>));
});
/*
// Register all the command handlers.
services.AddSingleton<IRequestHandler<CancelOrderCommand, bool>, CancelOrderCommandHandler>();
services.AddSingleton<IRequestHandler<IdentifiedCommand<CancelOrderCommand, bool>, bool>, CancelOrderIdentifiedCommandHandler>();
services.AddSingleton<IRequestHandler<CreateOrderCommand, bool>, CreateOrderCommandHandler>();
services.AddSingleton<IRequestHandler<IdentifiedCommand<CreateOrderCommand, bool>, bool>, CreateOrderIdentifiedCommandHandler>();
services.AddSingleton<IRequestHandler<CreateOrderDraftCommand, OrderDraftDTO>, CreateOrderDraftCommandHandler>();
services.AddSingleton<IRequestHandler<IdentifiedCommand<SetAwaitingValidationOrderStatusCommand, bool>, bool>, SetAwaitingValidationIdentifiedOrderStatusCommandHandler>();
services.AddSingleton<IRequestHandler<SetAwaitingValidationOrderStatusCommand, bool>, SetAwaitingValidationOrderStatusCommandHandler>();
services.AddSingleton<IRequestHandler<IdentifiedCommand<SetPaidOrderStatusCommand, bool>, bool>, SetPaidIdentifiedOrderStatusCommandHandler>();
services.AddSingleton<IRequestHandler<SetPaidOrderStatusCommand, bool>, SetPaidOrderStatusCommandHandler>();
services.AddSingleton<IRequestHandler<IdentifiedCommand<SetStockConfirmedOrderStatusCommand, bool>, bool>, SetStockConfirmedOrderStatusIdentifiedCommandHandler>();
services.AddSingleton<IRequestHandler<SetStockConfirmedOrderStatusCommand, bool>, SetStockConfirmedOrderStatusCommandHandler>();
services.AddSingleton<IRequestHandler<IdentifiedCommand<SetStockRejectedOrderStatusCommand, bool>, bool>, SetStockRejectedOrderStatusIdentifiedCommandHandler>();
services.AddSingleton<IRequestHandler<SetStockRejectedOrderStatusCommand, bool>, SetStockRejectedOrderStatusCommandHandler>();
services.AddSingleton<IRequestHandler<IdentifiedCommand<ShipOrderCommand, bool>, bool>, ShipOrderIdentifiedCommandHandler>();
services.AddSingleton<IRequestHandler<ShipOrderCommand, bool>, ShipOrderCommandHandler>();
// Register the DomainEventHandler classes (they implement INotificationHandler<>) in assembly holding the Domain Events
services.AddSingleton<INotificationHandler<OrderCancelledDomainEvent>, OrderCancelledDomainEventHandler>();
services.AddSingleton<INotificationHandler<OrderShippedDomainEvent>, OrderShippedDomainEventHandler>();
services.AddSingleton<INotificationHandler<OrderStatusChangedToAwaitingValidationDomainEvent>, OrderStatusChangedToAwaitingValidationDomainEventHandler>();
services.AddSingleton<INotificationHandler<OrderStatusChangedToPaidDomainEvent>, OrderStatusChangedToPaidDomainEventHandler>();
services.AddSingleton<INotificationHandler<OrderStatusChangedToStockConfirmedDomainEvent>, OrderStatusChangedToStockConfirmedDomainEventHandler>();
services.AddSingleton<INotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>, UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler>();
services.AddSingleton<INotificationHandler<OrderStartedDomainEvent>, ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler>();
*/
// Register the command validators for the validator behavior (validators based on FluentValidation library)
services.AddSingleton<IValidator<CancelOrderCommand>, CancelOrderCommandValidator>();
services.AddSingleton<IValidator<CreateOrderCommand>, CreateOrderCommandValidator>();
services.AddSingleton<IValidator<IdentifiedCommand<CreateOrderCommand, bool>>, IdentifiedCommandValidator>();
services.AddSingleton<IValidator<ShipOrderCommand>, ShipOrderCommandValidator>();
/*
// Build the MediatR pipeline
services.AddSingleton(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));
services.AddSingleton(typeof(IPipelineBehavior<,>), typeof(ValidatorBehavior<,>));
services.AddSingleton(typeof(IPipelineBehavior<,>), typeof(TransactionBehavior<,>));
*/
var queriesConnectionString = builder.Configuration["ConnectionString"];
services.AddScoped<IOrderQueries>(sp => new OrderQueries(queriesConnectionString));
services.AddScoped<IBuyerRepository, BuyerRepository>();
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IRequestManager, RequestManager>();
// Add integration event handlers.
services.AddSingleton<IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>, GracePeriodConfirmedIntegrationEventHandler>();
services.AddSingleton<IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>, OrderPaymentFailedIntegrationEventHandler>();
services.AddSingleton<IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>, OrderPaymentSucceededIntegrationEventHandler>();
services.AddSingleton<IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>, OrderStockConfirmedIntegrationEventHandler>();
services.AddSingleton<IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>, OrderStockRejectedIntegrationEventHandler>();
services.AddSingleton<IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>, UserCheckoutAcceptedIntegrationEventHandler>();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
@ -166,18 +227,13 @@ Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config)
{
var grpcPort = config.GetValue("GRPC_PORT", 5001);
var port = config.GetValue("PORT", 80);
return (port, grpcPort);
}
public partial class Program
{
public static string Namespace = typeof(Program).Assembly.GetName().Name;
private static string Namespace = typeof(Program).Assembly.GetName().Name;
public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
}
static class CustomExtensionsMethods
{
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration)
@ -275,7 +331,7 @@ static class CustomExtensionsMethods
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration)
{
services.AddSwaggerGen(options =>
return services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
@ -283,6 +339,9 @@ static class CustomExtensionsMethods
Version = "v1",
Description = "The Ordering Service HTTP API"
});
var identityUrl = configuration["IdentityUrlExternal"];
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
@ -290,8 +349,8 @@ static class CustomExtensionsMethods
{
Implicit = new OpenApiOAuthFlow()
{
AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"),
TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"),
AuthorizationUrl = new Uri($"{identityUrl}/connect/authorize"),
TokenUrl = new Uri($"{identityUrl}/connect/token"),
Scopes = new Dictionary<string, string>()
{
{ "orders", "Ordering API" }
@ -299,9 +358,9 @@ static class CustomExtensionsMethods
}
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
return services;
}
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration)
@ -393,11 +452,10 @@ static class CustomExtensionsMethods
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var eventBusSubscriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, sp, subscriptionName);
return new EventBusServiceBus(serviceBusPersisterConnection, logger, eventBusSubscriptionsManager, sp, subscriptionName);
});
}
else
@ -407,15 +465,14 @@ static class CustomExtensionsMethods
var subscriptionClientName = configuration["SubscriptionClientName"];
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var eventBusSubscriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
if (!int.TryParse(configuration["EventBusRetryCount"], out var retryCount))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
retryCount = 5;
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubscriptionsManager, subscriptionClientName, retryCount);
});
}


+ 1
- 1
src/Services/Ordering/Ordering.BackgroundTasks/Extensions/CustomExtensionMethods.cs View File

@ -132,7 +132,7 @@ namespace Ordering.BackgroundTasks.Extensions
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl,null)
.WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl, null)
.ReadFrom.Configuration(configuration)
.CreateLogger();


+ 4
- 2
src/Services/Ordering/Ordering.BackgroundTasks/Services/GracePeriodManagerService.cs View File

@ -36,10 +36,12 @@ namespace Ordering.BackgroundTasks.Services
_logger.LogDebug("GracePeriodManagerService background task is doing background work.");
CheckConfirmedGracePeriodOrders();
try {
try
{
await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
}
catch (TaskCanceledException exception) {
catch (TaskCanceledException exception)
{
_logger.LogCritical(exception, "TaskCanceledException Error", exception.Message);
}
}


+ 1
- 1
src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
/// <summary>
/// Event used when the order is paid
/// </summary>


+ 2
- 2
src/Services/Ordering/Ordering.Domain/SeedWork/Enumeration.cs View File

@ -9,14 +9,14 @@ public abstract class Enumeration : IComparable
protected Enumeration(int id, string name) => (Id, Name) = (id, name);
public override string ToString() => Name;
public static IEnumerable<T> GetAll<T>() where T : Enumeration =>
typeof(T).GetFields(BindingFlags.Public |
BindingFlags.Static |
BindingFlags.DeclaredOnly)
.Select(f => f.GetValue(null))
.Cast<T>();
public override bool Equals(object obj)
{
if (obj is not Enumeration otherValue)


+ 0
- 11
src/Services/Ordering/Ordering.FunctionalTests/HttpClientExtensions.cs View File

@ -1,11 +0,0 @@
namespace Ordering.FunctionalTests;
static class HttpClientExtensions
{
public static HttpClient CreateIdempotentClient(this TestServer server)
{
var client = server.CreateClient();
client.DefaultRequestHeaders.Add("x-requestid", Guid.NewGuid().ToString());
return client;
}
}

+ 2
- 7
src/Services/Ordering/Ordering.FunctionalTests/Ordering.FunctionalTests.csproj View File

@ -6,16 +6,12 @@
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
@ -28,7 +24,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Web\WebMVC\WebMVC.csproj" />
<ProjectReference Include="..\Ordering.API\Ordering.API.csproj" />
<ProjectReference Include="..\Ordering.Domain\Ordering.Domain.csproj" />
<ProjectReference Include="..\Ordering.Infrastructure\Ordering.Infrastructure.csproj" />


+ 43
- 24
src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarioBase.cs View File

@ -1,36 +1,42 @@
namespace Ordering.FunctionalTests;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Hosting;
public class OrderingScenarioBase
namespace Ordering.FunctionalTests;
public class OrderingScenarioBase : WebApplicationFactory<Program>
{
public TestServer CreateServer()
{
var path = Assembly.GetAssembly(typeof(OrderingScenarioBase))
.Location;
Services.MigrateDbContext<OrderingContext>((context, services) =>
{
var env = services.GetService<IWebHostEnvironment>();
var settings = services.GetService<IOptions<OrderingSettings>>();
var logger = services.GetService<ILogger<OrderingContextSeed>>();
var hostBuilder = new WebHostBuilder()
.UseContentRoot(Path.GetDirectoryName(path))
.ConfigureAppConfiguration(cb =>
{
cb.AddJsonFile("appsettings.json", optional: false)
.AddEnvironmentVariables();
});
new OrderingContextSeed()
.SeedAsync(context, env, settings, logger)
.Wait();
})
.MigrateDbContext<IntegrationEventLogContext>((_, __) => { });
return Server;
}
var testServer = new TestServer(hostBuilder);
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureServices(servies =>
{
servies.AddSingleton<IStartupFilter, AuthStartupFilter>();
});
testServer.Host
.MigrateDbContext<OrderingContext>((context, services) =>
{
var env = services.GetService<IWebHostEnvironment>();
var settings = services.GetService<IOptions<OrderingSettings>>();
var logger = services.GetService<ILogger<OrderingContextSeed>>();
builder.ConfigureAppConfiguration(c =>
{
var directory = Path.GetDirectoryName(typeof(OrderingScenarioBase).Assembly.Location)!;
new OrderingContextSeed()
.SeedAsync(context, env, settings, logger)
.Wait();
})
.MigrateDbContext<IntegrationEventLogContext>((_, __) => { });
c.AddJsonFile(Path.Combine(directory, "appsettings.json"), optional: false);
});
return testServer;
return base.CreateHost(builder);
}
public static class Get
@ -48,4 +54,17 @@ public class OrderingScenarioBase
public static string CancelOrder = "api/v1/orders/cancel";
public static string ShipOrder = "api/v1/orders/ship";
}
private class AuthStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseMiddleware<AutoAuthorizeMiddleware>();
next(app);
};
}
}
}

+ 12
- 6
src/Services/Ordering/Ordering.FunctionalTests/OrderingScenarios.cs View File

@ -1,7 +1,6 @@
using System.Net;
using System.Text;
using System.Text.Json;
using WebMVC.Services.ModelDTOs;
using Xunit;
namespace Ordering.FunctionalTests
@ -16,6 +15,7 @@ namespace Ordering.FunctionalTests
var response = await server.CreateClient()
.GetAsync(Get.Orders);
var s = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
}
@ -23,8 +23,11 @@ namespace Ordering.FunctionalTests
public async Task Cancel_order_no_order_created_bad_request_response()
{
using var server = CreateServer();
var content = new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json");
var response = await server.CreateIdempotentClient()
var content = new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json")
{
Headers = { { "x-requestid", Guid.NewGuid().ToString() } }
};
var response = await server.CreateClient()
.PutAsync(Put.CancelOrder, content);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
@ -34,8 +37,11 @@ namespace Ordering.FunctionalTests
public async Task Ship_order_no_order_created_bad_request_response()
{
using var server = CreateServer();
var content = new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json");
var response = await server.CreateIdempotentClient()
var content = new StringContent(BuildOrder(), UTF8Encoding.UTF8, "application/json")
{
Headers = { { "x-requestid", Guid.NewGuid().ToString() } }
};
var response = await server.CreateClient()
.PutAsync(Put.ShipOrder, content);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
@ -43,7 +49,7 @@ namespace Ordering.FunctionalTests
string BuildOrder()
{
var order = new OrderDTO()
var order = new
{
OrderNumber = "-1"
};


+ 1
- 2
src/Services/Ordering/Ordering.SignalrHub/Program.cs View File

@ -1,5 +1,4 @@
var appName = "Ordering.SignalrHub";
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ApplicationName = typeof(Program).Assembly.FullName,


+ 2
- 2
src/Services/Ordering/Ordering.UnitTests/Application/IdentifiedCommandHandlerTest.cs View File

@ -27,7 +27,7 @@ public class IdentifiedCommandHandlerTest
.Returns(Task.FromResult(true));
//Act
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object, _loggerMock.Object);
var handler = new CreateOrderIdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object);
var cltToken = new System.Threading.CancellationToken();
var result = await handler.Handle(fakeOrderCmd, cltToken);
@ -50,7 +50,7 @@ public class IdentifiedCommandHandlerTest
.Returns(Task.FromResult(true));
//Act
var handler = new IdentifiedCommandHandler<CreateOrderCommand, bool>(_mediator.Object, _requestManager.Object, _loggerMock.Object);
var handler = new CreateOrderIdentifiedCommandHandler(_mediator.Object, _requestManager.Object, _loggerMock.Object);
var cltToken = new System.Threading.CancellationToken();
var result = await handler.Handle(fakeOrderCmd, cltToken);


+ 4
- 4
src/Services/Ordering/Ordering.UnitTests/Application/OrdersWebApiTest.cs View File

@ -29,7 +29,7 @@ public class OrdersWebApiTest
var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), Guid.NewGuid().ToString()) as OkResult;
//Assert
Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK);
Assert.Equal((int)System.Net.HttpStatusCode.OK, actionResult.StatusCode);
}
@ -45,7 +45,7 @@ public class OrdersWebApiTest
var actionResult = await orderController.CancelOrderAsync(new CancelOrderCommand(1), String.Empty) as BadRequestResult;
//Assert
Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.BadRequest);
Assert.Equal((int)System.Net.HttpStatusCode.BadRequest, actionResult.StatusCode);
}
[Fact]
@ -60,7 +60,7 @@ public class OrdersWebApiTest
var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), Guid.NewGuid().ToString()) as OkResult;
//Assert
Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.OK);
Assert.Equal((int)System.Net.HttpStatusCode.OK, actionResult.StatusCode);
}
@ -76,7 +76,7 @@ public class OrdersWebApiTest
var actionResult = await orderController.ShipOrderAsync(new ShipOrderCommand(1), String.Empty) as BadRequestResult;
//Assert
Assert.Equal(actionResult.StatusCode, (int)System.Net.HttpStatusCode.BadRequest);
Assert.Equal((int)System.Net.HttpStatusCode.BadRequest, actionResult.StatusCode);
}
[Fact]


+ 1
- 1
src/Services/Payment/Payment.API/IntegrationEvents/EventHandling/OrderStatusChangedToStockConfirmedIntegrationEventHandler.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.EventHandling;
public class OrderStatusChangedToStockConfirmedIntegrationEventHandler :
IIntegrationEventHandler<OrderStatusChangedToStockConfirmedIntegrationEvent>
{


+ 1
- 1
src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderStatusChangedToStockConfirmedIntegrationEvent.cs View File

@ -1,5 +1,5 @@
namespace Microsoft.eShopOnContainers.Payment.API.IntegrationEvents.Events;
public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent
{
public int OrderId { get; }


+ 1
- 23
src/Services/Payment/Payment.API/Program.cs View File

@ -1,5 +1,4 @@
var appName = "Payment.API";
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ApplicationName = typeof(Program).Assembly.FullName,
@ -165,27 +164,6 @@ Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
.CreateLogger();
}
IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
var config = builder.Build();
if (config.GetValue<bool>("UseVault", false))
{
TokenCredential credential = new ClientSecretCredential(
config["Vault:TenantId"],
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
builder.AddAzureKeyVault(new Uri($"https://{config["Vault:Name"]}.vault.azure.net/"), credential);
}
return builder.Build();
}
public partial class Program
{
public static string Namespace = typeof(Program).Assembly.GetName().Name;


+ 3
- 1
src/Services/Payment/Payment.API/appsettings.Development.json View File

@ -1,6 +1,8 @@
{
"Logging": {
"IncludeScopes": false,
"Console": {
"IncludeScopes": false
},
"LogLevel": {
"Default": "Debug",
"System": "Information",


+ 1
- 1
src/Services/Webhooks/Webhooks.API/Infrastructure/HttpGlobalExceptionFilter.cs View File

@ -26,7 +26,7 @@ public class HttpGlobalExceptionFilter : IExceptionFilter
Detail = "Please refer to the errors property for additional details."
};
problemDetails.Errors.Add("DomainValidations", new [] { context.Exception.Message });
problemDetails.Errors.Add("DomainValidations", new[] { context.Exception.Message });
context.Result = new BadRequestObjectResult(problemDetails);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;


+ 5
- 4
src/Services/Webhooks/Webhooks.API/Program.cs View File

@ -1,6 +1,7 @@
CreateWebHostBuilder(args).Build()
.MigrateDbContext<WebhooksContext>((_, __) => { })
.Run();
// TODO: Don't do this twice...
var host = CreateWebHostBuilder(args).Build();
host.Services.MigrateDbContext<WebhooksContext>((_, __) => { });
host.Run();
IWebHostBuilder CreateWebHostBuilder(string[] args) =>
@ -16,4 +17,4 @@ IWebHostBuilder CreateWebHostBuilder(string[] args) =>
builder.AddConsole();
builder.AddDebug();
builder.AddAzureWebAppDiagnostics();
});
});

+ 36
- 36
src/Services/Webhooks/Webhooks.API/Startup.cs View File

@ -33,7 +33,7 @@ public class Startup
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
{
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
@ -64,7 +64,7 @@ public class Startup
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Webhooks.API V1");
c.SwaggerEndpoint($"{(!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty)}/swagger/v1/swagger.json", "Webhooks.API V1");
c.OAuthClientId("webhooksswaggerui");
c.OAuthAppName("WebHooks Service Swagger UI");
});
@ -137,7 +137,7 @@ internal static class CustomExtensionMethods
public static IServiceCollection AddSwagger(this IServiceCollection services, IConfiguration configuration)
{
services.AddSwaggerGen(options =>
{
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "eShopOnContainers - Webhooks HTTP API",
@ -239,47 +239,47 @@ internal static class CustomExtensionMethods
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var subscriptionClientName = configuration["SubscriptionClientName"];
return new DefaultServiceBusPersisterConnection(configuration["EventBusConnection"]);
});
}
else
var subscriptionClientName = configuration["SubscriptionClientName"];
return new DefaultServiceBusPersisterConnection(configuration["EventBusConnection"]);
});
}
else
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
var factory = new ConnectionFactory()
{
HostName = configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{
factory.UserName = configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{
factory.UserName = configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{
factory.Password = configuration["EventBusPassword"];
}
if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{
factory.Password = configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
return services;
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)


+ 8
- 7
src/Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj View File

@ -20,12 +20,6 @@
</ItemGroup>
<ItemGroup>
<Content Include="Services\Basket\appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Services\Catalog\appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Setup\CatalogBrands.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@ -41,13 +35,20 @@
<Content Include="Services\Ordering\appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Services\Basket\appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Services\Catalog\appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save