Browse Source

Merge branch 'dev' into igorsychev/ocelot

# Conflicts:
#	src/Services/Basket/Basket.API/Basket.API.csproj
#	src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs
#	src/Services/Catalog/Catalog.API/Catalog.API.csproj
#	src/Services/Identity/Identity.API/Identity.API.csproj
#	src/Services/Location/Locations.API/Locations.API.csproj
#	src/Services/Marketing/Marketing.API/Marketing.API.csproj
#	src/Services/Ordering/Ordering.API/Ordering.API.csproj
#	src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj
#	src/Services/Payment/Payment.API/Payment.API.csproj
pull/965/head
Igor_Sychev 6 years ago
parent
commit
7d43c228fc
159 changed files with 1968 additions and 1146 deletions
  1. +1
    -1
      .gitignore
  2. +2
    -2
      deploy/az/servicebus/readme.md
  3. +6
    -0
      docker-compose.override.yml
  4. +3
    -0
      docker-compose.yml
  5. +1
    -1
      src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj
  6. +1
    -1
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj
  7. +1
    -1
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs
  8. +1
    -1
      src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs
  9. +1
    -1
      src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj
  10. +1
    -1
      src/BuildingBlocks/EventBus/EventBus/EventBus.csproj
  11. +32
    -0
      src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs
  12. +2
    -2
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs
  13. +13
    -3
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
  14. +1
    -1
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj
  15. +0
    -0
      src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs
  16. +18
    -11
      src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs
  17. +1
    -1
      src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj
  18. +3
    -3
      src/BuildingBlocks/WebHostCustomization/WebHost.Customization/WebHostExtensions.cs
  19. +0
    -175
      src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak
  20. +10
    -10
      src/Services/Basket/Basket.API/Basket.API.csproj
  21. +22
    -3
      src/Services/Basket/Basket.API/Controllers/BasketController.cs
  22. +14
    -2
      src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs
  23. +22
    -9
      src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs
  24. +67
    -39
      src/Services/Basket/Basket.API/Program.cs
  25. +5
    -4
      src/Services/Basket/Basket.API/Startup.cs
  26. +9
    -6
      src/Services/Basket/Basket.API/appsettings.json
  27. +1
    -1
      src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj
  28. +0
    -1
      src/Services/Basket/Basket.FunctionalTests/BasketScenarios.cs
  29. +22
    -6
      src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs
  30. +1
    -1
      src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj
  31. +8
    -5
      src/Services/Catalog/Catalog.API/Catalog.API.csproj
  32. +7
    -7
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs
  33. +2
    -2
      src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs
  34. +23
    -10
      src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs
  35. +28
    -16
      src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToAwaitingValidationIntegrationEventHandler.cs
  36. +20
    -8
      src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs
  37. +70
    -39
      src/Services/Catalog/Catalog.API/Program.cs
  38. +4
    -1
      src/Services/Catalog/Catalog.API/Properties/launchSettings.json
  39. +4
    -3
      src/Services/Catalog/Catalog.API/Startup.cs
  40. +9
    -6
      src/Services/Catalog/Catalog.API/appsettings.json
  41. +4
    -2
      src/Services/Catalog/Catalog.API/web.config
  42. +1
    -1
      src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj
  43. +1
    -1
      src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj
  44. +3
    -26
      src/Services/Identity/Identity.API/Controllers/AccountController.cs
  45. +6
    -6
      src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs
  46. +9
    -3
      src/Services/Identity/Identity.API/Identity.API.csproj
  47. +0
    -11
      src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs
  48. +0
    -13
      src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs
  49. +0
    -8
      src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs
  50. +86
    -57
      src/Services/Identity/Identity.API/Program.cs
  51. +24
    -21
      src/Services/Identity/Identity.API/Startup.cs
  52. +9
    -6
      src/Services/Identity/Identity.API/appsettings.json
  53. +15
    -5
      src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs
  54. +8
    -5
      src/Services/Location/Locations.API/Locations.API.csproj
  55. +67
    -37
      src/Services/Location/Locations.API/Program.cs
  56. +3
    -2
      src/Services/Location/Locations.API/Startup.cs
  57. +9
    -6
      src/Services/Location/Locations.API/appsettings.json
  58. +1
    -1
      src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj
  59. +1
    -1
      src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs
  60. +17
    -6
      src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs
  61. +9
    -6
      src/Services/Marketing/Marketing.API/Marketing.API.csproj
  62. +70
    -40
      src/Services/Marketing/Marketing.API/Program.cs
  63. +2
    -1
      src/Services/Marketing/Marketing.API/Startup.cs
  64. +9
    -4
      src/Services/Marketing/Marketing.API/appsettings.json
  65. +1
    -1
      src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj
  66. +6
    -3
      src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs
  67. +21
    -13
      src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs
  68. +18
    -6
      src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs
  69. +6
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs
  70. +16
    -6
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs
  71. +65
    -11
      src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs
  72. +6
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs
  73. +6
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs
  74. +6
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/SetStockConfirmedOrderStatusCommandHandler.cs
  75. +6
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/SetStockRejectedOrderStatusCommandHandler.cs
  76. +8
    -3
      src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs
  77. +10
    -9
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs
  78. +3
    -3
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs
  79. +3
    -3
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs
  80. +3
    -3
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs
  81. +3
    -3
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs
  82. +3
    -1
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs
  83. +3
    -3
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs
  84. +25
    -3
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs
  85. +26
    -4
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs
  86. +25
    -3
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs
  87. +26
    -4
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs
  88. +29
    -7
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs
  89. +54
    -24
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs
  90. +16
    -4
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs
  91. +33
    -1
      src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs
  92. +0
    -1
      src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs
  93. +5
    -5
      src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs
  94. +9
    -6
      src/Services/Ordering/Ordering.API/Ordering.API.csproj
  95. +70
    -39
      src/Services/Ordering/Ordering.API/Program.cs
  96. +6
    -6
      src/Services/Ordering/Ordering.API/Startup.cs
  97. +9
    -6
      src/Services/Ordering/Ordering.API/appsettings.json
  98. +6
    -3
      src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj
  99. +59
    -17
      src/Services/Ordering/Ordering.BackgroundTasks/Program.cs
  100. +12
    -9
      src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs

+ 1
- 1
.gitignore View File

@ -26,7 +26,7 @@ bld/
# Visual Studio 2015 cache/options directory
.vs/
# Files created by bundling and minification on startup
# .js files created on build:
src/Web/WebMVC/wwwroot/js/site*
# Uncomment if you have tasks that create the project's static files in wwwroot


+ 2
- 2
deploy/az/servicebus/readme.md View File

@ -8,7 +8,7 @@ The ARM template `sbusdeploy.json` and its parameter file (`sbusdeploy.parameter
## Editing sbusdeploy.parameters.json file
You can edit the `sbusdeploy.parameters.parameters.json` file to set your values, but is not needed. The only parameter than can
You can edit the `sbusdeploy.parameters.json` file to set your values, but is not needed. The only parameter than can
be set is:
1. `namespaceprefix` is a string that is used to create the namespace. ARM script creates unique values by appending a unique string to this parameter value, so you can leave the default value.
@ -21,4 +21,4 @@ i. e. if you are in windows, to deploy servicebus in a new resourcegroup located
```
create-resources.cmd servicebus\sbusdeploy newResourceGroup -c westus
```
```

+ 6
- 0
docker-compose.override.yml View File

@ -7,6 +7,12 @@ version: '3.4'
# An external IP or DNS name has to be used (instead localhost and the 10.0.75.1 IP) when testing the Web apps and the Xamarin apps from remote machines/devices using the same WiFi, for instance.
services:
seq:
environment:
- ACCEPT_EULA=Y
ports:
- "5340:80"
sql.data:
environment:
- SA_PASSWORD=Pass@word


+ 3
- 0
docker-compose.yml View File

@ -1,6 +1,9 @@
version: '3.4'
services:
seq:
image: datalust/seq:latest
sql.data:
image: microsoft/mssql-server-linux:2017-latest


+ 1
- 1
src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj View File

@ -6,7 +6,7 @@
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />


+ 1
- 1
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj View File

@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />


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

@ -57,7 +57,7 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}


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

@ -57,7 +57,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}


+ 1
- 1
src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj View File

@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />


+ 1
- 1
src/BuildingBlocks/EventBus/EventBus/EventBus.csproj View File

@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>
</Project>

+ 32
- 0
src/BuildingBlocks/EventBus/EventBus/Extensions/GenericTypeExtensions.cs View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions
{
public static class GenericTypeExtensions
{
public static string GetGenericTypeName(this Type type)
{
var typeName = string.Empty;
if (type.IsGenericType)
{
var genericTypes = string.Join(",", type.GetGenericArguments().Select(t => t.Name).ToArray());
typeName = $"{type.Name.Remove(type.Name.IndexOf('`'))}<{genericTypes}>";
}
else
{
typeName = type.Name;
}
return typeName;
}
public static string GetGenericTypeName(this object @object)
{
return @object.GetType().GetGenericTypeName();
}
}
}

src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersisterConnection.cs → src/BuildingBlocks/EventBus/EventBusRabbitMQ/DefaultRabbitMQPersistentConnection.cs View File

@ -72,7 +72,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
.Or<BrokerUnreachableException>()
.WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
{
_logger.LogWarning(ex.ToString());
_logger.LogWarning(ex, "RabbitMQ Client could not connect after {TimeOut}s ({ExceptionMessage})", $"{time.TotalSeconds:n1}", ex.Message);
}
);
@ -88,7 +88,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
_connection.CallbackException += OnCallbackException;
_connection.ConnectionBlocked += OnConnectionBlocked;
_logger.LogInformation($"RabbitMQ persistent connection acquired a connection {_connection.Endpoint.HostName} and is subscribed to failure events");
_logger.LogInformation("RabbitMQ Client acquired a persistent connection to '{HostName}' and is subscribed to failure events", _connection.Endpoint.HostName);
return true;
}

