Moved all usings to globalusing file
This commit is contained in:
		
							parent
							
								
									818995d8b8
								
							
						
					
					
						commit
						666fba815f
					
				| @ -1,23 +1,16 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Behaviors | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors; | ||||
| public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> | ||||
| { | ||||
|     public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> | ||||
|     private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger; | ||||
|     public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger) => _logger = logger; | ||||
| 
 | ||||
|     public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) | ||||
|     { | ||||
|         private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger; | ||||
|         public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger) => _logger = logger; | ||||
|         _logger.LogInformation("----- Handling command {CommandName} ({@Command})", request.GetGenericTypeName(), request); | ||||
|         var response = await next(); | ||||
|         _logger.LogInformation("----- Command {CommandName} handled - response: {@Response}", request.GetGenericTypeName(), response); | ||||
| 
 | ||||
|         public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) | ||||
|         { | ||||
|             _logger.LogInformation("----- Handling command {CommandName} ({@Command})", request.GetGenericTypeName(), request); | ||||
|             var response = await next(); | ||||
|             _logger.LogInformation("----- Command {CommandName} handled - response: {@Response}", request.GetGenericTypeName(), response); | ||||
| 
 | ||||
|             return response; | ||||
|         } | ||||
|         return response; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,74 +1,64 @@ | ||||
| using MediatR; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors; | ||||
| 
 | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.IntegrationEvents; | ||||
| using Serilog.Context; | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Behaviors | ||||
| public class TransactionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> | ||||
| { | ||||
|     public class TransactionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> | ||||
|     private readonly ILogger<TransactionBehaviour<TRequest, TResponse>> _logger; | ||||
|     private readonly OrderingContext _dbContext; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|     public TransactionBehaviour(OrderingContext dbContext, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService, | ||||
|         ILogger<TransactionBehaviour<TRequest, TResponse>> logger) | ||||
|     { | ||||
|         private readonly ILogger<TransactionBehaviour<TRequest, TResponse>> _logger; | ||||
|         private readonly OrderingContext _dbContext; | ||||
|         private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
|         _dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext)); | ||||
|         _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentException(nameof(orderingIntegrationEventService)); | ||||
|         _logger = logger ?? throw new ArgumentException(nameof(ILogger)); | ||||
|     } | ||||
| 
 | ||||
|         public TransactionBehaviour(OrderingContext dbContext, | ||||
|             IOrderingIntegrationEventService orderingIntegrationEventService, | ||||
|             ILogger<TransactionBehaviour<TRequest, TResponse>> logger) | ||||
|     public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) | ||||
|     { | ||||
|         var response = default(TResponse); | ||||
|         var typeName = request.GetGenericTypeName(); | ||||
| 
 | ||||
|         try | ||||
|         { | ||||
|             _dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext)); | ||||
|             _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentException(nameof(orderingIntegrationEventService)); | ||||
|             _logger = logger ?? throw new ArgumentException(nameof(ILogger)); | ||||
|         } | ||||
| 
 | ||||
|         public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) | ||||
|         { | ||||
|             var response = default(TResponse); | ||||
|             var typeName = request.GetGenericTypeName(); | ||||
| 
 | ||||
|             try | ||||
|             if (_dbContext.HasActiveTransaction) | ||||
|             { | ||||
|                 if (_dbContext.HasActiveTransaction) | ||||
|                 return await next(); | ||||
|             } | ||||
| 
 | ||||
|             var strategy = _dbContext.Database.CreateExecutionStrategy(); | ||||
| 
 | ||||
|             await strategy.ExecuteAsync(async () => | ||||
|             { | ||||
|                 Guid transactionId; | ||||
| 
 | ||||
|                 using (var transaction = await _dbContext.BeginTransactionAsync()) | ||||
|                 using (LogContext.PushProperty("TransactionContext", transaction.TransactionId)) | ||||
|                 { | ||||
|                     return await next(); | ||||
|                     _logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request); | ||||
| 
 | ||||
|                     response = await next(); | ||||
| 
 | ||||
|                     _logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName); | ||||
| 
 | ||||
|                     await _dbContext.CommitTransactionAsync(transaction); | ||||
| 
 | ||||
|                     transactionId = transaction.TransactionId; | ||||
|                 } | ||||
| 
 | ||||
|                 var strategy = _dbContext.Database.CreateExecutionStrategy(); | ||||
|                 await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync(transactionId); | ||||
|             }); | ||||
| 
 | ||||
|                 await strategy.ExecuteAsync(async () => | ||||
|                 { | ||||
|                     Guid transactionId; | ||||
|             return response; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logger.LogError(ex, "ERROR Handling transaction for {CommandName} ({@Command})", typeName, request); | ||||
| 
 | ||||
|                     using (var transaction = await _dbContext.BeginTransactionAsync()) | ||||
|                     using (LogContext.PushProperty("TransactionContext", transaction.TransactionId)) | ||||
|                     { | ||||
|                         _logger.LogInformation("----- Begin transaction {TransactionId} for {CommandName} ({@Command})", transaction.TransactionId, typeName, request); | ||||
| 
 | ||||
|                         response = await next(); | ||||
| 
 | ||||
|                         _logger.LogInformation("----- Commit transaction {TransactionId} for {CommandName}", transaction.TransactionId, typeName); | ||||
| 
 | ||||
|                         await _dbContext.CommitTransactionAsync(transaction); | ||||
| 
 | ||||
|                         transactionId = transaction.TransactionId; | ||||
|                     } | ||||
| 
 | ||||
|                     await _orderingIntegrationEventService.PublishEventsThroughEventBusAsync(transactionId); | ||||
|                 }); | ||||
| 
 | ||||
|                 return response; | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 _logger.LogError(ex, "ERROR Handling transaction for {CommandName} ({@Command})", typeName, request); | ||||
| 
 | ||||
|                 throw; | ||||
|             } | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,46 +1,36 @@ | ||||
| using FluentValidation; | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.Domain.Exceptions; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Behaviors | ||||
| public class ValidatorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> | ||||
| { | ||||
|     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, ILogger<ValidatorBehavior<TRequest, TResponse>> logger) | ||||
|     { | ||||
|         private readonly ILogger<ValidatorBehavior<TRequest, TResponse>> _logger; | ||||
|         private readonly IValidator<TRequest>[] _validators; | ||||
|         _validators = validators; | ||||
|         _logger = logger; | ||||
|     } | ||||
| 
 | ||||
|         public ValidatorBehavior(IValidator<TRequest>[] validators, ILogger<ValidatorBehavior<TRequest, TResponse>> 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) | ||||
|             .Where(error => error != null) | ||||
|             .ToList(); | ||||
| 
 | ||||
|         if (failures.Any()) | ||||
|         { | ||||
|             _validators = validators; | ||||
|             _logger = logger; | ||||
|             _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)); | ||||
|         } | ||||
| 
 | ||||
|         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) | ||||
|                 .Where(error => error != null) | ||||
|                 .ToList(); | ||||
| 
 | ||||
|             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)); | ||||
|             } | ||||
| 
 | ||||