+ 13
- 3
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs View File

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@ -76,7 +77,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
.Or<SocketException>()
.WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
{
_logger.LogWarning(ex.ToString());
_logger.LogWarning(ex, "Could not publish event: {EventId} after {Timeout}s ({ExceptionMessage})", @event.Id, $"{time.TotalSeconds:n1}", ex.Message);
});
using (var channel = _persistentConnection.CreateModel())
@ -107,6 +108,8 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
public void SubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
_logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
DoInternalSubscription(eventName);
_subsManager.AddDynamicSubscription<TH>(eventName);
}
@ -117,6 +120,9 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
{
var eventName = _subsManager.GetEventKey<T>();
DoInternalSubscription(eventName);
_logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
_subsManager.AddSubscription<T, TH>();
}
@ -140,9 +146,13 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
}
public void Unsubscribe<T, TH>()
where TH : IIntegrationEventHandler<T>
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>
{
var eventName = _subsManager.GetEventKey<T>();
_logger.LogInformation("Unsubscribing from event {EventName}", eventName);
_subsManager.RemoveSubscription<T, TH>();
}
@ -215,7 +225,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
foreach (var subscription in subscriptions)
{
if (subscription.IsDynamic)
{
{
var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler;
if (handler == null) continue;
dynamic eventData = JObject.Parse(message);


+ 1
- 1
src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.csproj View File

@ -9,7 +9,7 @@
<PackageReference Include="Autofac" Version="4.9.1" />
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="Polly" Version="7.0.3" />
<PackageReference Include="RabbitMQ.Client" Version="5.1.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />


src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersisterConnection.cs → src/BuildingBlocks/EventBus/EventBusRabbitMQ/IRabbitMQPersistentConnection.cs View File


+ 18
- 11
src/BuildingBlocks/EventBus/EventBusServiceBus/EventBusServiceBus.cs View File

@ -27,7 +27,7 @@
ILifetimeScope autofac)
{
_serviceBusPersisterConnection = serviceBusPersisterConnection;
_logger = logger;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager();
_subscriptionClient = new SubscriptionClient(serviceBusPersisterConnection.ServiceBusConnectionStringBuilder,
@ -61,6 +61,8 @@
public void SubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
_logger.LogInformation("Subscribing to dynamic event {EventName} with {EventHandler}", eventName, nameof(TH));
_subsManager.AddDynamicSubscription<TH>(eventName);
}
@ -83,10 +85,12 @@
}
catch (ServiceBusException)
{
_logger.LogInformation($"The messaging entity {eventName} already exists.");
_logger.LogWarning("The messaging entity {eventName} already exists.", eventName);
}
}
_logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, nameof(TH));
_subsManager.AddSubscription<T, TH>();
}
@ -105,15 +109,19 @@
}
catch (MessagingEntityNotFoundException)
{
_logger.LogInformation($"The messaging entity {eventName} Could not be found.");
_logger.LogWarning("The messaging entity {eventName} Could not be found.", eventName);
}
_logger.LogInformation("Unsubscribing from event {EventName}", eventName);
_subsManager.RemoveSubscription<T, TH>();
}
public void UnsubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler
{
_logger.LogInformation("Unsubscribing from dynamic event {EventName}", eventName);
_subsManager.RemoveDynamicSubscription<TH>(eventName);
}
@ -136,17 +144,16 @@
await _subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}
},
new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 10, AutoComplete = false });
new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 10, AutoComplete = false });
}
private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
var ex = exceptionReceivedEventArgs.Exception;
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
Console.WriteLine("Exception context for troubleshooting:");
Console.WriteLine($"- Endpoint: {context.Endpoint}");
Console.WriteLine($"- Entity Path: {context.EntityPath}");
Console.WriteLine($"- Executing Action: {context.Action}");
_logger.LogError(ex, "ERROR handling message: {ExceptionMessage} - Context: {@ExceptionContext}", ex.Message, context);
return Task.CompletedTask;
}
@ -172,7 +179,7 @@
var handler = scope.ResolveOptional(subscription.HandlerType);
if (handler == null) continue;
var eventType = _subsManager.GetEventTypeByName(eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
}
@ -194,7 +201,7 @@
}
catch (MessagingEntityNotFoundException)
{
_logger.LogInformation($"The messaging entity { RuleDescription.DefaultRuleName } Could not be found.");
_logger.LogWarning("The messaging entity {DefaultRuleName} Could not be found.", RuleDescription.DefaultRuleName);
}
}
}

+ 1
- 1
src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEF.csproj View File

@ -10,7 +10,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.2" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EventBus\EventBus.csproj" />


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

@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Hosting
try
{
logger.LogInformation($"Migrating database associated with context {typeof(TContext).Name}");
logger.LogInformation("Migrating database associated with context {DbContextName}", typeof(TContext).Name);
if (underK8s)
{
@ -55,11 +55,11 @@ namespace Microsoft.AspNetCore.Hosting
retry.Execute(() => InvokeSeeder(seeder, context, services));
}
logger.LogInformation($"Migrated database associated with context {typeof(TContext).Name}");
logger.LogInformation("Migrated database associated with context {DbContextName}", typeof(TContext).Name);
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred while migrating the database used on context {typeof(TContext).Name}");
logger.LogError(ex, "An error occurred while migrating the database used on context {DbContextName}", typeof(TContext).Name);
if (underK8s)
{
throw; // Rethrow under k8s because we rely on k8s to re-run the pod


+ 0
- 175
src/Mobile/eShopOnContainers/eShopOnContainers.TestRunner.iOS/eShopOnContainers.TestRunner.iOS.csproj.bak View File

@ -1,175 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{B68C2B56-7581-46AE-B55D-D25DDFD3BFE3}</ProjectGuid>
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Exe</OutputType>
<RootNamespace>eShopOnContainers.TestRunner.iOS</RootNamespace>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<AssemblyName>eShopOnContainers.TestRunner.iOS</AssemblyName>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>x86_64</MtouchArch>
<MtouchLink>SdkOnly</MtouchLink>
<MtouchDebug>True</MtouchDebug>
<MtouchSdkVersion>10.1</MtouchSdkVersion>
<MtouchProfiling>False</MtouchProfiling>
<MtouchFastDev>False</MtouchFastDev>
<MtouchUseLlvm>False</MtouchUseLlvm>
<MtouchUseThumb>False</MtouchUseThumb>
<MtouchEnableBitcode>False</MtouchEnableBitcode>
<MtouchUseSGen>False</MtouchUseSGen>
<MtouchUseRefCounting>False</MtouchUseRefCounting>
<OptimizePNGs>True</OptimizePNGs>
<MtouchTlsProvider>Default</MtouchTlsProvider>
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler>
<MtouchFloat32>False</MtouchFloat32>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MtouchLink>None</MtouchLink>
<MtouchArch>x86_64</MtouchArch>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\iPhone\Debug</OutputPath>
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>true</MtouchDebug>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\iPhone\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<ConsolePause>false</ConsolePause>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Ad-Hoc|iPhone' ">
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\Ad-Hoc</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<BuildIpa>True</BuildIpa>
<CodesignProvision>Automatic:AdHoc</CodesignProvision>
<CodesignKey>iPhone Distribution</CodesignKey>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AppStore|iPhone' ">
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\iPhone\AppStore</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<MtouchArch>ARMv7, ARM64</MtouchArch>
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
<CodesignProvision>Automatic:AppStore</CodesignProvision>
<CodesignKey>iPhone Distribution</CodesignKey>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<None Include="app.config" />
<None Include="Info.plist" />
<Compile Include="Properties\AssemblyInfo.cs" />
<InterfaceDefinition Include="Resources\LaunchScreen.xib" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Xamarin.Forms.Platform, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Xamarin.Forms.Platform.iOS, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Xamarin.Forms.Xaml, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.3.166-pre4\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Xamarin.iOS" />
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\netstandard1.0\xunit.abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.assert, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.core, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.execution.dotnet, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.execution.dotnet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.runner.devices, Version=2.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.runner.devices.2.1.0\lib\Xamarin.iOS\xunit.runner.devices.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.runner.utility.dotnet, Version=2.2.0.3444, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\..\packages\xunit.runner.utility.2.2.0-beta4-build3444\lib\netstandard1.1\xunit.runner.utility.dotnet.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Content Include="AppDelegate.cs.txt" />
<Content Include="Entitlements.plist" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\eShopOnContainers.UnitTests\eShopOnContainers.UnitTests.csproj">
<Project>{f7b6a162-bc4d-4924-b16a-713f9b0344e7}</Project>
<Name>eShopOnContainers.UnitTests</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
<Import Project="..\..\packages\Xamarin.Forms.2.3.3.166-pre4\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.3.166-pre4\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Este proyecto hace referencia a los paquetes NuGet que faltan en este equipo. Use la restauración de paquetes NuGet para descargarlos. Para obtener más información, consulte http://go.microsoft.com/fwlink/?LinkID=322105. El archivo que falta es {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Xamarin.Forms.2.3.3.166-pre4\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.2.3.3.166-pre4\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20\Xamarin.Forms.targets'))" />
<Error Condition="!Exists('..\..\packages\xunit.runner.devices.2.1.0\build\Xamarin.iOS\xunit.runner.devices.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.devices.2.1.0\build\Xamarin.iOS\xunit.runner.devices.targets'))" />
</Target>
<Import Project="..\..\packages\xunit.runner.devices.2.1.0\build\Xamarin.iOS\xunit.runner.devices.targets" Condition="Exists('..\..\packages\xunit.runner.devices.2.1.0\build\Xamarin.iOS\xunit.runner.devices.targets')" />
</Project>

+ 10
- 10
src/Services/Basket/Basket.API/Basket.API.csproj View File

@ -13,25 +13,25 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.4" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.9.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.6" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
</ItemGroup>
<ItemGroup>


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

@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.eShopOnContainers.Services.Basket.API.Services;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Net;
using System.Threading.Tasks;
@ -19,9 +21,15 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
private readonly IBasketRepository _repository;
private readonly IIdentityService _identityService;
private readonly IEventBus _eventBus;
private readonly ILogger<BasketController> _logger;
public BasketController(IBasketRepository repository, IIdentityService identityService, IEventBus eventBus)
public BasketController(
ILogger<BasketController> logger,
IBasketRepository repository,
IIdentityService identityService,
IEventBus eventBus)
{
_logger = logger;
_repository = repository;
_identityService = identityService;
_eventBus = eventBus;
@ -50,7 +58,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
public async Task<ActionResult> CheckoutAsync([FromBody]BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId)
{
var userId = _identityService.GetUserIdentity();
basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ?
guid : basketCheckout.RequestId;
@ -70,7 +78,18 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers
// Once basket is checkout, sends an integration event to
// ordering.api to convert basket to order and proceeds with
// order creation process
_eventBus.Publish(eventMessage);
try
{
_logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", eventMessage.Id, Program.AppName, eventMessage);
_eventBus.Publish(eventMessage);
}
catch (Exception ex)
{
_logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName);
throw;
}
return Accepted();
}


+ 14
- 2
src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs View File

@ -1,6 +1,9 @@
using Basket.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Threading.Tasks;
@ -9,15 +12,24 @@ namespace Basket.API.IntegrationEvents.EventHandling
public class OrderStartedIntegrationEventHandler : IIntegrationEventHandler<OrderStartedIntegrationEvent>
{
private readonly IBasketRepository _repository;
private readonly ILogger<OrderStartedIntegrationEventHandler> _logger;
public OrderStartedIntegrationEventHandler(IBasketRepository repository)
public OrderStartedIntegrationEventHandler(
IBasketRepository repository,
ILogger<OrderStartedIntegrationEventHandler> logger)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderStartedIntegrationEvent @event)
{
await _repository.DeleteBasketAsync(@event.UserId.ToString());
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
await _repository.DeleteBasketAsync(@event.UserId.ToString());
}
}
}
}


+ 22
- 9
src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs View File

@ -1,6 +1,8 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Linq;
using System.Threading.Tasks;
@ -9,22 +11,31 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even
{
public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>
{
private readonly ILogger<ProductPriceChangedIntegrationEventHandler> _logger;
private readonly IBasketRepository _repository;
public ProductPriceChangedIntegrationEventHandler(IBasketRepository repository)
public ProductPriceChangedIntegrationEventHandler(
ILogger<ProductPriceChangedIntegrationEventHandler> logger,
IBasketRepository repository)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
public async Task Handle(ProductPriceChangedIntegrationEvent @event)
{
var userIds = _repository.GetUsers();
foreach (var id in userIds)
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
var basket = await _repository.GetBasketAsync(id);
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket);
var userIds = _repository.GetUsers();
foreach (var id in userIds)
{
var basket = await _repository.GetBasketAsync(id);
await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket);
}
}
}
@ -35,17 +46,19 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Even
if (itemsToUpdate != null)
{
_logger.LogInformation("----- ProductPriceChangedIntegrationEventHandler - Updating items in basket for user: {BuyerId} ({@Items})", basket.BuyerId, itemsToUpdate);
foreach (var item in itemsToUpdate)
{
if(item.UnitPrice == oldPrice)
{
if (item.UnitPrice == oldPrice)
{
var originalPrice = item.UnitPrice;
item.UnitPrice = newPrice;
item.OldUnitPrice = originalPrice;
}
}
await _repository.UpdateBasketAsync(basket);
}
}
}
}
}


+ 67
- 39
src/Services/Basket/Basket.API/Program.cs View File

@ -12,52 +12,80 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
{
public class Program
{
public static void Main(string[] args)
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args)
{
BuildWebHost(args).Run();
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.UseFailing(options =>
{
options.ConfigPath = "/Failing";
})
.UseContentRoot(Directory.GetCurrentDirectory())
options.ConfigPath = "/Failing")
.UseStartup<Startup>()
.ConfigureAppConfiguration((builderContext, config) =>
{
var builtConfig = config.Build();
var configurationBuilder = new ConfigurationBuilder();
if (Convert.ToBoolean(builtConfig["UseVault"]))
{
configurationBuilder.AddAzureKeyVault(
$"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
builtConfig["Vault:ClientId"],
builtConfig["Vault:ClientSecret"]);
}
configurationBuilder.AddEnvironmentVariables();
config.AddConfiguration(configurationBuilder.Build());
})
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
builder.AddAzureWebAppDiagnostics();
})
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.UseApplicationInsights()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseConfiguration(configuration)
.UseSerilog()
.Build();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
private static 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))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
}
}

+ 5
- 4
src/Services/Basket/Basket.API/Startup.cs View File

@ -50,7 +50,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
RegisterAppInsights(services);
RegisterAppInsights(services);
// Add framework services.
services.AddMvc(options =>
@ -66,7 +66,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
services.AddCustomHealthCheck(Configuration);
services.Configure<BasketSettings>(Configuration);
services.Configure<BasketSettings>(Configuration);
//By connecting here we are making sure that our service
//cannot start until redis is ready. This might slow down startup,
@ -179,9 +179,10 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))


+ 9
- 6
src/Services/Basket/Basket.API/appsettings.json View File

@ -1,10 +1,13 @@
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
"Serilog": {
"SeqServerUrl": null,
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.eShopOnContainers": "Information",
"System": "Warning"
}
}
},
"IdentityUrl": "http://localhost:5105",


+ 1
- 1
src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj View File

@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">


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

@ -73,7 +73,6 @@ namespace Basket.FunctionalTests
string BuildCheckout()
{
var checkoutBasket = new
{
City = "city",


+ 22
- 6
src/Services/Basket/Basket.UnitTests/Application/BasketWebApiTest.cs View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Basket.API.Controllers;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.Collections.Generic;
@ -20,12 +21,14 @@ namespace UnitTest.Basket.Application
private readonly Mock<IBasketRepository> _basketRepositoryMock;
private readonly Mock<IBasketIdentityService> _identityServiceMock;
private readonly Mock<IEventBus> _serviceBusMock;
private readonly Mock<ILogger<BasketController>> _loggerMock;
public BasketWebApiTest()
{
_basketRepositoryMock = new Mock<IBasketRepository>();
_identityServiceMock = new Mock<IBasketIdentityService>();
_serviceBusMock = new Mock<IEventBus>();
_loggerMock = new Mock<ILogger<BasketController>>();
}
[Fact]
@ -43,7 +46,11 @@ namespace UnitTest.Basket.Application
//Act
var basketController = new BasketController(
_basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
_loggerMock.Object,
_basketRepositoryMock.Object,
_identityServiceMock.Object,
_serviceBusMock.Object);
var actionResult = await basketController.GetBasketByIdAsync(fakeCustomerId);
//Assert
@ -65,14 +72,17 @@ namespace UnitTest.Basket.Application
//Act
var basketController = new BasketController(
_basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
_loggerMock.Object,
_basketRepositoryMock.Object,
_identityServiceMock.Object,
_serviceBusMock.Object);
var actionResult = await basketController.UpdateBasketAsync(fakeCustomerBasket);
//Assert
Assert.Equal((actionResult.Result as OkObjectResult).StatusCode, (int)System.Net.HttpStatusCode.OK);
Assert.Equal(((CustomerBasket)actionResult.Value).BuyerId, fakeCustomerId);
}
}
[Fact]
public async Task Doing_Checkout_Without_Basket_Should_Return_Bad_Request()
@ -84,7 +94,10 @@ namespace UnitTest.Basket.Application
//Act
var basketController = new BasketController(
_basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
_loggerMock.Object,
_basketRepositoryMock.Object,
_identityServiceMock.Object,
_serviceBusMock.Object);
var result = await basketController.CheckoutAsync(new BasketCheckout(), Guid.NewGuid().ToString()) as BadRequestResult;
Assert.NotNull(result);
@ -102,7 +115,10 @@ namespace UnitTest.Basket.Application
_identityServiceMock.Setup(x => x.GetUserIdentity()).Returns(fakeCustomerId);
var basketController = new BasketController(
_basketRepositoryMock.Object, _identityServiceMock.Object, _serviceBusMock.Object);
_loggerMock.Object,
_basketRepositoryMock.Object,
_identityServiceMock.Object,
_serviceBusMock.Object);
basketController.ControllerContext = new ControllerContext()
{
@ -122,7 +138,7 @@ namespace UnitTest.Basket.Application
}
private CustomerBasket GetCustomerBasketFake(string fakeCustomerId)
{
{
return new CustomerBasket(fakeCustomerId)
{
Items = new List<BasketItem>()


+ 1
- 1
src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj View File

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>


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

@ -35,13 +35,13 @@
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureStorage" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.4" />
<PackageReference Include="AspNetCore.HealthChecks.AzureStorage" Version="2.2.1" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.1" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.9.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
@ -51,7 +51,10 @@
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
</ItemGroup>


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

@ -77,14 +77,14 @@
}
catch (Exception ex)
{
logger.LogError(ex.Message);
logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPreconfiguredCatalogBrands();
}
return File.ReadAllLines(csvFileCatalogBrands)
.Skip(1) // skip header row
.SelectTry(x => CreateCatalogBrand(x))
.OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
.OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@ -132,14 +132,14 @@
}
catch (Exception ex)
{
logger.LogError(ex.Message);
logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPreconfiguredCatalogTypes();
}
return File.ReadAllLines(csvFileCatalogTypes)
.Skip(1) // skip header row
.SelectTry(x => CreateCatalogType(x))
.OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
.OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@ -187,7 +187,7 @@
}
catch (Exception ex)
{
logger.LogError(ex.Message);
logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPreconfiguredItems();
}
@ -198,7 +198,7 @@
.Skip(1) // skip header row
.Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") )
.SelectTry(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup))
.OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
.OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@ -378,7 +378,7 @@
sleepDurationProvider: retry => TimeSpan.FromSeconds(5),
onRetry: (exception, timeSpan, retry, ctx) =>
{
logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}");
logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries);
}
);
}


+ 2
- 2
src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs View File

@ -49,7 +49,7 @@ namespace Catalog.API.Infrastructure.Filters
if (env.IsDevelopment())
{
json.DeveloperMeesage = context.Exception;
json.DeveloperMessage = context.Exception;
}
context.Result = new InternalServerErrorObjectResult(json);
@ -62,7 +62,7 @@ namespace Catalog.API.Infrastructure.Filters
{
public string[] Messages { get; set; }
public object DeveloperMeesage { get; set; }
public object DeveloperMessage { get; set; }
}
}
}

+ 23
- 10
src/Services/Catalog/Catalog.API/IntegrationEvents/CatalogIntegrationEventService.cs View File

@ -4,7 +4,10 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
using Microsoft.eShopOnContainers.Services.Catalog.API;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Data.Common;
using System.Threading.Tasks;
@ -17,10 +20,15 @@ namespace Catalog.API.IntegrationEvents
private readonly IEventBus _eventBus;
private readonly CatalogContext _catalogContext;
private readonly IIntegrationEventLogService _eventLogService;
private readonly ILogger<CatalogIntegrationEventService> _logger;
public CatalogIntegrationEventService(IEventBus eventBus, CatalogContext catalogContext,
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
public CatalogIntegrationEventService(
ILogger<CatalogIntegrationEventService> logger,
IEventBus eventBus,
CatalogContext catalogContext,
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_catalogContext = catalogContext ?? throw new ArgumentNullException(nameof(catalogContext));
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
@ -31,26 +39,31 @@ namespace Catalog.API.IntegrationEvents
{
try
{
_logger.LogInformation("----- Publishing integration event: {IntegrationEventId_published} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt);
await _eventLogService.MarkEventAsInProgressAsync(evt.Id);
_eventBus.Publish(evt);
await _eventLogService.MarkEventAsPublishedAsync(evt.Id);
}
catch (Exception)
catch (Exception ex)
{
_logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", evt.Id, Program.AppName, evt);
await _eventLogService.MarkEventAsFailedAsync(evt.Id);
}
}
}
public async Task SaveEventAndCatalogContextChangesAsync(IntegrationEvent evt)
{
_logger.LogInformation("----- CatalogIntegrationEventService - Saving changes and integrationEvent: {IntegrationEventId}", evt.Id);
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
await ResilientTransaction.New(_catalogContext)
.ExecuteAsync(async () => {
// Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction
await _catalogContext.SaveChangesAsync();
await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction());
});
await ResilientTransaction.New(_catalogContext).ExecuteAsync(async () =>
{
// Achieving atomicity between original catalog database operation and the IntegrationEventLog thanks to a local transaction
await _catalogContext.SaveChangesAsync();
await _eventLogService.SaveEventAsync(evt, _catalogContext.Database.CurrentTransaction.GetDbTransaction());
});
}
}
}

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

@ -8,39 +8,51 @@
using System.Linq;
using global::Catalog.API.IntegrationEvents;
using IntegrationEvents.Events;
using Serilog.Context;
using Microsoft.Extensions.Logging;
public class OrderStatusChangedToAwaitingValidationIntegrationEventHandler :
IIntegrationEventHandler<OrderStatusChangedToAwaitingValidationIntegrationEvent>
{
private readonly CatalogContext _catalogContext;
private readonly ICatalogIntegrationEventService _catalogIntegrationEventService;
private readonly ILogger<OrderStatusChangedToAwaitingValidationIntegrationEventHandler> _logger;
public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(CatalogContext catalogContext,
ICatalogIntegrationEventService catalogIntegrationEventService)
public OrderStatusChangedToAwaitingValidationIntegrationEventHandler(
CatalogContext catalogContext,
ICatalogIntegrationEventService catalogIntegrationEventService,
ILogger<OrderStatusChangedToAwaitingValidationIntegrationEventHandler> logger)
{
_catalogContext = catalogContext;
_catalogIntegrationEventService = catalogIntegrationEventService;
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent command)
public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event)
{
var confirmedOrderStockItems = new List<ConfirmedOrderStockItem>();
foreach (var orderStockItem in command.OrderStockItems)
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
var hasStock = catalogItem.AvailableStock >= orderStockItem.Units;
var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
confirmedOrderStockItems.Add(confirmedOrderStockItem);
}
var confirmedOrderStockItems = new List<ConfirmedOrderStockItem>();
foreach (var orderStockItem in @event.OrderStockItems)
{
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
var hasStock = catalogItem.AvailableStock >= orderStockItem.Units;
var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);
var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
? (IntegrationEvent) new OrderStockRejectedIntegrationEvent(command.OrderId, confirmedOrderStockItems)
: new OrderStockConfirmedIntegrationEvent(command.OrderId);
confirmedOrderStockItems.Add(confirmedOrderStockItem);
}
await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
? (IntegrationEvent)new OrderStockRejectedIntegrationEvent(@event.OrderId, confirmedOrderStockItems)
: new OrderStockConfirmedIntegrationEvent(@event.OrderId);
await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
}
}
}
}