|             return await next(); | ||||
|         } | ||||
|         return await next(); | ||||
|     } | ||||
| } | ||||
| @ -1,20 +1,16 @@ | ||||
| using MediatR; | ||||
| using System.Runtime.Serialization; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| public class CancelOrderCommand : IRequest<bool> | ||||
| { | ||||
|     public class CancelOrderCommand : IRequest<bool> | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public int OrderNumber { get; set; } | ||||
|     public CancelOrderCommand() | ||||
|     { | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public int OrderNumber { get; set; } | ||||
|         public CancelOrderCommand() | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
|         public CancelOrderCommand(int orderNumber) | ||||
|         { | ||||
|             OrderNumber = orderNumber; | ||||
|         } | ||||
|     } | ||||
|     public CancelOrderCommand(int orderNumber) | ||||
|     { | ||||
|         OrderNumber = orderNumber; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,57 +1,48 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| // Regular CommandHandler | ||||
| public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, bool> | ||||
| { | ||||
|     // Regular CommandHandler | ||||
|     public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, bool> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|     public CancelOrderCommandHandler(IOrderRepository orderRepository) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|         public CancelOrderCommandHandler(IOrderRepository orderRepository) | ||||
|         { | ||||
|             _orderRepository = orderRepository; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Handler which processes the command when | ||||
|         /// customer executes cancel order from app | ||||
|         /// </summary> | ||||
|         /// <param name="command"></param> | ||||
|         /// <returns></returns> | ||||
|         public async Task<bool> Handle(CancelOrderCommand command, CancellationToken cancellationToken) | ||||
|         { | ||||
|             var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|             if (orderToUpdate == null) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             orderToUpdate.SetCancelledStatus(); | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|         } | ||||
|         _orderRepository = orderRepository; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool> | ||||
|     /// <summary> | ||||
|     /// Handler which processes the command when | ||||
|     /// customer executes cancel order from app | ||||
|     /// </summary> | ||||
|     /// <param name="command"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<bool> Handle(CancelOrderCommand command, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public CancelOrderIdentifiedCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<CancelOrderCommand, bool>> logger) | ||||
|             : base(mediator, requestManager, logger) | ||||
|         var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|         if (orderToUpdate == null) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|         orderToUpdate.SetCancelledStatus(); | ||||
|         return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Use for Idempotency in Command process | ||||
| public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool> | ||||
| { | ||||
|     public CancelOrderIdentifiedCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<CancelOrderCommand, bool>> logger) | ||||
|         : base(mediator, requestManager, logger) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override bool CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return true;                // Ignore duplicate requests for processing order. | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,106 +1,101 @@ | ||||
| using MediatR; | ||||
| using Ordering.API.Application.Models; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Runtime.Serialization; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
| // DDD and CQRS patterns comment: Note that it is recommended to implement immutable Commands | ||||
| // In this case, its immutability is achieved by having all the setters as private | ||||
| // plus only being able to update the data just once, when creating the object through its constructor. | ||||
| // References on Immutable Commands:   | ||||
| // http://cqrs.nu/Faq | ||||
| // https://docs.spine3.org/motivation/immutability.html  | ||||
| // http://blog.gauffin.org/2012/06/griffin-container-introducing-command-support/ | ||||
| // https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/how-to-implement-a-lightweight-class-with-auto-implemented-properties | ||||
| 
 | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; | ||||
| 
 | ||||
| [DataContract] | ||||
| public class CreateOrderCommand | ||||
|     : IRequest<bool> | ||||
| { | ||||
|     // DDD and CQRS patterns comment: Note that it is recommended to implement immutable Commands | ||||
|     // In this case, its immutability is achieved by having all the setters as private | ||||
|     // plus only being able to update the data just once, when creating the object through its constructor. | ||||
|     // References on Immutable Commands:   | ||||
|     // http://cqrs.nu/Faq | ||||
|     // https://docs.spine3.org/motivation/immutability.html  | ||||
|     // http://blog.gauffin.org/2012/06/griffin-container-introducing-command-support/ | ||||
|     // https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/how-to-implement-a-lightweight-class-with-auto-implemented-properties | ||||
|     [DataMember] | ||||
|     private readonly List<OrderItemDTO> _orderItems; | ||||
| 
 | ||||
|     [DataContract] | ||||
|     public class CreateOrderCommand | ||||
|         : IRequest<bool> | ||||
|     [DataMember] | ||||
|     public string UserId { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string UserName { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string City { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string Street { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string State { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string Country { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string ZipCode { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string CardNumber { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string CardHolderName { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public DateTime CardExpiration { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public string CardSecurityNumber { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public int CardTypeId { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public IEnumerable<OrderItemDTO> OrderItems => _orderItems; | ||||
| 
 | ||||
|     public CreateOrderCommand() | ||||
|     { | ||||
|         [DataMember] | ||||
|         private readonly List<OrderItemDTO> _orderItems; | ||||
|         _orderItems = new List<OrderItemDTO>(); | ||||
|     } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string UserId { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string UserName { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string City { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string Street { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string State { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string Country { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string ZipCode { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string CardNumber { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string CardHolderName { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public DateTime CardExpiration { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public string CardSecurityNumber { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public int CardTypeId { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public IEnumerable<OrderItemDTO> OrderItems => _orderItems; | ||||
| 
 | ||||
|         public CreateOrderCommand() | ||||
|         { | ||||
|             _orderItems = new List<OrderItemDTO>(); | ||||
|         } | ||||
| 
 | ||||
|         public CreateOrderCommand(List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode, | ||||
|             string cardNumber, string cardHolderName, DateTime cardExpiration, | ||||
|             string cardSecurityNumber, int cardTypeId) : this() | ||||
|         { | ||||
|             _orderItems = basketItems.ToOrderItemsDTO().ToList(); | ||||
|             UserId = userId; | ||||
|             UserName = userName; | ||||
|             City = city; | ||||
|             Street = street; | ||||
|             State = state; | ||||
|             Country = country; | ||||
|             ZipCode = zipcode; | ||||
|             CardNumber = cardNumber; | ||||
|             CardHolderName = cardHolderName; | ||||
|             CardExpiration = cardExpiration; | ||||
|             CardSecurityNumber = cardSecurityNumber; | ||||
|             CardTypeId = cardTypeId; | ||||
|             CardExpiration = cardExpiration; | ||||
|         } | ||||
|     public CreateOrderCommand(List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode, | ||||
|         string cardNumber, string cardHolderName, DateTime cardExpiration, | ||||
|         string cardSecurityNumber, int cardTypeId) : this() | ||||
|     { | ||||
|         _orderItems = basketItems.ToOrderItemsDTO().ToList(); | ||||
|         UserId = userId; | ||||
|         UserName = userName; | ||||
|         City = city; | ||||
|         Street = street; | ||||
|         State = state; | ||||
|         Country = country; | ||||
|         ZipCode = zipcode; | ||||
|         CardNumber = cardNumber; | ||||
|         CardHolderName = cardHolderName; | ||||
|         CardExpiration = cardExpiration; | ||||
|         CardSecurityNumber = cardSecurityNumber; | ||||
|         CardTypeId = cardTypeId; | ||||
|         CardExpiration = cardExpiration; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|         public record OrderItemDTO | ||||
|         { | ||||
|             public int ProductId { get; init; } | ||||
|     public record OrderItemDTO | ||||
|     { | ||||
|         public int ProductId { get; init; } | ||||
| 
 | ||||
|             public string ProductName { get; init; } | ||||
|         public string ProductName { get; init; } | ||||
| 
 | ||||
|             public decimal UnitPrice { get; init; } | ||||
|         public decimal UnitPrice { get; init; } | ||||
| 
 | ||||
|             public decimal Discount { get; init; } | ||||
|         public decimal Discount { get; init; } | ||||
| 
 | ||||
|             public int Units { get; init; } | ||||
|         public int Units { get; init; } | ||||
| 
 | ||||
|             public string PictureUrl { get; init; } | ||||
|         } | ||||
|         public string PictureUrl { get; init; } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,82 +1,72 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| 
 | ||||
| // Regular CommandHandler | ||||
| public class CreateOrderCommandHandler | ||||
|     : IRequestHandler<CreateOrderCommand, bool> | ||||
| { | ||||
|     using Domain.AggregatesModel.OrderAggregate; | ||||
|     using global::Ordering.API.Application.IntegrationEvents; | ||||
|     using global::Ordering.API.Application.IntegrationEvents.Events; | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using System; | ||||
|     using System.Threading; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly IIdentityService _identityService; | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
|     private readonly ILogger<CreateOrderCommandHandler> _logger; | ||||
| 
 | ||||
|     // Regular CommandHandler | ||||
|     public class CreateOrderCommandHandler | ||||
|         : IRequestHandler<CreateOrderCommand, bool> | ||||
|     // Using DI to inject infrastructure persistence Repositories | ||||
|     public CreateOrderCommandHandler(IMediator mediator, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService, | ||||
|         IOrderRepository orderRepository, | ||||
|         IIdentityService identityService, | ||||
|         ILogger<CreateOrderCommandHandler> logger) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         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, | ||||
|             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) | ||||
|         { | ||||
|             // 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); | ||||
|             } | ||||
| 
 | ||||
|             _logger.LogInformation("----- Creating Order - Order: {@Order}", order); | ||||
| 
 | ||||
|             _orderRepository.Add(order); | ||||
| 
 | ||||
|             return await _orderRepository.UnitOfWork | ||||
|                 .SaveEntitiesAsync(cancellationToken); | ||||
|         } | ||||
|         _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)); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool> | ||||
|     public async Task<bool> Handle(CreateOrderCommand message, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public CreateOrderIdentifiedCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<CreateOrderCommand, bool>> logger) | ||||
|             : base(mediator, requestManager, logger) | ||||
|         // 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); | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for creating order. | ||||
|         } | ||||
|         _logger.LogInformation("----- Creating Order - Order: {@Order}", order); | ||||
| 
 | ||||
|         _orderRepository.Add(order); | ||||
| 
 | ||||
|         return await _orderRepository.UnitOfWork | ||||
|             .SaveEntitiesAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Use for Idempotency in Command process | ||||
| public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool> | ||||
| { | ||||
|     public CreateOrderIdentifiedCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<CreateOrderCommand, bool>> logger) | ||||
|         : base(mediator, requestManager, logger) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override bool CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return true;                // Ignore duplicate requests for creating order. | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,21 +1,16 @@ | ||||
| using MediatR; | ||||
| using Ordering.API.Application.Models; | ||||
| using System.Collections.Generic; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
| public class CreateOrderDraftCommand : IRequest<OrderDraftDTO> | ||||
| { | ||||
|     public class CreateOrderDraftCommand : IRequest<OrderDraftDTO> | ||||
| 
 | ||||
|     public string BuyerId { get; private set; } | ||||
| 
 | ||||
|     public IEnumerable<BasketItem> Items { get; private set; } | ||||
| 
 | ||||
|     public CreateOrderDraftCommand(string buyerId, IEnumerable<BasketItem> items) | ||||
|     { | ||||
| 
 | ||||
|         public string BuyerId { get; private set; } | ||||
| 
 | ||||
|         public IEnumerable<BasketItem> Items { get; private set; } | ||||
| 
 | ||||
|         public CreateOrderDraftCommand(string buyerId, IEnumerable<BasketItem> items) | ||||
|         { | ||||
|             BuyerId = buyerId; | ||||
|             Items = items; | ||||
|         } | ||||
|         BuyerId = buyerId; | ||||
|         Items = items; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,71 +1,58 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| 
 | ||||
| // Regular CommandHandler | ||||
| public class CreateOrderDraftCommandHandler | ||||
|     : IRequestHandler<CreateOrderDraftCommand, OrderDraftDTO> | ||||
| { | ||||
|     using Domain.AggregatesModel.OrderAggregate; | ||||
|     using global::Ordering.API.Application.Models; | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Linq; | ||||
|     using System.Threading; | ||||
|     using System.Threading.Tasks; | ||||
|     using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly IIdentityService _identityService; | ||||
|     private readonly IMediator _mediator; | ||||
| 
 | ||||
|     // Regular CommandHandler | ||||
|     public class CreateOrderDraftCommandHandler | ||||
|         : IRequestHandler<CreateOrderDraftCommand, OrderDraftDTO> | ||||
|     // Using DI to inject infrastructure persistence Repositories | ||||
|     public CreateOrderDraftCommandHandler(IMediator mediator, IIdentityService identityService) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         private readonly IIdentityService _identityService; | ||||
|         private readonly IMediator _mediator; | ||||
| 
 | ||||
|         // Using DI to inject infrastructure persistence Repositories | ||||
|         public CreateOrderDraftCommandHandler(IMediator mediator, IIdentityService identityService) | ||||
|         { | ||||
|             _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); | ||||
|             _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|         } | ||||
| 
 | ||||
|         public Task<OrderDraftDTO> Handle(CreateOrderDraftCommand message, CancellationToken cancellationToken) | ||||
|         { | ||||
| 
 | ||||
|             var order = Order.NewDraft(); | ||||
|             var orderItems = message.Items.Select(i => i.ToOrderItemDTO()); | ||||
|             foreach (var item in orderItems) | ||||
|             { | ||||
|                 order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); | ||||
|             } | ||||
| 
 | ||||
|             return Task.FromResult(OrderDraftDTO.FromOrder(order)); | ||||
|         } | ||||
|         _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); | ||||
|         _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public record OrderDraftDTO | ||||
|     public Task<OrderDraftDTO> Handle(CreateOrderDraftCommand message, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public IEnumerable<OrderItemDTO> OrderItems { get; init; } | ||||
|         public decimal Total { get; init; } | ||||
| 
 | ||||
|         public static OrderDraftDTO FromOrder(Order order) | ||||
|         var order = Order.NewDraft(); | ||||
|         var orderItems = message.Items.Select(i => i.ToOrderItemDTO()); | ||||
|         foreach (var item in orderItems) | ||||
|         { | ||||
|             return new OrderDraftDTO() | ||||
|             { | ||||
|                 OrderItems = order.OrderItems.Select(oi => new OrderItemDTO | ||||
|                 { | ||||
|                     Discount = oi.GetCurrentDiscount(), | ||||
|                     ProductId = oi.ProductId, | ||||
|                     UnitPrice = oi.GetUnitPrice(), | ||||
|                     PictureUrl = oi.GetPictureUri(), | ||||
|                     Units = oi.GetUnits(), | ||||
|                     ProductName = oi.GetOrderItemProductName() | ||||
|                 }), | ||||
|                 Total = order.GetTotal() | ||||
|             }; | ||||
|             order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); | ||||
|         } | ||||
| 
 | ||||
|         return Task.FromResult(OrderDraftDTO.FromOrder(order)); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| public record OrderDraftDTO | ||||
| { | ||||
|     public IEnumerable<OrderItemDTO> OrderItems { get; init; } | ||||
|     public decimal Total { get; init; } | ||||
| 
 | ||||
|     public static OrderDraftDTO FromOrder(Order order) | ||||
|     { | ||||
|         return new OrderDraftDTO() | ||||
|         { | ||||
|             OrderItems = order.OrderItems.Select(oi => new OrderItemDTO | ||||
|             { | ||||
|                 Discount = oi.GetCurrentDiscount(), | ||||
|                 ProductId = oi.ProductId, | ||||
|                 UnitPrice = oi.GetUnitPrice(), | ||||
|                 PictureUrl = oi.GetPictureUri(), | ||||
|                 Units = oi.GetUnits(), | ||||
|                 ProductName = oi.GetOrderItemProductName() | ||||
|             }), | ||||
|             Total = order.GetTotal() | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,17 +1,13 @@ | ||||
| using MediatR; | ||||
| using System; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
| public class IdentifiedCommand<T, R> : IRequest<R> | ||||
|     where T : IRequest<R> | ||||
| { | ||||
|     public class IdentifiedCommand<T, R> : IRequest<R> | ||||
|         where T : IRequest<R> | ||||
|     public T Command { get; } | ||||
|     public Guid Id { get; } | ||||
|     public IdentifiedCommand(T command, Guid id) | ||||
|     { | ||||
|         public T Command { get; } | ||||
|         public Guid Id { get; } | ||||
|         public IdentifiedCommand(T command, Guid id) | ||||
|         { | ||||
|             Command = command; | ||||
|             Id = id; | ||||
|         } | ||||
|         Command = command; | ||||
|         Id = id; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,116 +1,107 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.Commands; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands | ||||
| /// <summary> | ||||
| /// Provides a base implementation for handling duplicate request and ensuring idempotent updates, in the cases where | ||||
| /// a requestid sent by client is used to detect duplicate requests. | ||||
| /// </summary> | ||||
| /// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam> | ||||
| /// <typeparam name="R">Return value of the inner command handler</typeparam> | ||||
| public class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<T, R>, R> | ||||
|     where T : IRequest<R> | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Provides a base implementation for handling duplicate request and ensuring idempotent updates, in the cases where | ||||
|     /// a requestid sent by client is used to detect duplicate requests. | ||||
|     /// </summary> | ||||
|     /// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam> | ||||
|     /// <typeparam name="R">Return value of the inner command handler</typeparam> | ||||
|     public class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<T, R>, R> | ||||
|         where T : IRequest<R> | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly IRequestManager _requestManager; | ||||
|     private readonly ILogger<IdentifiedCommandHandler<T, R>> _logger; | ||||
| 
 | ||||
|     public IdentifiedCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<T, R>> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly IRequestManager _requestManager; | ||||
|         private readonly ILogger<IdentifiedCommandHandler<T, R>> _logger; | ||||
|         _mediator = mediator; | ||||
|         _requestManager = requestManager; | ||||
|         _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public IdentifiedCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<T, R>> logger) | ||||
|     /// <summary> | ||||
|     /// Creates the result value to return if a previous request was found | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     protected virtual R CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return default(R); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// This method handles the command. It just ensures that no other request exists with the same ID, and if this is the case | ||||
|     /// just enqueues the original inner command. | ||||
|     /// </summary> | ||||
|     /// <param name="message">IdentifiedCommand which contains both original command & request ID</param> | ||||
|     /// <returns>Return value of inner command or default value if request same ID was found</returns> | ||||
|     public async Task<R> Handle(IdentifiedCommand<T, R> message, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var alreadyExists = await _requestManager.ExistAsync(message.Id); | ||||
|         if (alreadyExists) | ||||
|         { | ||||
|             _mediator = mediator; | ||||
|             _requestManager = requestManager; | ||||
|             _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); | ||||
|             return CreateResultForDuplicateRequest(); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Creates the result value to return if a previous request was found | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         protected virtual R CreateResultForDuplicateRequest() | ||||
|         else | ||||
|         { | ||||
|             return default(R); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// This method handles the command. It just ensures that no other request exists with the same ID, and if this is the case | ||||
|         /// just enqueues the original inner command. | ||||
|         /// </summary> | ||||
|         /// <param name="message">IdentifiedCommand which contains both original command & request ID</param> | ||||
|         /// <returns>Return value of inner command or default value if request same ID was found</returns> | ||||
|         public async Task<R> Handle(IdentifiedCommand<T, R> message, CancellationToken cancellationToken) | ||||
|         { | ||||
|             var alreadyExists = await _requestManager.ExistAsync(message.Id); | ||||
|             if (alreadyExists) | ||||
|             await _requestManager.CreateRequestForCommandAsync<T>(message.Id); | ||||
|             try | ||||
|             { | ||||
|                 return CreateResultForDuplicateRequest(); | ||||
|                 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, cancellationToken); | ||||
| 
 | ||||
|                 _logger.LogInformation( | ||||
|                     "----- Command result: {@Result} - {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                     result, | ||||
|                     commandName, | ||||
|                     idProperty, | ||||
|                     commandId, | ||||
|                     command); | ||||
| 
 | ||||
|                 return result; | ||||
|             } | ||||
|             else | ||||
|             catch | ||||
|             { | ||||
|                 await _requestManager.CreateRequestForCommandAsync<T>(message.Id); | ||||
|                 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, cancellationToken); | ||||
| 
 | ||||
|                     _logger.LogInformation( | ||||
|                         "----- Command result: {@Result} - {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                         result, | ||||
|                         commandName, | ||||
|                         idProperty, | ||||
|                         commandId, | ||||
|                         command); | ||||
| 
 | ||||
|                     return result; | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     return default(R); | ||||
|                 } | ||||
|                 return default(R); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,13 @@ | ||||
| using MediatR; | ||||
| using System.Runtime.Serialization; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| public class SetAwaitingValidationOrderStatusCommand : IRequest<bool> | ||||
| { | ||||
|     public class SetAwaitingValidationOrderStatusCommand : IRequest<bool> | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|     public SetAwaitingValidationOrderStatusCommand(int orderNumber) | ||||
|     { | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|         public SetAwaitingValidationOrderStatusCommand(int orderNumber) | ||||
|         { | ||||
|             OrderNumber = orderNumber; | ||||
|         } | ||||
|         OrderNumber = orderNumber; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,57 +1,48 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| // Regular CommandHandler | ||||
| public class SetAwaitingValidationOrderStatusCommandHandler : IRequestHandler<SetAwaitingValidationOrderStatusCommand, bool> | ||||
| { | ||||
|     // Regular CommandHandler | ||||
|     public class SetAwaitingValidationOrderStatusCommandHandler : IRequestHandler<SetAwaitingValidationOrderStatusCommand, bool> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|     public SetAwaitingValidationOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|         public SetAwaitingValidationOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|         { | ||||
|             _orderRepository = orderRepository; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Handler which processes the command when | ||||
|         /// graceperiod has finished | ||||
|         /// </summary> | ||||
|         /// <param name="command"></param> | ||||
|         /// <returns></returns> | ||||
|         public async Task<bool> Handle(SetAwaitingValidationOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|         { | ||||
|             var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|             if (orderToUpdate == null) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             orderToUpdate.SetAwaitingValidationStatus(); | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|         } | ||||
|         _orderRepository = orderRepository; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class SetAwaitingValidationIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool> | ||||
|     /// <summary> | ||||
|     /// Handler which processes the command when | ||||
|     /// graceperiod has finished | ||||
|     /// </summary> | ||||
|     /// <param name="command"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<bool> Handle(SetAwaitingValidationOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public SetAwaitingValidationIdentifiedOrderStatusCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool>> logger) | ||||
|             : base(mediator, requestManager, logger) | ||||
|         var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|         if (orderToUpdate == null) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|         orderToUpdate.SetAwaitingValidationStatus(); | ||||
|         return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Use for Idempotency in Command process | ||||
| public class SetAwaitingValidationIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool> | ||||
| { | ||||
|     public SetAwaitingValidationIdentifiedOrderStatusCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool>> logger) | ||||
|         : base(mediator, requestManager, logger) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override bool CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return true;                // Ignore duplicate requests for processing order. | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,13 @@ | ||||
| using MediatR; | ||||
| using System.Runtime.Serialization; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| public class SetPaidOrderStatusCommand : IRequest<bool> | ||||
| { | ||||
|     public class SetPaidOrderStatusCommand : IRequest<bool> | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|     public SetPaidOrderStatusCommand(int orderNumber) | ||||
|     { | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|         public SetPaidOrderStatusCommand(int orderNumber) | ||||
|         { | ||||
|             OrderNumber = orderNumber; | ||||
|         } | ||||
|         OrderNumber = orderNumber; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,60 +1,51 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| // Regular CommandHandler | ||||
| public class SetPaidOrderStatusCommandHandler : IRequestHandler<SetPaidOrderStatusCommand, bool> | ||||
| { | ||||
|     // Regular CommandHandler | ||||
|     public class SetPaidOrderStatusCommandHandler : IRequestHandler<SetPaidOrderStatusCommand, bool> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|     public SetPaidOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|         public SetPaidOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|         { | ||||
|             _orderRepository = orderRepository; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Handler which processes the command when | ||||
|         /// Shipment service confirms the payment | ||||
|         /// </summary> | ||||
|         /// <param name="command"></param> | ||||
|         /// <returns></returns> | ||||
|         public async Task<bool> Handle(SetPaidOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|         { | ||||
|             // Simulate a work time for validating the payment | ||||
|             await Task.Delay(10000, cancellationToken); | ||||
| 
 | ||||
|             var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|             if (orderToUpdate == null) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             orderToUpdate.SetPaidStatus(); | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|         } | ||||
|         _orderRepository = orderRepository; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class SetPaidIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool> | ||||
|     /// <summary> | ||||
|     /// Handler which processes the command when | ||||
|     /// Shipment service confirms the payment | ||||
|     /// </summary> | ||||
|     /// <param name="command"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<bool> Handle(SetPaidOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public SetPaidIdentifiedOrderStatusCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool>> logger) | ||||
|             : base(mediator, requestManager, logger) | ||||
|         // Simulate a work time for validating the payment | ||||
|         await Task.Delay(10000, cancellationToken); | ||||
| 
 | ||||
|         var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|         if (orderToUpdate == null) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|         orderToUpdate.SetPaidStatus(); | ||||
|         return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Use for Idempotency in Command process | ||||
| public class SetPaidIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool> | ||||
| { | ||||
|     public SetPaidIdentifiedOrderStatusCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool>> logger) | ||||
|         : base(mediator, requestManager, logger) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override bool CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return true;                // Ignore duplicate requests for processing order. | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,13 @@ | ||||
| using MediatR; | ||||
| using System.Runtime.Serialization; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| public class SetStockConfirmedOrderStatusCommand : IRequest<bool> | ||||
| { | ||||
|     public class SetStockConfirmedOrderStatusCommand : IRequest<bool> | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|     public SetStockConfirmedOrderStatusCommand(int orderNumber) | ||||
|     { | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|         public SetStockConfirmedOrderStatusCommand(int orderNumber) | ||||
|         { | ||||
|             OrderNumber = orderNumber; | ||||
|         } | ||||
|         OrderNumber = orderNumber; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,60 +1,51 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| // Regular CommandHandler | ||||
| public class SetStockConfirmedOrderStatusCommandHandler : IRequestHandler<SetStockConfirmedOrderStatusCommand, bool> | ||||
| { | ||||
|     // Regular CommandHandler | ||||
|     public class SetStockConfirmedOrderStatusCommandHandler : IRequestHandler<SetStockConfirmedOrderStatusCommand, bool> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|     public SetStockConfirmedOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|         public SetStockConfirmedOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|         { | ||||
|             _orderRepository = orderRepository; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Handler which processes the command when | ||||
|         /// Stock service confirms the request | ||||
|         /// </summary> | ||||
|         /// <param name="command"></param> | ||||
|         /// <returns></returns> | ||||
|         public async Task<bool> Handle(SetStockConfirmedOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|         { | ||||
|             // Simulate a work time for confirming the stock | ||||
|             await Task.Delay(10000, cancellationToken); | ||||
| 
 | ||||
|             var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|             if (orderToUpdate == null) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             orderToUpdate.SetStockConfirmedStatus(); | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|         } | ||||
|         _orderRepository = orderRepository; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool> | ||||
|     /// <summary> | ||||
|     /// Handler which processes the command when | ||||
|     /// Stock service confirms the request | ||||
|     /// </summary> | ||||
|     /// <param name="command"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<bool> Handle(SetStockConfirmedOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public SetStockConfirmedOrderStatusIdenfifiedCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>> logger) | ||||
|             : base(mediator, requestManager, logger) | ||||
|         // Simulate a work time for confirming the stock | ||||
|         await Task.Delay(10000, cancellationToken); | ||||
| 
 | ||||
|         var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|         if (orderToUpdate == null) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|         orderToUpdate.SetStockConfirmedStatus(); | ||||
|         return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Use for Idempotency in Command process | ||||
| public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool> | ||||
| { | ||||
|     public SetStockConfirmedOrderStatusIdenfifiedCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>> logger) | ||||
|         : base(mediator, requestManager, logger) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override bool CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return true;                // Ignore duplicate requests for processing order. | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,22 +1,17 @@ | ||||
| using MediatR; | ||||
| using System.Collections.Generic; | ||||
| using System.Runtime.Serialization; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| public class SetStockRejectedOrderStatusCommand : IRequest<bool> | ||||
| { | ||||
|     public class SetStockRejectedOrderStatusCommand : IRequest<bool> | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public List<int> OrderStockItems { get; private set; } | ||||
| 
 | ||||
|     public SetStockRejectedOrderStatusCommand(int orderNumber, List<int> orderStockItems) | ||||
|     { | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public List<int> OrderStockItems { get; private set; } | ||||
| 
 | ||||
|         public SetStockRejectedOrderStatusCommand(int orderNumber, List<int> orderStockItems) | ||||
|         { | ||||
|             OrderNumber = orderNumber; | ||||
|             OrderStockItems = orderStockItems; | ||||
|         } | ||||
|         OrderNumber = orderNumber; | ||||
|         OrderStockItems = orderStockItems; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,61 +1,52 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| // Regular CommandHandler | ||||
| public class SetStockRejectedOrderStatusCommandHandler : IRequestHandler<SetStockRejectedOrderStatusCommand, bool> | ||||
| { | ||||
|     // Regular CommandHandler | ||||
|     public class SetStockRejectedOrderStatusCommandHandler : IRequestHandler<SetStockRejectedOrderStatusCommand, bool> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|     public SetStockRejectedOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|         public SetStockRejectedOrderStatusCommandHandler(IOrderRepository orderRepository) | ||||
|         { | ||||
|             _orderRepository = orderRepository; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Handler which processes the command when | ||||
|         /// Stock service rejects the request | ||||
|         /// </summary> | ||||
|         /// <param name="command"></param> | ||||
|         /// <returns></returns> | ||||
|         public async Task<bool> Handle(SetStockRejectedOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|         { | ||||
|             // Simulate a work time for rejecting the stock | ||||
|             await Task.Delay(10000, cancellationToken); | ||||
| 
 | ||||
|             var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|             if (orderToUpdate == null) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             orderToUpdate.SetCancelledStatusWhenStockIsRejected(command.OrderStockItems); | ||||
| 
 | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|         } | ||||
|         _orderRepository = orderRepository; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool> | ||||
|     /// <summary> | ||||
|     /// Handler which processes the command when | ||||
|     /// Stock service rejects the request | ||||
|     /// </summary> | ||||
|     /// <param name="command"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<bool> Handle(SetStockRejectedOrderStatusCommand command, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public SetStockRejectedOrderStatusIdenfifiedCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>> logger) | ||||
|             : base(mediator, requestManager, logger) | ||||
|         // Simulate a work time for rejecting the stock | ||||
|         await Task.Delay(10000, cancellationToken); | ||||
| 
 | ||||
|         var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|         if (orderToUpdate == null) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|         orderToUpdate.SetCancelledStatusWhenStockIsRejected(command.OrderStockItems); | ||||
| 
 | ||||
|         return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Use for Idempotency in Command process | ||||
| public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool> | ||||
| { | ||||
|     public SetStockRejectedOrderStatusIdenfifiedCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>> logger) | ||||
|         : base(mediator, requestManager, logger) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override bool CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return true;                // Ignore duplicate requests for processing order. | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,13 @@ | ||||
| using MediatR; | ||||
| using System.Runtime.Serialization; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| public class ShipOrderCommand : IRequest<bool> | ||||
| { | ||||
|     public class ShipOrderCommand : IRequest<bool> | ||||
| 
 | ||||
|     [DataMember] | ||||
|     public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|     public ShipOrderCommand(int orderNumber) | ||||
|     { | ||||
| 
 | ||||
|         [DataMember] | ||||
|         public int OrderNumber { get; private set; } | ||||
| 
 | ||||
|         public ShipOrderCommand(int orderNumber) | ||||
|         { | ||||
|             OrderNumber = orderNumber; | ||||
|         } | ||||
|         OrderNumber = orderNumber; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,57 +1,48 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Commands | ||||
| // Regular CommandHandler | ||||
| public class ShipOrderCommandHandler : IRequestHandler<ShipOrderCommand, bool> | ||||
| { | ||||
|     // Regular CommandHandler | ||||
|     public class ShipOrderCommandHandler : IRequestHandler<ShipOrderCommand, bool> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|     public ShipOrderCommandHandler(IOrderRepository orderRepository) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
| 
 | ||||
|         public ShipOrderCommandHandler(IOrderRepository orderRepository) | ||||
|         { | ||||
|             _orderRepository = orderRepository; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Handler which processes the command when | ||||
|         /// administrator executes ship order from app | ||||
|         /// </summary> | ||||
|         /// <param name="command"></param> | ||||
|         /// <returns></returns> | ||||
|         public async Task<bool> Handle(ShipOrderCommand command, CancellationToken cancellationToken) | ||||
|         { | ||||
|             var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|             if (orderToUpdate == null) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             orderToUpdate.SetShippedStatus(); | ||||
|             return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|         } | ||||
|         _orderRepository = orderRepository; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Use for Idempotency in Command process | ||||
|     public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool> | ||||
|     /// <summary> | ||||
|     /// Handler which processes the command when | ||||
|     /// administrator executes ship order from app | ||||
|     /// </summary> | ||||
|     /// <param name="command"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<bool> Handle(ShipOrderCommand command, CancellationToken cancellationToken) | ||||
|     { | ||||
|         public ShipOrderIdentifiedCommandHandler( | ||||
|             IMediator mediator, | ||||
|             IRequestManager requestManager, | ||||
|             ILogger<IdentifiedCommandHandler<ShipOrderCommand, bool>> logger) | ||||
|             : base(mediator, requestManager, logger) | ||||
|         var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber); | ||||
|         if (orderToUpdate == null) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         protected override bool CreateResultForDuplicateRequest() | ||||
|         { | ||||
|             return true;                // Ignore duplicate requests for processing order. | ||||
|         } | ||||
|         orderToUpdate.SetShippedStatus(); | ||||
|         return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Use for Idempotency in Command process | ||||
| public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool> | ||||
| { | ||||
|     public ShipOrderIdentifiedCommandHandler( | ||||
|         IMediator mediator, | ||||
|         IRequestManager requestManager, | ||||
|         ILogger<IdentifiedCommandHandler<ShipOrderCommand, bool>> logger) | ||||
|         : base(mediator, requestManager, logger) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     protected override bool CreateResultForDuplicateRequest() | ||||
|     { | ||||
|         return true;                // Ignore duplicate requests for processing order. | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,38 +1,29 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.Domain.Events; | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified; | ||||
| 
 | ||||
| namespace Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified | ||||
| public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler | ||||
|                 : INotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent> | ||||
| { | ||||
|     public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler | ||||
|                    : INotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly ILoggerFactory _logger; | ||||
| 
 | ||||
|     public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler( | ||||
|         IOrderRepository orderRepository, ILoggerFactory logger) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         private readonly ILoggerFactory _logger; | ||||
|         _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler( | ||||
|             IOrderRepository orderRepository, ILoggerFactory logger) | ||||
|         { | ||||
|             _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|     // Domain Logic comment: | ||||
|     // When the Buyer and Buyer's payment method have been created or verified that they existed,  | ||||
|     // then we can update the original Order with the BuyerId and PaymentId (foreign keys) | ||||
|     public async Task Handle(BuyerAndPaymentMethodVerifiedDomainEvent buyerPaymentMethodVerifiedEvent, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId); | ||||
|         orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id); | ||||
|         orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); | ||||
| 
 | ||||
|         // Domain Logic comment: | ||||
|         // When the Buyer and Buyer's payment method have been created or verified that they existed,  | ||||
|         // then we can update the original Order with the BuyerId and PaymentId (foreign keys) | ||||
|         public async Task Handle(BuyerAndPaymentMethodVerifiedDomainEvent buyerPaymentMethodVerifiedEvent, CancellationToken cancellationToken) | ||||
|         { | ||||
|             var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId); | ||||
|             orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id); | ||||
|             orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id); | ||||
| 
 | ||||
|             _logger.CreateLogger<UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler>() | ||||
|                 .LogTrace("Order with Id: {OrderId} has been successfully updated with a payment method {PaymentMethod} ({Id})", | ||||
|                     buyerPaymentMethodVerifiedEvent.OrderId, nameof(buyerPaymentMethodVerifiedEvent.Payment), buyerPaymentMethodVerifiedEvent.Payment.Id); | ||||
|         } | ||||
|         _logger.CreateLogger<UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler>() | ||||
|             .LogTrace("Order with Id: {OrderId} has been successfully updated with a payment method {PaymentMethod} ({Id})", | ||||
|                 buyerPaymentMethodVerifiedEvent.OrderId, nameof(buyerPaymentMethodVerifiedEvent.Payment), buyerPaymentMethodVerifiedEvent.Payment.Id); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,47 +1,37 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.IntegrationEvents; | ||||
| using Ordering.API.Application.IntegrationEvents.Events; | ||||
| using Ordering.Domain.Events; | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events; | ||||
| 
 | ||||
| namespace Ordering.API.Application.DomainEventHandlers.OrderCancelled | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderCancelled; | ||||
| 
 | ||||
| public class OrderCancelledDomainEventHandler | ||||
|                 : INotificationHandler<OrderCancelledDomainEvent> | ||||
| { | ||||
|     public class OrderCancelledDomainEventHandler | ||||
|                    : INotificationHandler<OrderCancelledDomainEvent> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly IBuyerRepository _buyerRepository; | ||||
|     private readonly ILoggerFactory _logger; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|     public OrderCancelledDomainEventHandler( | ||||
|         IOrderRepository orderRepository, | ||||
|         ILoggerFactory logger, | ||||
|         IBuyerRepository buyerRepository, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         private readonly IBuyerRepository _buyerRepository; | ||||
|         private readonly ILoggerFactory _logger; | ||||
|         private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
|         _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|         _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|     } | ||||
| 
 | ||||
|         public OrderCancelledDomainEventHandler( | ||||
|             IOrderRepository orderRepository, | ||||
|             ILoggerFactory logger, | ||||
|             IBuyerRepository buyerRepository, | ||||
|             IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|         { | ||||
|             _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|             _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|             _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|         } | ||||
|     public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) | ||||
|     { | ||||
|         _logger.CreateLogger<OrderCancelledDomainEvent>() | ||||
|             .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", | ||||
|                 orderCancelledDomainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id); | ||||
| 
 | ||||
|         public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) | ||||
|         { | ||||
|             _logger.CreateLogger<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()); | ||||
| 
 | ||||
|             var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id); | ||||
|             var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | ||||
| 
 | ||||
|             var orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | ||||
|             await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToCancelledIntegrationEvent); | ||||
|         } | ||||
|         var orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | ||||
|         await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToCancelledIntegrationEvent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,52 +1,39 @@ | ||||
| namespace Ordering.API.Application.DomainEventHandlers.OrderGracePeriodConfirmed | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderGracePeriodConfirmed; | ||||
|      | ||||
| public class OrderStatusChangedToAwaitingValidationDomainEventHandler | ||||
|                 : INotificationHandler<OrderStatusChangedToAwaitingValidationDomainEvent> | ||||
| { | ||||
|     using Domain.Events; | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Ordering.API.Application.IntegrationEvents; | ||||
|     using Ordering.API.Application.IntegrationEvents.Events; | ||||
|     using System; | ||||
|     using System.Linq; | ||||
|     using System.Threading; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly ILoggerFactory _logger; | ||||
|     private readonly IBuyerRepository _buyerRepository; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|     public class OrderStatusChangedToAwaitingValidationDomainEventHandler | ||||
|                    : INotificationHandler<OrderStatusChangedToAwaitingValidationDomainEvent> | ||||
|     public OrderStatusChangedToAwaitingValidationDomainEventHandler( | ||||
|         IOrderRepository orderRepository, ILoggerFactory logger, | ||||
|         IBuyerRepository buyerRepository, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         private readonly ILoggerFactory _logger; | ||||
|         private readonly IBuyerRepository _buyerRepository; | ||||
|         private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|         public OrderStatusChangedToAwaitingValidationDomainEventHandler( | ||||
|             IOrderRepository orderRepository, ILoggerFactory logger, | ||||
|             IBuyerRepository buyerRepository, | ||||
|             IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|         { | ||||
|             _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|             _buyerRepository = buyerRepository; | ||||
|             _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|         } | ||||
| 
 | ||||
|         public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent, CancellationToken cancellationToken) | ||||
|         { | ||||
|             _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); | ||||
| 
 | ||||
|             var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | ||||
| 
 | ||||
|             var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems | ||||
|                 .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); | ||||
| 
 | ||||
|             var orderStatusChangedToAwaitingValidationIntegrationEvent = new OrderStatusChangedToAwaitingValidationIntegrationEvent( | ||||
|                 order.Id, order.OrderStatus.Name, buyer.Name, orderStockList); | ||||
|             await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToAwaitingValidationIntegrationEvent); | ||||
|         } | ||||
|         _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         _buyerRepository = buyerRepository; | ||||
|         _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent, CancellationToken cancellationToken) | ||||
|     { | ||||
|         _logger.CreateLogger<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); | ||||
| 
 | ||||
|         var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | ||||
| 
 | ||||
|         var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems | ||||
|             .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); | ||||
| 
 | ||||
|         var orderStatusChangedToAwaitingValidationIntegrationEvent = new OrderStatusChangedToAwaitingValidationIntegrationEvent( | ||||
|             order.Id, order.OrderStatus.Name, buyer.Name, orderStockList); | ||||
|         await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToAwaitingValidationIntegrationEvent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,57 +1,44 @@ | ||||
| namespace Ordering.API.Application.DomainEventHandlers.OrderPaid | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderPaid; | ||||
|      | ||||
| public class OrderStatusChangedToPaidDomainEventHandler | ||||
|                 : INotificationHandler<OrderStatusChangedToPaidDomainEvent> | ||||
| { | ||||
|     using Domain.Events; | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Ordering.API.Application.IntegrationEvents; | ||||
|     using Ordering.API.Application.IntegrationEvents.Events; | ||||
|     using System; | ||||
|     using System.Linq; | ||||
|     using System.Threading; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly ILoggerFactory _logger; | ||||
|     private readonly IBuyerRepository _buyerRepository; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|     public class OrderStatusChangedToPaidDomainEventHandler | ||||
|                    : INotificationHandler<OrderStatusChangedToPaidDomainEvent> | ||||
| 
 | ||||
|     public OrderStatusChangedToPaidDomainEventHandler( | ||||
|         IOrderRepository orderRepository, ILoggerFactory logger, | ||||
|         IBuyerRepository buyerRepository, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService | ||||
|         ) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         private readonly ILoggerFactory _logger; | ||||
|         private readonly IBuyerRepository _buyerRepository; | ||||
|         private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
| 
 | ||||
|         public OrderStatusChangedToPaidDomainEventHandler( | ||||
|             IOrderRepository orderRepository, ILoggerFactory logger, | ||||
|             IBuyerRepository buyerRepository, | ||||
|             IOrderingIntegrationEventService orderingIntegrationEventService | ||||
|             ) | ||||
|         { | ||||
|             _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|             _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|             _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); | ||||
|         } | ||||
| 
 | ||||
|         public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken) | ||||
|         { | ||||
|             _logger.CreateLogger<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()); | ||||
| 
 | ||||
|             var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems | ||||
|                 .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); | ||||
| 
 | ||||
|             var orderStatusChangedToPaidIntegrationEvent = new OrderStatusChangedToPaidIntegrationEvent( | ||||
|                 orderStatusChangedToPaidDomainEvent.OrderId, | ||||
|                 order.OrderStatus.Name, | ||||
|                 buyer.Name, | ||||
|                 orderStockList); | ||||
| 
 | ||||
|             await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToPaidIntegrationEvent); | ||||
|         } | ||||
|         _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|         _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken) | ||||
|     { | ||||
|         _logger.CreateLogger<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()); | ||||
| 
 | ||||
|         var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems | ||||
|             .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); | ||||
| 
 | ||||
|         var orderStatusChangedToPaidIntegrationEvent = new OrderStatusChangedToPaidIntegrationEvent( | ||||
|             orderStatusChangedToPaidDomainEvent.OrderId, | ||||
|             order.OrderStatus.Name, | ||||
|             buyer.Name, | ||||
|             orderStockList); | ||||
| 
 | ||||
|         await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToPaidIntegrationEvent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,47 +1,35 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.IntegrationEvents; | ||||
| using Ordering.API.Application.IntegrationEvents.Events; | ||||
| using Ordering.Domain.Events; | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderShipped; | ||||
| 
 | ||||
| namespace Ordering.API.Application.DomainEventHandlers.OrderShipped | ||||
| public class OrderShippedDomainEventHandler | ||||
|                 : INotificationHandler<OrderShippedDomainEvent> | ||||
| { | ||||
|     public class OrderShippedDomainEventHandler | ||||
|                    : INotificationHandler<OrderShippedDomainEvent> | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly IBuyerRepository _buyerRepository; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
|     private readonly ILoggerFactory _logger; | ||||
| 
 | ||||
|     public OrderShippedDomainEventHandler( | ||||
|         IOrderRepository orderRepository, | ||||
|         ILoggerFactory logger, | ||||
|         IBuyerRepository buyerRepository, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         private readonly IBuyerRepository _buyerRepository; | ||||
|         private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
|         private readonly ILoggerFactory _logger; | ||||
|         _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|         _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|     } | ||||
| 
 | ||||
|         public OrderShippedDomainEventHandler( | ||||
|             IOrderRepository orderRepository, | ||||
|             ILoggerFactory logger, | ||||
|             IBuyerRepository buyerRepository, | ||||
|             IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|         { | ||||
|             _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|             _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|             _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|         } | ||||
|     public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken) | ||||
|     { | ||||
|         _logger.CreateLogger<OrderShippedDomainEvent>() | ||||
|             .LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})", | ||||
|                 orderShippedDomainEvent.Order.Id, nameof(OrderStatus.Shipped), OrderStatus.Shipped.Id); | ||||
| 
 | ||||
|         public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken) | ||||
|         { | ||||
|             _logger.CreateLogger<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()); | ||||
| 
 | ||||
|             var order = await _orderRepository.GetAsync(orderShippedDomainEvent.Order.Id); | ||||
|             var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString()); | ||||
| 
 | ||||
|             var orderStatusChangedToShippedIntegrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | ||||
|             await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToShippedIntegrationEvent); | ||||
|         } | ||||
|         var orderStatusChangedToShippedIntegrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | ||||
|         await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToShippedIntegrationEvent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,16 +1,15 @@ | ||||
| namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; | ||||
| 
 | ||||
| public class SendEmailToCustomerWhenOrderStartedDomainEventHandler | ||||
| //: IAsyncNotificationHandler<OrderStartedDomainEvent> | ||||
| { | ||||
|     public class SendEmailToCustomerWhenOrderStartedDomainEventHandler | ||||
|     //: IAsyncNotificationHandler<OrderStartedDomainEvent> | ||||
|     public SendEmailToCustomerWhenOrderStartedDomainEventHandler() | ||||
|     { | ||||
|         public SendEmailToCustomerWhenOrderStartedDomainEventHandler() | ||||
|         { | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         //public async Task Handle(OrderStartedDomainEvent orderNotification) | ||||
|         //{ | ||||
|         //    //TBD - Send email logic | ||||
|         //} | ||||
|     } | ||||
| 
 | ||||
|     //public async Task Handle(OrderStartedDomainEvent orderNotification) | ||||
|     //{ | ||||
|     //    //TBD - Send email logic | ||||
|     //} | ||||
| } | ||||
|  | ||||
| @ -1,67 +1,55 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.IntegrationEvents; | ||||
| using Ordering.API.Application.IntegrationEvents.Events; | ||||
| using Ordering.Domain.Events; | ||||
| using System; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; | ||||
| 
 | ||||
| namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent | ||||
| public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler | ||||
|                     : INotificationHandler<OrderStartedDomainEvent> | ||||
| { | ||||
|     public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler | ||||
|                         : INotificationHandler<OrderStartedDomainEvent> | ||||
|     private readonly ILoggerFactory _logger; | ||||
|     private readonly IBuyerRepository _buyerRepository; | ||||
|     private readonly IIdentityService _identityService; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|     public ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler( | ||||
|         ILoggerFactory logger, | ||||
|         IBuyerRepository buyerRepository, | ||||
|         IIdentityService identityService, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|     { | ||||
|         private readonly ILoggerFactory _logger; | ||||
|         private readonly IBuyerRepository _buyerRepository; | ||||
|         private readonly IIdentityService _identityService; | ||||
|         private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
|         _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|         _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); | ||||
|         _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler( | ||||
|             ILoggerFactory logger, | ||||
|             IBuyerRepository buyerRepository, | ||||
|             IIdentityService identityService, | ||||
|             IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|     public async Task Handle(OrderStartedDomainEvent orderStartedEvent, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1; | ||||
|         var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId); | ||||
|         bool buyerOriginallyExisted = (buyer == null) ? false : true; | ||||
| 
 | ||||
|         if (!buyerOriginallyExisted) | ||||
|         { | ||||
|             _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|             _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); | ||||
|             _orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|             buyer = new Buyer(orderStartedEvent.UserId, orderStartedEvent.UserName); | ||||
|         } | ||||
| 
 | ||||
|         public async Task Handle(OrderStartedDomainEvent orderStartedEvent, CancellationToken cancellationToken) | ||||
|         { | ||||
|             var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1; | ||||
|             var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId); | ||||
|             bool buyerOriginallyExisted = (buyer == null) ? false : true; | ||||
|         buyer.VerifyOrAddPaymentMethod(cardTypeId, | ||||
|                                         $"Payment Method on {DateTime.UtcNow}", | ||||
|                                         orderStartedEvent.CardNumber, | ||||
|                                         orderStartedEvent.CardSecurityNumber, | ||||
|                                         orderStartedEvent.CardHolderName, | ||||
|                                         orderStartedEvent.CardExpiration, | ||||
|                                         orderStartedEvent.Order.Id); | ||||
| 
 | ||||
|             if (!buyerOriginallyExisted) | ||||
|             { | ||||
|                 buyer = new Buyer(orderStartedEvent.UserId, orderStartedEvent.UserName); | ||||
|             } | ||||
|         var buyerUpdated = buyerOriginallyExisted ? | ||||
|             _buyerRepository.Update(buyer) : | ||||
|             _buyerRepository.Add(buyer); | ||||
| 
 | ||||
|             buyer.VerifyOrAddPaymentMethod(cardTypeId, | ||||
|                                            $"Payment Method on {DateTime.UtcNow}", | ||||
|                                            orderStartedEvent.CardNumber, | ||||
|                                            orderStartedEvent.CardSecurityNumber, | ||||
|                                            orderStartedEvent.CardHolderName, | ||||
|                                            orderStartedEvent.CardExpiration, | ||||
|                                            orderStartedEvent.Order.Id); | ||||
|         await _buyerRepository.UnitOfWork | ||||
|             .SaveEntitiesAsync(cancellationToken); | ||||
| 
 | ||||
|             var buyerUpdated = buyerOriginallyExisted ? | ||||
|                 _buyerRepository.Update(buyer) : | ||||
|                 _buyerRepository.Add(buyer); | ||||
| 
 | ||||
|             await _buyerRepository.UnitOfWork | ||||
|                 .SaveEntitiesAsync(cancellationToken); | ||||
| 
 | ||||
|             var orderStatusChangedTosubmittedIntegrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(orderStartedEvent.Order.Id, orderStartedEvent.Order.OrderStatus.Name, buyer.Name); | ||||
|             await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedTosubmittedIntegrationEvent); | ||||
|             _logger.CreateLogger<ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler>() | ||||
|                 .LogTrace("Buyer {BuyerId} and related payment method were validated or updated for orderId: {OrderId}.", | ||||
|                     buyerUpdated.Id, orderStartedEvent.Order.Id); | ||||
|         } | ||||
|         var orderStatusChangedTosubmittedIntegrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(orderStartedEvent.Order.Id, orderStartedEvent.Order.OrderStatus.Name, buyer.Name); | ||||
|         await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedTosubmittedIntegrationEvent); | ||||
|         _logger.CreateLogger<ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler>() | ||||
|             .LogTrace("Buyer {BuyerId} and related payment method were validated or updated for orderId: {OrderId}.", | ||||
|                 buyerUpdated.Id, orderStartedEvent.Order.Id); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,47 +1,35 @@ | ||||
| namespace Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed; | ||||
| 
 | ||||
| public class OrderStatusChangedToStockConfirmedDomainEventHandler | ||||
|                 : INotificationHandler<OrderStatusChangedToStockConfirmedDomainEvent> | ||||
| { | ||||
|     using Domain.Events; | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Ordering.API.Application.IntegrationEvents; | ||||
|     using Ordering.API.Application.IntegrationEvents.Events; | ||||
|     using System; | ||||
|     using System.Threading; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IOrderRepository _orderRepository; | ||||
|     private readonly IBuyerRepository _buyerRepository; | ||||
|     private readonly ILoggerFactory _logger; | ||||
|     private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|     public class OrderStatusChangedToStockConfirmedDomainEventHandler | ||||
|                    : INotificationHandler<OrderStatusChangedToStockConfirmedDomainEvent> | ||||
|     public OrderStatusChangedToStockConfirmedDomainEventHandler( | ||||
|         IOrderRepository orderRepository, | ||||
|         IBuyerRepository buyerRepository, | ||||
|         ILoggerFactory logger, | ||||
|         IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|     { | ||||
|         private readonly IOrderRepository _orderRepository; | ||||
|         private readonly IBuyerRepository _buyerRepository; | ||||
|         private readonly ILoggerFactory _logger; | ||||
|         private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; | ||||
| 
 | ||||
|         public OrderStatusChangedToStockConfirmedDomainEventHandler( | ||||
|             IOrderRepository orderRepository, | ||||
|             IBuyerRepository buyerRepository, | ||||
|             ILoggerFactory logger, | ||||
|             IOrderingIntegrationEventService orderingIntegrationEventService) | ||||
|         { | ||||
|             _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|             _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|             _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|         } | ||||
| 
 | ||||
|         public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent, CancellationToken cancellationToken) | ||||
|         { | ||||
|             _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()); | ||||
| 
 | ||||
|             var orderStatusChangedToStockConfirmedIntegrationEvent = new OrderStatusChangedToStockConfirmedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | ||||
|             await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToStockConfirmedIntegrationEvent); | ||||
|         } | ||||
|         _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); | ||||
|         _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         _orderingIntegrationEventService = orderingIntegrationEventService; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent, CancellationToken cancellationToken) | ||||
|     { | ||||
|         _logger.CreateLogger<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()); | ||||
| 
 | ||||
|         var orderStatusChangedToStockConfirmedIntegrationEvent = new OrderStatusChangedToStockConfirmedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name); | ||||
|         await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedToStockConfirmedIntegrationEvent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,52 +1,42 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.Commands; | ||||
| using Ordering.API.Application.IntegrationEvents.Events; | ||||
| using Serilog.Context; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents.EventHandling | ||||
| public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent> | ||||
| { | ||||
|     public class GracePeriodConfirmedIntegrationEventHandler : IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent> | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly ILogger<GracePeriodConfirmedIntegrationEventHandler> _logger; | ||||
| 
 | ||||
|     public GracePeriodConfirmedIntegrationEventHandler( | ||||
|         IMediator mediator, | ||||
|         ILogger<GracePeriodConfirmedIntegrationEventHandler> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly ILogger<GracePeriodConfirmedIntegrationEventHandler> _logger; | ||||
|         _mediator = mediator; | ||||
|         _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public GracePeriodConfirmedIntegrationEventHandler( | ||||
|             IMediator mediator, | ||||
|             ILogger<GracePeriodConfirmedIntegrationEventHandler> logger) | ||||
|     /// <summary> | ||||
|     /// Event handler which confirms that the grace period | ||||
|     /// has been completed and order will not initially be cancelled. | ||||
|     /// Therefore, the order process continues for validation.  | ||||
|     /// </summary> | ||||
|     /// <param name="event">        | ||||
|     /// </param> | ||||
|     /// <returns></returns> | ||||
|     public async Task Handle(GracePeriodConfirmedIntegrationEvent @event) | ||||
|     { | ||||
|         using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|         { | ||||
|             _mediator = mediator; | ||||
|             _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|             _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Event handler which confirms that the grace period | ||||
|         /// has been completed and order will not initially be cancelled. | ||||
|         /// Therefore, the order process continues for validation.  | ||||
|         /// </summary> | ||||
|         /// <param name="event">        | ||||
|         /// </param> | ||||
|         /// <returns></returns> | ||||
|         public async Task Handle(GracePeriodConfirmedIntegrationEvent @event) | ||||
|         { | ||||
|             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); | ||||
| 
 | ||||
|                 var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId); | ||||
|             _logger.LogInformation( | ||||
|                 "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                 command.GetGenericTypeName(), | ||||
|                 nameof(command.OrderNumber), | ||||
|                 command.OrderNumber, | ||||
|                 command); | ||||
| 
 | ||||
|                 _logger.LogInformation( | ||||
|                     "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                     command.GetGenericTypeName(), | ||||
|                     nameof(command.OrderNumber), | ||||
|                     command.OrderNumber, | ||||
|                     command); | ||||
| 
 | ||||
|                 await _mediator.Send(command); | ||||
|             } | ||||
|             await _mediator.Send(command); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,46 +1,35 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.EventHandling | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; | ||||
| 
 | ||||
| public class OrderPaymentFailedIntegrationEventHandler : | ||||
|     IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent> | ||||
| { | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Ordering.API.Application.Commands; | ||||
|     using Ordering.API.Application.IntegrationEvents.Events; | ||||
|     using Serilog.Context; | ||||
|     using System; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly ILogger<OrderPaymentFailedIntegrationEventHandler> _logger; | ||||
| 
 | ||||
|     public class OrderPaymentFailedIntegrationEventHandler : | ||||
|         IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent> | ||||
|     public OrderPaymentFailedIntegrationEventHandler( | ||||
|         IMediator mediator, | ||||
|         ILogger<OrderPaymentFailedIntegrationEventHandler> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly ILogger<OrderPaymentFailedIntegrationEventHandler> _logger; | ||||
|         _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public OrderPaymentFailedIntegrationEventHandler( | ||||
|             IMediator mediator, | ||||
|             ILogger<OrderPaymentFailedIntegrationEventHandler> logger) | ||||
|     public async Task Handle(OrderPaymentFailedIntegrationEvent @event) | ||||
|     { | ||||
|         using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|         { | ||||
|             _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|             _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | ||||
| 
 | ||||
|         public async Task Handle(OrderPaymentFailedIntegrationEvent @event) | ||||
|         { | ||||
|             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); | ||||
| 
 | ||||
|                 var command = new CancelOrderCommand(@event.OrderId); | ||||
|             _logger.LogInformation( | ||||
|                 "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                 command.GetGenericTypeName(), | ||||
|                 nameof(command.OrderNumber), | ||||
|                 command.OrderNumber, | ||||
|                 command); | ||||
| 
 | ||||
|                 _logger.LogInformation( | ||||
|                     "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                     command.GetGenericTypeName(), | ||||
|                     nameof(command.OrderNumber), | ||||
|                     command.OrderNumber, | ||||
|                     command); | ||||
| 
 | ||||
|                 await _mediator.Send(command); | ||||
|             } | ||||
|             await _mediator.Send(command); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,46 +1,35 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.EventHandling | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; | ||||
|     | ||||
| public class OrderPaymentSucceededIntegrationEventHandler : | ||||
|     IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent> | ||||
| { | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Ordering.API.Application.Commands; | ||||
|     using Ordering.API.Application.IntegrationEvents.Events; | ||||
|     using Serilog.Context; | ||||
|     using System; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly ILogger<OrderPaymentSucceededIntegrationEventHandler> _logger; | ||||
| 
 | ||||
|     public class OrderPaymentSucceededIntegrationEventHandler : | ||||
|         IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent> | ||||
|     public OrderPaymentSucceededIntegrationEventHandler( | ||||
|         IMediator mediator, | ||||
|         ILogger<OrderPaymentSucceededIntegrationEventHandler> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly ILogger<OrderPaymentSucceededIntegrationEventHandler> _logger; | ||||
|         _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public OrderPaymentSucceededIntegrationEventHandler( | ||||
|             IMediator mediator, | ||||
|             ILogger<OrderPaymentSucceededIntegrationEventHandler> logger) | ||||
|     public async Task Handle(OrderPaymentSucceededIntegrationEvent @event) | ||||
|     { | ||||
|         using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|         { | ||||
|             _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|             _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | ||||
| 
 | ||||
|         public async Task Handle(OrderPaymentSucceededIntegrationEvent @event) | ||||
|         { | ||||
|             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); | ||||
| 
 | ||||
|                 var command = new SetPaidOrderStatusCommand(@event.OrderId); | ||||
|             _logger.LogInformation( | ||||
|                 "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                 command.GetGenericTypeName(), | ||||
|                 nameof(command.OrderNumber), | ||||
|                 command.OrderNumber, | ||||
|                 command); | ||||
| 
 | ||||
|                 _logger.LogInformation( | ||||
|                     "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                     command.GetGenericTypeName(), | ||||
|                     nameof(command.OrderNumber), | ||||
|                     command.OrderNumber, | ||||
|                     command); | ||||
| 
 | ||||
|                 await _mediator.Send(command); | ||||
|             } | ||||
|             await _mediator.Send(command); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,46 +1,35 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.EventHandling | ||||
| namespace Ordering.API.Application.IntegrationEvents.EventHandling; | ||||
| 
 | ||||
| public class OrderStockConfirmedIntegrationEventHandler : | ||||
|     IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent> | ||||
| { | ||||
|     using Events; | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Ordering.API.Application.Commands; | ||||
|     using Serilog.Context; | ||||
|     using System; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly ILogger<OrderStockConfirmedIntegrationEventHandler> _logger; | ||||
| 
 | ||||
|     public class OrderStockConfirmedIntegrationEventHandler : | ||||
|         IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent> | ||||
|     public OrderStockConfirmedIntegrationEventHandler( | ||||
|         IMediator mediator, | ||||
|         ILogger<OrderStockConfirmedIntegrationEventHandler> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly ILogger<OrderStockConfirmedIntegrationEventHandler> _logger; | ||||
|         _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public OrderStockConfirmedIntegrationEventHandler( | ||||
|             IMediator mediator, | ||||
|             ILogger<OrderStockConfirmedIntegrationEventHandler> logger) | ||||
|     public async Task Handle(OrderStockConfirmedIntegrationEvent @event) | ||||
|     { | ||||
|         using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|         { | ||||
|             _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|             _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | ||||
| 
 | ||||
|         public async Task Handle(OrderStockConfirmedIntegrationEvent @event) | ||||
|         { | ||||
|             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); | ||||
| 
 | ||||
|                 var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId); | ||||
|             _logger.LogInformation( | ||||
|                 "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                 command.GetGenericTypeName(), | ||||
|                 nameof(command.OrderNumber), | ||||
|                 command.OrderNumber, | ||||
|                 command); | ||||
| 
 | ||||
|                 _logger.LogInformation( | ||||
|                     "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                     command.GetGenericTypeName(), | ||||
|                     nameof(command.OrderNumber), | ||||
|                     command.OrderNumber, | ||||
|                     command); | ||||
| 
 | ||||
|                 await _mediator.Send(command); | ||||
|             } | ||||
|             await _mediator.Send(command); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,50 +1,38 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.EventHandling | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; | ||||
| public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler<OrderStockRejectedIntegrationEvent> | ||||
| { | ||||
|     using Events; | ||||
|     using MediatR; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Ordering.API.Application.Commands; | ||||
|     using Serilog.Context; | ||||
|     using System.Linq; | ||||
|     using System.Threading.Tasks; | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly ILogger<OrderStockRejectedIntegrationEventHandler> _logger; | ||||
| 
 | ||||
|     public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler<OrderStockRejectedIntegrationEvent> | ||||
|     public OrderStockRejectedIntegrationEventHandler( | ||||
|         IMediator mediator, | ||||
|         ILogger<OrderStockRejectedIntegrationEventHandler> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly ILogger<OrderStockRejectedIntegrationEventHandler> _logger; | ||||
|         _mediator = mediator; | ||||
|         _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public OrderStockRejectedIntegrationEventHandler( | ||||
|             IMediator mediator, | ||||
|             ILogger<OrderStockRejectedIntegrationEventHandler> logger) | ||||
|     public async Task Handle(OrderStockRejectedIntegrationEvent @event) | ||||
|     { | ||||
|         using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|         { | ||||
|             _mediator = mediator; | ||||
|             _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|             _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | ||||
| 
 | ||||
|         public async Task Handle(OrderStockRejectedIntegrationEvent @event) | ||||
|         { | ||||
|             using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|             { | ||||
|                 _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | ||||
|             var orderStockRejectedItems = @event.OrderStockItems | ||||
|                 .FindAll(c => !c.HasStock) | ||||
|                 .Select(c => c.ProductId) | ||||
|                 .ToList(); | ||||
| 
 | ||||
|                 var orderStockRejectedItems = @event.OrderStockItems | ||||
|                     .FindAll(c => !c.HasStock) | ||||
|                     .Select(c => c.ProductId) | ||||
|                     .ToList(); | ||||
|             var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems); | ||||
| 
 | ||||
|                 var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems); | ||||
|             _logger.LogInformation( | ||||
|                 "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                 command.GetGenericTypeName(), | ||||
|                 nameof(command.OrderNumber), | ||||
|                 command.OrderNumber, | ||||
|                 command); | ||||
| 
 | ||||
|                 _logger.LogInformation( | ||||
|                     "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                     command.GetGenericTypeName(), | ||||
|                     nameof(command.OrderNumber), | ||||
|                     command.OrderNumber, | ||||
|                     command); | ||||
| 
 | ||||
|                 await _mediator.Send(command); | ||||
|             } | ||||
|             await _mediator.Send(command); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,80 +1,69 @@ | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.IntegrationEvents.Events; | ||||
| using Serilog.Context; | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents.EventHandling | ||||
| public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent> | ||||
| { | ||||
|     public class UserCheckoutAcceptedIntegrationEventHandler : IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent> | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly ILogger<UserCheckoutAcceptedIntegrationEventHandler> _logger; | ||||
| 
 | ||||
|     public UserCheckoutAcceptedIntegrationEventHandler( | ||||
|         IMediator mediator, | ||||
|         ILogger<UserCheckoutAcceptedIntegrationEventHandler> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly ILogger<UserCheckoutAcceptedIntegrationEventHandler> _logger; | ||||
|         _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public UserCheckoutAcceptedIntegrationEventHandler( | ||||
|             IMediator mediator, | ||||
|             ILogger<UserCheckoutAcceptedIntegrationEventHandler> logger) | ||||
|     /// <summary> | ||||
|     /// Integration event handler which starts the create order process | ||||
|     /// </summary> | ||||
|     /// <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 @event) | ||||
|     { | ||||
|         using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|         { | ||||
|             _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|             _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Integration event handler which starts the create order process | ||||
|         /// </summary> | ||||
|         /// <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 @event) | ||||
|         { | ||||
|             using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) | ||||
|             var result = false; | ||||
| 
 | ||||
|             if (@event.RequestId != Guid.Empty) | ||||
|             { | ||||
|                 _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)) | ||||
|                 { | ||||
|                     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) | ||||
|                     { | ||||
|                         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); | ||||
|                         } | ||||
|                         _logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     _logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 _logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,10 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;     | ||||
| 
 | ||||
| public record GracePeriodConfirmedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     public int OrderId { get; } | ||||
| 
 | ||||
|     public record GracePeriodConfirmedIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
| 
 | ||||
|         public GracePeriodConfirmedIntegrationEvent(int orderId) => | ||||
|             OrderId = orderId; | ||||
|     } | ||||
|     public GracePeriodConfirmedIntegrationEvent(int orderId) => | ||||
|         OrderId = orderId; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,11 +1,8 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| public record OrderPaymentFailedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     public int OrderId { get; } | ||||
| 
 | ||||
|     public record OrderPaymentFailedIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
| 
 | ||||
|         public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId; | ||||
|     } | ||||
| } | ||||
|     public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId; | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,8 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| public record OrderPaymentSucceededIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     public int OrderId { get; } | ||||
| 
 | ||||
|     public record OrderPaymentSucceededIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
| 
 | ||||
|         public OrderPaymentSucceededIntegrationEvent(int orderId) => OrderId = orderId; | ||||
|     } | ||||
| } | ||||
|     public OrderPaymentSucceededIntegrationEvent(int orderId) => OrderId = orderId; | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,12 @@ | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| // Integration Events notes:  | ||||
| // An Event is “something that has happened in the past”, therefore its name has to be    | ||||
| // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. | ||||
| public record OrderStartedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     // Integration Events notes:  | ||||
|     // An Event is “something that has happened in the past”, therefore its name has to be    | ||||
|     // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. | ||||
|     public record OrderStartedIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public string UserId { get; init; } | ||||
|     public string UserId { get; init; } | ||||
| 
 | ||||
|         public OrderStartedIntegrationEvent(string userId) | ||||
|             => UserId = userId; | ||||
|     } | ||||
| } | ||||
|     public OrderStartedIntegrationEvent(string userId) | ||||
|         => UserId = userId; | ||||
| } | ||||
|  | ||||
| @ -1,34 +1,30 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
|    | ||||
| public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     using System.Collections.Generic; | ||||
|     public int OrderId { get; } | ||||
|     public string OrderStatus { get; } | ||||
|     public string BuyerName { get; } | ||||
|     public IEnumerable<OrderStockItem> OrderStockItems { get; } | ||||
| 
 | ||||
|     public record OrderStatusChangedToAwaitingValidationIntegrationEvent : IntegrationEvent | ||||
|     public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName, | ||||
|         IEnumerable<OrderStockItem> orderStockItems) | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
|         public string OrderStatus { get; } | ||||
|         public string BuyerName { get; } | ||||
|         public IEnumerable<OrderStockItem> OrderStockItems { get; } | ||||
| 
 | ||||
|         public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName, | ||||
|             IEnumerable<OrderStockItem> orderStockItems) | ||||
|         { | ||||
|             OrderId = orderId; | ||||
|             OrderStockItems = orderStockItems; | ||||
|             OrderStatus = orderStatus; | ||||
|             BuyerName = buyerName; | ||||
|         } | ||||
|         OrderId = orderId; | ||||
|         OrderStockItems = orderStockItems; | ||||
|         OrderStatus = orderStatus; | ||||
|         BuyerName = buyerName; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     public record OrderStockItem | ||||
| public record OrderStockItem | ||||
| { | ||||
|     public int ProductId { get; } | ||||
|     public int Units { get; } | ||||
| 
 | ||||
|     public OrderStockItem(int productId, int units) | ||||
|     { | ||||
|         public int ProductId { get; } | ||||
|         public int Units { get; } | ||||
| 
 | ||||
|         public OrderStockItem(int productId, int units) | ||||
|         { | ||||
|             ProductId = productId; | ||||
|             Units = units; | ||||
|         } | ||||
|         ProductId = productId; | ||||
|         Units = units; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,18 +1,15 @@ | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| public record OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     public record OrderStatusChangedToCancelledIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
|         public string OrderStatus { get; } | ||||
|         public string BuyerName { get; } | ||||
|     public int OrderId { get; } | ||||
|     public string OrderStatus { get; } | ||||
|     public string BuyerName { get; } | ||||
| 
 | ||||
|         public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|         { | ||||
|             OrderId = orderId; | ||||
|             OrderStatus = orderStatus; | ||||
|             BuyerName = buyerName; | ||||
|         } | ||||
|     public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|     { | ||||
|         OrderId = orderId; | ||||
|         OrderStatus = orderStatus; | ||||
|         BuyerName = buyerName; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,24 +1,21 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     using System.Collections.Generic; | ||||
|     public int OrderId { get; } | ||||
|     public string OrderStatus { get; } | ||||
|     public string BuyerName { get; } | ||||
|     public IEnumerable<OrderStockItem> OrderStockItems { get; } | ||||
| 
 | ||||
|     public record OrderStatusChangedToPaidIntegrationEvent : IntegrationEvent | ||||
|     public OrderStatusChangedToPaidIntegrationEvent(int orderId, | ||||
|         string orderStatus, | ||||
|         string buyerName, | ||||
|         IEnumerable<OrderStockItem> orderStockItems) | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
|         public string OrderStatus { get; } | ||||
|         public string BuyerName { get; } | ||||
|         public IEnumerable<OrderStockItem> OrderStockItems { get; } | ||||
| 
 | ||||
|         public OrderStatusChangedToPaidIntegrationEvent(int orderId, | ||||
|             string orderStatus, | ||||
|             string buyerName, | ||||
|             IEnumerable<OrderStockItem> orderStockItems) | ||||
|         { | ||||
|             OrderId = orderId; | ||||
|             OrderStockItems = orderStockItems; | ||||
|             OrderStatus = orderStatus; | ||||
|             BuyerName = buyerName; | ||||
|         } | ||||
|         OrderId = orderId; | ||||
|         OrderStockItems = orderStockItems; | ||||
|         OrderStatus = orderStatus; | ||||
|         BuyerName = buyerName; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,18 +1,15 @@ | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     public record OrderStatusChangedToShippedIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
|         public string OrderStatus { get; } | ||||
|         public string BuyerName { get; } | ||||
|     public int OrderId { get; } | ||||
|     public string OrderStatus { get; } | ||||
|     public string BuyerName { get; } | ||||
| 
 | ||||
|         public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|         { | ||||
|             OrderId = orderId; | ||||
|             OrderStatus = orderStatus; | ||||
|             BuyerName = buyerName; | ||||
|         } | ||||
|     public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|     { | ||||
|         OrderId = orderId; | ||||
|         OrderStatus = orderStatus; | ||||
|         BuyerName = buyerName; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,18 +1,15 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     public int OrderId { get; } | ||||
|     public string OrderStatus { get; } | ||||
|     public string BuyerName { get; } | ||||
| 
 | ||||
|     public record OrderStatusChangedToStockConfirmedIntegrationEvent : IntegrationEvent | ||||
|     public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
|         public string OrderStatus { get; } | ||||
|         public string BuyerName { get; } | ||||
| 
 | ||||
|         public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|         { | ||||
|             OrderId = orderId; | ||||
|             OrderStatus = orderStatus; | ||||
|             BuyerName = buyerName; | ||||
|         } | ||||
|         OrderId = orderId; | ||||
|         OrderStatus = orderStatus; | ||||
|         BuyerName = buyerName; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,18 +1,15 @@ | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| public record OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     public record OrderStatusChangedToSubmittedIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
|         public string OrderStatus { get; } | ||||
|         public string BuyerName { get; } | ||||
|     public int OrderId { get; } | ||||
|     public string OrderStatus { get; } | ||||
|     public string BuyerName { get; } | ||||
| 
 | ||||
|         public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|         { | ||||
|             OrderId = orderId; | ||||
|             OrderStatus = orderStatus; | ||||
|             BuyerName = buyerName; | ||||
|         } | ||||
|     public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName) | ||||
|     { | ||||
|         OrderId = orderId; | ||||
|         OrderStatus = orderStatus; | ||||
|         BuyerName = buyerName; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,11 +1,8 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| public record OrderStockConfirmedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     public int OrderId { get; } | ||||
| 
 | ||||
|     public record OrderStockConfirmedIntegrationEvent : IntegrationEvent | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
| 
 | ||||
|         public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId; | ||||
|     } | ||||
| } | ||||
|     public OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId; | ||||
| } | ||||
|  | ||||
| @ -1,31 +1,27 @@ | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;     | ||||
| 
 | ||||
| public record OrderStockRejectedIntegrationEvent : IntegrationEvent | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
|     using System.Collections.Generic; | ||||
|     public int OrderId { get; } | ||||
| 
 | ||||
|     public record OrderStockRejectedIntegrationEvent : IntegrationEvent | ||||
|     public List<ConfirmedOrderStockItem> OrderStockItems { get; } | ||||
| 
 | ||||
|     public OrderStockRejectedIntegrationEvent(int orderId, | ||||
|         List<ConfirmedOrderStockItem> orderStockItems) | ||||
|     { | ||||
|         public int OrderId { get; } | ||||
| 
 | ||||
|         public List<ConfirmedOrderStockItem> OrderStockItems { get; } | ||||
| 
 | ||||
|         public OrderStockRejectedIntegrationEvent(int orderId, | ||||
|             List<ConfirmedOrderStockItem> orderStockItems) | ||||
|         { | ||||
|             OrderId = orderId; | ||||
|             OrderStockItems = orderStockItems; | ||||
|         } | ||||
|         OrderId = orderId; | ||||
|         OrderStockItems = orderStockItems; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     public record ConfirmedOrderStockItem | ||||
| public record ConfirmedOrderStockItem | ||||
| { | ||||
|     public int ProductId { get; } | ||||
|     public bool HasStock { get; } | ||||
| 
 | ||||
|     public ConfirmedOrderStockItem(int productId, bool hasStock) | ||||
|     { | ||||
|         public int ProductId { get; } | ||||
|         public bool HasStock { get; } | ||||
| 
 | ||||
|         public ConfirmedOrderStockItem(int productId, bool hasStock) | ||||
|         { | ||||
|             ProductId = productId; | ||||
|             HasStock = hasStock; | ||||
|         } | ||||
|         ProductId = productId; | ||||
|         HasStock = hasStock; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,62 +1,57 @@ | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
| using Ordering.API.Application.Models; | ||||
| using System; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents.Events | ||||
| { | ||||
|     public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent | ||||
|     {         | ||||
|         public string UserId { get; } | ||||
| public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent | ||||
| {         | ||||
|     public string UserId { get; } | ||||
|          | ||||
|         public string UserName { get; } | ||||
|     public string UserName { get; } | ||||
|          | ||||
|         public string City { get; set; } | ||||
|     public string City { get; set; } | ||||
|          | ||||
|         public string Street { get; set; } | ||||
|     public string Street { get; set; } | ||||
|          | ||||
|         public string State { get; set; } | ||||
|     public string State { get; set; } | ||||
| 
 | ||||
|         public string Country { get; set; } | ||||
|     public string Country { get; set; } | ||||
| 
 | ||||
|         public string ZipCode { get; set; } | ||||
|     public string ZipCode { get; set; } | ||||
| 
 | ||||
|         public string CardNumber { get; set; } | ||||
|     public string CardNumber { get; set; } | ||||
| 
 | ||||
|         public string CardHolderName { get; set; } | ||||
|     public string CardHolderName { get; set; } | ||||
| 
 | ||||
|         public DateTime CardExpiration { get; set; } | ||||
|     public DateTime CardExpiration { get; set; } | ||||
| 
 | ||||
|         public string CardSecurityNumber { get; set; } | ||||
|     public string CardSecurityNumber { get; set; } | ||||
| 
 | ||||
|         public int CardTypeId { get; set; } | ||||
|     public int CardTypeId { get; set; } | ||||
| 
 | ||||
|         public string Buyer { get; set; } | ||||
|     public string Buyer { get; set; } | ||||
| 
 | ||||
|         public Guid RequestId { get; set; } | ||||
|     public Guid RequestId { get; set; } | ||||
| 
 | ||||
|         public CustomerBasket Basket { get; } | ||||
| 
 | ||||
|         public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street, | ||||
|             string state, string country, string zipCode, string cardNumber, string cardHolderName, | ||||
|             DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId, | ||||
|             CustomerBasket basket) | ||||
|         { | ||||
|             UserId = userId; | ||||
|             City = city; | ||||
|             Street = street; | ||||
|             State = state; | ||||
|             Country = country; | ||||
|             ZipCode = zipCode; | ||||
|             CardNumber = cardNumber; | ||||
|             CardHolderName = cardHolderName; | ||||
|             CardExpiration = cardExpiration; | ||||
|             CardSecurityNumber = cardSecurityNumber; | ||||
|             CardTypeId = cardTypeId; | ||||
|             Buyer = buyer; | ||||
|             Basket = basket; | ||||
|             RequestId = requestId; | ||||
|             UserName = userName; | ||||
|         } | ||||
|     public CustomerBasket Basket { get; } | ||||
| 
 | ||||
|     public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street, | ||||
|         string state, string country, string zipCode, string cardNumber, string cardHolderName, | ||||
|         DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId, | ||||
|         CustomerBasket basket) | ||||
|     { | ||||
|         UserId = userId; | ||||
|         City = city; | ||||
|         Street = street; | ||||
|         State = state; | ||||
|         Country = country; | ||||
|         ZipCode = zipCode; | ||||
|         CardNumber = cardNumber; | ||||
|         CardHolderName = cardHolderName; | ||||
|         CardExpiration = cardExpiration; | ||||
|         CardSecurityNumber = cardSecurityNumber; | ||||
|         CardTypeId = cardTypeId; | ||||
|         Buyer = buyer; | ||||
|         Basket = basket; | ||||
|         RequestId = requestId; | ||||
|         UserName = userName; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,7 @@ | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
| using System; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents | ||||
| public interface IOrderingIntegrationEventService | ||||
| { | ||||
|     public interface IOrderingIntegrationEventService | ||||
|     { | ||||
|         Task PublishEventsThroughEventBusAsync(Guid transactionId); | ||||
|         Task AddAndSaveEventAsync(IntegrationEvent evt); | ||||
|     } | ||||
|     Task PublishEventsThroughEventBusAsync(Guid transactionId); | ||||
|     Task AddAndSaveEventAsync(IntegrationEvent evt); | ||||
| } | ||||
|  | ||||
| @ -1,65 +1,53 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System; | ||||
| using System.Data.Common; | ||||
| using System.Threading.Tasks; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents; | ||||
| 
 | ||||
| namespace Ordering.API.Application.IntegrationEvents | ||||
| public class OrderingIntegrationEventService : IOrderingIntegrationEventService | ||||
| { | ||||
|     public class OrderingIntegrationEventService : IOrderingIntegrationEventService | ||||
|     private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory; | ||||
|     private readonly IEventBus _eventBus; | ||||
|     private readonly OrderingContext _orderingContext; | ||||
|     private readonly IIntegrationEventLogService _eventLogService; | ||||
|     private readonly ILogger<OrderingIntegrationEventService> _logger; | ||||
| 
 | ||||
|     public OrderingIntegrationEventService(IEventBus eventBus, | ||||
|         OrderingContext orderingContext, | ||||
|         IntegrationEventLogContext eventLogContext, | ||||
|         Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory, | ||||
|         ILogger<OrderingIntegrationEventService> logger) | ||||
|     { | ||||
|         private readonly Func<DbConnection, IIntegrationEventLogService> _integrationEventLogServiceFactory; | ||||
|         private readonly IEventBus _eventBus; | ||||
|         private readonly OrderingContext _orderingContext; | ||||
|         private readonly IIntegrationEventLogService _eventLogService; | ||||
|         private readonly ILogger<OrderingIntegrationEventService> _logger; | ||||
|         _orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext)); | ||||
|         _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); | ||||
|         _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); | ||||
|         _eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection()); | ||||
|         _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|     } | ||||
| 
 | ||||
|         public OrderingIntegrationEventService(IEventBus eventBus, | ||||
|             OrderingContext orderingContext, | ||||
|             IntegrationEventLogContext eventLogContext, | ||||
|             Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory, | ||||
|             ILogger<OrderingIntegrationEventService> logger) | ||||
|     public async Task PublishEventsThroughEventBusAsync(Guid transactionId) | ||||
|     { | ||||
|         var pendingLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync(transactionId); | ||||
| 
 | ||||
|         foreach (var logEvt in pendingLogEvents) | ||||
|         { | ||||
|             _orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext)); | ||||
|             _integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory)); | ||||
|             _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); | ||||
|             _eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection()); | ||||
|             _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
|         } | ||||
|             _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", logEvt.EventId, Program.AppName, logEvt.IntegrationEvent); | ||||
| 
 | ||||
|         public async Task PublishEventsThroughEventBusAsync(Guid transactionId) | ||||
|         { | ||||
|             var pendingLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync(transactionId); | ||||
| 
 | ||||
|             foreach (var logEvt in pendingLogEvents) | ||||
|             try | ||||
|             { | ||||
|                 _logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", logEvt.EventId, Program.AppName, logEvt.IntegrationEvent); | ||||
|                 await _eventLogService.MarkEventAsInProgressAsync(logEvt.EventId); | ||||
|                 _eventBus.Publish(logEvt.IntegrationEvent); | ||||
|                 await _eventLogService.MarkEventAsPublishedAsync(logEvt.EventId); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 _logger.LogError(ex, "ERROR publishing integration event: {IntegrationEventId} from {AppName}", logEvt.EventId, Program.AppName); | ||||
| 
 | ||||
|                 try | ||||
|                 { | ||||
|                     await _eventLogService.MarkEventAsInProgressAsync(logEvt.EventId); | ||||
|                     _eventBus.Publish(logEvt.IntegrationEvent); | ||||
|                     await _eventLogService.MarkEventAsPublishedAsync(logEvt.EventId); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     _logger.LogError(ex, "ERROR publishing integration event: {IntegrationEventId} from {AppName}", logEvt.EventId, Program.AppName); | ||||
| 
 | ||||
|                     await _eventLogService.MarkEventAsFailedAsync(logEvt.EventId); | ||||
|                 } | ||||
|                 await _eventLogService.MarkEventAsFailedAsync(logEvt.EventId); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|         public async Task AddAndSaveEventAsync(IntegrationEvent evt) | ||||
|         { | ||||
|             _logger.LogInformation("----- Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, evt); | ||||
|     public async Task AddAndSaveEventAsync(IntegrationEvent evt) | ||||
|     { | ||||
|         _logger.LogInformation("----- Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, evt); | ||||
| 
 | ||||
|             await _eventLogService.SaveEventAsync(evt, _orderingContext.GetCurrentTransaction()); | ||||
|         } | ||||
|         await _eventLogService.SaveEventAsync(evt, _orderingContext.GetCurrentTransaction()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| namespace Ordering.API.Application.Models | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; | ||||
| 
 | ||||
| public class BasketItem | ||||
| { | ||||
|     public class BasketItem | ||||
|     { | ||||
|         public string Id { get; init; } | ||||
|         public int ProductId { get; init; } | ||||
|         public string ProductName { get; init; } | ||||
|         public decimal UnitPrice { get; init; } | ||||
|         public decimal OldUnitPrice { get; init; } | ||||
|         public int Quantity { get; init; } | ||||
|         public string PictureUrl { get; init; } | ||||
|     } | ||||
|     public string Id { get; init; } | ||||
|     public int ProductId { get; init; } | ||||
|     public string ProductName { get; init; } | ||||
|     public decimal UnitPrice { get; init; } | ||||
|     public decimal OldUnitPrice { get; init; } | ||||
|     public int Quantity { get; init; } | ||||
|     public string PictureUrl { get; init; } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,16 +1,13 @@ | ||||
| using System.Collections.Generic; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Models | ||||
| public class CustomerBasket | ||||
| { | ||||
|     public class CustomerBasket | ||||
|     { | ||||
|         public string BuyerId { get; set; } | ||||
|         public List<BasketItem> Items { get; set; } | ||||
|     public string BuyerId { get; set; } | ||||
|     public List<BasketItem> Items { get; set; } | ||||
| 
 | ||||
|         public CustomerBasket(string buyerId, List<BasketItem> items) | ||||
|         { | ||||
|             BuyerId = buyerId; | ||||
|             Items = items; | ||||
|         } | ||||
|     public CustomerBasket(string buyerId, List<BasketItem> items) | ||||
|     { | ||||
|         BuyerId = buyerId; | ||||
|         Items = items; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,10 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; | ||||
| 
 | ||||
| public interface IOrderQueries | ||||
| { | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Threading.Tasks; | ||||
|     Task<Order> GetOrderAsync(int id); | ||||
| 
 | ||||
|     public interface IOrderQueries | ||||
|     { | ||||
|         Task<Order> GetOrderAsync(int id); | ||||
|     Task<IEnumerable<OrderSummary>> GetOrdersFromUserAsync(Guid userId); | ||||
| 
 | ||||
|         Task<IEnumerable<OrderSummary>> GetOrdersFromUserAsync(Guid userId); | ||||
| 
 | ||||
|         Task<IEnumerable<CardType>> GetCardTypesAsync(); | ||||
|     } | ||||
|     Task<IEnumerable<CardType>> GetCardTypesAsync(); | ||||
| } | ||||
|  | ||||
| @ -1,105 +1,98 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; | ||||
|      | ||||
| public class OrderQueries | ||||
|     : IOrderQueries | ||||
| { | ||||
|     using Dapper; | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Data.SqlClient; | ||||
|     using System.Threading.Tasks; | ||||
|     private string _connectionString = string.Empty; | ||||
| 
 | ||||
|     public class OrderQueries | ||||
|         : IOrderQueries | ||||
|     public OrderQueries(string constr) | ||||
|     { | ||||
|         private string _connectionString = string.Empty; | ||||
|         _connectionString = !string.IsNullOrWhiteSpace(constr) ? constr : throw new ArgumentNullException(nameof(constr)); | ||||
|     } | ||||
| 
 | ||||
|         public OrderQueries(string constr) | ||||
| 
 | ||||
|     public async Task<Order> GetOrderAsync(int id) | ||||
|     { | ||||
|         using (var connection = new SqlConnection(_connectionString)) | ||||
|         { | ||||
|             _connectionString = !string.IsNullOrWhiteSpace(constr) ? constr : throw new ArgumentNullException(nameof(constr)); | ||||
|         } | ||||
|             connection.Open(); | ||||
| 
 | ||||
|             var result = await connection.QueryAsync<dynamic>( | ||||
|                 @"select o.[Id] as ordernumber,o.OrderDate as date, o.Description as description,
 | ||||
|                     o.Address_City as city, o.Address_Country as country, o.Address_State as state, o.Address_Street as street, o.Address_ZipCode as zipcode, | ||||
|                     os.Name as status,  | ||||
|                     oi.ProductName as productname, oi.Units as units, oi.UnitPrice as unitprice, oi.PictureUrl as pictureurl | ||||
|                     FROM ordering.Orders o | ||||
|                     LEFT JOIN ordering.Orderitems oi ON o.Id = oi.orderid  | ||||
|                     LEFT JOIN ordering.orderstatus os on o.OrderStatusId = os.Id | ||||
|                     WHERE o.Id=@id"
 | ||||
|                     , new { id } | ||||
|                 ); | ||||
| 
 | ||||
|         public async Task<Order> GetOrderAsync(int id) | ||||
|         { | ||||
|             using (var connection = new SqlConnection(_connectionString)) | ||||
|             { | ||||
|                 connection.Open(); | ||||
|             if (result.AsList().Count == 0) | ||||
|                 throw new KeyNotFoundException(); | ||||
| 
 | ||||
|                 var result = await connection.QueryAsync<dynamic>( | ||||
|                    @"select o.[Id] as ordernumber,o.OrderDate as date, o.Description as description,
 | ||||
|                         o.Address_City as city, o.Address_Country as country, o.Address_State as state, o.Address_Street as street, o.Address_ZipCode as zipcode, | ||||
|                         os.Name as status,  | ||||
|                         oi.ProductName as productname, oi.Units as units, oi.UnitPrice as unitprice, oi.PictureUrl as pictureurl | ||||
|                         FROM ordering.Orders o | ||||
|                         LEFT JOIN ordering.Orderitems oi ON o.Id = oi.orderid  | ||||
|                         LEFT JOIN ordering.orderstatus os on o.OrderStatusId = os.Id | ||||
|                         WHERE o.Id=@id"
 | ||||
|                         , new { id } | ||||
|                     ); | ||||
| 
 | ||||
|                 if (result.AsList().Count == 0) | ||||
|                     throw new KeyNotFoundException(); | ||||
| 
 | ||||
|                 return MapOrderItems(result); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public async Task<IEnumerable<OrderSummary>> GetOrdersFromUserAsync(Guid userId) | ||||
|         { | ||||
|             using (var connection = new SqlConnection(_connectionString)) | ||||
|             { | ||||
|                 connection.Open(); | ||||
| 
 | ||||
|                 return await connection.QueryAsync<OrderSummary>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status], SUM(oi.units*oi.unitprice) as total
 | ||||
|                      FROM [ordering].[Orders] o | ||||
|                      LEFT JOIN[ordering].[orderitems] oi ON  o.Id = oi.orderid  | ||||
|                      LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id                      | ||||
|                      LEFT JOIN[ordering].[buyers] ob on o.BuyerId = ob.Id | ||||
|                      WHERE ob.IdentityGuid = @userId | ||||
|                      GROUP BY o.[Id], o.[OrderDate], os.[Name]  | ||||
|                      ORDER BY o.[Id]", new { userId });
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public async Task<IEnumerable<CardType>> GetCardTypesAsync() | ||||
|         { | ||||
|             using (var connection = new SqlConnection(_connectionString)) | ||||
|             { | ||||
|                 connection.Open(); | ||||
| 
 | ||||
|                 return await connection.QueryAsync<CardType>("SELECT * FROM ordering.cardtypes"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private Order MapOrderItems(dynamic result) | ||||
|         { | ||||
|             var order = new Order | ||||
|             { | ||||
|                 ordernumber = result[0].ordernumber, | ||||
|                 date = result[0].date, | ||||
|                 status = result[0].status, | ||||
|                 description = result[0].description, | ||||
|                 street = result[0].street, | ||||
|                 city = result[0].city, | ||||
|                 zipcode = result[0].zipcode, | ||||
|                 country = result[0].country, | ||||
|                 orderitems = new List<Orderitem>(), | ||||
|                 total = 0 | ||||
|             }; | ||||
| 
 | ||||
|             foreach (dynamic item in result) | ||||
|             { | ||||
|                 var orderitem = new Orderitem | ||||
|                 { | ||||
|                     productname = item.productname, | ||||
|                     units = item.units, | ||||
|                     unitprice = (double)item.unitprice, | ||||
|                     pictureurl = item.pictureurl | ||||
|                 }; | ||||
| 
 | ||||
|                 order.total += item.units * item.unitprice; | ||||
|                 order.orderitems.Add(orderitem); | ||||
|             } | ||||
| 
 | ||||
|             return order; | ||||
|             return MapOrderItems(result); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public async Task<IEnumerable<OrderSummary>> GetOrdersFromUserAsync(Guid userId) | ||||
|     { | ||||
|         using (var connection = new SqlConnection(_connectionString)) | ||||
|         { | ||||
|             connection.Open(); | ||||
| 
 | ||||
|             return await connection.QueryAsync<OrderSummary>(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status], SUM(oi.units*oi.unitprice) as total
 | ||||
|                     FROM [ordering].[Orders] o | ||||
|                     LEFT JOIN[ordering].[orderitems] oi ON  o.Id = oi.orderid  | ||||
|                     LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id                      | ||||
|                     LEFT JOIN[ordering].[buyers] ob on o.BuyerId = ob.Id | ||||
|                     WHERE ob.IdentityGuid = @userId | ||||
|                     GROUP BY o.[Id], o.[OrderDate], os.[Name]  | ||||
|                     ORDER BY o.[Id]", new { userId });
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public async Task<IEnumerable<CardType>> GetCardTypesAsync() | ||||
|     { | ||||
|         using (var connection = new SqlConnection(_connectionString)) | ||||
|         { | ||||
|             connection.Open(); | ||||
| 
 | ||||
|             return await connection.QueryAsync<CardType>("SELECT * FROM ordering.cardtypes"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private Order MapOrderItems(dynamic result) | ||||
|     { | ||||
|         var order = new Order | ||||
|         { | ||||
|             ordernumber = result[0].ordernumber, | ||||
|             date = result[0].date, | ||||
|             status = result[0].status, | ||||
|             description = result[0].description, | ||||
|             street = result[0].street, | ||||
|             city = result[0].city, | ||||
|             zipcode = result[0].zipcode, | ||||
|             country = result[0].country, | ||||
|             orderitems = new List<Orderitem>(), | ||||
|             total = 0 | ||||
|         }; | ||||
| 
 | ||||
|         foreach (dynamic item in result) | ||||
|         { | ||||
|             var orderitem = new Orderitem | ||||
|             { | ||||
|                 productname = item.productname, | ||||
|                 units = item.units, | ||||
|                 unitprice = (double)item.unitprice, | ||||
|                 pictureurl = item.pictureurl | ||||
|             }; | ||||
| 
 | ||||
|             order.total += item.units * item.unitprice; | ||||
|             order.orderitems.Add(orderitem); | ||||
|         } | ||||
| 
 | ||||
|         return order; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,41 +1,37 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries | ||||
| public record Orderitem | ||||
| { | ||||
|     public record Orderitem | ||||
|     { | ||||
|         public string productname { get; init; } | ||||
|         public int units { get; init; } | ||||
|         public double unitprice { get; init; } | ||||
|         public string pictureurl { get; init; } | ||||
|     } | ||||
| 
 | ||||
|     public record Order | ||||
|     { | ||||
|         public int ordernumber { get; init; } | ||||
|         public DateTime date { get; init; } | ||||
|         public string status { get; init; } | ||||
|         public string description { get; init; } | ||||
|         public string street { get; init; } | ||||
|         public string city { get; init; } | ||||
|         public string zipcode { get; init; } | ||||
|         public string country { get; init; } | ||||
|         public List<Orderitem> orderitems { get; set; } | ||||
|         public decimal total { get; set; } | ||||
|     } | ||||
| 
 | ||||
|     public record OrderSummary | ||||
|     { | ||||
|         public int ordernumber { get; init; } | ||||
|         public DateTime date { get; init; } | ||||
|         public string status { get; init; } | ||||
|         public double total { get; init; } | ||||
|     } | ||||
| 
 | ||||
|     public record CardType | ||||
|     { | ||||
|         public int Id { get; init; } | ||||
|         public string Name { get; init; } | ||||
|     } | ||||
|     public string productname { get; init; } | ||||
|     public int units { get; init; } | ||||
|     public double unitprice { get; init; } | ||||
|     public string pictureurl { get; init; } | ||||
| } | ||||
| 
 | ||||
| public record Order | ||||
| { | ||||
|     public int ordernumber { get; init; } | ||||
|     public DateTime date { get; init; } | ||||
|     public string status { get; init; } | ||||
|     public string description { get; init; } | ||||
|     public string street { get; init; } | ||||
|     public string city { get; init; } | ||||
|     public string zipcode { get; init; } | ||||
|     public string country { get; init; } | ||||
|     public List<Orderitem> orderitems { get; set; } | ||||
|     public decimal total { get; set; } | ||||
| } | ||||
| 
 | ||||
| public record OrderSummary | ||||
| { | ||||
|     public int ordernumber { get; init; } | ||||
|     public DateTime date { get; init; } | ||||
|     public string status { get; init; } | ||||
|     public double total { get; init; } | ||||
| } | ||||
| 
 | ||||
| public record CardType | ||||
| { | ||||
|     public int Id { get; init; } | ||||
|     public string Name { get; init; } | ||||
| } | ||||
|  | ||||
| @ -1,16 +1,11 @@ | ||||
| using FluentValidation; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.Commands; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Validations | ||||
| public class CancelOrderCommandValidator : AbstractValidator<CancelOrderCommand> | ||||
| { | ||||
|     public class CancelOrderCommandValidator : AbstractValidator<CancelOrderCommand> | ||||
|     public CancelOrderCommandValidator(ILogger<CancelOrderCommandValidator> logger) | ||||
|     { | ||||
|         public CancelOrderCommandValidator(ILogger<CancelOrderCommandValidator> logger) | ||||
|         { | ||||
|             RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); | ||||
|         RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); | ||||
| 
 | ||||
|             logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|         } | ||||
|         logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,40 +1,33 @@ | ||||
| using FluentValidation; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; | ||||
| 
 | ||||
| using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Validations | ||||
| public class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand> | ||||
| { | ||||
|     public class CreateOrderCommandValidator : AbstractValidator<CreateOrderCommand> | ||||
|     public CreateOrderCommandValidator(ILogger<CreateOrderCommandValidator> logger) | ||||
|     { | ||||
|         public CreateOrderCommandValidator(ILogger<CreateOrderCommandValidator> logger) | ||||
|         { | ||||
|             RuleFor(command => command.City).NotEmpty(); | ||||
|             RuleFor(command => command.Street).NotEmpty(); | ||||
|             RuleFor(command => command.State).NotEmpty(); | ||||
|             RuleFor(command => command.Country).NotEmpty(); | ||||
|             RuleFor(command => command.ZipCode).NotEmpty(); | ||||
|             RuleFor(command => command.CardNumber).NotEmpty().Length(12, 19); | ||||
|             RuleFor(command => command.CardHolderName).NotEmpty(); | ||||
|             RuleFor(command => command.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date"); | ||||
|             RuleFor(command => command.CardSecurityNumber).NotEmpty().Length(3); | ||||
|             RuleFor(command => command.CardTypeId).NotEmpty(); | ||||
|             RuleFor(command => command.OrderItems).Must(ContainOrderItems).WithMessage("No order items found"); | ||||
|         RuleFor(command => command.City).NotEmpty(); | ||||
|         RuleFor(command => command.Street).NotEmpty(); | ||||
|         RuleFor(command => command.State).NotEmpty(); | ||||
|         RuleFor(command => command.Country).NotEmpty(); | ||||
|         RuleFor(command => command.ZipCode).NotEmpty(); | ||||
|         RuleFor(command => command.CardNumber).NotEmpty().Length(12, 19); | ||||
|         RuleFor(command => command.CardHolderName).NotEmpty(); | ||||
|         RuleFor(command => command.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date"); | ||||
|         RuleFor(command => command.CardSecurityNumber).NotEmpty().Length(3); | ||||
|         RuleFor(command => command.CardTypeId).NotEmpty(); | ||||
|         RuleFor(command => command.OrderItems).Must(ContainOrderItems).WithMessage("No order items found"); | ||||
| 
 | ||||
|             logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|         } | ||||
| 
 | ||||
|         private bool BeValidExpirationDate(DateTime dateTime) | ||||
|         { | ||||
|             return dateTime >= DateTime.UtcNow; | ||||
|         } | ||||
| 
 | ||||
|         private bool ContainOrderItems(IEnumerable<OrderItemDTO> orderItems) | ||||
|         { | ||||
|             return orderItems.Any(); | ||||
|         } | ||||
|         logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     private bool BeValidExpirationDate(DateTime dateTime) | ||||
|     { | ||||
|         return dateTime >= DateTime.UtcNow; | ||||
|     } | ||||
| 
 | ||||
|     private bool ContainOrderItems(IEnumerable<OrderItemDTO> orderItems) | ||||
|     { | ||||
|         return orderItems.Any(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,16 +1,11 @@ | ||||
| using FluentValidation; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.Extensions.Logging; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Validations | ||||
| public class IdentifiedCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand, bool>> | ||||
| { | ||||
|     public class IdentifiedCommandValidator : AbstractValidator<IdentifiedCommand<CreateOrderCommand, bool>> | ||||
|     public IdentifiedCommandValidator(ILogger<IdentifiedCommandValidator> logger) | ||||
|     { | ||||
|         public IdentifiedCommandValidator(ILogger<IdentifiedCommandValidator> logger) | ||||
|         { | ||||
|             RuleFor(command => command.Id).NotEmpty(); | ||||
|         RuleFor(command => command.Id).NotEmpty(); | ||||
| 
 | ||||
|             logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|         } | ||||
|         logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,16 +1,11 @@ | ||||
| using FluentValidation; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.Commands; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Validations | ||||
| public class ShipOrderCommandValidator : AbstractValidator<ShipOrderCommand> | ||||
| { | ||||
|     public class ShipOrderCommandValidator : AbstractValidator<ShipOrderCommand> | ||||
|     public ShipOrderCommandValidator(ILogger<ShipOrderCommandValidator> logger) | ||||
|     { | ||||
|         public ShipOrderCommandValidator(ILogger<ShipOrderCommandValidator> logger) | ||||
|         { | ||||
|             RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); | ||||
|         RuleFor(order => order.OrderNumber).NotEmpty().WithMessage("No orderId found"); | ||||
| 
 | ||||
|             logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|         } | ||||
|         logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name); | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,13 +1,10 @@ | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers | ||||
| public class HomeController : Controller | ||||
| { | ||||
|     public class HomeController : Controller | ||||
|     // GET: /<controller>/ | ||||
|     public IActionResult Index() | ||||
|     { | ||||
|         // GET: /<controller>/ | ||||
|         public IActionResult Index() | ||||
|         { | ||||
|             return new RedirectResult("~/swagger"); | ||||
|         } | ||||
|         return new RedirectResult("~/swagger"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,153 +1,143 @@ | ||||
| using MediatR; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; | ||||
| 
 | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Ordering.API.Application.Commands; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Net; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers | ||||
| [Route("api/v1/[controller]")]
 | ||||
| [Authorize] | ||||
| [ApiController] | ||||
| public class OrdersController : ControllerBase | ||||
| { | ||||
|     [Route("api/v1/[controller]")]
 | ||||
|     [Authorize] | ||||
|     [ApiController] | ||||
|     public class OrdersController : ControllerBase | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly IOrderQueries _orderQueries; | ||||
|     private readonly IIdentityService _identityService; | ||||
|     private readonly ILogger<OrdersController> _logger; | ||||
| 
 | ||||
|     public OrdersController( | ||||
|         IMediator mediator, | ||||
|         IOrderQueries orderQueries, | ||||
|         IIdentityService identityService, | ||||
|         ILogger<OrdersController> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly IOrderQueries _orderQueries; | ||||
|         private readonly IIdentityService _identityService; | ||||
|         private readonly 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)); | ||||
|     } | ||||
| 
 | ||||
|         public OrdersController( | ||||
|             IMediator mediator, | ||||
|             IOrderQueries orderQueries, | ||||
|             IIdentityService identityService, | ||||
|             ILogger<OrdersController> logger) | ||||
|     [Route("cancel")] | ||||
|     [HttpPut] | ||||
|     [ProducesResponseType((int)HttpStatusCode.OK)] | ||||
|     [ProducesResponseType((int)HttpStatusCode.BadRequest)] | ||||
|     public async Task<IActionResult> CancelOrderAsync([FromBody] CancelOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) | ||||
|     { | ||||
|         bool commandResult = false; | ||||
| 
 | ||||
|         if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) | ||||
|         { | ||||
|             _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)); | ||||
|         } | ||||
|             var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid); | ||||
| 
 | ||||
|         [Route("cancel")] | ||||
|         [HttpPut] | ||||
|         [ProducesResponseType((int)HttpStatusCode.OK)] | ||||
|         [ProducesResponseType((int)HttpStatusCode.BadRequest)] | ||||
|         public async Task<IActionResult> CancelOrderAsync([FromBody] CancelOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) | ||||
|         { | ||||
|             bool commandResult = false; | ||||
| 
 | ||||
|             if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) | ||||
|             { | ||||
|                 var requestCancelOrder = new IdentifiedCommand<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); | ||||
|             } | ||||
| 
 | ||||
|             if (!commandResult) | ||||
|             { | ||||
|                 return BadRequest(); | ||||
|             } | ||||
| 
 | ||||
|             return Ok(); | ||||
|         } | ||||
| 
 | ||||
|         [Route("ship")] | ||||
|         [HttpPut] | ||||
|         [ProducesResponseType((int)HttpStatusCode.OK)] | ||||
|         [ProducesResponseType((int)HttpStatusCode.BadRequest)] | ||||
|         public async Task<IActionResult> ShipOrderAsync([FromBody] ShipOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) | ||||
|         { | ||||
|             bool commandResult = false; | ||||
| 
 | ||||
|             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); | ||||
|             } | ||||
| 
 | ||||
|             if (!commandResult) | ||||
|             { | ||||
|                 return BadRequest(); | ||||
|             } | ||||
| 
 | ||||
|             return Ok(); | ||||
|         } | ||||
| 
 | ||||
|         [Route("{orderId:int}")] | ||||
|         [HttpGet] | ||||
|         [ProducesResponseType(typeof(Order), (int)HttpStatusCode.OK)] | ||||
|         [ProducesResponseType((int)HttpStatusCode.NotFound)] | ||||
|         public async Task<ActionResult> GetOrderAsync(int orderId) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 //Todo: It's good idea to take advantage of GetOrderByIdQuery and handle by GetCustomerByIdQueryHandler | ||||
|                 //var order customer = await _mediator.Send(new GetOrderByIdQuery(orderId)); | ||||
|                 var order = await _orderQueries.GetOrderAsync(orderId); | ||||
| 
 | ||||
|                 return Ok(order); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|                 return NotFound(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet] | ||||
|         [ProducesResponseType(typeof(IEnumerable<OrderSummary>), (int)HttpStatusCode.OK)] | ||||
|         public async Task<ActionResult<IEnumerable<OrderSummary>>> GetOrdersAsync() | ||||
|         { | ||||
|             var userid = _identityService.GetUserIdentity(); | ||||
|             var orders = await _orderQueries.GetOrdersFromUserAsync(Guid.Parse(userid)); | ||||
| 
 | ||||
|             return Ok(orders); | ||||
|         } | ||||
| 
 | ||||
|         [Route("cardtypes")] | ||||
|         [HttpGet] | ||||
|         [ProducesResponseType(typeof(IEnumerable<CardType>), (int)HttpStatusCode.OK)] | ||||
|         public async Task<ActionResult<IEnumerable<CardType>>> GetCardTypesAsync() | ||||
|         { | ||||
|             var cardTypes = await _orderQueries.GetCardTypesAsync(); | ||||
| 
 | ||||
|             return Ok(cardTypes); | ||||
|         } | ||||
| 
 | ||||
|         [Route("draft")] | ||||
|         [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); | ||||
|                 requestCancelOrder.GetGenericTypeName(), | ||||
|                 nameof(requestCancelOrder.Command.OrderNumber), | ||||
|                 requestCancelOrder.Command.OrderNumber, | ||||
|                 requestCancelOrder); | ||||
| 
 | ||||
|             return await _mediator.Send(createOrderDraftCommand); | ||||
|             commandResult = await _mediator.Send(requestCancelOrder); | ||||
|         } | ||||
| 
 | ||||
|         if (!commandResult) | ||||
|         { | ||||
|             return BadRequest(); | ||||
|         } | ||||
| 
 | ||||
|         return Ok(); | ||||
|     } | ||||
| 
 | ||||
|     [Route("ship")] | ||||
|     [HttpPut] | ||||
|     [ProducesResponseType((int)HttpStatusCode.OK)] | ||||
|     [ProducesResponseType((int)HttpStatusCode.BadRequest)] | ||||
|     public async Task<IActionResult> ShipOrderAsync([FromBody] ShipOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) | ||||
|     { | ||||
|         bool commandResult = false; | ||||
| 
 | ||||
|         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); | ||||
|         } | ||||
| 
 | ||||
|         if (!commandResult) | ||||
|         { | ||||
|             return BadRequest(); | ||||
|         } | ||||
| 
 | ||||
|         return Ok(); | ||||
|     } | ||||
| 
 | ||||
|     [Route("{orderId:int}")] | ||||
|     [HttpGet] | ||||
|     [ProducesResponseType(typeof(Order), (int)HttpStatusCode.OK)] | ||||
|     [ProducesResponseType((int)HttpStatusCode.NotFound)] | ||||
|     public async Task<ActionResult> GetOrderAsync(int orderId) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             //Todo: It's good idea to take advantage of GetOrderByIdQuery and handle by GetCustomerByIdQueryHandler | ||||
|             //var order customer = await _mediator.Send(new GetOrderByIdQuery(orderId)); | ||||
|             var order = await _orderQueries.GetOrderAsync(orderId); | ||||
| 
 | ||||
|             return Ok(order); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             return NotFound(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     [HttpGet] | ||||
|     [ProducesResponseType(typeof(IEnumerable<OrderSummary>), (int)HttpStatusCode.OK)] | ||||
|     public async Task<ActionResult<IEnumerable<OrderSummary>>> GetOrdersAsync() | ||||
|     { | ||||
|         var userid = _identityService.GetUserIdentity(); | ||||
|         var orders = await _orderQueries.GetOrdersFromUserAsync(Guid.Parse(userid)); | ||||
| 
 | ||||
|         return Ok(orders); | ||||
|     } | ||||
| 
 | ||||
|     [Route("cardtypes")] | ||||
|     [HttpGet] | ||||
|     [ProducesResponseType(typeof(IEnumerable<CardType>), (int)HttpStatusCode.OK)] | ||||
|     public async Task<ActionResult<IEnumerable<CardType>>> GetCardTypesAsync() | ||||
|     { | ||||
|         var cardTypes = await _orderQueries.GetCardTypesAsync(); | ||||
| 
 | ||||
|         return Ok(cardTypes); | ||||
|     } | ||||
| 
 | ||||
|     [Route("draft")] | ||||
|     [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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,28 +1,27 @@ | ||||
| using System.Collections.Generic; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand; | ||||
| 
 | ||||
| namespace Ordering.API.Application.Models | ||||
| public static class BasketItemExtensions | ||||
| { | ||||
|     public static class BasketItemExtensions | ||||
|     public static IEnumerable<OrderItemDTO> ToOrderItemsDTO(this IEnumerable<BasketItem> basketItems) | ||||
|     { | ||||
|         public static IEnumerable<OrderItemDTO> ToOrderItemsDTO(this IEnumerable<BasketItem> basketItems) | ||||
|         foreach (var item in basketItems) | ||||
|         { | ||||
|             foreach (var item in basketItems) | ||||
|             { | ||||
|                 yield return item.ToOrderItemDTO(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static OrderItemDTO ToOrderItemDTO(this BasketItem item) | ||||
|         { | ||||
|             return new OrderItemDTO() | ||||
|             { | ||||
|                 ProductId = item.ProductId, | ||||
|                 ProductName = item.ProductName, | ||||
|                 PictureUrl = item.PictureUrl, | ||||
|                 UnitPrice = item.UnitPrice, | ||||
|                 Units = item.Quantity | ||||
|             }; | ||||
|             yield return item.ToOrderItemDTO(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static OrderItemDTO ToOrderItemDTO(this BasketItem item) | ||||
|     { | ||||
|         return new OrderItemDTO() | ||||
|         { | ||||
|             ProductId = item.ProductId, | ||||
|             ProductName = item.ProductName, | ||||
|             PictureUrl = item.PictureUrl, | ||||
|             UnitPrice = item.UnitPrice, | ||||
|             Units = item.Quantity | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,50 +1,45 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Extensions; | ||||
| 
 | ||||
| namespace Ordering.API.Extensions | ||||
| public static class LinqSelectExtensions | ||||
| { | ||||
|     public static class LinqSelectExtensions | ||||
|     public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector) | ||||
|     { | ||||
|         public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector) | ||||
|         foreach (TSource element in enumerable) | ||||
|         { | ||||
|             foreach (TSource element in enumerable) | ||||
|             SelectTryResult<TSource, TResult> returnedValue; | ||||
|             try | ||||
|             { | ||||
|                 SelectTryResult<TSource, TResult> returnedValue; | ||||
|                 try | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex); | ||||
|                 } | ||||
|                 yield return returnedValue; | ||||
|                 returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public class SelectTryResult<TSource, TResult> | ||||
|         { | ||||
|             internal SelectTryResult(TSource source, TResult result, Exception exception) | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 Source = source; | ||||
|                 Result = result; | ||||
|                 CaughtException = exception; | ||||
|                 returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex); | ||||
|             } | ||||
| 
 | ||||
|             public TSource Source { get; private set; } | ||||
|             public TResult Result { get; private set; } | ||||
|             public Exception CaughtException { get; private set; } | ||||
|             yield return returnedValue; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler) | ||||
|     { | ||||
|         return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); | ||||
|     } | ||||
| 
 | ||||
|     public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler) | ||||
|     { | ||||
|         return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); | ||||
|     } | ||||
| 
 | ||||
|     public class SelectTryResult<TSource, TResult> | ||||
|     { | ||||
|         internal SelectTryResult(TSource source, TResult result, Exception exception) | ||||
|         { | ||||
|             Source = source; | ||||
|             Result = result; | ||||
|             CaughtException = exception; | ||||
|         } | ||||
| 
 | ||||
|         public TSource Source { get; private set; } | ||||
|         public TResult Result { get; private set; } | ||||
|         public Exception CaughtException { get; private set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,90 +1,78 @@ | ||||
| using Google.Protobuf.Collections; | ||||
| using Grpc.Core; | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using ApiModels = Ordering.API.Application.Models; | ||||
| using AppCommand = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| namespace GrpcOrdering; | ||||
| 
 | ||||
| namespace GrpcOrdering | ||||
| public class OrderingService : OrderingGrpc.OrderingGrpcBase | ||||
| { | ||||
|     public class OrderingService : OrderingGrpc.OrderingGrpcBase | ||||
|     private readonly IMediator _mediator; | ||||
|     private readonly ILogger<OrderingService> _logger; | ||||
| 
 | ||||
|     public OrderingService(IMediator mediator, ILogger<OrderingService> logger) | ||||
|     { | ||||
|         private readonly IMediator _mediator; | ||||
|         private readonly ILogger<OrderingService> _logger; | ||||
|         _mediator = mediator; | ||||
|         _logger = logger; | ||||
|     } | ||||
| 
 | ||||
|         public OrderingService(IMediator mediator, ILogger<OrderingService> logger) | ||||
|     public override async Task<OrderDraftDTO> CreateOrderDraftFromBasketData(CreateOrderDraftCommand createOrderDraftCommand, ServerCallContext context) | ||||
|     { | ||||
|         _logger.LogInformation("Begin grpc call from method {Method} for ordering get order draft {CreateOrderDraftCommand}", context.Method, createOrderDraftCommand); | ||||
|         _logger.LogTrace( | ||||
|             "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|             createOrderDraftCommand.GetGenericTypeName(), | ||||
|             nameof(createOrderDraftCommand.BuyerId), | ||||
|             createOrderDraftCommand.BuyerId, | ||||
|             createOrderDraftCommand); | ||||
| 
 | ||||
|         var command = new AppCommand.CreateOrderDraftCommand( | ||||
|                         createOrderDraftCommand.BuyerId, | ||||
|                         this.MapBasketItems(createOrderDraftCommand.Items)); | ||||
| 
 | ||||
| 
 | ||||
|         var data = await _mediator.Send(command); | ||||
| 
 | ||||
|         if (data != null) | ||||
|         { | ||||
|             _mediator = mediator; | ||||
|             _logger = logger; | ||||
|             context.Status = new Status(StatusCode.OK, $" ordering get order draft {createOrderDraftCommand} do exist"); | ||||
| 
 | ||||
|             return this.MapResponse(data); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             context.Status = new Status(StatusCode.NotFound, $" ordering get order draft {createOrderDraftCommand} do not exist"); | ||||
|         } | ||||
| 
 | ||||
|         public override async Task<OrderDraftDTO> CreateOrderDraftFromBasketData(CreateOrderDraftCommand createOrderDraftCommand, ServerCallContext context) | ||||
|         return new OrderDraftDTO(); | ||||
|     } | ||||
| 
 | ||||
|     public OrderDraftDTO MapResponse(AppCommand.OrderDraftDTO order) | ||||
|     { | ||||
|         var result = new OrderDraftDTO() | ||||
|         { | ||||
|             _logger.LogInformation("Begin grpc call from method {Method} for ordering get order draft {CreateOrderDraftCommand}", context.Method, createOrderDraftCommand); | ||||
|             _logger.LogTrace( | ||||
|                 "----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})", | ||||
|                 createOrderDraftCommand.GetGenericTypeName(), | ||||
|                 nameof(createOrderDraftCommand.BuyerId), | ||||
|                 createOrderDraftCommand.BuyerId, | ||||
|                 createOrderDraftCommand); | ||||
|             Total = (double)order.Total, | ||||
|         }; | ||||
| 
 | ||||
|             var command = new AppCommand.CreateOrderDraftCommand( | ||||
|                             createOrderDraftCommand.BuyerId, | ||||
|                             this.MapBasketItems(createOrderDraftCommand.Items)); | ||||
| 
 | ||||
| 
 | ||||
|             var data = await _mediator.Send(command); | ||||
| 
 | ||||
|             if (data != null) | ||||
|             { | ||||
|                 context.Status = new Status(StatusCode.OK, $" ordering get order draft {createOrderDraftCommand} do exist"); | ||||
| 
 | ||||
|                 return this.MapResponse(data); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 context.Status = new Status(StatusCode.NotFound, $" ordering get order draft {createOrderDraftCommand} do not exist"); | ||||
|             } | ||||
| 
 | ||||
|             return new OrderDraftDTO(); | ||||
|         } | ||||
| 
 | ||||
|         public OrderDraftDTO MapResponse(AppCommand.OrderDraftDTO order) | ||||
|         order.OrderItems.ToList().ForEach(i => result.OrderItems.Add(new OrderItemDTO() | ||||
|         { | ||||
|             var result = new OrderDraftDTO() | ||||
|             { | ||||
|                 Total = (double)order.Total, | ||||
|             }; | ||||
|             Discount = (double)i.Discount, | ||||
|             PictureUrl = i.PictureUrl, | ||||
|             ProductId = i.ProductId, | ||||
|             ProductName = i.ProductName, | ||||
|             UnitPrice = (double)i.UnitPrice, | ||||
|             Units = i.Units, | ||||
|         })); | ||||
| 
 | ||||
|             order.OrderItems.ToList().ForEach(i => result.OrderItems.Add(new OrderItemDTO() | ||||
|             { | ||||
|                 Discount = (double)i.Discount, | ||||
|                 PictureUrl = i.PictureUrl, | ||||
|                 ProductId = i.ProductId, | ||||
|                 ProductName = i.ProductName, | ||||
|                 UnitPrice = (double)i.UnitPrice, | ||||
|                 Units = i.Units, | ||||
|             })); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         public IEnumerable<ApiModels.BasketItem> MapBasketItems(RepeatedField<BasketItem> items) | ||||
|     public IEnumerable<ApiModels.BasketItem> MapBasketItems(RepeatedField<BasketItem> items) | ||||
|     { | ||||
|         return items.Select(x => new ApiModels.BasketItem() | ||||
|         { | ||||
|             return items.Select(x => new ApiModels.BasketItem() | ||||
|             { | ||||
|                 Id = x.Id, | ||||
|                 ProductId = x.ProductId, | ||||
|                 ProductName = x.ProductName, | ||||
|                 UnitPrice = (decimal)x.UnitPrice, | ||||
|                 OldUnitPrice = (decimal)x.OldUnitPrice, | ||||
|                 Quantity = x.Quantity, | ||||
|                 PictureUrl = x.PictureUrl, | ||||
|             }); | ||||
|         } | ||||
|             Id = x.Id, | ||||
|             ProductId = x.ProductId, | ||||
|             ProductName = x.ProductName, | ||||
|             UnitPrice = (decimal)x.UnitPrice, | ||||
|             OldUnitPrice = (decimal)x.OldUnitPrice, | ||||
|             Quantity = x.Quantity, | ||||
|             PictureUrl = x.PictureUrl, | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,14 +1,10 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults | ||||
| { | ||||
|     using AspNetCore.Http; | ||||
|     using Microsoft.AspNetCore.Mvc; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; | ||||
| 
 | ||||
|     public class InternalServerErrorObjectResult : ObjectResult | ||||
| public class InternalServerErrorObjectResult : ObjectResult | ||||
| { | ||||
|     public InternalServerErrorObjectResult(object error) | ||||
|         : base(error) | ||||
|     { | ||||
|         public InternalServerErrorObjectResult(object error) | ||||
|             : base(error) | ||||
|         { | ||||
|             StatusCode = StatusCodes.Status500InternalServerError; | ||||
|         } | ||||
|         StatusCode = StatusCodes.Status500InternalServerError; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,34 +1,27 @@ | ||||
| using Microsoft.AspNetCore.Mvc.Authorization; | ||||
| using Microsoft.OpenApi.Models; | ||||
| using Swashbuckle.AspNetCore.SwaggerGen; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Auth; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Auth | ||||
| public class AuthorizationHeaderParameterOperationFilter : IOperationFilter | ||||
| { | ||||
|     public class AuthorizationHeaderParameterOperationFilter : IOperationFilter | ||||
|     public void Apply(OpenApiOperation operation, OperationFilterContext context) | ||||
|     { | ||||
|         public void Apply(OpenApiOperation operation, OperationFilterContext context) | ||||
|         var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors; | ||||
|         var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter); | ||||
|         var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter); | ||||
| 
 | ||||
|         if (isAuthorized && !allowAnonymous) | ||||
|         { | ||||
|             var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors; | ||||
|             var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter); | ||||
|             var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter); | ||||
|             if (operation.Parameters == null) | ||||
|                 operation.Parameters = new List<OpenApiParameter>(); | ||||
| 
 | ||||
|             if (isAuthorized && !allowAnonymous) | ||||
| 
 | ||||
|             operation.Parameters.Add(new OpenApiParameter | ||||
|             { | ||||
|                 if (operation.Parameters == null) | ||||
|                     operation.Parameters = new List<OpenApiParameter>(); | ||||
| 
 | ||||
| 
 | ||||
|                 operation.Parameters.Add(new OpenApiParameter | ||||
|                 { | ||||
|                     Name = "Authorization", | ||||
|                     In = ParameterLocation.Header, | ||||
|                     Description = "access token", | ||||
|                     Required = true | ||||
|                 }); | ||||
|             } | ||||
|                 Name = "Authorization", | ||||
|                 In = ParameterLocation.Header, | ||||
|                 Description = "access token", | ||||
|                 Required = true | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,50 +1,38 @@ | ||||
| using Autofac; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories; | ||||
| using System.Reflection; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules | ||||
| public class ApplicationModule | ||||
|     : Autofac.Module | ||||
| { | ||||
| 
 | ||||
|     public class ApplicationModule | ||||
|         : Autofac.Module | ||||
|     public string QueriesConnectionString { get; } | ||||
| 
 | ||||
|     public ApplicationModule(string qconstr) | ||||
|     { | ||||
|         QueriesConnectionString = qconstr; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected override void Load(ContainerBuilder builder) | ||||
|     { | ||||
| 
 | ||||
|         public string QueriesConnectionString { get; } | ||||
|         builder.Register(c => new OrderQueries(QueriesConnectionString)) | ||||
|             .As<IOrderQueries>() | ||||
|             .InstancePerLifetimeScope(); | ||||
| 
 | ||||
|         public ApplicationModule(string qconstr) | ||||
|         { | ||||
|             QueriesConnectionString = qconstr; | ||||
|         builder.RegisterType<BuyerRepository>() | ||||
|             .As<IBuyerRepository>() | ||||
|             .InstancePerLifetimeScope(); | ||||
| 
 | ||||
|         } | ||||
|         builder.RegisterType<OrderRepository>() | ||||
|             .As<IOrderRepository>() | ||||
|             .InstancePerLifetimeScope(); | ||||
| 
 | ||||
|         protected override void Load(ContainerBuilder builder) | ||||
|         { | ||||
|         builder.RegisterType<RequestManager>() | ||||
|             .As<IRequestManager>() | ||||
|             .InstancePerLifetimeScope(); | ||||
| 
 | ||||
|             builder.Register(c => new OrderQueries(QueriesConnectionString)) | ||||
|                 .As<IOrderQueries>() | ||||
|                 .InstancePerLifetimeScope(); | ||||
|         builder.RegisterAssemblyTypes(typeof(CreateOrderCommandHandler).GetTypeInfo().Assembly) | ||||
|             .AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); | ||||
| 
 | ||||
|             builder.RegisterType<BuyerRepository>() | ||||
|                 .As<IBuyerRepository>() | ||||
|                 .InstancePerLifetimeScope(); | ||||
| 
 | ||||
|             builder.RegisterType<OrderRepository>() | ||||
|                 .As<IOrderRepository>() | ||||
|                 .InstancePerLifetimeScope(); | ||||
| 
 | ||||
|             builder.RegisterType<RequestManager>() | ||||
|                .As<IRequestManager>() | ||||
|                .InstancePerLifetimeScope(); | ||||
| 
 | ||||
|             builder.RegisterAssemblyTypes(typeof(CreateOrderCommandHandler).GetTypeInfo().Assembly) | ||||
|                 .AsClosedTypesOf(typeof(IIntegrationEventHandler<>)); | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,47 +1,36 @@ | ||||
| using Autofac; | ||||
| using FluentValidation; | ||||
| using MediatR; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; | ||||
| using Ordering.API.Application.Behaviors; | ||||
| using Ordering.API.Application.DomainEventHandlers.OrderStartedEvent; | ||||
| using Ordering.API.Application.Validations; | ||||
| using System.Linq; | ||||
| using System.Reflection; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules | ||||
| public class MediatorModule : Autofac.Module | ||||
| { | ||||
|     public class MediatorModule : Autofac.Module | ||||
|     protected override void Load(ContainerBuilder builder) | ||||
|     { | ||||
|         protected override void Load(ContainerBuilder builder) | ||||
|         builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly) | ||||
|             .AsImplementedInterfaces(); | ||||
| 
 | ||||
|         // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands | ||||
|         builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly) | ||||
|             .AsClosedTypesOf(typeof(IRequestHandler<,>)); | ||||
| 
 | ||||
|         // Register the DomainEventHandler classes (they implement INotificationHandler<>) in assembly holding the Domain Events | ||||
|         builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) | ||||
|             .AsClosedTypesOf(typeof(INotificationHandler<>)); | ||||
| 
 | ||||
|         // Register the Command's Validators (Validators based on FluentValidation library) | ||||
|         builder | ||||
|             .RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly) | ||||
|             .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) | ||||
|             .AsImplementedInterfaces(); | ||||
| 
 | ||||
| 
 | ||||
|         builder.Register<ServiceFactory>(context => | ||||
|         { | ||||
|             builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly) | ||||
|                 .AsImplementedInterfaces(); | ||||
|             var componentContext = context.Resolve<IComponentContext>(); | ||||
|             return t => { object o; return componentContext.TryResolve(t, out o) ? o : null; }; | ||||
|         }); | ||||
| 
 | ||||
|             // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands | ||||
|             builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly) | ||||
|                 .AsClosedTypesOf(typeof(IRequestHandler<,>)); | ||||
|         builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>)); | ||||
|         builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); | ||||
|         builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>)); | ||||
| 
 | ||||
|             // Register the DomainEventHandler classes (they implement INotificationHandler<>) in assembly holding the Domain Events | ||||
|             builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler).GetTypeInfo().Assembly) | ||||
|                 .AsClosedTypesOf(typeof(INotificationHandler<>)); | ||||
| 
 | ||||
|             // Register the Command's Validators (Validators based on FluentValidation library) | ||||
|             builder | ||||
|                 .RegisterAssemblyTypes(typeof(CreateOrderCommandValidator).GetTypeInfo().Assembly) | ||||
|                 .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) | ||||
|                 .AsImplementedInterfaces(); | ||||
| 
 | ||||
| 
 | ||||
|             builder.Register<ServiceFactory>(context => | ||||
|             { | ||||
|                 var componentContext = context.Resolve<IComponentContext>(); | ||||
|                 return t => { object o; return componentContext.TryResolve(t, out o) ? o : null; }; | ||||
|             }); | ||||
| 
 | ||||
|             builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>)); | ||||
|             builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>)); | ||||
|             builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>)); | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,10 +1,4 @@ | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.EntityFrameworkCore.Design; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using System.IO; | ||||
| 
 | ||||
| namespace Ordering.API.Infrastructure.Factories | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Factories | ||||
| { | ||||
|     public class OrderingDbContextFactory : IDesignTimeDbContextFactory<OrderingContext> | ||||
|     { | ||||
|  | ||||
| @ -1,36 +1,29 @@ | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.OpenApi.Models; | ||||
| using Swashbuckle.AspNetCore.SwaggerGen; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters; | ||||
| 
 | ||||
| namespace Ordering.API.Infrastructure.Filters | ||||
| public class AuthorizeCheckOperationFilter : IOperationFilter | ||||
| { | ||||
|     public class AuthorizeCheckOperationFilter : IOperationFilter | ||||
|     public void Apply(OpenApiOperation operation, OperationFilterContext context) | ||||
|     { | ||||
|         public void Apply(OpenApiOperation operation, OperationFilterContext context) | ||||
|         // Check for authorize attribute | ||||
|         var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() || | ||||
|                             context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any(); | ||||
| 
 | ||||
|         if (!hasAuthorize) return; | ||||
| 
 | ||||
|         operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); | ||||
|         operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); | ||||
| 
 | ||||
|         var oAuthScheme = new OpenApiSecurityScheme | ||||
|         { | ||||
|             // Check for authorize attribute | ||||
|             var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() || | ||||
|                                context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any(); | ||||
|             Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } | ||||
|         }; | ||||
| 
 | ||||
|             if (!hasAuthorize) return; | ||||
| 
 | ||||
|             operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); | ||||
|             operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); | ||||
| 
 | ||||
|             var oAuthScheme = new OpenApiSecurityScheme | ||||
|         operation.Security = new List<OpenApiSecurityRequirement> | ||||
|             { | ||||
|                 Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } | ||||
|             }; | ||||
| 
 | ||||
|             operation.Security = new List<OpenApiSecurityRequirement> | ||||
|                 new OpenApiSecurityRequirement | ||||
|                 { | ||||
|                     new OpenApiSecurityRequirement | ||||
|                     { | ||||
|                         [ oAuthScheme ] = new [] { "orderingapi" } | ||||
|                     } | ||||
|                 }; | ||||
|         } | ||||
|                     [ oAuthScheme ] = new [] { "orderingapi" } | ||||
|                 } | ||||
|             }; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,71 +1,60 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters; | ||||
| 
 | ||||
| public class HttpGlobalExceptionFilter : IExceptionFilter | ||||
| { | ||||
|     using AspNetCore.Mvc; | ||||
|     using global::Ordering.Domain.Exceptions; | ||||
|     using Microsoft.AspNetCore.Hosting; | ||||
|     using Microsoft.AspNetCore.Http; | ||||
|     using Microsoft.AspNetCore.Mvc.Filters; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; | ||||
|     using Microsoft.Extensions.Hosting; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using System.Net; | ||||
|     private readonly IWebHostEnvironment env; | ||||
|     private readonly ILogger<HttpGlobalExceptionFilter> logger; | ||||
| 
 | ||||
|     public class HttpGlobalExceptionFilter : IExceptionFilter | ||||
|     public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger<HttpGlobalExceptionFilter> logger) | ||||
|     { | ||||
|         private readonly IWebHostEnvironment env; | ||||
|         private readonly ILogger<HttpGlobalExceptionFilter> logger; | ||||
|         this.env = env; | ||||
|         this.logger = logger; | ||||
|     } | ||||
| 
 | ||||
|         public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger<HttpGlobalExceptionFilter> logger) | ||||
|     public void OnException(ExceptionContext context) | ||||
|     { | ||||
|         logger.LogError(new EventId(context.Exception.HResult), | ||||
|             context.Exception, | ||||
|             context.Exception.Message); | ||||
| 
 | ||||
|         if (context.Exception.GetType() == typeof(OrderingDomainException)) | ||||
|         { | ||||
|             this.env = env; | ||||
|             this.logger = logger; | ||||
|         } | ||||
| 
 | ||||
|         public void OnException(ExceptionContext context) | ||||
|         { | ||||
|             logger.LogError(new EventId(context.Exception.HResult), | ||||
|                 context.Exception, | ||||
|                 context.Exception.Message); | ||||
| 
 | ||||
|             if (context.Exception.GetType() == typeof(OrderingDomainException)) | ||||
|             var problemDetails = new ValidationProblemDetails() | ||||
|             { | ||||
|                 var problemDetails = new ValidationProblemDetails() | ||||
|                 { | ||||
|                     Instance = context.HttpContext.Request.Path, | ||||
|                     Status = StatusCodes.Status400BadRequest, | ||||
|                     Detail = "Please refer to the errors property for additional details." | ||||
|                 }; | ||||
|                 Instance = context.HttpContext.Request.Path, | ||||
|                 Status = StatusCodes.Status400BadRequest, | ||||
|                 Detail = "Please refer to the errors property for additional details." | ||||
|             }; | ||||
| 
 | ||||
|                 problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); | ||||
|             problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); | ||||
| 
 | ||||
|                 context.Result = new BadRequestObjectResult(problemDetails); | ||||
|                 context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var json = new JsonErrorResponse | ||||
|                 { | ||||
|                     Messages = new[] { "An error occur.Try it again." } | ||||
|                 }; | ||||
| 
 | ||||
|                 if (env.IsDevelopment()) | ||||
|                 { | ||||
|                     json.DeveloperMessage = context.Exception; | ||||
|                 } | ||||
| 
 | ||||
|                 // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | ||||
|                 // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | ||||
|                 context.Result = new InternalServerErrorObjectResult(json); | ||||
|                 context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; | ||||
|             } | ||||
|             context.ExceptionHandled = true; | ||||
|             context.Result = new BadRequestObjectResult(problemDetails); | ||||
|             context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; | ||||
|         } | ||||
| 
 | ||||
|         private class JsonErrorResponse | ||||
|         else | ||||
|         { | ||||
|             public string[] Messages { get; set; } | ||||
|             var json = new JsonErrorResponse | ||||
|             { | ||||
|                 Messages = new[] { "An error occur.Try it again." } | ||||
|             }; | ||||
| 
 | ||||
|             public object DeveloperMessage { get; set; } | ||||
|             if (env.IsDevelopment()) | ||||
|             { | ||||
|                 json.DeveloperMessage = context.Exception; | ||||
|             } | ||||
| 
 | ||||
|             // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 | ||||
|             // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information | ||||
|             context.Result = new InternalServerErrorObjectResult(json); | ||||
|             context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; | ||||
|         } | ||||
|         context.ExceptionHandled = true; | ||||
|     } | ||||
| 
 | ||||
|     private class JsonErrorResponse | ||||
|     { | ||||
|         public string[] Messages { get; set; } | ||||
| 
 | ||||
|         public object DeveloperMessage { get; set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,191 +1,174 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure; | ||||
| 
 | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
| 
 | ||||
| public class OrderingContextSeed | ||||
| { | ||||
|     using global::Ordering.API.Extensions; | ||||
|     using Microsoft.AspNetCore.Hosting; | ||||
|     using Microsoft.EntityFrameworkCore; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Microsoft.Extensions.Options; | ||||
|     using Ordering.Infrastructure; | ||||
|     using Polly; | ||||
|     using Polly.Retry; | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Data.SqlClient; | ||||
|     using System.IO; | ||||
|     using System.Linq; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
|     public class OrderingContextSeed | ||||
|     public async Task SeedAsync(OrderingContext context, IWebHostEnvironment env, IOptions<OrderingSettings> settings, ILogger<OrderingContextSeed> logger) | ||||
|     { | ||||
|         public async Task SeedAsync(OrderingContext context, IWebHostEnvironment env, IOptions<OrderingSettings> settings, ILogger<OrderingContextSeed> logger) | ||||
|         var policy = CreatePolicy(logger, nameof(OrderingContextSeed)); | ||||
| 
 | ||||
|         await policy.ExecuteAsync(async () => | ||||
|         { | ||||
|             var policy = CreatePolicy(logger, nameof(OrderingContextSeed)); | ||||
| 
 | ||||
|             await policy.ExecuteAsync(async () => | ||||
|             var useCustomizationData = settings.Value | ||||
|             .UseCustomizationData; | ||||
| 
 | ||||
|             var contentRootPath = env.ContentRootPath; | ||||
| 
 | ||||
| 
 | ||||
|             using (context) | ||||
|             { | ||||
|                 context.Database.Migrate(); | ||||
| 
 | ||||
|                 var useCustomizationData = settings.Value | ||||
|                 .UseCustomizationData; | ||||
| 
 | ||||
|                 var contentRootPath = env.ContentRootPath; | ||||
| 
 | ||||
| 
 | ||||
|                 using (context) | ||||
|                 if (!context.CardTypes.Any()) | ||||
|                 { | ||||
|                     context.Database.Migrate(); | ||||
| 
 | ||||
|                     if (!context.CardTypes.Any()) | ||||
|                     { | ||||
|                         context.CardTypes.AddRange(useCustomizationData | ||||
|                                                 ? GetCardTypesFromFile(contentRootPath, logger) | ||||
|                                                 : GetPredefinedCardTypes()); | ||||
| 
 | ||||
|                         await context.SaveChangesAsync(); | ||||
|                     } | ||||
| 
 | ||||
|                     if (!context.OrderStatus.Any()) | ||||
|                     { | ||||
|                         context.OrderStatus.AddRange(useCustomizationData | ||||
|                                                 ? GetOrderStatusFromFile(contentRootPath, logger) | ||||
|                                                 : GetPredefinedOrderStatus()); | ||||
|                     } | ||||
|                     context.CardTypes.AddRange(useCustomizationData | ||||
|                                             ? GetCardTypesFromFile(contentRootPath, logger) | ||||
|                                             : GetPredefinedCardTypes()); | ||||
| 
 | ||||
|                     await context.SaveChangesAsync(); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         private IEnumerable<CardType> GetCardTypesFromFile(string contentRootPath, ILogger<OrderingContextSeed> log) | ||||
|         { | ||||
|             string csvFileCardTypes = Path.Combine(contentRootPath, "Setup", "CardTypes.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileCardTypes)) | ||||
|             { | ||||
|                 return GetPredefinedCardTypes(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { "CardType" }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileCardTypes); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); | ||||
|                 return GetPredefinedCardTypes(); | ||||
|             } | ||||
| 
 | ||||
|             int id = 1; | ||||
|             return File.ReadAllLines(csvFileCardTypes) | ||||
|                                         .Skip(1) // skip header column | ||||
|                                         .SelectTry(x => CreateCardType(x, ref id)) | ||||
|                                         .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) | ||||
|                                         .Where(x => x != null); | ||||
|         } | ||||
| 
 | ||||
|         private CardType CreateCardType(string value, ref int id) | ||||
|         { | ||||
|             if (String.IsNullOrEmpty(value)) | ||||
|             { | ||||
|                 throw new Exception("Orderstatus is null or empty"); | ||||
|             } | ||||
| 
 | ||||
|             return new CardType(id++, value.Trim('"').Trim()); | ||||
|         } | ||||
| 
 | ||||
|         private IEnumerable<CardType> GetPredefinedCardTypes() | ||||
|         { | ||||
|             return Enumeration.GetAll<CardType>(); | ||||
|         } | ||||
| 
 | ||||
|         private IEnumerable<OrderStatus> GetOrderStatusFromFile(string contentRootPath, ILogger<OrderingContextSeed> log) | ||||
|         { | ||||
|             string csvFileOrderStatus = Path.Combine(contentRootPath, "Setup", "OrderStatus.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileOrderStatus)) | ||||
|             { | ||||
|                 return GetPredefinedOrderStatus(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { "OrderStatus" }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileOrderStatus); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); | ||||
|                 return GetPredefinedOrderStatus(); | ||||
|             } | ||||
| 
 | ||||
|             int id = 1; | ||||
|             return File.ReadAllLines(csvFileOrderStatus) | ||||
|                                         .Skip(1) // skip header row | ||||
|                                         .SelectTry(x => CreateOrderStatus(x, ref id)) | ||||
|                                         .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) | ||||
|                                         .Where(x => x != null); | ||||
|         } | ||||
| 
 | ||||
|         private OrderStatus CreateOrderStatus(string value, ref int id) | ||||
|         { | ||||
|             if (String.IsNullOrEmpty(value)) | ||||
|             { | ||||
|                 throw new Exception("Orderstatus is null or empty"); | ||||
|             } | ||||
| 
 | ||||
|             return new OrderStatus(id++, value.Trim('"').Trim().ToLowerInvariant()); | ||||
|         } | ||||
| 
 | ||||
|         private IEnumerable<OrderStatus> GetPredefinedOrderStatus() | ||||
|         { | ||||
|             return new List<OrderStatus>() | ||||
|             { | ||||
|                 OrderStatus.Submitted, | ||||
|                 OrderStatus.AwaitingValidation, | ||||
|                 OrderStatus.StockConfirmed, | ||||
|                 OrderStatus.Paid, | ||||
|                 OrderStatus.Shipped, | ||||
|                 OrderStatus.Cancelled | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         private string[] GetHeaders(string[] requiredHeaders, string csvfile) | ||||
|         { | ||||
|             string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); | ||||
| 
 | ||||
|             if (csvheaders.Count() != requiredHeaders.Count()) | ||||
|             { | ||||
|                 throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var requiredHeader in requiredHeaders) | ||||
|             { | ||||
|                 if (!csvheaders.Contains(requiredHeader)) | ||||
|                 if (!context.OrderStatus.Any()) | ||||
|                 { | ||||
|                     throw new Exception($"does not contain required header '{requiredHeader}'"); | ||||
|                     context.OrderStatus.AddRange(useCustomizationData | ||||
|                                             ? GetOrderStatusFromFile(contentRootPath, logger) | ||||
|                                             : GetPredefinedOrderStatus()); | ||||
|                 } | ||||
| 
 | ||||
|                 await context.SaveChangesAsync(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|             return csvheaders; | ||||
|         } | ||||
|     private IEnumerable<CardType> GetCardTypesFromFile(string contentRootPath, ILogger<OrderingContextSeed> log) | ||||
|     { | ||||
|         string csvFileCardTypes = Path.Combine(contentRootPath, "Setup", "CardTypes.csv"); | ||||
| 
 | ||||
| 
 | ||||
|         private AsyncRetryPolicy CreatePolicy(ILogger<OrderingContextSeed> logger, string prefix, int retries = 3) | ||||
|         if (!File.Exists(csvFileCardTypes)) | ||||
|         { | ||||
|             return Policy.Handle<SqlException>(). | ||||
|                 WaitAndRetryAsync( | ||||
|                     retryCount: retries, | ||||
|                     sleepDurationProvider: retry => TimeSpan.FromSeconds(5), | ||||
|                     onRetry: (exception, timeSpan, retry, ctx) => | ||||
|                     { | ||||
|                         logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); | ||||
|                     } | ||||
|                 ); | ||||
|             return GetPredefinedCardTypes(); | ||||
|         } | ||||
| 
 | ||||
|         string[] csvheaders; | ||||
|         try | ||||
|         { | ||||
|             string[] requiredHeaders = { "CardType" }; | ||||
|             csvheaders = GetHeaders(requiredHeaders, csvFileCardTypes); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); | ||||
|             return GetPredefinedCardTypes(); | ||||
|         } | ||||
| 
 | ||||
|         int id = 1; | ||||
|         return File.ReadAllLines(csvFileCardTypes) | ||||
|                                     .Skip(1) // skip header column | ||||
|                                     .SelectTry(x => CreateCardType(x, ref id)) | ||||
|                                     .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) | ||||
|                                     .Where(x => x != null); | ||||
|     } | ||||
| 
 | ||||
|     private CardType CreateCardType(string value, ref int id) | ||||
|     { | ||||
|         if (String.IsNullOrEmpty(value)) | ||||
|         { | ||||
|             throw new Exception("Orderstatus is null or empty"); | ||||
|         } | ||||
| 
 | ||||
|         return new CardType(id++, value.Trim('"').Trim()); | ||||
|     } | ||||
| 
 | ||||
|     private IEnumerable<CardType> GetPredefinedCardTypes() | ||||
|     { | ||||
|         return Enumeration.GetAll<CardType>(); | ||||
|     } | ||||
| 
 | ||||
|     private IEnumerable<OrderStatus> GetOrderStatusFromFile(string contentRootPath, ILogger<OrderingContextSeed> log) | ||||
|     { | ||||
|         string csvFileOrderStatus = Path.Combine(contentRootPath, "Setup", "OrderStatus.csv"); | ||||
| 
 | ||||
|         if (!File.Exists(csvFileOrderStatus)) | ||||
|         { | ||||
|             return GetPredefinedOrderStatus(); | ||||
|         } | ||||
| 
 | ||||
|         string[] csvheaders; | ||||
|         try | ||||
|         { | ||||
|             string[] requiredHeaders = { "OrderStatus" }; | ||||
|             csvheaders = GetHeaders(requiredHeaders, csvFileOrderStatus); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); | ||||
|             return GetPredefinedOrderStatus(); | ||||
|         } | ||||
| 
 | ||||
|         int id = 1; | ||||
|         return File.ReadAllLines(csvFileOrderStatus) | ||||
|                                     .Skip(1) // skip header row | ||||
|                                     .SelectTry(x => CreateOrderStatus(x, ref id)) | ||||
|                                     .OnCaughtException(ex => { log.LogError(ex, "EXCEPTION ERROR: {Message}", ex.Message); return null; }) | ||||
|                                     .Where(x => x != null); | ||||
|     } | ||||
| 
 | ||||
|     private OrderStatus CreateOrderStatus(string value, ref int id) | ||||
|     { | ||||
|         if (String.IsNullOrEmpty(value)) | ||||
|         { | ||||
|             throw new Exception("Orderstatus is null or empty"); | ||||
|         } | ||||
| 
 | ||||
|         return new OrderStatus(id++, value.Trim('"').Trim().ToLowerInvariant()); | ||||
|     } | ||||
| 
 | ||||
|     private IEnumerable<OrderStatus> GetPredefinedOrderStatus() | ||||
|     { | ||||
|         return new List<OrderStatus>() | ||||
|         { | ||||
|             OrderStatus.Submitted, | ||||
|             OrderStatus.AwaitingValidation, | ||||
|             OrderStatus.StockConfirmed, | ||||
|             OrderStatus.Paid, | ||||
|             OrderStatus.Shipped, | ||||
|             OrderStatus.Cancelled | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     private string[] GetHeaders(string[] requiredHeaders, string csvfile) | ||||
|     { | ||||
|         string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); | ||||
| 
 | ||||
|         if (csvheaders.Count() != requiredHeaders.Count()) | ||||
|         { | ||||
|             throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); | ||||
|         } | ||||
| 
 | ||||
|         foreach (var requiredHeader in requiredHeaders) | ||||
|         { | ||||
|             if (!csvheaders.Contains(requiredHeader)) | ||||
|             { | ||||
|                 throw new Exception($"does not contain required header '{requiredHeader}'"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return csvheaders; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private AsyncRetryPolicy CreatePolicy(ILogger<OrderingContextSeed> logger, string prefix, int retries = 3) | ||||
|     { | ||||
|         return Policy.Handle<SqlException>(). | ||||
|             WaitAndRetryAsync( | ||||
|                 retryCount: retries, | ||||
|                 sleepDurationProvider: retry => TimeSpan.FromSeconds(5), | ||||
|                 onRetry: (exception, timeSpan, retry, ctx) => | ||||
|                 { | ||||
|                     logger.LogWarning(exception, "[{prefix}] Exception {ExceptionType} with message {Message} detected on attempt {retry} of {retries}", prefix, exception.GetType().Name, exception.Message, retry, retries); | ||||
|                 } | ||||
|             ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services | ||||
| { | ||||
|     public interface IIdentityService | ||||
|     { | ||||
|         string GetUserIdentity(); | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; | ||||
| 
 | ||||
|         string GetUserName(); | ||||
|     } | ||||
| public interface IIdentityService | ||||
| { | ||||
|     string GetUserIdentity(); | ||||
| 
 | ||||
|     string GetUserName(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,26 +1,21 @@ | ||||
|  | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using System; | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services | ||||
| public class IdentityService : IIdentityService | ||||
| { | ||||
|     public class IdentityService : IIdentityService | ||||
|     private IHttpContextAccessor _context; | ||||
| 
 | ||||
|     public IdentityService(IHttpContextAccessor context) | ||||
|     { | ||||
|         private IHttpContextAccessor _context; | ||||
|         _context = context ?? throw new ArgumentNullException(nameof(context)); | ||||
|     } | ||||
| 
 | ||||
|         public IdentityService(IHttpContextAccessor context) | ||||
|         { | ||||
|             _context = context ?? throw new ArgumentNullException(nameof(context)); | ||||
|         } | ||||
|     public string GetUserIdentity() | ||||
|     { | ||||
|         return _context.HttpContext.User.FindFirst("sub").Value; | ||||
|     } | ||||
| 
 | ||||
|         public string GetUserIdentity() | ||||
|         { | ||||
|             return _context.HttpContext.User.FindFirst("sub").Value; | ||||
|         } | ||||
| 
 | ||||
|         public string GetUserName() | ||||
|         { | ||||
|             return _context.HttpContext.User.Identity.Name; | ||||
|         } | ||||
|     public string GetUserName() | ||||
|     { | ||||
|         return _context.HttpContext.User.Identity.Name; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,14 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API; | ||||
| 
 | ||||
| public class OrderingSettings | ||||
| { | ||||
|     public class OrderingSettings | ||||
|     { | ||||
|         public bool UseCustomizationData { get; set; } | ||||
|     public bool UseCustomizationData { get; set; } | ||||
| 
 | ||||
|         public string ConnectionString { get; set; } | ||||
|     public string ConnectionString { get; set; } | ||||
| 
 | ||||
|         public string EventBusConnection { get; set; } | ||||
|     public string EventBusConnection { get; set; } | ||||
| 
 | ||||
|         public int GracePeriodTime { get; set; } | ||||
|     public int GracePeriodTime { get; set; } | ||||
| 
 | ||||
|         public int CheckUpdateTime { get; set; } | ||||
|     } | ||||
|     public int CheckUpdateTime { get; set; } | ||||
| } | ||||
|  | ||||
| @ -1,22 +1,4 @@ | ||||
| using Microsoft.AspNetCore; | ||||
| using Microsoft.AspNetCore.Hosting; | ||||
| using Microsoft.AspNetCore.Server.Kestrel.Core; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure; | ||||
| using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Microsoft.Extensions.Options; | ||||
| using Serilog; | ||||
| using System; | ||||
| using System.IO; | ||||
| using System.Net; | ||||
| using Azure.Identity; | ||||
| using Azure.Core; | ||||
| 
 | ||||
| var configuration = GetConfiguration(); | ||||
| var configuration = GetConfiguration(); | ||||
| 
 | ||||
| Log.Logger = CreateSerilogLogger(configuration); | ||||
| 
 | ||||
|  | ||||
| @ -1,429 +1,391 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API | ||||
|  | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API; | ||||
| 
 | ||||
| public class Startup | ||||
| { | ||||
|     using AspNetCore.Http; | ||||
|     using Autofac; | ||||
|     using Autofac.Extensions.DependencyInjection; | ||||
|     using global::Ordering.API.Application.IntegrationEvents; | ||||
|     using global::Ordering.API.Application.IntegrationEvents.Events; | ||||
|     using global::Ordering.API.Infrastructure.Filters; | ||||
|     using GrpcOrdering; | ||||
|     using HealthChecks.UI.Client; | ||||
|     using Infrastructure.AutofacModules; | ||||
|     using Infrastructure.Filters; | ||||
|     using Infrastructure.Services; | ||||
|     using Microsoft.AspNetCore.Builder; | ||||
|     using Microsoft.AspNetCore.Diagnostics.HealthChecks; | ||||
|     using Microsoft.AspNetCore.Hosting; | ||||
|     using Microsoft.AspNetCore.Mvc; | ||||
|     using Microsoft.Azure.ServiceBus; | ||||
|     using Microsoft.EntityFrameworkCore; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.API.Controllers; | ||||
|     using Microsoft.Extensions.Configuration; | ||||
|     using Microsoft.Extensions.DependencyInjection; | ||||
|     using Microsoft.Extensions.Diagnostics.HealthChecks; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Microsoft.OpenApi.Models; | ||||
|     using Ordering.Infrastructure; | ||||
|     using RabbitMQ.Client; | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Data.Common; | ||||
|     using System.IdentityModel.Tokens.Jwt; | ||||
|     using System.IO; | ||||
|     using System.Reflection; | ||||
| 
 | ||||
|     public class Startup | ||||
|     public Startup(IConfiguration configuration) | ||||
|     { | ||||
|         public Startup(IConfiguration configuration) | ||||
|         { | ||||
|             Configuration = configuration; | ||||
|         } | ||||
| 
 | ||||
|         public IConfiguration Configuration { get; } | ||||
| 
 | ||||
|         public virtual IServiceProvider ConfigureServices(IServiceCollection services) | ||||
|         { | ||||
|             services | ||||
|                 .AddGrpc(options => | ||||
|                 { | ||||
|                     options.EnableDetailedErrors = true; | ||||
|                 }) | ||||
|                 .Services | ||||
|                 .AddApplicationInsights(Configuration) | ||||
|                 .AddCustomMvc() | ||||
|                 .AddHealthChecks(Configuration) | ||||
|                 .AddCustomDbContext(Configuration) | ||||
|                 .AddCustomSwagger(Configuration) | ||||
|                 .AddCustomIntegrations(Configuration) | ||||
|                 .AddCustomConfiguration(Configuration) | ||||
|                 .AddEventBus(Configuration) | ||||
|                 .AddCustomAuthentication(Configuration); | ||||
|             //configure autofac | ||||
| 
 | ||||
|             var container = new ContainerBuilder(); | ||||
|             container.Populate(services); | ||||
| 
 | ||||
|             container.RegisterModule(new MediatorModule()); | ||||
|             container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); | ||||
| 
 | ||||
|             return new AutofacServiceProvider(container.Build()); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) | ||||
|         { | ||||
|             //loggerFactory.AddAzureWebAppDiagnostics(); | ||||
|             //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | ||||
| 
 | ||||
|             var pathBase = Configuration["PATH_BASE"]; | ||||
|             if (!string.IsNullOrEmpty(pathBase)) | ||||
|             { | ||||
|                 loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase); | ||||
|                 app.UsePathBase(pathBase); | ||||
|             } | ||||
| 
 | ||||
|             app.UseSwagger() | ||||
|                .UseSwaggerUI(c => | ||||
|                { | ||||
|                    c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1"); | ||||
|                    c.OAuthClientId("orderingswaggerui"); | ||||
|                    c.OAuthAppName("Ordering Swagger UI"); | ||||
|                }); | ||||
| 
 | ||||
|             app.UseRouting(); | ||||
|             app.UseCors("CorsPolicy"); | ||||
|             ConfigureAuth(app); | ||||
| 
 | ||||
|             app.UseEndpoints(endpoints => | ||||
|             { | ||||
|                 endpoints.MapGrpcService<OrderingService>(); | ||||
|                 endpoints.MapDefaultControllerRoute(); | ||||
|                 endpoints.MapControllers(); | ||||
|                 endpoints.MapGet("/_proto/", async ctx => | ||||
|                 { | ||||
|                     ctx.Response.ContentType = "text/plain"; | ||||
|                     using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read); | ||||
|                     using var sr = new StreamReader(fs); | ||||
|                     while (!sr.EndOfStream) | ||||
|                     { | ||||
|                         var line = await sr.ReadLineAsync(); | ||||
|                         if (line != "/* >>" || line != "<< */") | ||||
|                         { | ||||
|                             await ctx.Response.WriteAsync(line); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|                 endpoints.MapHealthChecks("/hc", new HealthCheckOptions() | ||||
|                 { | ||||
|                     Predicate = _ => true, | ||||
|                     ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | ||||
|                 }); | ||||
|                 endpoints.MapHealthChecks("/liveness", new HealthCheckOptions | ||||
|                 { | ||||
|                     Predicate = r => r.Name.Contains("self") | ||||
|                 }); | ||||
|             }); | ||||
| 
 | ||||
|             ConfigureEventBus(app); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         private void ConfigureEventBus(IApplicationBuilder app) | ||||
|         { | ||||
|             var eventBus = app.ApplicationServices.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>(); | ||||
| 
 | ||||
|             eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>(); | ||||
|             eventBus.Subscribe<GracePeriodConfirmedIntegrationEvent, IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>>(); | ||||
|             eventBus.Subscribe<OrderStockConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>>(); | ||||
|             eventBus.Subscribe<OrderStockRejectedIntegrationEvent, IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>>(); | ||||
|             eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>(); | ||||
|             eventBus.Subscribe<OrderPaymentSucceededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>>(); | ||||
|         } | ||||
| 
 | ||||
|         protected virtual void ConfigureAuth(IApplicationBuilder app) | ||||
|         { | ||||
|             app.UseAuthentication(); | ||||
|             app.UseAuthorization(); | ||||
|         } | ||||
|         Configuration = configuration; | ||||
|     } | ||||
| 
 | ||||
|     static class CustomExtensionsMethods | ||||
|     public IConfiguration Configuration { get; } | ||||
| 
 | ||||
|     public virtual IServiceProvider ConfigureServices(IServiceCollection services) | ||||
|     { | ||||
|         public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) | ||||
|         { | ||||
|             services.AddApplicationInsightsTelemetry(configuration); | ||||
|             services.AddApplicationInsightsKubernetesEnricher(); | ||||
|         services | ||||
|             .AddGrpc(options => | ||||
|             { | ||||
|                 options.EnableDetailedErrors = true; | ||||
|             }) | ||||
|             .Services | ||||
|             .AddApplicationInsights(Configuration) | ||||
|             .AddCustomMvc() | ||||
|             .AddHealthChecks(Configuration) | ||||
|             .AddCustomDbContext(Configuration) | ||||
|             .AddCustomSwagger(Configuration) | ||||
|             .AddCustomIntegrations(Configuration) | ||||
|             .AddCustomConfiguration(Configuration) | ||||
|             .AddEventBus(Configuration) | ||||
|             .AddCustomAuthentication(Configuration); | ||||
|         //configure autofac | ||||
| 
 | ||||
|             return services; | ||||
|         var container = new ContainerBuilder(); | ||||
|         container.Populate(services); | ||||
| 
 | ||||
|         container.RegisterModule(new MediatorModule()); | ||||
|         container.RegisterModule(new ApplicationModule(Configuration["ConnectionString"])); | ||||
| 
 | ||||
|         return new AutofacServiceProvider(container.Build()); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) | ||||
|     { | ||||
|         //loggerFactory.AddAzureWebAppDiagnostics(); | ||||
|         //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); | ||||
| 
 | ||||
|         var pathBase = Configuration["PATH_BASE"]; | ||||
|         if (!string.IsNullOrEmpty(pathBase)) | ||||
|         { | ||||
|             loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase); | ||||
|             app.UsePathBase(pathBase); | ||||
|         } | ||||
| 
 | ||||
|         public static IServiceCollection AddCustomMvc(this IServiceCollection services) | ||||
|         { | ||||
|             // Add framework services. | ||||
|             services.AddControllers(options => | ||||
|                 { | ||||
|                     options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | ||||
|                 }) | ||||
|                 // Added for functional tests | ||||
|                 .AddApplicationPart(typeof(OrdersController).Assembly) | ||||
|                 .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true) | ||||
|                 .SetCompatibilityVersion(CompatibilityVersion.Version_3_0); | ||||
| 
 | ||||
|             services.AddCors(options => | ||||
|         app.UseSwagger() | ||||
|             .UseSwaggerUI(c => | ||||
|             { | ||||
|                 options.AddPolicy("CorsPolicy", | ||||
|                     builder => builder | ||||
|                     .SetIsOriginAllowed((host) => true) | ||||
|                     .AllowAnyMethod() | ||||
|                     .AllowAnyHeader() | ||||
|                     .AllowCredentials()); | ||||
|                 c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1"); | ||||
|                 c.OAuthClientId("orderingswaggerui"); | ||||
|                 c.OAuthAppName("Ordering Swagger UI"); | ||||
|             }); | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
|         app.UseRouting(); | ||||
|         app.UseCors("CorsPolicy"); | ||||
|         ConfigureAuth(app); | ||||
| 
 | ||||
|         public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | ||||
|         app.UseEndpoints(endpoints => | ||||
|         { | ||||
|             var hcBuilder = services.AddHealthChecks(); | ||||
| 
 | ||||
|             hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); | ||||
| 
 | ||||
|             hcBuilder | ||||
|                 .AddSqlServer( | ||||
|                     configuration["ConnectionString"], | ||||
|                     name: "OrderingDB-check", | ||||
|                     tags: new string[] { "orderingdb" }); | ||||
| 
 | ||||
|             if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             endpoints.MapGrpcService<OrderingService>(); | ||||
|             endpoints.MapDefaultControllerRoute(); | ||||
|             endpoints.MapControllers(); | ||||
|             endpoints.MapGet("/_proto/", async ctx => | ||||
|             { | ||||
|                 hcBuilder | ||||
|                     .AddAzureServiceBusTopic( | ||||
|                         configuration["EventBusConnection"], | ||||
|                         topicName: "eshop_event_bus", | ||||
|                         name: "ordering-servicebus-check", | ||||
|                         tags: new string[] { "servicebus" }); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 hcBuilder | ||||
|                     .AddRabbitMQ( | ||||
|                         $"amqp://{configuration["EventBusConnection"]}", | ||||
|                         name: "ordering-rabbitmqbus-check", | ||||
|                         tags: new string[] { "rabbitmqbus" }); | ||||
|             } | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
| 
 | ||||
|         public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) | ||||
|         { | ||||
|             services.AddDbContext<OrderingContext>(options => | ||||
|                    { | ||||
|                        options.UseSqlServer(configuration["ConnectionString"], | ||||
|                            sqlServerOptionsAction: sqlOptions => | ||||
|                            { | ||||
|                                sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); | ||||
|                                sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | ||||
|                            }); | ||||
|                    }, | ||||
|                        ServiceLifetime.Scoped  //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) | ||||
|                    ); | ||||
| 
 | ||||
|             services.AddDbContext<IntegrationEventLogContext>(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); | ||||
|                                      }); | ||||
|                 ctx.Response.ContentType = "text/plain"; | ||||
|                 using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read); | ||||
|                 using var sr = new StreamReader(fs); | ||||
|                 while (!sr.EndOfStream) | ||||
|                 { | ||||
|                     var line = await sr.ReadLineAsync(); | ||||
|                     if (line != "/* >>" || line != "<< */") | ||||
|                     { | ||||
|                         await ctx.Response.WriteAsync(line); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
| 
 | ||||
|         public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) | ||||
|         { | ||||
|             services.AddSwaggerGen(options => | ||||
|             endpoints.MapHealthChecks("/hc", new HealthCheckOptions() | ||||
|             { | ||||
|                 options.DescribeAllEnumsAsStrings(); | ||||
|                 options.SwaggerDoc("v1", new OpenApiInfo | ||||
|                 { | ||||
|                     Title = "eShopOnContainers - Ordering HTTP API", | ||||
|                     Version = "v1", | ||||
|                     Description = "The Ordering Service HTTP API" | ||||
|                 }); | ||||
|                 options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme | ||||
|                 { | ||||
|                     Type = SecuritySchemeType.OAuth2, | ||||
|                     Flows = new OpenApiOAuthFlows() | ||||
|                     { | ||||
|                         Implicit = new OpenApiOAuthFlow() | ||||
|                         { | ||||
|                             AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"), | ||||
|                             TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"), | ||||
|                             Scopes = new Dictionary<string, string>() | ||||
|                             { | ||||
|                                 { "orders", "Ordering API" } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|                 options.OperationFilter<AuthorizeCheckOperationFilter>(); | ||||
|                 Predicate = _ => true, | ||||
|                 ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | ||||
|             }); | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
| 
 | ||||
|         public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) | ||||
|         { | ||||
|             services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | ||||
|             services.AddTransient<IIdentityService, IdentityService>(); | ||||
|             services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | ||||
|                 sp => (DbConnection c) => new IntegrationEventLogService(c)); | ||||
| 
 | ||||
|             services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>(); | ||||
| 
 | ||||
|             if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             endpoints.MapHealthChecks("/liveness", new HealthCheckOptions | ||||
|             { | ||||
|                 services.AddSingleton<IServiceBusPersisterConnection>(sp => | ||||
|                 { | ||||
|                     var serviceBusConnectionString = configuration["EventBusConnection"]; | ||||
|                     var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); | ||||
|                     var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                     return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); | ||||
|                 }); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 services.AddSingleton<IRabbitMQPersistentConnection>(sp => | ||||
|                 { | ||||
|                     var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | ||||
| 
 | ||||
| 
 | ||||
|                     var factory = new ConnectionFactory() | ||||
|                     { | ||||
|                         HostName = configuration["EventBusConnection"], | ||||
|                         DispatchConsumersAsync = true | ||||
|                     }; | ||||
| 
 | ||||
|                     if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) | ||||
|                     { | ||||
|                         factory.UserName = configuration["EventBusUserName"]; | ||||
|                     } | ||||
| 
 | ||||
|                     if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) | ||||
|                     { | ||||
|                         factory.Password = configuration["EventBusPassword"]; | ||||
|                     } | ||||
| 
 | ||||
|                     var retryCount = 5; | ||||
|                     if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | ||||
|                     { | ||||
|                         retryCount = int.Parse(configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
| 
 | ||||
|                     return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
| 
 | ||||
|         public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) | ||||
|         { | ||||
|             services.AddOptions(); | ||||
|             services.Configure<OrderingSettings>(configuration); | ||||
|             services.Configure<ApiBehaviorOptions>(options => | ||||
|             { | ||||
|                 options.InvalidModelStateResponseFactory = context => | ||||
|                 { | ||||
|                     var problemDetails = new ValidationProblemDetails(context.ModelState) | ||||
|                     { | ||||
|                         Instance = context.HttpContext.Request.Path, | ||||
|                         Status = StatusCodes.Status400BadRequest, | ||||
|                         Detail = "Please refer to the errors property for additional details." | ||||
|                     }; | ||||
| 
 | ||||
|                     return new BadRequestObjectResult(problemDetails) | ||||
|                     { | ||||
|                         ContentTypes = { "application/problem+json", "application/problem+xml" } | ||||
|                     }; | ||||
|                 }; | ||||
|                 Predicate = r => r.Name.Contains("self") | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
|         ConfigureEventBus(app); | ||||
|     } | ||||
| 
 | ||||
|         public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) | ||||
|         { | ||||
|             if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
|                 { | ||||
|                     var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|                     return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                         eventBusSubcriptionsManager, iLifetimeScope); | ||||
|                 }); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | ||||
|                 { | ||||
|                     var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
|                     var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | ||||
|                     var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                     var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | ||||
|                     var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
|     private void ConfigureEventBus(IApplicationBuilder app) | ||||
|     { | ||||
|         var eventBus = app.ApplicationServices.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>(); | ||||
| 
 | ||||
|                     var retryCount = 5; | ||||
|                     if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | ||||
|                     { | ||||
|                         retryCount = int.Parse(configuration["EventBusRetryCount"]); | ||||
|                     } | ||||
|         eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<GracePeriodConfirmedIntegrationEvent, IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderStockConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderStockRejectedIntegrationEvent, IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>(); | ||||
|         eventBus.Subscribe<OrderPaymentSucceededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>>(); | ||||
|     } | ||||
| 
 | ||||
|                     return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
| 
 | ||||
|         public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | ||||
|         { | ||||
|             // prevent from mapping "sub" claim to nameidentifier. | ||||
|             JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | ||||
| 
 | ||||
|             var identityUrl = configuration.GetValue<string>("IdentityUrl"); | ||||
| 
 | ||||
|             services.AddAuthentication(options => | ||||
|             { | ||||
|                 options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; | ||||
|                 options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; | ||||
| 
 | ||||
|             }).AddJwtBearer(options => | ||||
|             { | ||||
|                 options.Authority = identityUrl; | ||||
|                 options.RequireHttpsMetadata = false; | ||||
|                 options.Audience = "orders"; | ||||
|             }); | ||||
| 
 | ||||
|             return services; | ||||
|         } | ||||
|     protected virtual void ConfigureAuth(IApplicationBuilder app) | ||||
|     { | ||||
|         app.UseAuthentication(); | ||||
|         app.UseAuthorization(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static class CustomExtensionsMethods | ||||
| { | ||||
|     public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddApplicationInsightsTelemetry(configuration); | ||||
|         services.AddApplicationInsightsKubernetesEnricher(); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomMvc(this IServiceCollection services) | ||||
|     { | ||||
|         // Add framework services. | ||||
|         services.AddControllers(options => | ||||
|             { | ||||
|                 options.Filters.Add(typeof(HttpGlobalExceptionFilter)); | ||||
|             }) | ||||
|             // Added for functional tests | ||||
|             .AddApplicationPart(typeof(OrdersController).Assembly) | ||||
|             .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true) | ||||
|             .SetCompatibilityVersion(CompatibilityVersion.Version_3_0); | ||||
| 
 | ||||
|         services.AddCors(options => | ||||
|         { | ||||
|             options.AddPolicy("CorsPolicy", | ||||
|                 builder => builder | ||||
|                 .SetIsOriginAllowed((host) => true) | ||||
|                 .AllowAnyMethod() | ||||
|                 .AllowAnyHeader() | ||||
|                 .AllowCredentials()); | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         var hcBuilder = services.AddHealthChecks(); | ||||
| 
 | ||||
|         hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); | ||||
| 
 | ||||
|         hcBuilder | ||||
|             .AddSqlServer( | ||||
|                 configuration["ConnectionString"], | ||||
|                 name: "OrderingDB-check", | ||||
|                 tags: new string[] { "orderingdb" }); | ||||
| 
 | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             hcBuilder | ||||
|                 .AddAzureServiceBusTopic( | ||||
|                     configuration["EventBusConnection"], | ||||
|                     topicName: "eshop_event_bus", | ||||
|                     name: "ordering-servicebus-check", | ||||
|                     tags: new string[] { "servicebus" }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             hcBuilder | ||||
|                 .AddRabbitMQ( | ||||
|                     $"amqp://{configuration["EventBusConnection"]}", | ||||
|                     name: "ordering-rabbitmqbus-check", | ||||
|                     tags: new string[] { "rabbitmqbus" }); | ||||
|         } | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomDbContext(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddDbContext<OrderingContext>(options => | ||||
|                 { | ||||
|                     options.UseSqlServer(configuration["ConnectionString"], | ||||
|                         sqlServerOptionsAction: sqlOptions => | ||||
|                         { | ||||
|                             sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); | ||||
|                             sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | ||||
|                         }); | ||||
|                 }, | ||||
|                     ServiceLifetime.Scoped  //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) | ||||
|                 ); | ||||
| 
 | ||||
|         services.AddDbContext<IntegrationEventLogContext>(options => | ||||
|         { | ||||
|             options.UseSqlServer(configuration["ConnectionString"], | ||||
|                                     sqlServerOptionsAction: sqlOptions => | ||||
|                                     { | ||||
|                                         sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); | ||||
|                                         //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency  | ||||
|                                         sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); | ||||
|                                     }); | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddSwaggerGen(options => | ||||
|         { | ||||
|             options.DescribeAllEnumsAsStrings(); | ||||
|             options.SwaggerDoc("v1", new OpenApiInfo | ||||
|             { | ||||
|                 Title = "eShopOnContainers - Ordering HTTP API", | ||||
|                 Version = "v1", | ||||
|                 Description = "The Ordering Service HTTP API" | ||||
|             }); | ||||
|             options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme | ||||
|             { | ||||
|                 Type = SecuritySchemeType.OAuth2, | ||||
|                 Flows = new OpenApiOAuthFlows() | ||||
|                 { | ||||
|                     Implicit = new OpenApiOAuthFlow() | ||||
|                     { | ||||
|                         AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"), | ||||
|                         TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"), | ||||
|                         Scopes = new Dictionary<string, string>() | ||||
|                         { | ||||
|                             { "orders", "Ordering API" } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             options.OperationFilter<AuthorizeCheckOperationFilter>(); | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | ||||
|         services.AddTransient<IIdentityService, IdentityService>(); | ||||
|         services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( | ||||
|             sp => (DbConnection c) => new IntegrationEventLogService(c)); | ||||
| 
 | ||||
|         services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>(); | ||||
| 
 | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             services.AddSingleton<IServiceBusPersisterConnection>(sp => | ||||
|             { | ||||
|                 var serviceBusConnectionString = configuration["EventBusConnection"]; | ||||
|                 var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); | ||||
|                 var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
| 
 | ||||
|                 return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); | ||||
|             }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             services.AddSingleton<IRabbitMQPersistentConnection>(sp => | ||||
|             { | ||||
|                 var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); | ||||
| 
 | ||||
| 
 | ||||
|                 var factory = new ConnectionFactory() | ||||
|                 { | ||||
|                     HostName = configuration["EventBusConnection"], | ||||
|                     DispatchConsumersAsync = true | ||||
|                 }; | ||||
| 
 | ||||
|                 if (!string.IsNullOrEmpty(configuration["EventBusUserName"])) | ||||
|                 { | ||||
|                     factory.UserName = configuration["EventBusUserName"]; | ||||
|                 } | ||||
| 
 | ||||
|                 if (!string.IsNullOrEmpty(configuration["EventBusPassword"])) | ||||
|                 { | ||||
|                     factory.Password = configuration["EventBusPassword"]; | ||||
|                 } | ||||
| 
 | ||||
|                 var retryCount = 5; | ||||
|                 if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | ||||
|                 { | ||||
|                     retryCount = int.Parse(configuration["EventBusRetryCount"]); | ||||
|                 } | ||||
| 
 | ||||
|                 return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomConfiguration(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         services.AddOptions(); | ||||
|         services.Configure<OrderingSettings>(configuration); | ||||
|         services.Configure<ApiBehaviorOptions>(options => | ||||
|         { | ||||
|             options.InvalidModelStateResponseFactory = context => | ||||
|             { | ||||
|                 var problemDetails = new ValidationProblemDetails(context.ModelState) | ||||
|                 { | ||||
|                     Instance = context.HttpContext.Request.Path, | ||||
|                     Status = StatusCodes.Status400BadRequest, | ||||
|                     Detail = "Please refer to the errors property for additional details." | ||||
|                 }; | ||||
| 
 | ||||
|                 return new BadRequestObjectResult(problemDetails) | ||||
|                 { | ||||
|                     ContentTypes = { "application/problem+json", "application/problem+xml" } | ||||
|                 }; | ||||
|             }; | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         if (configuration.GetValue<bool>("AzureServiceBusEnabled")) | ||||
|         { | ||||
|             services.AddSingleton<IEventBus, EventBusServiceBus>(sp => | ||||
|             { | ||||
|                 var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>(); | ||||
|                 var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                 var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>(); | ||||
|                 var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|                 return new EventBusServiceBus(serviceBusPersisterConnection, logger, | ||||
|                     eventBusSubcriptionsManager, iLifetimeScope); | ||||
|             }); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp => | ||||
|             { | ||||
|                 var subscriptionClientName = configuration["SubscriptionClientName"]; | ||||
|                 var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>(); | ||||
|                 var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>(); | ||||
|                 var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>(); | ||||
|                 var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|                 var retryCount = 5; | ||||
|                 if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"])) | ||||
|                 { | ||||
|                     retryCount = int.Parse(configuration["EventBusRetryCount"]); | ||||
|                 } | ||||
| 
 | ||||
|                 return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| 
 | ||||
|     public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | ||||
|     { | ||||
|         // prevent from mapping "sub" claim to nameidentifier. | ||||
|         JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | ||||
| 
 | ||||
|         var identityUrl = configuration.GetValue<string>("IdentityUrl"); | ||||
| 
 | ||||
|         services.AddAuthentication(options => | ||||
|         { | ||||
|             options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; | ||||
|             options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme; | ||||
| 
 | ||||
|         }).AddJwtBearer(options => | ||||
|         { | ||||
|             options.Authority = identityUrl; | ||||
|             options.RequireHttpsMetadata = false; | ||||
|             options.Audience = "orders"; | ||||
|         }); | ||||
| 
 | ||||
|         return services; | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user