+ 20
- 8
src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/OrderStatusChangedToPaidIntegrationEventHandler.cs View File

@ -4,28 +4,40 @@
using System.Threading.Tasks;
using Infrastructure;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events;
using Microsoft.Extensions.Logging;
using Serilog.Context;
public class OrderStatusChangedToPaidIntegrationEventHandler :
IIntegrationEventHandler<OrderStatusChangedToPaidIntegrationEvent>
{
private readonly CatalogContext _catalogContext;
private readonly ILogger<OrderStatusChangedToPaidIntegrationEventHandler> _logger;
public OrderStatusChangedToPaidIntegrationEventHandler(CatalogContext catalogContext)
public OrderStatusChangedToPaidIntegrationEventHandler(
CatalogContext catalogContext,
ILogger<OrderStatusChangedToPaidIntegrationEventHandler> logger)
{
_catalogContext = catalogContext;
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderStatusChangedToPaidIntegrationEvent command)
public async Task Handle(OrderStatusChangedToPaidIntegrationEvent @event)
{
//we're not blocking stock/inventory
foreach (var orderStockItem in command.OrderStockItems)
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
catalogItem.RemoveStock(orderStockItem.Units);
}
//we're not blocking stock/inventory
foreach (var orderStockItem in @event.OrderStockItems)
{
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
catalogItem.RemoveStock(orderStockItem.Units);
}
await _catalogContext.SaveChangesAsync();
await _catalogContext.SaveChangesAsync();
}
}
}
}

+ 70
- 39
src/Services/Catalog/Catalog.API/Program.cs View File

@ -9,66 +9,97 @@ using Microsoft.Extensions.Options;
using Serilog;
using System;
using System.IO;
namespace Microsoft.eShopOnContainers.Services.Catalog.API
{
public class Program
{
public static void Main(string[] args)
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args)
{
BuildWebHost(args)
.MigrateDbContext<CatalogContext>((context,services)=>
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Applying migrations ({ApplicationContext})...", AppName);
host.MigrateDbContext<CatalogContext>((context, services) =>
{
var env = services.GetService<IHostingEnvironment>();
var settings = services.GetService<IOptions<CatalogSettings>>();
var logger = services.GetService<ILogger<CatalogContextSeed>>();
new CatalogContextSeed()
.SeedAsync(context,env,settings,logger)
.Wait();
.SeedAsync(context, env, settings, logger)
.Wait();
})
.MigrateDbContext<IntegrationEventLogContext>((_,__)=> { })
.Run();
.MigrateDbContext<IntegrationEventLogContext>((_, __) => { });
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.UseApplicationInsights()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseWebRoot("Pics")
.ConfigureAppConfiguration((builderContext, config) =>
{
var builtConfig = config.Build();
.UseConfiguration(configuration)
.UseSerilog()
.Build();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
var configurationBuilder = new ConfigurationBuilder();
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
if (Convert.ToBoolean(builtConfig["UseVault"]))
{
configurationBuilder.AddAzureKeyVault(
$"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
builtConfig["Vault:ClientId"],
builtConfig["Vault:ClientSecret"]);
}
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
configurationBuilder.AddEnvironmentVariables();
var config = builder.Build();
config.AddConfiguration(configurationBuilder.Build());
})
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
builder.AddAzureWebAppDiagnostics();
})
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.Build();
if (config.GetValue<bool>("UseVault", false))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
}
}

+ 4
- 1
src/Services/Catalog/Catalog.API/Properties/launchSettings.json View File

@ -13,7 +13,10 @@
"launchBrowser": true,
"launchUrl": "/swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ConnectionString": "server=localhost,5433;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word",
"ASPNETCORE_ENVIRONMENT": "Development",
"EventBusConnection": "localhost",
"Serilog:SeqServerUrl": "http://locahost:5340"
}
},
"Microsoft.eShopOnContainers.Services.Catalog.API": {


+ 4
- 3
src/Services/Catalog/Catalog.API/Startup.cs View File

@ -60,17 +60,18 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//Configure logs
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}


+ 9
- 6
src/Services/Catalog/Catalog.API/appsettings.json View File

@ -2,12 +2,15 @@
"ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word",
"PicBaseUrl": "http://localhost:5101/api/v1/catalog/items/[0]/pic/",
"UseCustomizationData": false,
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"System": "Information",
"Microsoft": "Information"
"Serilog": {
"SeqServerUrl": null,
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.eShopOnContainers": "Information",
"System": "Warning"
}
}
},
"AzureServiceBusEnabled": false,


+ 4
- 2
src/Services/Catalog/Catalog.API/web.config View File

@ -2,8 +2,10 @@
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="false" stdoutLogEnabled="false" />
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="false" stdoutLogEnabled="false">
<environmentVariables />
</aspNetCore>
</system.webServer>
</configuration>

+ 1
- 1
src/Services/Catalog/Catalog.FunctionalTests/Catalog.FunctionalTests.csproj View File

@ -33,7 +33,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">


+ 1
- 1
src/Services/Catalog/Catalog.UnitTests/Catalog.UnitTests.csproj View File

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>


+ 3
- 26
src/Services/Identity/Identity.API/Controllers/AccountController.cs View File

@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
{
/// <summary>
/// This sample controller implements a typical login/logout/provision workflow for local and external accounts.
/// This sample controller implements a typical login/logout/provision workflow for local accounts.
/// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production!
/// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval
/// </summary>
@ -58,8 +58,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context?.IdP != null)
{
// if IdP is passed, then bypass showing the login screen
return ExternalLogin(context.IdP, returnUrl);
throw new NotImplementedException("External login is not implemented!");
}
var vm = await BuildLoginViewModelAsync(returnUrl, context);
@ -209,7 +208,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
}
catch (Exception ex)
{
_logger.LogCritical(ex.Message);
_logger.LogError(ex, "LOGOUT ERROR: {ExceptionMessage}", ex.Message);
}
}
@ -238,28 +237,6 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Controllers
return Redirect(redirectUrl);
}
/// <summary>
/// initiate roundtrip to external authentication provider
/// </summary>
[HttpGet]
public IActionResult ExternalLogin(string provider, string returnUrl)
{
if (returnUrl != null)
{
returnUrl = UrlEncoder.Default.Encode(returnUrl);
}
returnUrl = "/account/externallogincallback?returnUrl=" + returnUrl;
// start challenge and roundtrip the return URL
var props = new AuthenticationProperties
{
RedirectUri = returnUrl,
Items = { { "scheme", provider } }
};
return new ChallengeResult(provider, props);
}
// GET: /Account/Register
[HttpGet]
[AllowAnonymous]


+ 6
- 6
src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs View File

@ -51,7 +51,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
{
retryForAvaiability++;
logger.LogError(ex.Message,$"There is an error migrating data for ApplicationDbContext");
logger.LogError(ex, "EXCEPTION ERROR while migrating {DbContextName}", nameof(ApplicationDbContext));
await SeedAsync(context,env,logger,settings, retryForAvaiability);
}
@ -80,7 +80,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
}
catch (Exception ex)
{
logger.LogError(ex.Message);
logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetDefaultUser();
}
@ -89,7 +89,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
.Skip(1) // skip header column
.Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") )
.SelectTry(column => CreateApplicationUser(column, csvheaders))
.OnCaughtException(ex => { logger.LogError(ex.Message); return null; })
.OnCaughtException(ex => { logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null)
.ToList();
@ -199,7 +199,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
if (!File.Exists(imagesZipFile))
{
logger.LogError($" zip file '{imagesZipFile}' does not exists.");
logger.LogError("Zip file '{ZipFileName}' does not exists.", imagesZipFile);
return;
}
@ -221,14 +221,14 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data
}
else
{
logger.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'");
logger.LogWarning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile);
}
}
}
}
catch (Exception ex)
{
logger.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}");
logger.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); ;
}
}
}


+ 9
- 3
src/Services/Identity/Identity.API/Identity.API.csproj View File

@ -17,18 +17,24 @@
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.9.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" />
<PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="2.2.0-preview2-35157" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.3.0" />
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.3.2" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.Redis" Version="2.2.0-preview2-35157" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="1.0.172" />


+ 0
- 11
src/Services/Identity/Identity.API/Models/AccountViewModels/ExternalLoginConfirmationViewModel.cs View File

@ -1,11 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.AccountViewModels
{
public class ExternalLoginConfirmationViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
}

+ 0
- 13
src/Services/Identity/Identity.API/Models/ManageViewModels/ManageLoginsViewModel.cs View File

@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ManageViewModels
{
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
}
}

+ 0
- 8
src/Services/Identity/Identity.API/Models/ManageViewModels/RemoveLoginViewModel.cs View File

@ -1,8 +0,0 @@
namespace Microsoft.eShopOnContainers.Services.Identity.API.Models.ManageViewModels
{
public class RemoveLoginViewModel
{
public string LoginProvider { get; set; }
public string ProviderKey { get; set; }
}
}

+ 86
- 57
src/Services/Identity/Identity.API/Program.cs View File

@ -14,70 +14,99 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
{
public class Program
{
public static void Main(string[] args)
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args)
{
BuildWebHost(args)
.MigrateDbContext<PersistedGrantDbContext>((_, __) => { })
.MigrateDbContext<ApplicationDbContext>((context, services) =>
{
var env = services.GetService<IHostingEnvironment>();
var logger = services.GetService<ILogger<ApplicationDbContextSeed>>();
var settings = services.GetService<IOptions<AppSettings>>();
new ApplicationDbContextSeed()
.SeedAsync(context, env, logger, settings)
.Wait();
})
.MigrateDbContext<ConfigurationDbContext>((context,services)=>
{
var configuration = services.GetService<IConfiguration>();
new ConfigurationDbContextSeed()
.SeedAsync(context, configuration)
.Wait();
}).Run();
}
var configuration = GetConfiguration();
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.ConfigureAppConfiguration((builderContext, config) =>
{
var builtConfig = config.Build();
Log.Logger = CreateSerilogLogger(configuration);
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
var configurationBuilder = new ConfigurationBuilder();
Log.Information("Applying migrations ({ApplicationContext})...", AppName);
host.MigrateDbContext<PersistedGrantDbContext>((_, __) => { })
.MigrateDbContext<ApplicationDbContext>((context, services) =>
{
var env = services.GetService<IHostingEnvironment>();
var logger = services.GetService<ILogger<ApplicationDbContextSeed>>();
var settings = services.GetService<IOptions<AppSettings>>();
if (Convert.ToBoolean(builtConfig["UseVault"]))
new ApplicationDbContextSeed()
.SeedAsync(context, env, logger, settings)
.Wait();
})
.MigrateDbContext<ConfigurationDbContext>((context, services) =>
{
configurationBuilder.AddAzureKeyVault(
$"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
builtConfig["Vault:ClientId"],
builtConfig["Vault:ClientSecret"]);
}
configurationBuilder.AddEnvironmentVariables();
config.AddConfiguration(configurationBuilder.Build());
})
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
builder.AddAzureWebAppDiagnostics();
})
new ConfigurationDbContextSeed()
.SeedAsync(context, configuration)
.Wait();
});
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.UseApplicationInsights()
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseConfiguration(configuration)
.UseSerilog()
.Build();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
private static 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))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
}
}

+ 24
- 21
src/Services/Identity/Identity.API/Startup.cs View File

@ -42,12 +42,12 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["ConnectionString"],
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
}));
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
}));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
@ -90,22 +90,22 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
});
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.MigrationsAssembly(migrationsAssembly);
//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.AddTransient<IProfileService, ProfileService>();
@ -117,8 +117,11 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
{
//loggerFactory.AddConsole(Configuration.GetSection("Logging"));
//loggerFactory.AddDebug();
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
if (env.IsDevelopment())
{
@ -133,7 +136,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}


+ 9
- 6
src/Services/Identity/Identity.API/appsettings.json View File

@ -5,12 +5,15 @@
"SpaClient": "http://localhost:5104",
"XamarinCallback": "http://localhost:5105/xamarincallback",
"UseCustomizationData": false,
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"System": "Information",
"Microsoft": "Information"
"Serilog": {
"SeqServerUrl": null,
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.eShopOnContainers": "Information",
"System": "Warning"
}
}
},
"ApplicationInsights": {


+ 15
- 5
src/Services/Location/Locations.API/Infrastructure/Services/LocationsService.cs View File

@ -6,6 +6,7 @@
using Microsoft.eShopOnContainers.Services.Locations.API.IntegrationEvents.Events;
using Microsoft.eShopOnContainers.Services.Locations.API.Model;
using Microsoft.eShopOnContainers.Services.Locations.API.ViewModel;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -14,11 +15,16 @@
{
private readonly ILocationsRepository _locationsRepository;
private readonly IEventBus _eventBus;
private readonly ILogger<LocationsService> _logger;
public LocationsService(ILocationsRepository locationsRepository, IEventBus eventBus)
public LocationsService(
ILocationsRepository locationsRepository,
IEventBus eventBus,
ILogger<LocationsService> logger)
{
_locationsRepository = locationsRepository ?? throw new ArgumentNullException(nameof(locationsRepository));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<Locations> GetLocationAsync(int locationId)
@ -37,11 +43,11 @@
}
public async Task<bool> AddOrUpdateUserLocationAsync(string userId, LocationRequest currentPosition)
{
{
// Get the list of ordered regions the user currently is within
var currentUserAreaLocationList = await _locationsRepository.GetCurrentUserRegionsListAsync(currentPosition);
if(currentUserAreaLocationList is null)
if (currentUserAreaLocationList is null)
{
throw new LocationDomainException("User current area not found");
}
@ -66,13 +72,17 @@
{
var newUserLocations = MapUserLocationDetails(newLocations);
var @event = new UserLocationUpdatedIntegrationEvent(userId, newUserLocations);
_logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
_eventBus.Publish(@event);
}
private List<UserLocationDetails> MapUserLocationDetails(List<Locations> newLocations)
{
var result = new List<UserLocationDetails>();
newLocations.ForEach(location => {
newLocations.ForEach(location =>
{
result.Add(new UserLocationDetails()
{
LocationId = location.LocationId,


+ 8
- 5
src/Services/Location/Locations.API/Locations.API.csproj View File

@ -7,12 +7,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.4" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.9.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
@ -25,7 +25,10 @@
<PackageReference Include="MongoDB.Driver" Version="2.7.3" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
</ItemGroup>
<ItemGroup>


+ 67
- 37
src/Services/Location/Locations.API/Program.cs View File

@ -11,48 +11,78 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API
{
public class Program
{
public static void Main(string[] args)
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args)
{
BuildWebHost(args).Run();
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseContentRoot(Directory.GetCurrentDirectory())
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.ConfigureAppConfiguration((builderContext, config) =>
{
var builtConfig = config.Build();
var configurationBuilder = new ConfigurationBuilder();
if (Convert.ToBoolean(builtConfig["UseVault"]))
{
configurationBuilder.AddAzureKeyVault(
$"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
builtConfig["Vault:ClientId"],
builtConfig["Vault:ClientSecret"]);
}
configurationBuilder.AddEnvironmentVariables();
config.AddConfiguration(configurationBuilder.Build());
})
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
builder.AddAzureWebAppDiagnostics();
})
.UseApplicationInsights()
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseConfiguration(configuration)
.UseSerilog()
.Build();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
private static 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))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
}
}
}

+ 3
- 2
src/Services/Location/Locations.API/Startup.cs View File

@ -153,9 +153,10 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))


+ 9
- 6
src/Services/Location/Locations.API/appsettings.json View File

@ -2,12 +2,15 @@
"ConnectionString": "mongodb://nosql.data",
"Database": "LocationsDb",
"IdentityUrl": "http://localhost:5105",
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"System": "Information",
"Microsoft": "Information"
"Serilog": {
"SeqServerUrl": null,
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.eShopOnContainers": "Information",
"System": "Warning"
}
}
},
"AzureServiceBusEnabled": false,


+ 1
- 1
src/Services/Location/Locations.FunctionalTests/Locations.FunctionalTests.csproj View File

@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">


+ 1
- 1
src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs View File

@ -77,7 +77,7 @@
sleepDurationProvider: retry => TimeSpan.FromSeconds(5),
onRetry: (exception, timeSpan, retry, ctx) =>
{
logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}");
logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries);
}
);
}


+ 17
- 6
src/Services/Marketing/Marketing.API/IntegrationEvents/Handlers/UserLocationUpdatedIntegrationEventHandler.cs View File

@ -4,6 +4,8 @@
using Marketing.API.Model;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Repositories;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -12,20 +14,29 @@
: IIntegrationEventHandler<UserLocationUpdatedIntegrationEvent>
{
private readonly IMarketingDataRepository _marketingDataRepository;
private readonly ILogger<UserLocationUpdatedIntegrationEventHandler> _logger;
public UserLocationUpdatedIntegrationEventHandler(IMarketingDataRepository repository)
public UserLocationUpdatedIntegrationEventHandler(
IMarketingDataRepository repository,
ILogger<UserLocationUpdatedIntegrationEventHandler> logger)
{
_marketingDataRepository = repository ?? throw new ArgumentNullException(nameof(repository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task Handle(UserLocationUpdatedIntegrationEvent @event)
{
var userMarketingData = await _marketingDataRepository.GetAsync(@event.UserId);
userMarketingData = userMarketingData ??
new MarketingData() { UserId = @event.UserId };
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
userMarketingData.Locations = MapUpdatedUserLocations(@event.LocationList);
await _marketingDataRepository.UpdateLocationAsync(userMarketingData);
var userMarketingData = await _marketingDataRepository.GetAsync(@event.UserId);
userMarketingData = userMarketingData ??
new MarketingData() { UserId = @event.UserId };
userMarketingData.Locations = MapUpdatedUserLocations(@event.LocationList);
await _marketingDataRepository.UpdateLocationAsync(userMarketingData);
}
}
private List<Location> MapUpdatedUserLocations(List<UserLocationDetails> newUserLocations)


+ 9
- 6
src/Services/Marketing/Marketing.API/Marketing.API.csproj View File

@ -20,15 +20,15 @@
<Folder Include="Infrastructure\MarketingMigrations\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.4" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureStorage" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.1" />
<PackageReference Include="AspNetCore.HealthChecks.AzureStorage" Version="2.2.1" />
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.9.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
@ -41,7 +41,10 @@
<PackageReference Include="MongoDB.Driver" Version="2.7.3" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.7.3" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
</ItemGroup>
<ItemGroup>


+ 70
- 40
src/Services/Marketing/Marketing.API/Program.cs View File

@ -12,59 +12,89 @@
public class Program
{
public static void Main(string[] args)
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args)
{
BuildWebHost(args)
.MigrateDbContext<MarketingContext>((context, services) =>
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{
var logger = services.GetService<ILogger<MarketingContextSeed>>();
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Applying migrations ({ApplicationContext})...", AppName);
host.MigrateDbContext<MarketingContext>((context, services) =>
{
var logger = services.GetService<ILogger<MarketingContextSeed>>();
new MarketingContextSeed()
.SeedAsync(context, logger)
.Wait();
});
new MarketingContextSeed()
.SeedAsync(context,logger)
.Wait();
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
}).Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.UseApplicationInsights()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseWebRoot("Pics")
.ConfigureAppConfiguration((builderContext, config) =>
{
var builtConfig = config.Build();
.UseConfiguration(configuration)
.UseSerilog()
.Build();
var configurationBuilder = new ConfigurationBuilder();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
if (Convert.ToBoolean(builtConfig["UseVault"]))
{
configurationBuilder.AddAzureKeyVault(
$"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
builtConfig["Vault:ClientId"],
builtConfig["Vault:ClientSecret"]);
}
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
configurationBuilder.AddEnvironmentVariables();
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
config.AddConfiguration(configurationBuilder.Build());
})
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
builder.AddAzureWebAppDiagnostics();
})
.UseApplicationInsights()
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.Build();
var config = builder.Build();
if (config.GetValue<bool>("UseVault", false))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
}
}

+ 2
- 1
src/Services/Marketing/Marketing.API/Startup.cs View File

@ -179,7 +179,8 @@
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerFactory)
{
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];


+ 9
- 4
src/Services/Marketing/Marketing.API/appsettings.json View File

@ -1,8 +1,13 @@
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace"
"Serilog": {
"SeqServerUrl": null,
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.eShopOnContainers": "Information",
"System": "Warning"
}
}
},
"ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word",


+ 1
- 1
src/Services/Marketing/Marketing.FunctionalTests/Marketing.FunctionalTests.csproj View File

@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">


+ 6
- 3
src/Services/Ordering/Ordering.API/Application/Behaviors/LoggingBehavior.cs View File

@ -1,9 +1,11 @@
using MediatR;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
namespace Ordering.API.Infrastructure.Behaviors
namespace Ordering.API.Application.Behaviors
{
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
@ -12,9 +14,10 @@ namespace Ordering.API.Infrastructure.Behaviors
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
_logger.LogInformation($"Handling {typeof(TRequest).Name}");
_logger.LogInformation("----- Handling command {CommandName} ({@Command})", request.GetGenericTypeName(), request);
var response = await next();
_logger.LogInformation($"Handled {typeof(TResponse).Name}");
_logger.LogInformation("----- Command {CommandName} handled - response: {@Response}", request.GetGenericTypeName(), response);
return response;
}
}


+ 21
- 13
src/Services/Ordering/Ordering.API/Application/Behaviors/TransactionBehaviour.cs View File

@ -1,11 +1,11 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.IntegrationEvents;
using Serilog.Context;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -28,33 +28,41 @@ namespace Ordering.API.Application.Behaviors
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
TResponse response = default(TResponse);
var response = default(TResponse);
var typeName = request.GetGenericTypeName();
try
{
var strategy = _dbContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
if (_dbContext.HasActiveTransaction)
{
_logger.LogInformation($"Begin transaction {typeof(TRequest).Name}");
return await next();
}
var strategy = _dbContext.Database.CreateExecutionStrategy();
await _dbContext.BeginTransactionAsync();
await strategy.ExecuteAsync(async () =>
{
using (var transaction = await _dbContext.BeginTransactionAsync())
using (LogContext.PushProperty("TransactionContext", transaction.TransactionId))
{
_logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request);
response = await next();
response = await next();
await _dbContext.CommitTransactionAsync();
_logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName);
_logger.LogInformation($"Committed transaction {typeof(TRequest).Name}");
await _dbContext.CommitTransactionAsync(transaction);
}
await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync();
});
return response;
}
catch (Exception)
catch (Exception ex)
{
_logger.LogInformation($"Rollback transaction executed {typeof(TRequest).Name}");
_logger.LogError(ex, "ERROR Handling transaction for {CommandName} ({@Command})", typeName, request);
_dbContext.RollbackTransaction();
throw;
}
}


+ 18
- 6
src/Services/Ordering/Ordering.API/Application/Behaviors/ValidatorBehavior.cs View File

@ -1,20 +1,31 @@
using FluentValidation;
using MediatR;
using Microsoft.Extensions.Logging;
using Ordering.Domain.Exceptions;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
namespace Ordering.API.Infrastructure.Behaviors
namespace Ordering.API.Application.Behaviors
{
public class ValidatorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<ValidatorBehavior<TRequest, TResponse>> _logger;
private readonly IValidator<TRequest>[] _validators;
public ValidatorBehavior(IValidator<TRequest>[] validators) => _validators = validators;
public ValidatorBehavior(IValidator<TRequest>[] validators, ILogger<ValidatorBehavior<TRequest, TResponse>> logger)
{
_validators = validators;
_logger = logger;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var typeName = request.GetGenericTypeName();
_logger.LogInformation("----- Validating command {CommandType}", typeName);
var failures = _validators
.Select(v => v.Validate(request))
.SelectMany(result => result.Errors)
@ -23,12 +34,13 @@ namespace Ordering.API.Infrastructure.Behaviors
if (failures.Any())
{
_logger.LogWarning("Validation errors - {CommandType} - Command: {@Command} - Errors: {@ValidationErrors}", typeName, request, failures);
throw new OrderingDomainException(
$"Command Validation Errors for type {typeof(TRequest).Name}", new ValidationException("Validation exception", failures));
}
var response = await next();
return response;
return await next();
}
}
}
}

+ 6
- 1
src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommandHandler.cs View File

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
@ -40,7 +41,11 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process
public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool>
{
public CancelOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
public CancelOrderIdentifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<CancelOrderCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}


+ 16
- 6
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs View File

@ -6,6 +6,7 @@
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
@ -18,17 +19,20 @@
private readonly IIdentityService _identityService;
private readonly IMediator _mediator;
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
private readonly ILogger<CreateOrderCommandHandler> _logger;
// Using DI to inject infrastructure persistence Repositories
public CreateOrderCommandHandler(IMediator mediator,
IOrderingIntegrationEventService orderingIntegrationEventService,
IOrderRepository orderRepository,
IIdentityService identityService)
IOrderRepository orderRepository,
IIdentityService identityService,
ILogger<CreateOrderCommandHandler> logger)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<bool> Handle(CreateOrderCommand message, CancellationToken cancellationToken)
@ -36,20 +40,22 @@
// Add Integration event to clean the basket
var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId);
await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStartedIntegrationEvent);
// Add/Update the Buyer AggregateRoot
// DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root
// methods and constructor so validations, invariants and business logic
// make sure that consistency is preserved across the whole aggregate
var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode);
var order = new Order(message.UserId, message.UserName, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration);
foreach (var item in message.OrderItems)
{
order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units);
}
_orderRepository.Add(order);
_logger.LogInformation("----- Creating Order - Order: {@Order}", order);
_orderRepository.Add(order);
return await _orderRepository.UnitOfWork
.SaveEntitiesAsync();
@ -60,7 +66,11 @@
// Use for Idempotency in Command process
public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool>
{
public CreateOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
public CreateOrderIdentifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<CreateOrderCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}


+ 65
- 11
src/Services/Ordering/Ordering.API/Application/Commands/IdentifiedCommandHandler.cs View File

@ -1,5 +1,10 @@
using MediatR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.Behaviors;
using Ordering.API.Application.Commands;
using System;
using System.Threading;
using System.Threading.Tasks;
@ -16,11 +21,16 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
{
private readonly IMediator _mediator;
private readonly IRequestManager _requestManager;
private readonly ILogger<IdentifiedCommandHandler<T, R>> _logger;
public IdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager)
public IdentifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<T, R>> logger)
{
_mediator = mediator;
_requestManager = requestManager;
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
/// <summary>
@ -48,16 +58,60 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
else
{
await _requestManager.CreateRequestForCommandAsync<T>(message.Id);
try
{
// Send the embeded business command to mediator so it runs its related CommandHandler
var result = await _mediator.Send(message.Command);
return result;
}
catch
{
return default(R);
}
try
{
var command = message.Command;
var commandName = command.GetGenericTypeName();
var idProperty = string.Empty;
var commandId = string.Empty;
switch (command)
{
case CreateOrderCommand createOrderCommand:
idProperty = nameof(createOrderCommand.UserId);
commandId = createOrderCommand.UserId;
break;
case CancelOrderCommand cancelOrderCommand:
idProperty = nameof(cancelOrderCommand.OrderNumber);
commandId = $"{cancelOrderCommand.OrderNumber}";
break;
case ShipOrderCommand shipOrderCommand:
idProperty = nameof(shipOrderCommand.OrderNumber);
commandId = $"{shipOrderCommand.OrderNumber}";
break;
default:
idProperty = "Id?";
commandId = "n/a";
break;
}
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
commandName,
idProperty,
commandId,
command);
// Send the embeded business command to mediator so it runs its related CommandHandler
var result = await _mediator.Send(command);
_logger.LogInformation(
"----- Command result: {@Result} - {CommandName} - {IdProperty}: {CommandId} ({@Command})",
result,
commandName,
idProperty,
commandId,
command);
return result;
}
catch
{
return default(R);
}
}
}
}

+ 6
- 1
src/Services/Ordering/Ordering.API/Application/Commands/SetAwaitingValidationOrderStatusCommandHandler.cs View File

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
@ -40,7 +41,11 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process
public class SetAwaitingValidationIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool>
{
public SetAwaitingValidationIdentifiedOrderStatusCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
public SetAwaitingValidationIdentifiedOrderStatusCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}


+ 6
- 1
src/Services/Ordering/Ordering.API/Application/Commands/SetPaidOrderStatusCommandHandler.cs View File

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
@ -43,7 +44,11 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process
public class SetPaidIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool>
{
public SetPaidIdentifiedOrderStatusCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
public SetPaidIdentifiedOrderStatusCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}


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

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
@ -43,7 +44,11 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process
public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>
{
public SetStockConfirmedOrderStatusIdenfifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
public SetStockConfirmedOrderStatusIdenfifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}


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

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
@ -44,7 +45,11 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process
public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>
{
public SetStockRejectedOrderStatusIdenfifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
public SetStockRejectedOrderStatusIdenfifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}


+ 8
- 3
src/Services/Ordering/Ordering.API/Application/Commands/ShipOrderCommandHandler.cs View File

@ -2,6 +2,7 @@
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
@ -9,7 +10,7 @@ namespace Ordering.API.Application.Commands
{
// Regular CommandHandler
public class ShipOrderCommandHandler : IRequestHandler<ShipOrderCommand, bool>
{
{
private readonly IOrderRepository _orderRepository;
public ShipOrderCommandHandler(IOrderRepository orderRepository)
@ -26,7 +27,7 @@ namespace Ordering.API.Application.Commands
public async Task<bool> Handle(ShipOrderCommand command, CancellationToken cancellationToken)
{
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
if(orderToUpdate == null)
if (orderToUpdate == null)
{
return false;
}
@ -40,7 +41,11 @@ namespace Ordering.API.Application.Commands
// Use for Idempotency in Command process
public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool>
{
public ShipOrderIdentifiedCommandHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager)
public ShipOrderIdentifiedCommandHandler(
IMediator mediator,
IRequestManager requestManager,
ILogger<IdentifiedCommandHandler<ShipOrderCommand, bool>> logger)
: base(mediator, requestManager, logger)
{
}


+ 10
- 9
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/BuyerAndPaymentMethodVerified/UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler.cs View File

@ -8,16 +8,16 @@ using System.Threading.Tasks;
namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified
{
public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler
public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler
: INotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>
{
private readonly IOrderRepository _orderRepository;
private readonly ILoggerFactory _logger;
private readonly IOrderRepository _orderRepository;
private readonly ILoggerFactory _logger;
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(
IOrderRepository orderRepository, ILoggerFactory logger)
IOrderRepository orderRepository, ILoggerFactory logger)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@ -28,10 +28,11 @@ namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVeri
{
var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId);
orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id);
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
_logger.CreateLogger(nameof(UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler))
.LogTrace($"Order with Id: {buyerPaymentMethodVerifiedEvent.OrderId} has been successfully updated with a payment method id: { buyerPaymentMethodVerifiedEvent.Payment.Id }");
_logger.CreateLogger<UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler>()
.LogTrace("Order with Id: {OrderId} has been successfully updated with a payment method {PaymentMethod} ({Id})",
buyerPaymentMethodVerifiedEvent.OrderId, nameof(buyerPaymentMethodVerifiedEvent.Payment), buyerPaymentMethodVerifiedEvent.Payment.Id);
}
}
}
}

+ 3
- 3
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs View File

@ -33,9 +33,9 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderCancelled
public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken)
{
_logger.CreateLogger(nameof(OrderCancelledDomainEvent))
.LogTrace($"Order with Id: {orderCancelledDomainEvent.Order.Id} has been successfully updated with " +
$"a status order id: {OrderStatus.Shipped.Id}");
_logger.CreateLogger<OrderCancelledDomainEvent>()
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
orderCancelledDomainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id);
var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());


+ 3
- 3
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs View File

@ -33,9 +33,9 @@
public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent, CancellationToken cancellationToken)
{
_logger.CreateLogger(nameof(OrderStatusChangedToAwaitingValidationDomainEvent))
.LogTrace($"Order with Id: {orderStatusChangedToAwaitingValidationDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.AwaitingValidation.Id}");
_logger.CreateLogger<OrderStatusChangedToAwaitingValidationDomainEvent>()
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
orderStatusChangedToAwaitingValidationDomainEvent.OrderId, nameof(OrderStatus.AwaitingValidation), OrderStatus.AwaitingValidation.Id);
var order = await _orderRepository.GetAsync(orderStatusChangedToAwaitingValidationDomainEvent.OrderId);


+ 3
- 3
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs View File

@ -35,9 +35,9 @@
public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken)
{
_logger.CreateLogger(nameof(OrderStatusChangedToPaidDomainEventHandler))
.LogTrace($"Order with Id: {orderStatusChangedToPaidDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.Paid.Id}");
_logger.CreateLogger<OrderStatusChangedToPaidDomainEventHandler>()
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
orderStatusChangedToPaidDomainEvent.OrderId, nameof(OrderStatus.Paid), OrderStatus.Paid.Id);
var order = await _orderRepository.GetAsync(orderStatusChangedToPaidDomainEvent.OrderId);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());


+ 3
- 3
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderShipped/OrderShippedDomainEventHandler.cs View File

@ -33,9 +33,9 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderShipped
public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken)
{
_logger.CreateLogger(nameof(OrderShippedDomainEvent))
.LogTrace($"Order with Id: {orderShippedDomainEvent.Order.Id} has been successfully updated with " +
$"a status order id: {OrderStatus.Shipped.Id}");
_logger.CreateLogger<OrderShippedDomainEvent>()
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
orderShippedDomainEvent.Order.Id, nameof(OrderStatus.Shipped), OrderStatus.Shipped.Id);
var order = await _orderRepository.GetAsync(orderShippedDomainEvent.Order.Id);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());


+ 3
- 1
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStartedEvent/ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler.cs View File

@ -61,7 +61,9 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent
var orderStatusChangedTosubmittedIntegrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(orderStartedEvent.Order.Id, orderStartedEvent.Order.OrderStatus.Name, buyer.Name);
await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedTosubmittedIntegrationEvent);
_logger.CreateLogger(nameof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler)).LogTrace($"Buyer {buyerUpdated.Id} and related payment method were validated or updated for orderId: {orderStartedEvent.Order.Id}.");
_logger.CreateLogger<ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler>()
.LogTrace("Buyer {BuyerId} and related payment method were validated or updated for orderId: {OrderId}.",
buyerUpdated.Id, orderStartedEvent.Order.Id);
}
}
}

+ 3
- 3
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs View File

@ -33,9 +33,9 @@
public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent, CancellationToken cancellationToken)
{
_logger.CreateLogger(nameof(OrderStatusChangedToStockConfirmedDomainEventHandler))
.LogTrace($"Order with Id: {orderStatusChangedToStockConfirmedDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.StockConfirmed.Id}");
_logger.CreateLogger<OrderStatusChangedToStockConfirmedDomainEventHandler>()
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
orderStatusChangedToStockConfirmedDomainEvent.OrderId, nameof(OrderStatus.StockConfirmed), OrderStatus.StockConfirmed.Id);
var order = await _orderRepository.GetAsync(orderStatusChangedToStockConfirmedDomainEvent.OrderId);
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());


+ 25
- 3
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/GracePeriodConfirmedIntegrationEventHandler.cs View File

@ -1,8 +1,13 @@
using MediatR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.API;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.Behaviors;
using Ordering.API.Application.Commands;
using Ordering.API.Application.IntegrationEvents.Events;
using Serilog.Context;
using System.Threading.Tasks;
namespace Ordering.API.Application.IntegrationEvents.EventHandling
@ -10,10 +15,14 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly ILogger<GracePeriodConfirmedIntegrationEventHandler> _logger;
public GracePeriodConfirmedIntegrationEventHandler(IMediator mediator)
public GracePeriodConfirmedIntegrationEventHandler(
IMediator mediator,
ILogger<GracePeriodConfirmedIntegrationEventHandler> logger)
{
_mediator = mediator;
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
/// <summary>
@ -26,8 +35,21 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
/// <returns></returns>
public async Task Handle(GracePeriodConfirmedIntegrationEvent @event)
{
var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId);
await _mediator.Send(command);
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderNumber),
command.OrderNumber,
command);
await _mediator.Send(command);
}
}
}
}

+ 26
- 4
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs View File

@ -2,26 +2,48 @@
{
using MediatR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.API;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.Behaviors;
using Ordering.API.Application.Commands;
using Ordering.API.Application.IntegrationEvents.Events;
using System;
using Serilog.Context;
using System.Threading.Tasks;
using System;
public class OrderPaymentFailedIntegrationEventHandler :
IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly ILogger<OrderPaymentFailedIntegrationEventHandler> _logger;
public OrderPaymentFailedIntegrationEventHandler(IMediator mediator)
public OrderPaymentFailedIntegrationEventHandler(
IMediator mediator,
ILogger<OrderPaymentFailedIntegrationEventHandler> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderPaymentFailedIntegrationEvent @event)
{
var command = new CancelOrderCommand(@event.OrderId);
await _mediator.Send(command);
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var command = new CancelOrderCommand(@event.OrderId);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderNumber),
command.OrderNumber,
command);
await _mediator.Send(command);
}
}
}
}

+ 25
- 3
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs View File

@ -2,9 +2,14 @@
{
using MediatR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.API;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.Behaviors;
using Ordering.API.Application.Commands;
using Ordering.API.Application.IntegrationEvents.Events;
using Serilog.Context;
using System;
using System.Threading.Tasks;
@ -12,16 +17,33 @@
IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly ILogger<OrderPaymentSuccededIntegrationEventHandler> _logger;
public OrderPaymentSuccededIntegrationEventHandler(IMediator mediator)
public OrderPaymentSuccededIntegrationEventHandler(
IMediator mediator,
ILogger<OrderPaymentSuccededIntegrationEventHandler> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderPaymentSuccededIntegrationEvent @event)
{
var command = new SetPaidOrderStatusCommand(@event.OrderId);
await _mediator.Send(command);
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var command = new SetPaidOrderStatusCommand(@event.OrderId);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderNumber),
command.OrderNumber,
command);
await _mediator.Send(command);
}
}
}
}

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

@ -1,27 +1,49 @@
namespace Ordering.API.Application.IntegrationEvents.EventHandling
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using System.Threading.Tasks;
using Events;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using MediatR;
using System;
using Ordering.API.Application.Commands;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using Microsoft.eShopOnContainers.Services.Ordering.API;
using Ordering.API.Application.Behaviors;
public class OrderStockConfirmedIntegrationEventHandler :
public class OrderStockConfirmedIntegrationEventHandler :
IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly ILogger<OrderStockConfirmedIntegrationEventHandler> _logger;
public OrderStockConfirmedIntegrationEventHandler(IMediator mediator)
public OrderStockConfirmedIntegrationEventHandler(
IMediator mediator,
ILogger<OrderStockConfirmedIntegrationEventHandler> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderStockConfirmedIntegrationEvent @event)
{
var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId);
await _mediator.Send(command);
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderNumber),
command.OrderNumber,
command);
await _mediator.Send(command);
}
}
}
}

+ 29
- 7
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockRejectedIntegrationEventHandler.cs View File

@ -1,31 +1,53 @@
namespace Ordering.API.Application.IntegrationEvents.EventHandling
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using System.Threading.Tasks;
using Events;
using System.Linq;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using MediatR;
using Ordering.API.Application.Commands;
using Microsoft.Extensions.Logging;
using Serilog.Context;
using Microsoft.eShopOnContainers.Services.Ordering.API;
using Ordering.API.Application.Behaviors;
public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly ILogger<OrderStockRejectedIntegrationEventHandler> _logger;
public OrderStockRejectedIntegrationEventHandler(IMediator mediator)
public OrderStockRejectedIntegrationEventHandler(
IMediator mediator,
ILogger<OrderStockRejectedIntegrationEventHandler> logger)
{
_mediator = mediator;
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
public async Task Handle(OrderStockRejectedIntegrationEvent @event)
{
var orderStockRejectedItems = @event.OrderStockItems
.FindAll(c => !c.HasStock)
.Select(c => c.ProductId)
.ToList();
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems);
await _mediator.Send(command);
var orderStockRejectedItems = @event.OrderStockItems
.FindAll(c => !c.HasStock)
.Select(c => c.ProductId)
.ToList();
var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
command.GetGenericTypeName(),
nameof(command.OrderNumber),
command.OrderNumber,
command);
await _mediator.Send(command);
}
}
}
}

+ 54
- 24
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/UserCheckoutAcceptedIntegrationEventHandler.cs View File

@ -1,52 +1,82 @@
using System;
using MediatR;
using MediatR;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.API;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.Behaviors;
using Ordering.API.Application.IntegrationEvents.Events;
using Serilog.Context;
using System.Threading.Tasks;
using System;
namespace Ordering.API.Application.IntegrationEvents.EventHandling
{
public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>
{
private readonly IMediator _mediator;
private readonly ILoggerFactory _logger;
private readonly ILogger<UserCheckoutAcceptedIntegrationEventHandler> _logger;
public UserCheckoutAcceptedIntegrationEventHandler(IMediator mediator,
ILoggerFactory logger)
public UserCheckoutAcceptedIntegrationEventHandler(
IMediator mediator,
ILogger<UserCheckoutAcceptedIntegrationEventHandler> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Integration event handler which starts the create order process
/// </summary>
/// <param name="eventMsg">
/// <param name="@event">
/// Integration event message which is sent by the
/// basket.api once it has successfully process the
/// order items.
/// </param>
/// <returns></returns>
public async Task Handle(UserCheckoutAcceptedIntegrationEvent eventMsg)
public async Task Handle(UserCheckoutAcceptedIntegrationEvent @event)
{
var result = false;
if (eventMsg.RequestId != Guid.Empty)
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
{
var createOrderCommand = new CreateOrderCommand(eventMsg.Basket.Items, eventMsg.UserId, eventMsg.UserName, eventMsg.City, eventMsg.Street,
eventMsg.State, eventMsg.Country, eventMsg.ZipCode,
eventMsg.CardNumber, eventMsg.CardHolderName, eventMsg.CardExpiration,
eventMsg.CardSecurityNumber, eventMsg.CardTypeId);
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, eventMsg.RequestId);
result = await _mediator.Send(requestCreateOrder);
}
_logger.CreateLogger(nameof(UserCheckoutAcceptedIntegrationEventHandler))
.LogTrace(result ? $"UserCheckoutAccepted integration event has been received and a create new order process is started with requestId: {eventMsg.RequestId}" :
$"UserCheckoutAccepted integration event has been received but a new order process has failed with requestId: {eventMsg.RequestId}");
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
var result = false;
if (@event.RequestId != Guid.Empty)
{
using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId))
{
var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street,
@event.State, @event.Country, @event.ZipCode,
@event.CardNumber, @event.CardHolderName, @event.CardExpiration,
@event.CardSecurityNumber, @event.CardTypeId);
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, @event.RequestId);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
requestCreateOrder.GetGenericTypeName(),
nameof(requestCreateOrder.Id),
requestCreateOrder.Id,
requestCreateOrder);
result = await _mediator.Send(requestCreateOrder);
if (result)
{
_logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId);
}
else
{
_logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId);
}
}
}
else
{
_logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event);
}
}
}
}
}

+ 16
- 4
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs View File

@ -5,7 +5,9 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
using Microsoft.eShopOnContainers.Services.Ordering.API;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
using Microsoft.Extensions.Logging;
using System;
using System.Data.Common;
using System.Diagnostics;
@ -21,39 +23,49 @@ namespace Ordering.API.Application.IntegrationEvents
private readonly OrderingContext _orderingContext;
private readonly IntegrationEventLogContext _eventLogContext;
private readonly IIntegrationEventLogService _eventLogService;
private readonly ILogger<OrderingIntegrationEventService> _logger;
public OrderingIntegrationEventService(IEventBus eventBus,
public OrderingIntegrationEventService(IEventBus eventBus,
OrderingContext orderingContext,
IntegrationEventLogContext eventLogContext,
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory)
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory,
ILogger<OrderingIntegrationEventService> logger)
{
_orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext));
_eventLogContext = eventLogContext ?? throw new ArgumentNullException(nameof(eventLogContext));
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
_eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection());
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task PublishEventsThroughEventBusAsync()
{
var pendindLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync();
foreach (var logEvt in pendindLogEvents)
{
_logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", logEvt.EventId, Program.AppName, logEvt.IntegrationEvent);
try
{
await _eventLogService.MarkEventAsInProgressAsync(logEvt.EventId);
_eventBus.Publish(logEvt.IntegrationEvent);
await _eventLogService.MarkEventAsPublishedAsync(logEvt.EventId);
}
catch (Exception)
catch (Exception ex)
{
_logger.LogError(ex, "ERROR publishing integration event: {IntegrationEventId} from {AppName}", logEvt.EventId, Program.AppName);
await _eventLogService.MarkEventAsFailedAsync(logEvt.EventId);
}
}
}
}
public async Task AddAndSaveEventAsync(IntegrationEvent evt)
{
_logger.LogInformation("----- Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, evt);
await _eventLogService.SaveEventAsync(evt, _orderingContext.GetCurrentTransaction.GetDbTransaction());
}
}


+ 33
- 1
src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs View File

@ -1,9 +1,12 @@
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
using Microsoft.Extensions.Logging;
using Ordering.API.Application.Behaviors;
using Ordering.API.Application.Commands;
using System;
using System.Collections.Generic;
@ -20,12 +23,18 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
private readonly IMediator _mediator;
private readonly IOrderQueries _orderQueries;
private readonly IIdentityService _identityService;
private readonly ILogger<OrdersController> _logger;
public OrdersController(IMediator mediator, IOrderQueries orderQueries, IIdentityService identityService)
public OrdersController(
IMediator mediator,
IOrderQueries orderQueries,
IIdentityService identityService,
ILogger<OrdersController> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries));
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
[Route("cancel")]
@ -39,6 +48,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
{
var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
requestCancelOrder.GetGenericTypeName(),
nameof(requestCancelOrder.Command.OrderNumber),
requestCancelOrder.Command.OrderNumber,
requestCancelOrder);
commandResult = await _mediator.Send(requestCancelOrder);
}
@ -61,6 +78,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
{
var requestShipOrder = new IdentifiedCommand<ShipOrderCommand, bool>(command, guid);
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
requestShipOrder.GetGenericTypeName(),
nameof(requestShipOrder.Command.OrderNumber),
requestShipOrder.Command.OrderNumber,
requestShipOrder);
commandResult = await _mediator.Send(requestShipOrder);
}
@ -114,6 +139,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
[HttpPost]
public async Task<ActionResult<OrderDraftDTO>> CreateOrderDraftFromBasketDataAsync([FromBody] CreateOrderDraftCommand createOrderDraftCommand)
{
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
createOrderDraftCommand.GetGenericTypeName(),
nameof(createOrderDraftCommand.BuyerId),
createOrderDraftCommand.BuyerId,
createOrderDraftCommand);
return await _mediator.Send(createOrderDraftCommand);
}
}

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

@ -7,7 +7,6 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
using Ordering.API.Application.Behaviors;
using Ordering.API.Application.DomainEventHandlers.OrderStartedEvent;
using Ordering.API.Application.Validations;
using Ordering.API.Infrastructure.Behaviors;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules
{


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

@ -75,7 +75,7 @@
}
catch (Exception ex)
{
log.LogError(ex.Message);
log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPredefinedCardTypes();
}
@ -83,7 +83,7 @@
return File.ReadAllLines(csvFileCardTypes)
.Skip(1) // skip header column
.SelectTry(x => CreateCardType(x, ref id))
.OnCaughtException(ex => { log.LogError(ex.Message); return null; })
.OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@ -119,7 +119,7 @@
}
catch (Exception ex)
{
log.LogError(ex.Message);
log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message);
return GetPredefinedOrderStatus();
}
@ -127,7 +127,7 @@
return File.ReadAllLines(csvFileOrderStatus)
.Skip(1) // skip header row
.SelectTry(x => CreateOrderStatus(x, ref id))
.OnCaughtException(ex => { log.LogError(ex.Message); return null; })
.OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; })
.Where(x => x != null);
}
@ -183,7 +183,7 @@
sleepDurationProvider: retry => TimeSpan.FromSeconds(5),
onRetry: (exception, timeSpan, retry, ctx) =>
{
logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}");
logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries);
}
);
}


+ 9
- 6
src/Services/Ordering/Ordering.API/Ordering.API.csproj View File

@ -5,6 +5,7 @@
<UserSecretsId>aspnet-Ordering.API-20161122013547</UserSecretsId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<ItemGroup>
@ -29,13 +30,12 @@
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.6.1" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.4" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="8.1.3" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="6.0.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.9.1" />
<PackageReference Include="Microsoft.ApplicationInsights.Kubernetes" Version="1.0.2" />
<PackageReference Include="Microsoft.ApplicationInsights.ServiceFabric" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
@ -45,10 +45,13 @@
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="MediatR" Version="6.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
<PackageReference Include="System.Reflection" Version="4.3.0" />
<PackageReference Include="Dapper" Version="1.50.7" />
<PackageReference Include="Dapper" Version="1.60.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Polly" Version="7.0.3" />
</ItemGroup>


+ 70
- 39
src/Services/Ordering/Ordering.API/Program.cs View File

@ -15,10 +15,22 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
{
public class Program
{
public static void Main(string[] args)
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
public static int Main(string[] args)
{
BuildWebHost(args)
.MigrateDbContext<OrderingContext>((context, services) =>
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Applying migrations ({ApplicationContext})...", AppName);
host.MigrateDbContext<OrderingContext>((context, services) =>
{
var env = services.GetService<IHostingEnvironment>();
var settings = services.GetService<IOptions<OrderingSettings>>();
@ -28,47 +40,66 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
.SeedAsync(context, env, settings, logger)
.Wait();
})
.MigrateDbContext<IntegrationEventLogContext>((_,__)=>{})
.Run();
.MigrateDbContext<IntegrationEventLogContext>((_, __) => { });
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.UseApplicationInsights()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((builderContext, config) =>
{
var builtConfig = config.Build();
.UseConfiguration(configuration)
.UseSerilog()
.Build();
var configurationBuilder = new ConfigurationBuilder();
if (Convert.ToBoolean(builtConfig["UseVault"]))
{
configurationBuilder.AddAzureKeyVault(
$"https://{builtConfig["Vault:Name"]}.vault.azure.net/",
builtConfig["Vault:ClientId"],
builtConfig["Vault:ClientSecret"]);
}
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
configurationBuilder.AddEnvironmentVariables();
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
config.AddConfiguration(configurationBuilder.Build());
})
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddConsole();
builder.AddDebug();
builder.AddAzureWebAppDiagnostics();
})
.UseApplicationInsights()
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.Build();
private static 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))
{
builder.AddAzureKeyVault(
$"https://{config["Vault:Name"]}.vault.azure.net/",
config["Vault:ClientId"],
config["Vault:ClientSecret"]);
}
return builder.Build();
}
}
}
}

+ 6
- 6
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -74,14 +74,15 @@
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
//loggerFactory.AddAzureWebAppDiagnostics();
//loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace);
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'");
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
}
app.UseCors("CorsPolicy");
@ -121,10 +122,9 @@
eventBus.Subscribe<OrderStockConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>>();
eventBus.Subscribe<OrderStockRejectedIntegrationEvent, IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>>();
eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>();
eventBus.Subscribe<OrderPaymentSuccededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>>();
eventBus.Subscribe<OrderPaymentSuccededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>>();
}
protected virtual void ConfigureAuth(IApplicationBuilder app)
{
if (Configuration.GetValue<bool>("UseLoadTest"))
@ -167,7 +167,7 @@
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddControllersAsServices(); //Injecting Controllers themselves thru DI
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
services.AddCors(options =>
{


+ 9
- 6
src/Services/Ordering/Ordering.API/appsettings.json View File

@ -2,12 +2,15 @@
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;",
"IdentityUrl": "http://localhost:5105",
"UseCustomizationData": false,
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Trace",
"System": "Information",
"Microsoft": "Information"
"Serilog": {
"SeqServerUrl": null,
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.eShopOnContainers": "Information",
"System": "Warning"
}
}
},
"AzureServiceBusEnabled": false,


+ 6
- 3
src/Services/Ordering/Ordering.BackgroundTasks/Ordering.BackgroundTasks.csproj View File

@ -15,16 +15,19 @@
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="2.2.2" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.0" />
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="2.2.4" />
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="2.2.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
<PackageReference Include="Dapper" Version="1.50.7" />
<PackageReference Include="Dapper" Version="1.60.1" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.HealthChecks" Version="1.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="2.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="4.0.0" />
</ItemGroup>
<ItemGroup>


+ 59
- 17
src/Services/Ordering/Ordering.BackgroundTasks/Program.cs View File

@ -1,33 +1,75 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.IO;
namespace Ordering.BackgroundTasks
{
public class Program
{
public static void Main(string[] args)
public static readonly string Namespace = typeof(Program).Namespace;
public static readonly string AppName = Namespace;
public static int Main(string[] args)
{
BuildWebHost(args).Run();
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration);
try
{
Log.Information("Configuring web host ({ApplicationContext})...", AppName);
var host = BuildWebHost(configuration, args);
Log.Information("Starting web host ({ApplicationContext})...", AppName);
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName);
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, builder) =>
{
builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
builder.AddDebug();
builder.AddConsole();
})
.UseSerilog((builderContext, config) =>
{
config
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console();
})
.CaptureStartupErrors(false)
.UseStartup<Startup>()
.UseConfiguration(configuration)
.UseSerilog()
.Build();
private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration)
{
var seqServerUrl = configuration["Serilog:SeqServerUrl"];
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.WithProperty("ApplicationContext", AppName)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl)
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
return builder.Build();
}
}
}

+ 12
- 9
src/Services/Ordering/Ordering.BackgroundTasks/Tasks/GracePeriodManagerTask.cs View File

@ -20,9 +20,10 @@ namespace Ordering.BackgroundTasks.Tasks
private readonly BackgroundTaskSettings _settings;
private readonly IEventBus _eventBus;
public GracePeriodManagerService(IOptions<BackgroundTaskSettings> settings,
IEventBus eventBus,
ILogger<GracePeriodManagerService> logger)
public GracePeriodManagerService(
IOptions<BackgroundTaskSettings> settings,
IEventBus eventBus,
ILogger<GracePeriodManagerService> logger)
{
_settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings));
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
@ -32,27 +33,27 @@ namespace Ordering.BackgroundTasks.Tasks
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"GracePeriodManagerService is starting.");
_logger.LogDebug("GracePeriodManagerService is starting.");
stoppingToken.Register(() => _logger.LogDebug($"#1 GracePeriodManagerService background task is stopping."));
stoppingToken.Register(() => _logger.LogDebug("#1 GracePeriodManagerService background task is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogDebug($"GracePeriodManagerService background task is doing background work.");
_logger.LogDebug("GracePeriodManagerService background task is doing background work.");
CheckConfirmedGracePeriodOrders();
await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
}
_logger.LogDebug($"GracePeriodManagerService background task is stopping.");
_logger.LogDebug("GracePeriodManagerService background task is stopping.");
await Task.CompletedTask;
}
private void CheckConfirmedGracePeriodOrders()
{
_logger.LogDebug($"Checking confirmed grace period orders");
_logger.LogDebug("Checking confirmed grace period orders");
var orderIds = GetConfirmedGracePeriodOrders();
@ -60,6 +61,8 @@ namespace Ordering.BackgroundTasks.Tasks
{
var confirmGracePeriodEvent = new GracePeriodConfirmedIntegrationEvent(orderId);
_logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", confirmGracePeriodEvent.Id, Program.AppName, confirmGracePeriodEvent);
_eventBus.Publish(confirmGracePeriodEvent);
}
}
@ -81,7 +84,7 @@ namespace Ordering.BackgroundTasks.Tasks
}
catch (SqlException exception)
{
_logger.LogCritical($"FATAL ERROR: Database connections could not be opened: {exception.Message}");
_logger.LogCritical(exception, "FATAL ERROR: Database connections could not be opened: {Message}", exception.Message);
}
}


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

Loading…
Cancel
Save