Moved all usings to globalusing file
This commit is contained in:
parent
818995d8b8
commit
666fba815f
@ -1,23 +1,16 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
|
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Behaviors
|
|
||||||
{
|
{
|
||||||
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;
|
_logger.LogInformation("----- Handling command {CommandName} ({@Command})", request.GetGenericTypeName(), request);
|
||||||
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger) => _logger = logger;
|
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)
|
return response;
|
||||||
{
|
|
||||||
_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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,74 +1,64 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Logging;
|
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;
|
_dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext));
|
||||||
private readonly OrderingContext _dbContext;
|
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentException(nameof(orderingIntegrationEventService));
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_logger = logger ?? throw new ArgumentException(nameof(ILogger));
|
||||||
|
}
|
||||||
|
|
||||||
public TransactionBehaviour(OrderingContext dbContext,
|
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
|
||||||
IOrderingIntegrationEventService orderingIntegrationEventService,
|
{
|
||||||
ILogger<TransactionBehaviour<TRequest, TResponse>> logger)
|
var response = default(TResponse);
|
||||||
|
var typeName = request.GetGenericTypeName();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_dbContext = dbContext ?? throw new ArgumentException(nameof(OrderingContext));
|
if (_dbContext.HasActiveTransaction)
|
||||||
_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)
|
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 () =>
|
return response;
|
||||||
{
|
}
|
||||||
Guid transactionId;
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "ERROR Handling transaction for {CommandName} ({@Command})", typeName, request);
|
||||||
|
|
||||||
using (var transaction = await _dbContext.BeginTransactionAsync())
|
throw;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,46 +1,36 @@
|
|||||||
using FluentValidation;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Behaviors;
|
||||||
using MediatR;
|
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ordering.Domain.Exceptions;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Behaviors
|
public class ValidatorBehavior<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;
|
_validators = validators;
|
||||||
private readonly IValidator<TRequest>[] _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.LogWarning("Validation errors - {CommandType} - Command: {@Command} - Errors: {@ValidationErrors}", typeName, request, failures);
|
||||||
_logger = logger;
|
|
||||||
|
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)
|
return await 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +1,16 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
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(int orderNumber)
|
||||||
public CancelOrderCommand()
|
{
|
||||||
{
|
OrderNumber = orderNumber;
|
||||||
|
|
||||||
}
|
|
||||||
public CancelOrderCommand(int orderNumber)
|
|
||||||
{
|
|
||||||
OrderNumber = orderNumber;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,48 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
// Regular CommandHandler
|
||||||
|
public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, bool>
|
||||||
{
|
{
|
||||||
// Regular CommandHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
public class CancelOrderCommandHandler : IRequestHandler<CancelOrderCommand, bool>
|
|
||||||
|
public CancelOrderCommandHandler(IOrderRepository orderRepository)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
// Use for Idempotency in Command process
|
/// Handler which processes the command when
|
||||||
public class CancelOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CancelOrderCommand, bool>
|
/// customer executes cancel order from app
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> Handle(CancelOrderCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
public CancelOrderIdentifiedCommandHandler(
|
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||||
IMediator mediator,
|
if (orderToUpdate == null)
|
||||||
IRequestManager requestManager,
|
|
||||||
ILogger<IdentifiedCommandHandler<CancelOrderCommand, bool>> logger)
|
|
||||||
: base(mediator, requestManager, logger)
|
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
orderToUpdate.SetCancelledStatus();
|
||||||
{
|
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
|
||||||
return true; // Ignore duplicate requests for processing order.
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
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
|
// 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
|
[DataMember]
|
||||||
// In this case, its immutability is achieved by having all the setters as private
|
private readonly List<OrderItemDTO> _orderItems;
|
||||||
// plus only being able to update the data just once, when creating the object through its constructor.
|
|
||||||
// References on Immutable Commands:
|
|
||||||
// http://cqrs.nu/Faq
|
|
||||||
// https://docs.spine3.org/motivation/immutability.html
|
|
||||||
// http://blog.gauffin.org/2012/06/griffin-container-introducing-command-support/
|
|
||||||
// https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/how-to-implement-a-lightweight-class-with-auto-implemented-properties
|
|
||||||
|
|
||||||
[DataContract]
|
[DataMember]
|
||||||
public class CreateOrderCommand
|
public string UserId { get; private set; }
|
||||||
: IRequest<bool>
|
|
||||||
|
[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]
|
_orderItems = new List<OrderItemDTO>();
|
||||||
private readonly List<OrderItemDTO> _orderItems;
|
}
|
||||||
|
|
||||||
[DataMember]
|
public CreateOrderCommand(List<BasketItem> basketItems, string userId, string userName, string city, string street, string state, string country, string zipcode,
|
||||||
public string UserId { get; private set; }
|
string cardNumber, string cardHolderName, DateTime cardExpiration,
|
||||||
|
string cardSecurityNumber, int cardTypeId) : this()
|
||||||
[DataMember]
|
{
|
||||||
public string UserName { get; private set; }
|
_orderItems = basketItems.ToOrderItemsDTO().ToList();
|
||||||
|
UserId = userId;
|
||||||
[DataMember]
|
UserName = userName;
|
||||||
public string City { get; private set; }
|
City = city;
|
||||||
|
Street = street;
|
||||||
[DataMember]
|
State = state;
|
||||||
public string Street { get; private set; }
|
Country = country;
|
||||||
|
ZipCode = zipcode;
|
||||||
[DataMember]
|
CardNumber = cardNumber;
|
||||||
public string State { get; private set; }
|
CardHolderName = cardHolderName;
|
||||||
|
CardExpiration = cardExpiration;
|
||||||
[DataMember]
|
CardSecurityNumber = cardSecurityNumber;
|
||||||
public string Country { get; private set; }
|
CardTypeId = cardTypeId;
|
||||||
|
CardExpiration = cardExpiration;
|
||||||
[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 record OrderItemDTO
|
public record OrderItemDTO
|
||||||
{
|
{
|
||||||
public int ProductId { get; init; }
|
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;
|
private readonly IOrderRepository _orderRepository;
|
||||||
using global::Ordering.API.Application.IntegrationEvents;
|
private readonly IIdentityService _identityService;
|
||||||
using global::Ordering.API.Application.IntegrationEvents.Events;
|
private readonly IMediator _mediator;
|
||||||
using MediatR;
|
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
|
private readonly ILogger<CreateOrderCommandHandler> _logger;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
// Regular CommandHandler
|
// Using DI to inject infrastructure persistence Repositories
|
||||||
public class CreateOrderCommandHandler
|
public CreateOrderCommandHandler(IMediator mediator,
|
||||||
: IRequestHandler<CreateOrderCommand, bool>
|
IOrderingIntegrationEventService orderingIntegrationEventService,
|
||||||
|
IOrderRepository orderRepository,
|
||||||
|
IIdentityService identityService,
|
||||||
|
ILogger<CreateOrderCommandHandler> logger)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
private readonly IIdentityService _identityService;
|
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
||||||
private readonly IMediator _mediator;
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
||||||
private readonly ILogger<CreateOrderCommandHandler> _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Handle(CreateOrderCommand message, CancellationToken cancellationToken)
|
||||||
// Use for Idempotency in Command process
|
|
||||||
public class CreateOrderIdentifiedCommandHandler : IdentifiedCommandHandler<CreateOrderCommand, bool>
|
|
||||||
{
|
{
|
||||||
public CreateOrderIdentifiedCommandHandler(
|
// Add Integration event to clean the basket
|
||||||
IMediator mediator,
|
var orderStartedIntegrationEvent = new OrderStartedIntegrationEvent(message.UserId);
|
||||||
IRequestManager requestManager,
|
await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStartedIntegrationEvent);
|
||||||
ILogger<IdentifiedCommandHandler<CreateOrderCommand, bool>> logger)
|
|
||||||
: base(mediator, requestManager, logger)
|
// 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()
|
_logger.LogInformation("----- Creating Order - Order: {@Order}", order);
|
||||||
{
|
|
||||||
return true; // Ignore duplicate requests for creating 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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Ordering.API.Application.Models;
|
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
BuyerId = buyerId;
|
||||||
public string BuyerId { get; private set; }
|
Items = items;
|
||||||
|
|
||||||
public IEnumerable<BasketItem> Items { get; private set; }
|
|
||||||
|
|
||||||
public CreateOrderDraftCommand(string buyerId, IEnumerable<BasketItem> 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;
|
private readonly IOrderRepository _orderRepository;
|
||||||
using global::Ordering.API.Application.Models;
|
private readonly IIdentityService _identityService;
|
||||||
using MediatR;
|
private readonly IMediator _mediator;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
|
|
||||||
|
|
||||||
// Regular CommandHandler
|
// Using DI to inject infrastructure persistence Repositories
|
||||||
public class CreateOrderDraftCommandHandler
|
public CreateOrderDraftCommandHandler(IMediator mediator, IIdentityService identityService)
|
||||||
: IRequestHandler<CreateOrderDraftCommand, OrderDraftDTO>
|
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
||||||
private readonly IIdentityService _identityService;
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<OrderDraftDTO> Handle(CreateOrderDraftCommand message, CancellationToken cancellationToken)
|
||||||
public record OrderDraftDTO
|
|
||||||
{
|
{
|
||||||
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()
|
order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units);
|
||||||
{
|
|
||||||
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()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using System;
|
|
||||||
|
|
||||||
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>
|
public T Command { get; }
|
||||||
where T : IRequest<R>
|
public Guid Id { get; }
|
||||||
|
public IdentifiedCommand(T command, Guid id)
|
||||||
{
|
{
|
||||||
public T Command { get; }
|
Command = command;
|
||||||
public Guid Id { get; }
|
Id = id;
|
||||||
public IdentifiedCommand(T command, Guid id)
|
|
||||||
{
|
|
||||||
Command = command;
|
|
||||||
Id = id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,116 +1,107 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
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
|
/// <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>
|
private readonly IMediator _mediator;
|
||||||
/// Provides a base implementation for handling duplicate request and ensuring idempotent updates, in the cases where
|
private readonly IRequestManager _requestManager;
|
||||||
/// a requestid sent by client is used to detect duplicate requests.
|
private readonly ILogger<IdentifiedCommandHandler<T, R>> _logger;
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the command handler that performs the operation if request is not duplicated</typeparam>
|
public IdentifiedCommandHandler(
|
||||||
/// <typeparam name="R">Return value of the inner command handler</typeparam>
|
IMediator mediator,
|
||||||
public class IdentifiedCommandHandler<T, R> : IRequestHandler<IdentifiedCommand<T, R>, R>
|
IRequestManager requestManager,
|
||||||
where T : IRequest<R>
|
ILogger<IdentifiedCommandHandler<T, R>> logger)
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
_mediator = mediator;
|
||||||
private readonly IRequestManager _requestManager;
|
_requestManager = requestManager;
|
||||||
private readonly ILogger<IdentifiedCommandHandler<T, R>> _logger;
|
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public IdentifiedCommandHandler(
|
/// <summary>
|
||||||
IMediator mediator,
|
/// Creates the result value to return if a previous request was found
|
||||||
IRequestManager requestManager,
|
/// </summary>
|
||||||
ILogger<IdentifiedCommandHandler<T, R>> logger)
|
/// <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;
|
return CreateResultForDuplicateRequest();
|
||||||
_requestManager = requestManager;
|
|
||||||
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <summary>
|
|
||||||
/// Creates the result value to return if a previous request was found
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected virtual R CreateResultForDuplicateRequest()
|
|
||||||
{
|
{
|
||||||
return default(R);
|
await _requestManager.CreateRequestForCommandAsync<T>(message.Id);
|
||||||
}
|
try
|
||||||
|
|
||||||
/// <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)
|
|
||||||
{
|
{
|
||||||
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);
|
return default(R);
|
||||||
try
|
|
||||||
{
|
|
||||||
var command = message.Command;
|
|
||||||
var commandName = command.GetGenericTypeName();
|
|
||||||
var idProperty = string.Empty;
|
|
||||||
var commandId = string.Empty;
|
|
||||||
|
|
||||||
switch (command)
|
|
||||||
{
|
|
||||||
case CreateOrderCommand createOrderCommand:
|
|
||||||
idProperty = nameof(createOrderCommand.UserId);
|
|
||||||
commandId = createOrderCommand.UserId;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CancelOrderCommand cancelOrderCommand:
|
|
||||||
idProperty = nameof(cancelOrderCommand.OrderNumber);
|
|
||||||
commandId = $"{cancelOrderCommand.OrderNumber}";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ShipOrderCommand shipOrderCommand:
|
|
||||||
idProperty = nameof(shipOrderCommand.OrderNumber);
|
|
||||||
commandId = $"{shipOrderCommand.OrderNumber}";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
idProperty = "Id?";
|
|
||||||
commandId = "n/a";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
commandName,
|
|
||||||
idProperty,
|
|
||||||
commandId,
|
|
||||||
command);
|
|
||||||
|
|
||||||
// Send the embeded business command to mediator so it runs its related CommandHandler
|
|
||||||
var result = await _mediator.Send(command, cancellationToken);
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
|
||||||
"----- Command result: {@Result} - {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
result,
|
|
||||||
commandName,
|
|
||||||
idProperty,
|
|
||||||
commandId,
|
|
||||||
command);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return default(R);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
OrderNumber = orderNumber;
|
||||||
[DataMember]
|
|
||||||
public int OrderNumber { get; private set; }
|
|
||||||
|
|
||||||
public SetAwaitingValidationOrderStatusCommand(int orderNumber)
|
|
||||||
{
|
|
||||||
OrderNumber = orderNumber;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,48 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
// Regular CommandHandler
|
||||||
|
public class SetAwaitingValidationOrderStatusCommandHandler : IRequestHandler<SetAwaitingValidationOrderStatusCommand, bool>
|
||||||
{
|
{
|
||||||
// Regular CommandHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
public class SetAwaitingValidationOrderStatusCommandHandler : IRequestHandler<SetAwaitingValidationOrderStatusCommand, bool>
|
|
||||||
|
public SetAwaitingValidationOrderStatusCommandHandler(IOrderRepository orderRepository)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
// Use for Idempotency in Command process
|
/// Handler which processes the command when
|
||||||
public class SetAwaitingValidationIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool>
|
/// graceperiod has finished
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> Handle(SetAwaitingValidationOrderStatusCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
public SetAwaitingValidationIdentifiedOrderStatusCommandHandler(
|
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||||
IMediator mediator,
|
if (orderToUpdate == null)
|
||||||
IRequestManager requestManager,
|
|
||||||
ILogger<IdentifiedCommandHandler<SetAwaitingValidationOrderStatusCommand, bool>> logger)
|
|
||||||
: base(mediator, requestManager, logger)
|
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
orderToUpdate.SetAwaitingValidationStatus();
|
||||||
{
|
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
|
||||||
return true; // Ignore duplicate requests for processing order.
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
OrderNumber = orderNumber;
|
||||||
[DataMember]
|
|
||||||
public int OrderNumber { get; private set; }
|
|
||||||
|
|
||||||
public SetPaidOrderStatusCommand(int orderNumber)
|
|
||||||
{
|
|
||||||
OrderNumber = orderNumber;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,51 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
// Regular CommandHandler
|
||||||
|
public class SetPaidOrderStatusCommandHandler : IRequestHandler<SetPaidOrderStatusCommand, bool>
|
||||||
{
|
{
|
||||||
// Regular CommandHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
public class SetPaidOrderStatusCommandHandler : IRequestHandler<SetPaidOrderStatusCommand, bool>
|
|
||||||
|
public SetPaidOrderStatusCommandHandler(IOrderRepository orderRepository)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
// Use for Idempotency in Command process
|
/// Handler which processes the command when
|
||||||
public class SetPaidIdentifiedOrderStatusCommandHandler : IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool>
|
/// Shipment service confirms the payment
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> Handle(SetPaidOrderStatusCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
public SetPaidIdentifiedOrderStatusCommandHandler(
|
// Simulate a work time for validating the payment
|
||||||
IMediator mediator,
|
await Task.Delay(10000, cancellationToken);
|
||||||
IRequestManager requestManager,
|
|
||||||
ILogger<IdentifiedCommandHandler<SetPaidOrderStatusCommand, bool>> logger)
|
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||||
: base(mediator, requestManager, logger)
|
if (orderToUpdate == null)
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
orderToUpdate.SetPaidStatus();
|
||||||
{
|
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
|
||||||
return true; // Ignore duplicate requests for processing order.
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
OrderNumber = orderNumber;
|
||||||
[DataMember]
|
|
||||||
public int OrderNumber { get; private set; }
|
|
||||||
|
|
||||||
public SetStockConfirmedOrderStatusCommand(int orderNumber)
|
|
||||||
{
|
|
||||||
OrderNumber = orderNumber;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,51 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
// Regular CommandHandler
|
||||||
|
public class SetStockConfirmedOrderStatusCommandHandler : IRequestHandler<SetStockConfirmedOrderStatusCommand, bool>
|
||||||
{
|
{
|
||||||
// Regular CommandHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
public class SetStockConfirmedOrderStatusCommandHandler : IRequestHandler<SetStockConfirmedOrderStatusCommand, bool>
|
|
||||||
|
public SetStockConfirmedOrderStatusCommandHandler(IOrderRepository orderRepository)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
// Use for Idempotency in Command process
|
/// Handler which processes the command when
|
||||||
public class SetStockConfirmedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>
|
/// Stock service confirms the request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> Handle(SetStockConfirmedOrderStatusCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
public SetStockConfirmedOrderStatusIdenfifiedCommandHandler(
|
// Simulate a work time for confirming the stock
|
||||||
IMediator mediator,
|
await Task.Delay(10000, cancellationToken);
|
||||||
IRequestManager requestManager,
|
|
||||||
ILogger<IdentifiedCommandHandler<SetStockConfirmedOrderStatusCommand, bool>> logger)
|
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||||
: base(mediator, requestManager, logger)
|
if (orderToUpdate == null)
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
orderToUpdate.SetStockConfirmedStatus();
|
||||||
{
|
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
|
||||||
return true; // Ignore duplicate requests for processing order.
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
OrderNumber = orderNumber;
|
||||||
[DataMember]
|
OrderStockItems = orderStockItems;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,52 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
// Regular CommandHandler
|
||||||
|
public class SetStockRejectedOrderStatusCommandHandler : IRequestHandler<SetStockRejectedOrderStatusCommand, bool>
|
||||||
{
|
{
|
||||||
// Regular CommandHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
public class SetStockRejectedOrderStatusCommandHandler : IRequestHandler<SetStockRejectedOrderStatusCommand, bool>
|
|
||||||
|
public SetStockRejectedOrderStatusCommandHandler(IOrderRepository orderRepository)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
// Use for Idempotency in Command process
|
/// Handler which processes the command when
|
||||||
public class SetStockRejectedOrderStatusIdenfifiedCommandHandler : IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>
|
/// Stock service rejects the request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> Handle(SetStockRejectedOrderStatusCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
public SetStockRejectedOrderStatusIdenfifiedCommandHandler(
|
// Simulate a work time for rejecting the stock
|
||||||
IMediator mediator,
|
await Task.Delay(10000, cancellationToken);
|
||||||
IRequestManager requestManager,
|
|
||||||
ILogger<IdentifiedCommandHandler<SetStockRejectedOrderStatusCommand, bool>> logger)
|
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||||
: base(mediator, requestManager, logger)
|
if (orderToUpdate == null)
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
orderToUpdate.SetCancelledStatusWhenStockIsRejected(command.OrderStockItems);
|
||||||
{
|
|
||||||
return true; // Ignore duplicate requests for processing order.
|
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
OrderNumber = orderNumber;
|
||||||
[DataMember]
|
|
||||||
public int OrderNumber { get; private set; }
|
|
||||||
|
|
||||||
public ShipOrderCommand(int orderNumber)
|
|
||||||
{
|
|
||||||
OrderNumber = orderNumber;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,48 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.Commands
|
// Regular CommandHandler
|
||||||
|
public class ShipOrderCommandHandler : IRequestHandler<ShipOrderCommand, bool>
|
||||||
{
|
{
|
||||||
// Regular CommandHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
public class ShipOrderCommandHandler : IRequestHandler<ShipOrderCommand, bool>
|
|
||||||
|
public ShipOrderCommandHandler(IOrderRepository orderRepository)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
// Use for Idempotency in Command process
|
/// Handler which processes the command when
|
||||||
public class ShipOrderIdentifiedCommandHandler : IdentifiedCommandHandler<ShipOrderCommand, bool>
|
/// administrator executes ship order from app
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> Handle(ShipOrderCommand command, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
public ShipOrderIdentifiedCommandHandler(
|
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
|
||||||
IMediator mediator,
|
if (orderToUpdate == null)
|
||||||
IRequestManager requestManager,
|
|
||||||
ILogger<IdentifiedCommandHandler<ShipOrderCommand, bool>> logger)
|
|
||||||
: base(mediator, requestManager, logger)
|
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CreateResultForDuplicateRequest()
|
orderToUpdate.SetShippedStatus();
|
||||||
{
|
return await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
|
||||||
return true; // Ignore duplicate requests for processing order.
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified;
|
||||||
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 Ordering.API.Application.DomainEventHandlers.BuyerAndPaymentMethodVerified
|
public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler
|
||||||
|
: INotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>
|
||||||
{
|
{
|
||||||
public class UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
: INotificationHandler<BuyerAndPaymentMethodVerifiedDomainEvent>
|
private readonly ILoggerFactory _logger;
|
||||||
|
|
||||||
|
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(
|
||||||
|
IOrderRepository orderRepository, ILoggerFactory logger)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
private readonly ILoggerFactory _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler(
|
// Domain Logic comment:
|
||||||
IOrderRepository orderRepository, ILoggerFactory logger)
|
// 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)
|
||||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
public async Task Handle(BuyerAndPaymentMethodVerifiedDomainEvent buyerPaymentMethodVerifiedEvent, CancellationToken cancellationToken)
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
{
|
||||||
}
|
var orderToUpdate = await _orderRepository.GetAsync(buyerPaymentMethodVerifiedEvent.OrderId);
|
||||||
|
orderToUpdate.SetBuyerId(buyerPaymentMethodVerifiedEvent.Buyer.Id);
|
||||||
|
orderToUpdate.SetPaymentId(buyerPaymentMethodVerifiedEvent.Payment.Id);
|
||||||
|
|
||||||
// Domain Logic comment:
|
_logger.CreateLogger<UpdateOrderWhenBuyerAndPaymentMethodVerifiedDomainEventHandler>()
|
||||||
// When the Buyer and Buyer's payment method have been created or verified that they existed,
|
.LogTrace("Order with Id: {OrderId} has been successfully updated with a payment method {PaymentMethod} ({Id})",
|
||||||
// then we can update the original Order with the BuyerId and PaymentId (foreign keys)
|
buyerPaymentMethodVerifiedEvent.OrderId, nameof(buyerPaymentMethodVerifiedEvent.Payment), buyerPaymentMethodVerifiedEvent.Payment.Id);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,37 @@
|
|||||||
using MediatR;
|
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
|
||||||
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 Ordering.API.Application.DomainEventHandlers.OrderCancelled
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderCancelled;
|
||||||
|
|
||||||
|
public class OrderCancelledDomainEventHandler
|
||||||
|
: INotificationHandler<OrderCancelledDomainEvent>
|
||||||
{
|
{
|
||||||
public class OrderCancelledDomainEventHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
: INotificationHandler<OrderCancelledDomainEvent>
|
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;
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
private readonly IBuyerRepository _buyerRepository;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
private readonly ILoggerFactory _logger;
|
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_orderingIntegrationEventService = orderingIntegrationEventService;
|
||||||
|
}
|
||||||
|
|
||||||
public OrderCancelledDomainEventHandler(
|
public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken)
|
||||||
IOrderRepository orderRepository,
|
{
|
||||||
ILoggerFactory logger,
|
_logger.CreateLogger<OrderCancelledDomainEvent>()
|
||||||
IBuyerRepository buyerRepository,
|
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
|
||||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
orderCancelledDomainEvent.Order.Id, nameof(OrderStatus.Cancelled), OrderStatus.Cancelled.Id);
|
||||||
{
|
|
||||||
_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)
|
var order = await _orderRepository.GetAsync(orderCancelledDomainEvent.Order.Id);
|
||||||
{
|
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());
|
||||||
_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 orderStatusChangedToCancelledIntegrationEvent = new OrderStatusChangedToCancelledIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name);
|
||||||
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());
|
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;
|
private readonly IOrderRepository _orderRepository;
|
||||||
using MediatR;
|
private readonly ILoggerFactory _logger;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
private readonly IBuyerRepository _buyerRepository;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ordering.API.Application.IntegrationEvents;
|
|
||||||
using Ordering.API.Application.IntegrationEvents.Events;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class OrderStatusChangedToAwaitingValidationDomainEventHandler
|
public OrderStatusChangedToAwaitingValidationDomainEventHandler(
|
||||||
: INotificationHandler<OrderStatusChangedToAwaitingValidationDomainEvent>
|
IOrderRepository orderRepository, ILoggerFactory logger,
|
||||||
|
IBuyerRepository buyerRepository,
|
||||||
|
IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
private readonly ILoggerFactory _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
private readonly IBuyerRepository _buyerRepository;
|
_buyerRepository = buyerRepository;
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_orderingIntegrationEventService = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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;
|
private readonly IOrderRepository _orderRepository;
|
||||||
using MediatR;
|
private readonly ILoggerFactory _logger;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
private readonly IBuyerRepository _buyerRepository;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ordering.API.Application.IntegrationEvents;
|
|
||||||
using Ordering.API.Application.IntegrationEvents.Events;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class OrderStatusChangedToPaidDomainEventHandler
|
|
||||||
: INotificationHandler<OrderStatusChangedToPaidDomainEvent>
|
public OrderStatusChangedToPaidDomainEventHandler(
|
||||||
|
IOrderRepository orderRepository, ILoggerFactory logger,
|
||||||
|
IBuyerRepository buyerRepository,
|
||||||
|
IOrderingIntegrationEventService orderingIntegrationEventService
|
||||||
|
)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
private readonly ILoggerFactory _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
private readonly IBuyerRepository _buyerRepository;
|
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
||||||
|
|
||||||
|
|
||||||
public OrderStatusChangedToPaidDomainEventHandler(
|
|
||||||
IOrderRepository orderRepository, ILoggerFactory logger,
|
|
||||||
IBuyerRepository buyerRepository,
|
|
||||||
IOrderingIntegrationEventService orderingIntegrationEventService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
|
||||||
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_logger.CreateLogger<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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderShipped;
|
||||||
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 Ordering.API.Application.DomainEventHandlers.OrderShipped
|
public class OrderShippedDomainEventHandler
|
||||||
|
: INotificationHandler<OrderShippedDomainEvent>
|
||||||
{
|
{
|
||||||
public class OrderShippedDomainEventHandler
|
private readonly IOrderRepository _orderRepository;
|
||||||
: INotificationHandler<OrderShippedDomainEvent>
|
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;
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
private readonly IBuyerRepository _buyerRepository;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
||||||
private readonly ILoggerFactory _logger;
|
_orderingIntegrationEventService = orderingIntegrationEventService;
|
||||||
|
}
|
||||||
|
|
||||||
public OrderShippedDomainEventHandler(
|
public async Task Handle(OrderShippedDomainEvent orderShippedDomainEvent, CancellationToken cancellationToken)
|
||||||
IOrderRepository orderRepository,
|
{
|
||||||
ILoggerFactory logger,
|
_logger.CreateLogger<OrderShippedDomainEvent>()
|
||||||
IBuyerRepository buyerRepository,
|
.LogTrace("Order with Id: {OrderId} has been successfully updated to status {Status} ({Id})",
|
||||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
orderShippedDomainEvent.Order.Id, nameof(OrderStatus.Shipped), OrderStatus.Shipped.Id);
|
||||||
{
|
|
||||||
_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)
|
var order = await _orderRepository.GetAsync(orderShippedDomainEvent.Order.Id);
|
||||||
{
|
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());
|
||||||
_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 orderStatusChangedToShippedIntegrationEvent = new OrderStatusChangedToShippedIntegrationEvent(order.Id, order.OrderStatus.Name, buyer.Name);
|
||||||
var buyer = await _buyerRepository.FindByIdAsync(order.GetBuyerId.Value.ToString());
|
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
|
public SendEmailToCustomerWhenOrderStartedDomainEventHandler()
|
||||||
//: IAsyncNotificationHandler<OrderStartedDomainEvent>
|
|
||||||
{
|
{
|
||||||
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.DomainEventHandlers.OrderStartedEvent;
|
||||||
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 Ordering.API.Application.DomainEventHandlers.OrderStartedEvent
|
public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler
|
||||||
|
: INotificationHandler<OrderStartedDomainEvent>
|
||||||
{
|
{
|
||||||
public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler
|
private readonly ILoggerFactory _logger;
|
||||||
: INotificationHandler<OrderStartedDomainEvent>
|
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;
|
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
||||||
private readonly IBuyerRepository _buyerRepository;
|
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
||||||
private readonly IIdentityService _identityService;
|
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler(
|
public async Task Handle(OrderStartedDomainEvent orderStartedEvent, CancellationToken cancellationToken)
|
||||||
ILoggerFactory logger,
|
{
|
||||||
IBuyerRepository buyerRepository,
|
var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1;
|
||||||
IIdentityService identityService,
|
var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId);
|
||||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
bool buyerOriginallyExisted = (buyer == null) ? false : true;
|
||||||
|
|
||||||
|
if (!buyerOriginallyExisted)
|
||||||
{
|
{
|
||||||
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
buyer = new Buyer(orderStartedEvent.UserId, orderStartedEvent.UserName);
|
||||||
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
|
||||||
_orderingIntegrationEventService = orderingIntegrationEventService ?? throw new ArgumentNullException(nameof(orderingIntegrationEventService));
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Handle(OrderStartedDomainEvent orderStartedEvent, CancellationToken cancellationToken)
|
buyer.VerifyOrAddPaymentMethod(cardTypeId,
|
||||||
{
|
$"Payment Method on {DateTime.UtcNow}",
|
||||||
var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1;
|
orderStartedEvent.CardNumber,
|
||||||
var buyer = await _buyerRepository.FindAsync(orderStartedEvent.UserId);
|
orderStartedEvent.CardSecurityNumber,
|
||||||
bool buyerOriginallyExisted = (buyer == null) ? false : true;
|
orderStartedEvent.CardHolderName,
|
||||||
|
orderStartedEvent.CardExpiration,
|
||||||
|
orderStartedEvent.Order.Id);
|
||||||
|
|
||||||
if (!buyerOriginallyExisted)
|
var buyerUpdated = buyerOriginallyExisted ?
|
||||||
{
|
_buyerRepository.Update(buyer) :
|
||||||
buyer = new Buyer(orderStartedEvent.UserId, orderStartedEvent.UserName);
|
_buyerRepository.Add(buyer);
|
||||||
}
|
|
||||||
|
|
||||||
buyer.VerifyOrAddPaymentMethod(cardTypeId,
|
await _buyerRepository.UnitOfWork
|
||||||
$"Payment Method on {DateTime.UtcNow}",
|
.SaveEntitiesAsync(cancellationToken);
|
||||||
orderStartedEvent.CardNumber,
|
|
||||||
orderStartedEvent.CardSecurityNumber,
|
|
||||||
orderStartedEvent.CardHolderName,
|
|
||||||
orderStartedEvent.CardExpiration,
|
|
||||||
orderStartedEvent.Order.Id);
|
|
||||||
|
|
||||||
var buyerUpdated = buyerOriginallyExisted ?
|
var orderStatusChangedTosubmittedIntegrationEvent = new OrderStatusChangedToSubmittedIntegrationEvent(orderStartedEvent.Order.Id, orderStartedEvent.Order.OrderStatus.Name, buyer.Name);
|
||||||
_buyerRepository.Update(buyer) :
|
await _orderingIntegrationEventService.AddAndSaveEventAsync(orderStatusChangedTosubmittedIntegrationEvent);
|
||||||
_buyerRepository.Add(buyer);
|
_logger.CreateLogger<ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler>()
|
||||||
|
.LogTrace("Buyer {BuyerId} and related payment method were validated or updated for orderId: {OrderId}.",
|
||||||
await _buyerRepository.UnitOfWork
|
buyerUpdated.Id, orderStartedEvent.Order.Id);
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
private readonly IOrderRepository _orderRepository;
|
||||||
using MediatR;
|
private readonly IBuyerRepository _buyerRepository;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
private readonly ILoggerFactory _logger;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ordering.API.Application.IntegrationEvents;
|
|
||||||
using Ordering.API.Application.IntegrationEvents.Events;
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class OrderStatusChangedToStockConfirmedDomainEventHandler
|
public OrderStatusChangedToStockConfirmedDomainEventHandler(
|
||||||
: INotificationHandler<OrderStatusChangedToStockConfirmedDomainEvent>
|
IOrderRepository orderRepository,
|
||||||
|
IBuyerRepository buyerRepository,
|
||||||
|
ILoggerFactory logger,
|
||||||
|
IOrderingIntegrationEventService orderingIntegrationEventService)
|
||||||
{
|
{
|
||||||
private readonly IOrderRepository _orderRepository;
|
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
|
||||||
private readonly IBuyerRepository _buyerRepository;
|
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
|
||||||
private readonly ILoggerFactory _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
|
_orderingIntegrationEventService = 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling;
|
||||||
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 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;
|
_mediator = mediator;
|
||||||
private readonly ILogger<GracePeriodConfirmedIntegrationEventHandler> _logger;
|
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public GracePeriodConfirmedIntegrationEventHandler(
|
/// <summary>
|
||||||
IMediator mediator,
|
/// Event handler which confirms that the grace period
|
||||||
ILogger<GracePeriodConfirmedIntegrationEventHandler> logger)
|
/// 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.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
||||||
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
var command = new SetAwaitingValidationOrderStatusCommand(@event.OrderId);
|
||||||
/// 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);
|
_logger.LogInformation(
|
||||||
|
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||||
|
command.GetGenericTypeName(),
|
||||||
|
nameof(command.OrderNumber),
|
||||||
|
command.OrderNumber,
|
||||||
|
command);
|
||||||
|
|
||||||
_logger.LogInformation(
|
await _mediator.Send(command);
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
command.GetGenericTypeName(),
|
|
||||||
nameof(command.OrderNumber),
|
|
||||||
command.OrderNumber,
|
|
||||||
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;
|
private readonly IMediator _mediator;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
private readonly ILogger<OrderPaymentFailedIntegrationEventHandler> _logger;
|
||||||
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;
|
|
||||||
|
|
||||||
public class OrderPaymentFailedIntegrationEventHandler :
|
public OrderPaymentFailedIntegrationEventHandler(
|
||||||
IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>
|
IMediator mediator,
|
||||||
|
ILogger<OrderPaymentFailedIntegrationEventHandler> logger)
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
private readonly ILogger<OrderPaymentFailedIntegrationEventHandler> _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public OrderPaymentFailedIntegrationEventHandler(
|
public async Task Handle(OrderPaymentFailedIntegrationEvent @event)
|
||||||
IMediator mediator,
|
{
|
||||||
ILogger<OrderPaymentFailedIntegrationEventHandler> logger)
|
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
||||||
{
|
{
|
||||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(OrderPaymentFailedIntegrationEvent @event)
|
var command = new CancelOrderCommand(@event.OrderId);
|
||||||
{
|
|
||||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
|
||||||
{
|
|
||||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
|
||||||
|
|
||||||
var command = new CancelOrderCommand(@event.OrderId);
|
_logger.LogInformation(
|
||||||
|
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||||
|
command.GetGenericTypeName(),
|
||||||
|
nameof(command.OrderNumber),
|
||||||
|
command.OrderNumber,
|
||||||
|
command);
|
||||||
|
|
||||||
_logger.LogInformation(
|
await _mediator.Send(command);
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
command.GetGenericTypeName(),
|
|
||||||
nameof(command.OrderNumber),
|
|
||||||
command.OrderNumber,
|
|
||||||
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;
|
private readonly IMediator _mediator;
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
|
private readonly ILogger<OrderPaymentSucceededIntegrationEventHandler> _logger;
|
||||||
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;
|
|
||||||
|
|
||||||
public class OrderPaymentSucceededIntegrationEventHandler :
|
public OrderPaymentSucceededIntegrationEventHandler(
|
||||||
IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>
|
IMediator mediator,
|
||||||
|
ILogger<OrderPaymentSucceededIntegrationEventHandler> logger)
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
private readonly ILogger<OrderPaymentSucceededIntegrationEventHandler> _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public OrderPaymentSucceededIntegrationEventHandler(
|
public async Task Handle(OrderPaymentSucceededIntegrationEvent @event)
|
||||||
IMediator mediator,
|
{
|
||||||
ILogger<OrderPaymentSucceededIntegrationEventHandler> logger)
|
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
||||||
{
|
{
|
||||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(OrderPaymentSucceededIntegrationEvent @event)
|
var command = new SetPaidOrderStatusCommand(@event.OrderId);
|
||||||
{
|
|
||||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
|
||||||
{
|
|
||||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
|
||||||
|
|
||||||
var command = new SetPaidOrderStatusCommand(@event.OrderId);
|
_logger.LogInformation(
|
||||||
|
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||||
|
command.GetGenericTypeName(),
|
||||||
|
nameof(command.OrderNumber),
|
||||||
|
command.OrderNumber,
|
||||||
|
command);
|
||||||
|
|
||||||
_logger.LogInformation(
|
await _mediator.Send(command);
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
command.GetGenericTypeName(),
|
|
||||||
nameof(command.OrderNumber),
|
|
||||||
command.OrderNumber,
|
|
||||||
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;
|
private readonly IMediator _mediator;
|
||||||
using MediatR;
|
private readonly ILogger<OrderStockConfirmedIntegrationEventHandler> _logger;
|
||||||
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;
|
|
||||||
|
|
||||||
public class OrderStockConfirmedIntegrationEventHandler :
|
public OrderStockConfirmedIntegrationEventHandler(
|
||||||
IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>
|
IMediator mediator,
|
||||||
|
ILogger<OrderStockConfirmedIntegrationEventHandler> logger)
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
private readonly ILogger<OrderStockConfirmedIntegrationEventHandler> _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public OrderStockConfirmedIntegrationEventHandler(
|
public async Task Handle(OrderStockConfirmedIntegrationEvent @event)
|
||||||
IMediator mediator,
|
{
|
||||||
ILogger<OrderStockConfirmedIntegrationEventHandler> logger)
|
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
||||||
{
|
{
|
||||||
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(OrderStockConfirmedIntegrationEvent @event)
|
var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId);
|
||||||
{
|
|
||||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
|
||||||
{
|
|
||||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
|
||||||
|
|
||||||
var command = new SetStockConfirmedOrderStatusCommand(@event.OrderId);
|
_logger.LogInformation(
|
||||||
|
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||||
|
command.GetGenericTypeName(),
|
||||||
|
nameof(command.OrderNumber),
|
||||||
|
command.OrderNumber,
|
||||||
|
command);
|
||||||
|
|
||||||
_logger.LogInformation(
|
await _mediator.Send(command);
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
command.GetGenericTypeName(),
|
|
||||||
nameof(command.OrderNumber),
|
|
||||||
command.OrderNumber,
|
|
||||||
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;
|
private readonly IMediator _mediator;
|
||||||
using MediatR;
|
private readonly ILogger<OrderStockRejectedIntegrationEventHandler> _logger;
|
||||||
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;
|
|
||||||
|
|
||||||
public class OrderStockRejectedIntegrationEventHandler : IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>
|
public OrderStockRejectedIntegrationEventHandler(
|
||||||
|
IMediator mediator,
|
||||||
|
ILogger<OrderStockRejectedIntegrationEventHandler> logger)
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
_mediator = mediator;
|
||||||
private readonly ILogger<OrderStockRejectedIntegrationEventHandler> _logger;
|
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public OrderStockRejectedIntegrationEventHandler(
|
public async Task Handle(OrderStockRejectedIntegrationEvent @event)
|
||||||
IMediator mediator,
|
{
|
||||||
ILogger<OrderStockRejectedIntegrationEventHandler> logger)
|
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
||||||
{
|
{
|
||||||
_mediator = mediator;
|
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
||||||
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Handle(OrderStockRejectedIntegrationEvent @event)
|
var orderStockRejectedItems = @event.OrderStockItems
|
||||||
{
|
.FindAll(c => !c.HasStock)
|
||||||
using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
|
.Select(c => c.ProductId)
|
||||||
{
|
.ToList();
|
||||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
|
||||||
|
|
||||||
var orderStockRejectedItems = @event.OrderStockItems
|
var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems);
|
||||||
.FindAll(c => !c.HasStock)
|
|
||||||
.Select(c => c.ProductId)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var command = new SetStockRejectedOrderStatusCommand(@event.OrderId, orderStockRejectedItems);
|
_logger.LogInformation(
|
||||||
|
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||||
|
command.GetGenericTypeName(),
|
||||||
|
nameof(command.OrderNumber),
|
||||||
|
command.OrderNumber,
|
||||||
|
command);
|
||||||
|
|
||||||
_logger.LogInformation(
|
await _mediator.Send(command);
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
command.GetGenericTypeName(),
|
|
||||||
nameof(command.OrderNumber),
|
|
||||||
command.OrderNumber,
|
|
||||||
command);
|
|
||||||
|
|
||||||
await _mediator.Send(command);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,80 +1,69 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.EventHandling;
|
||||||
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 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;
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
private readonly ILogger<UserCheckoutAcceptedIntegrationEventHandler> _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public UserCheckoutAcceptedIntegrationEventHandler(
|
/// <summary>
|
||||||
IMediator mediator,
|
/// Integration event handler which starts the create order process
|
||||||
ILogger<UserCheckoutAcceptedIntegrationEventHandler> logger)
|
/// </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.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
var result = false;
|
||||||
/// Integration event handler which starts the create order process
|
|
||||||
/// </summary>
|
if (@event.RequestId != Guid.Empty)
|
||||||
/// <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}"))
|
|
||||||
{
|
{
|
||||||
_logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);
|
using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId))
|
||||||
|
|
||||||
var result = false;
|
|
||||||
|
|
||||||
if (@event.RequestId != Guid.Empty)
|
|
||||||
{
|
{
|
||||||
using (LogContext.PushProperty("IdentifiedCommandId", @event.RequestId))
|
var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street,
|
||||||
|
@event.State, @event.Country, @event.ZipCode,
|
||||||
|
@event.CardNumber, @event.CardHolderName, @event.CardExpiration,
|
||||||
|
@event.CardSecurityNumber, @event.CardTypeId);
|
||||||
|
|
||||||
|
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, @event.RequestId);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||||
|
requestCreateOrder.GetGenericTypeName(),
|
||||||
|
nameof(requestCreateOrder.Id),
|
||||||
|
requestCreateOrder.Id,
|
||||||
|
requestCreateOrder);
|
||||||
|
|
||||||
|
result = await _mediator.Send(requestCreateOrder);
|
||||||
|
|
||||||
|
if (result)
|
||||||
{
|
{
|
||||||
var createOrderCommand = new CreateOrderCommand(@event.Basket.Items, @event.UserId, @event.UserName, @event.City, @event.Street,
|
_logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId);
|
||||||
@event.State, @event.Country, @event.ZipCode,
|
}
|
||||||
@event.CardNumber, @event.CardHolderName, @event.CardExpiration,
|
else
|
||||||
@event.CardSecurityNumber, @event.CardTypeId);
|
{
|
||||||
|
_logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId);
|
||||||
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, @event.RequestId);
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
requestCreateOrder.GetGenericTypeName(),
|
|
||||||
nameof(requestCreateOrder.Id),
|
|
||||||
requestCreateOrder.Id,
|
|
||||||
requestCreateOrder);
|
|
||||||
|
|
||||||
result = await _mediator.Send(requestCreateOrder);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("----- CreateOrderCommand suceeded - RequestId: {RequestId}", @event.RequestId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning("CreateOrderCommand failed - RequestId: {RequestId}", @event.RequestId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
_logger.LogWarning("Invalid IntegrationEvent - RequestId is missing - {@IntegrationEvent}", @event);
|
{
|
||||||
}
|
_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 GracePeriodConfirmedIntegrationEvent(int orderId) =>
|
||||||
{
|
OrderId = orderId;
|
||||||
public int OrderId { get; }
|
|
||||||
|
|
||||||
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 OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId;
|
||||||
{
|
}
|
||||||
public int OrderId { get; }
|
|
||||||
|
|
||||||
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 OrderPaymentSucceededIntegrationEvent(int orderId) => OrderId = orderId;
|
||||||
{
|
}
|
||||||
public int OrderId { get; }
|
|
||||||
|
|
||||||
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:
|
public string UserId { get; init; }
|
||||||
// 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 OrderStartedIntegrationEvent(string userId)
|
public OrderStartedIntegrationEvent(string userId)
|
||||||
=> UserId = 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;
|
public int OrderId { get; }
|
||||||
using System.Collections.Generic;
|
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; }
|
OrderId = orderId;
|
||||||
public string OrderStatus { get; }
|
OrderStockItems = orderStockItems;
|
||||||
public string BuyerName { get; }
|
OrderStatus = orderStatus;
|
||||||
public IEnumerable<OrderStockItem> OrderStockItems { get; }
|
BuyerName = buyerName;
|
||||||
|
|
||||||
public OrderStatusChangedToAwaitingValidationIntegrationEvent(int orderId, string orderStatus, string buyerName,
|
|
||||||
IEnumerable<OrderStockItem> orderStockItems)
|
|
||||||
{
|
|
||||||
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; }
|
ProductId = productId;
|
||||||
public int Units { get; }
|
Units = units;
|
||||||
|
|
||||||
public OrderStockItem(int productId, int 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 int OrderId { get; }
|
public string BuyerName { get; }
|
||||||
public string OrderStatus { get; }
|
|
||||||
public string BuyerName { get; }
|
|
||||||
|
|
||||||
public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName)
|
public OrderStatusChangedToCancelledIntegrationEvent(int orderId, string orderStatus, string buyerName)
|
||||||
{
|
{
|
||||||
OrderId = orderId;
|
OrderId = orderId;
|
||||||
OrderStatus = orderStatus;
|
OrderStatus = orderStatus;
|
||||||
BuyerName = buyerName;
|
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;
|
public int OrderId { get; }
|
||||||
using System.Collections.Generic;
|
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; }
|
OrderId = orderId;
|
||||||
public string OrderStatus { get; }
|
OrderStockItems = orderStockItems;
|
||||||
public string BuyerName { get; }
|
OrderStatus = orderStatus;
|
||||||
public IEnumerable<OrderStockItem> OrderStockItems { get; }
|
BuyerName = buyerName;
|
||||||
|
|
||||||
public OrderStatusChangedToPaidIntegrationEvent(int orderId,
|
|
||||||
string orderStatus,
|
|
||||||
string buyerName,
|
|
||||||
IEnumerable<OrderStockItem> orderStockItems)
|
|
||||||
{
|
|
||||||
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 int OrderId { get; }
|
public string BuyerName { get; }
|
||||||
public string OrderStatus { get; }
|
|
||||||
public string BuyerName { get; }
|
|
||||||
|
|
||||||
public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName)
|
public OrderStatusChangedToShippedIntegrationEvent(int orderId, string orderStatus, string buyerName)
|
||||||
{
|
{
|
||||||
OrderId = orderId;
|
OrderId = orderId;
|
||||||
OrderStatus = orderStatus;
|
OrderStatus = orderStatus;
|
||||||
BuyerName = buyerName;
|
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; }
|
OrderId = orderId;
|
||||||
public string OrderStatus { get; }
|
OrderStatus = orderStatus;
|
||||||
public string BuyerName { get; }
|
BuyerName = buyerName;
|
||||||
|
|
||||||
public OrderStatusChangedToStockConfirmedIntegrationEvent(int orderId, string orderStatus, string 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 int OrderId { get; }
|
public string BuyerName { get; }
|
||||||
public string OrderStatus { get; }
|
|
||||||
public string BuyerName { get; }
|
|
||||||
|
|
||||||
public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName)
|
public OrderStatusChangedToSubmittedIntegrationEvent(int orderId, string orderStatus, string buyerName)
|
||||||
{
|
{
|
||||||
OrderId = orderId;
|
OrderId = orderId;
|
||||||
OrderStatus = orderStatus;
|
OrderStatus = orderStatus;
|
||||||
BuyerName = buyerName;
|
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 OrderStockConfirmedIntegrationEvent(int orderId) => OrderId = orderId;
|
||||||
{
|
}
|
||||||
public int OrderId { get; }
|
|
||||||
|
|
||||||
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;
|
public int OrderId { get; }
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
public record OrderStockRejectedIntegrationEvent : IntegrationEvent
|
public List<ConfirmedOrderStockItem> OrderStockItems { get; }
|
||||||
|
|
||||||
|
public OrderStockRejectedIntegrationEvent(int orderId,
|
||||||
|
List<ConfirmedOrderStockItem> orderStockItems)
|
||||||
{
|
{
|
||||||
public int OrderId { get; }
|
OrderId = orderId;
|
||||||
|
OrderStockItems = orderStockItems;
|
||||||
public List<ConfirmedOrderStockItem> OrderStockItems { get; }
|
|
||||||
|
|
||||||
public OrderStockRejectedIntegrationEvent(int orderId,
|
|
||||||
List<ConfirmedOrderStockItem> 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; }
|
ProductId = productId;
|
||||||
public bool HasStock { get; }
|
HasStock = hasStock;
|
||||||
|
|
||||||
public ConfirmedOrderStockItem(int productId, bool hasStock)
|
|
||||||
{
|
|
||||||
ProductId = productId;
|
|
||||||
HasStock = hasStock;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,62 +1,57 @@
|
|||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents.Events;
|
||||||
using Ordering.API.Application.Models;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ordering.API.Application.IntegrationEvents.Events
|
public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
|
||||||
{
|
{
|
||||||
public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent
|
public string UserId { get; }
|
||||||
{
|
|
||||||
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 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 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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents;
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.IntegrationEvents;
|
||||||
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 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;
|
_orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext));
|
||||||
private readonly IEventBus _eventBus;
|
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
|
||||||
private readonly OrderingContext _orderingContext;
|
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
|
||||||
private readonly IIntegrationEventLogService _eventLogService;
|
_eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection());
|
||||||
private readonly ILogger<OrderingIntegrationEventService> _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public OrderingIntegrationEventService(IEventBus eventBus,
|
public async Task PublishEventsThroughEventBusAsync(Guid transactionId)
|
||||||
OrderingContext orderingContext,
|
{
|
||||||
IntegrationEventLogContext eventLogContext,
|
var pendingLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync(transactionId);
|
||||||
Func<DbConnection, IIntegrationEventLogService> integrationEventLogServiceFactory,
|
|
||||||
ILogger<OrderingIntegrationEventService> logger)
|
foreach (var logEvt in pendingLogEvents)
|
||||||
{
|
{
|
||||||
_orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext));
|
_logger.LogInformation("----- Publishing integration event: {IntegrationEventId} from {AppName} - ({@IntegrationEvent})", logEvt.EventId, Program.AppName, logEvt.IntegrationEvent);
|
||||||
_integrationEventLogServiceFactory = integrationEventLogServiceFactory ?? throw new ArgumentNullException(nameof(integrationEventLogServiceFactory));
|
|
||||||
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
|
|
||||||
_eventLogService = _integrationEventLogServiceFactory(_orderingContext.Database.GetDbConnection());
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PublishEventsThroughEventBusAsync(Guid transactionId)
|
try
|
||||||
{
|
|
||||||
var pendingLogEvents = await _eventLogService.RetrieveEventLogsPendingToPublishAsync(transactionId);
|
|
||||||
|
|
||||||
foreach (var logEvt in pendingLogEvents)
|
|
||||||
{
|
{
|
||||||
_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.MarkEventAsFailedAsync(logEvt.EventId);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task AddAndSaveEventAsync(IntegrationEvent evt)
|
public async Task AddAndSaveEventAsync(IntegrationEvent evt)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("----- Enqueuing integration event {IntegrationEventId} to repository ({@IntegrationEvent})", evt.Id, 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 Id { get; init; }
|
public string ProductName { get; init; }
|
||||||
public int ProductId { get; init; }
|
public decimal UnitPrice { get; init; }
|
||||||
public string ProductName { get; init; }
|
public decimal OldUnitPrice { get; init; }
|
||||||
public decimal UnitPrice { get; init; }
|
public int Quantity { get; init; }
|
||||||
public decimal OldUnitPrice { get; init; }
|
public string PictureUrl { 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)
|
public CustomerBasket(string buyerId, List<BasketItem> items)
|
||||||
{
|
{
|
||||||
BuyerId = buyerId;
|
BuyerId = buyerId;
|
||||||
Items = items;
|
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;
|
Task<Order> GetOrderAsync(int id);
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public interface IOrderQueries
|
Task<IEnumerable<OrderSummary>> GetOrdersFromUserAsync(Guid userId);
|
||||||
{
|
|
||||||
Task<Order> GetOrderAsync(int id);
|
|
||||||
|
|
||||||
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;
|
private string _connectionString = string.Empty;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data.SqlClient;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public class OrderQueries
|
public OrderQueries(string constr)
|
||||||
: IOrderQueries
|
|
||||||
{
|
{
|
||||||
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)
|
if (result.AsList().Count == 0)
|
||||||
{
|
throw new KeyNotFoundException();
|
||||||
using (var connection = new SqlConnection(_connectionString))
|
|
||||||
{
|
|
||||||
connection.Open();
|
|
||||||
|
|
||||||
var result = await connection.QueryAsync<dynamic>(
|
return MapOrderItems(result);
|
||||||
@"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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
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 string productname { get; init; }
|
public double unitprice { get; init; }
|
||||||
public int units { get; init; }
|
public string pictureurl { get; init; }
|
||||||
public double unitprice { get; init; }
|
}
|
||||||
public string pictureurl { get; init; }
|
|
||||||
}
|
public record Order
|
||||||
|
{
|
||||||
public record Order
|
public int ordernumber { get; init; }
|
||||||
{
|
public DateTime date { get; init; }
|
||||||
public int ordernumber { get; init; }
|
public string status { get; init; }
|
||||||
public DateTime date { get; init; }
|
public string description { get; init; }
|
||||||
public string status { get; init; }
|
public string street { get; init; }
|
||||||
public string description { get; init; }
|
public string city { get; init; }
|
||||||
public string street { get; init; }
|
public string zipcode { get; init; }
|
||||||
public string city { get; init; }
|
public string country { get; init; }
|
||||||
public string zipcode { get; init; }
|
public List<Orderitem> orderitems { get; set; }
|
||||||
public string country { get; init; }
|
public decimal total { get; set; }
|
||||||
public List<Orderitem> orderitems { get; set; }
|
}
|
||||||
public decimal total { get; set; }
|
|
||||||
}
|
public record OrderSummary
|
||||||
|
{
|
||||||
public record OrderSummary
|
public int ordernumber { get; init; }
|
||||||
{
|
public DateTime date { get; init; }
|
||||||
public int ordernumber { get; init; }
|
public string status { get; init; }
|
||||||
public DateTime date { get; init; }
|
public double total { get; init; }
|
||||||
public string status { get; init; }
|
}
|
||||||
public double total { get; init; }
|
|
||||||
}
|
public record CardType
|
||||||
|
{
|
||||||
public record CardType
|
public int Id { get; init; }
|
||||||
{
|
public string Name { get; init; }
|
||||||
public int Id { get; init; }
|
|
||||||
public string Name { get; init; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
using FluentValidation;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ordering.API.Application.Commands;
|
|
||||||
|
|
||||||
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using static Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands.CreateOrderCommand;
|
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.City).NotEmpty();
|
RuleFor(command => command.State).NotEmpty();
|
||||||
RuleFor(command => command.Street).NotEmpty();
|
RuleFor(command => command.Country).NotEmpty();
|
||||||
RuleFor(command => command.State).NotEmpty();
|
RuleFor(command => command.ZipCode).NotEmpty();
|
||||||
RuleFor(command => command.Country).NotEmpty();
|
RuleFor(command => command.CardNumber).NotEmpty().Length(12, 19);
|
||||||
RuleFor(command => command.ZipCode).NotEmpty();
|
RuleFor(command => command.CardHolderName).NotEmpty();
|
||||||
RuleFor(command => command.CardNumber).NotEmpty().Length(12, 19);
|
RuleFor(command => command.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date");
|
||||||
RuleFor(command => command.CardHolderName).NotEmpty();
|
RuleFor(command => command.CardSecurityNumber).NotEmpty().Length(3);
|
||||||
RuleFor(command => command.CardExpiration).NotEmpty().Must(BeValidExpirationDate).WithMessage("Please specify a valid card expiration date");
|
RuleFor(command => command.CardTypeId).NotEmpty();
|
||||||
RuleFor(command => command.CardSecurityNumber).NotEmpty().Length(3);
|
RuleFor(command => command.OrderItems).Must(ContainOrderItems).WithMessage("No order items found");
|
||||||
RuleFor(command => command.CardTypeId).NotEmpty();
|
|
||||||
RuleFor(command => command.OrderItems).Must(ContainOrderItems).WithMessage("No order items found");
|
|
||||||
|
|
||||||
logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name);
|
logger.LogTrace("----- INSTANCE CREATED - {ClassName}", GetType().Name);
|
||||||
}
|
|
||||||
|
|
||||||
private bool BeValidExpirationDate(DateTime dateTime)
|
|
||||||
{
|
|
||||||
return dateTime >= DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ContainOrderItems(IEnumerable<OrderItemDTO> orderItems)
|
|
||||||
{
|
|
||||||
return orderItems.Any();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private bool BeValidExpirationDate(DateTime dateTime)
|
||||||
|
{
|
||||||
|
return dateTime >= DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ContainOrderItems(IEnumerable<OrderItemDTO> orderItems)
|
||||||
|
{
|
||||||
|
return orderItems.Any();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
using FluentValidation;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Validations;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ordering.API.Application.Commands;
|
|
||||||
|
|
||||||
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>/
|
return new RedirectResult("~/swagger");
|
||||||
public IActionResult Index()
|
|
||||||
{
|
|
||||||
return new RedirectResult("~/swagger");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,153 +1,143 @@
|
|||||||
using MediatR;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
|
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Extensions;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
|
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
|
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]")]
|
private readonly IMediator _mediator;
|
||||||
[Authorize]
|
private readonly IOrderQueries _orderQueries;
|
||||||
[ApiController]
|
private readonly IIdentityService _identityService;
|
||||||
public class OrdersController : ControllerBase
|
private readonly ILogger<OrdersController> _logger;
|
||||||
|
|
||||||
|
public OrdersController(
|
||||||
|
IMediator mediator,
|
||||||
|
IOrderQueries orderQueries,
|
||||||
|
IIdentityService identityService,
|
||||||
|
ILogger<OrdersController> logger)
|
||||||
{
|
{
|
||||||
private readonly IMediator _mediator;
|
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
|
||||||
private readonly IOrderQueries _orderQueries;
|
_orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries));
|
||||||
private readonly IIdentityService _identityService;
|
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
||||||
private readonly ILogger<OrdersController> _logger;
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public OrdersController(
|
[Route("cancel")]
|
||||||
IMediator mediator,
|
[HttpPut]
|
||||||
IOrderQueries orderQueries,
|
[ProducesResponseType((int)HttpStatusCode.OK)]
|
||||||
IIdentityService identityService,
|
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
||||||
ILogger<OrdersController> logger)
|
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));
|
var requestCancelOrder = new IdentifiedCommand<CancelOrderCommand, bool>(command, guid);
|
||||||
_orderQueries = orderQueries ?? throw new ArgumentNullException(nameof(orderQueries));
|
|
||||||
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("cancel")]
|
|
||||||
[HttpPut]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.OK)]
|
|
||||||
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
|
|
||||||
public async Task<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(
|
_logger.LogInformation(
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
||||||
createOrderDraftCommand.GetGenericTypeName(),
|
requestCancelOrder.GetGenericTypeName(),
|
||||||
nameof(createOrderDraftCommand.BuyerId),
|
nameof(requestCancelOrder.Command.OrderNumber),
|
||||||
createOrderDraftCommand.BuyerId,
|
requestCancelOrder.Command.OrderNumber,
|
||||||
createOrderDraftCommand);
|
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;
|
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();
|
||||||
{
|
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Extensions;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
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;
|
returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
|
|
||||||
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;
|
returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex);
|
||||||
Result = result;
|
|
||||||
CaughtException = exception;
|
|
||||||
}
|
}
|
||||||
|
yield return returnedValue;
|
||||||
public TSource Source { get; private set; }
|
|
||||||
public TResult Result { get; private set; }
|
|
||||||
public Exception CaughtException { get; private set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
namespace GrpcOrdering;
|
||||||
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
|
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;
|
_mediator = mediator;
|
||||||
private readonly ILogger<OrderingService> _logger;
|
_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;
|
context.Status = new Status(StatusCode.OK, $" ordering get order draft {createOrderDraftCommand} do exist");
|
||||||
_logger = logger;
|
|
||||||
|
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);
|
Total = (double)order.Total,
|
||||||
_logger.LogTrace(
|
};
|
||||||
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({@Command})",
|
|
||||||
createOrderDraftCommand.GetGenericTypeName(),
|
|
||||||
nameof(createOrderDraftCommand.BuyerId),
|
|
||||||
createOrderDraftCommand.BuyerId,
|
|
||||||
createOrderDraftCommand);
|
|
||||||
|
|
||||||
var command = new AppCommand.CreateOrderDraftCommand(
|
order.OrderItems.ToList().ForEach(i => result.OrderItems.Add(new OrderItemDTO()
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
var result = new OrderDraftDTO()
|
Discount = (double)i.Discount,
|
||||||
{
|
PictureUrl = i.PictureUrl,
|
||||||
Total = (double)order.Total,
|
ProductId = i.ProductId,
|
||||||
};
|
ProductName = i.ProductName,
|
||||||
|
UnitPrice = (double)i.UnitPrice,
|
||||||
|
Units = i.Units,
|
||||||
|
}));
|
||||||
|
|
||||||
order.OrderItems.ToList().ForEach(i => result.OrderItems.Add(new OrderItemDTO()
|
return result;
|
||||||
{
|
}
|
||||||
Discount = (double)i.Discount,
|
|
||||||
PictureUrl = i.PictureUrl,
|
|
||||||
ProductId = i.ProductId,
|
|
||||||
ProductName = i.ProductName,
|
|
||||||
UnitPrice = (double)i.UnitPrice,
|
|
||||||
Units = i.Units,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return result;
|
public IEnumerable<ApiModels.BasketItem> MapBasketItems(RepeatedField<BasketItem> items)
|
||||||
}
|
{
|
||||||
|
return items.Select(x => new ApiModels.BasketItem()
|
||||||
public IEnumerable<ApiModels.BasketItem> MapBasketItems(RepeatedField<BasketItem> items)
|
|
||||||
{
|
{
|
||||||
return items.Select(x => new ApiModels.BasketItem()
|
Id = x.Id,
|
||||||
{
|
ProductId = x.ProductId,
|
||||||
Id = x.Id,
|
ProductName = x.ProductName,
|
||||||
ProductId = x.ProductId,
|
UnitPrice = (decimal)x.UnitPrice,
|
||||||
ProductName = x.ProductName,
|
OldUnitPrice = (decimal)x.OldUnitPrice,
|
||||||
UnitPrice = (decimal)x.UnitPrice,
|
Quantity = x.Quantity,
|
||||||
OldUnitPrice = (decimal)x.OldUnitPrice,
|
PictureUrl = x.PictureUrl,
|
||||||
Quantity = x.Quantity,
|
});
|
||||||
PictureUrl = x.PictureUrl,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults;
|
||||||
{
|
|
||||||
using AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
public class InternalServerErrorObjectResult : ObjectResult
|
public class InternalServerErrorObjectResult : ObjectResult
|
||||||
|
{
|
||||||
|
public InternalServerErrorObjectResult(object error)
|
||||||
|
: base(error)
|
||||||
{
|
{
|
||||||
public InternalServerErrorObjectResult(object error)
|
StatusCode = StatusCodes.Status500InternalServerError;
|
||||||
: base(error)
|
|
||||||
{
|
|
||||||
StatusCode = StatusCodes.Status500InternalServerError;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,27 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Auth;
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
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;
|
if (operation.Parameters == null)
|
||||||
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
|
operation.Parameters = new List<OpenApiParameter>();
|
||||||
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
|
|
||||||
|
|
||||||
if (isAuthorized && !allowAnonymous)
|
|
||||||
|
operation.Parameters.Add(new OpenApiParameter
|
||||||
{
|
{
|
||||||
if (operation.Parameters == null)
|
Name = "Authorization",
|
||||||
operation.Parameters = new List<OpenApiParameter>();
|
In = ParameterLocation.Header,
|
||||||
|
Description = "access token",
|
||||||
|
Required = true
|
||||||
operation.Parameters.Add(new OpenApiParameter
|
});
|
||||||
{
|
|
||||||
Name = "Authorization",
|
|
||||||
In = ParameterLocation.Header,
|
|
||||||
Description = "access token",
|
|
||||||
Required = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,38 @@
|
|||||||
using Autofac;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules;
|
||||||
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
|
public class ApplicationModule
|
||||||
|
: Autofac.Module
|
||||||
{
|
{
|
||||||
|
|
||||||
public class ApplicationModule
|
public string QueriesConnectionString { get; }
|
||||||
: Autofac.Module
|
|
||||||
|
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)
|
builder.RegisterType<BuyerRepository>()
|
||||||
{
|
.As<IBuyerRepository>()
|
||||||
QueriesConnectionString = qconstr;
|
.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))
|
builder.RegisterAssemblyTypes(typeof(CreateOrderCommandHandler).GetTypeInfo().Assembly)
|
||||||
.As<IOrderQueries>()
|
.AsClosedTypesOf(typeof(IIntegrationEventHandler<>));
|
||||||
.InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
builder.RegisterType<BuyerRepository>()
|
|
||||||
.As<IBuyerRepository>()
|
|
||||||
.InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
builder.RegisterType<OrderRepository>()
|
|
||||||
.As<IOrderRepository>()
|
|
||||||
.InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
builder.RegisterType<RequestManager>()
|
|
||||||
.As<IRequestManager>()
|
|
||||||
.InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
builder.RegisterAssemblyTypes(typeof(CreateOrderCommandHandler).GetTypeInfo().Assembly)
|
|
||||||
.AsClosedTypesOf(typeof(IIntegrationEventHandler<>));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,36 @@
|
|||||||
using Autofac;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.AutofacModules;
|
||||||
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
|
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)
|
var componentContext = context.Resolve<IComponentContext>();
|
||||||
.AsImplementedInterfaces();
|
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.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
|
||||||
builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly)
|
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
|
||||||
.AsClosedTypesOf(typeof(IRequestHandler<,>));
|
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;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Factories
|
||||||
using Microsoft.EntityFrameworkCore.Design;
|
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ordering.API.Infrastructure.Factories
|
|
||||||
{
|
{
|
||||||
public class OrderingDbContextFactory : IDesignTimeDbContextFactory<OrderingContext>
|
public class OrderingDbContextFactory : IDesignTimeDbContextFactory<OrderingContext>
|
||||||
{
|
{
|
||||||
|
@ -1,36 +1,29 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Filters;
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
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
|
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
||||||
var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() ||
|
};
|
||||||
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
|
|
||||||
|
|
||||||
if (!hasAuthorize) return;
|
operation.Security = new List<OpenApiSecurityRequirement>
|
||||||
|
|
||||||
operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" });
|
|
||||||
operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" });
|
|
||||||
|
|
||||||
var oAuthScheme = new OpenApiSecurityScheme
|
|
||||||
{
|
{
|
||||||
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
new OpenApiSecurityRequirement
|
||||||
};
|
|
||||||
|
|
||||||
operation.Security = new List<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;
|
private readonly IWebHostEnvironment env;
|
||||||
using global::Ordering.Domain.Exceptions;
|
private readonly ILogger<HttpGlobalExceptionFilter> logger;
|
||||||
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;
|
|
||||||
|
|
||||||
public class HttpGlobalExceptionFilter : IExceptionFilter
|
public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger<HttpGlobalExceptionFilter> logger)
|
||||||
{
|
{
|
||||||
private readonly IWebHostEnvironment env;
|
this.env = env;
|
||||||
private readonly ILogger<HttpGlobalExceptionFilter> logger;
|
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;
|
var problemDetails = new ValidationProblemDetails()
|
||||||
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()
|
Instance = context.HttpContext.Request.Path,
|
||||||
{
|
Status = StatusCodes.Status400BadRequest,
|
||||||
Instance = context.HttpContext.Request.Path,
|
Detail = "Please refer to the errors property for additional details."
|
||||||
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.Result = new BadRequestObjectResult(problemDetails);
|
||||||
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
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;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
private class JsonErrorResponse
|
|
||||||
{
|
{
|
||||||
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;
|
public async Task SeedAsync(OrderingContext context, IWebHostEnvironment env, IOptions<OrderingSettings> settings, ILogger<OrderingContextSeed> logger)
|
||||||
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)
|
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
|
if (!context.CardTypes.Any())
|
||||||
.UseCustomizationData;
|
|
||||||
|
|
||||||
var contentRootPath = env.ContentRootPath;
|
|
||||||
|
|
||||||
|
|
||||||
using (context)
|
|
||||||
{
|
{
|
||||||
context.Database.Migrate();
|
context.CardTypes.AddRange(useCustomizationData
|
||||||
|
? GetCardTypesFromFile(contentRootPath, logger)
|
||||||
if (!context.CardTypes.Any())
|
: GetPredefinedCardTypes());
|
||||||
{
|
|
||||||
context.CardTypes.AddRange(useCustomizationData
|
|
||||||
? GetCardTypesFromFile(contentRootPath, logger)
|
|
||||||
: GetPredefinedCardTypes());
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.OrderStatus.Any())
|
|
||||||
{
|
|
||||||
context.OrderStatus.AddRange(useCustomizationData
|
|
||||||
? GetOrderStatusFromFile(contentRootPath, logger)
|
|
||||||
: GetPredefinedOrderStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<CardType> GetCardTypesFromFile(string contentRootPath, ILogger<OrderingContextSeed> log)
|
if (!context.OrderStatus.Any())
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
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");
|
||||||
|
|
||||||
|
if (!File.Exists(csvFileCardTypes))
|
||||||
private AsyncRetryPolicy CreatePolicy(ILogger<OrderingContextSeed> logger, string prefix, int retries = 3)
|
|
||||||
{
|
{
|
||||||
return Policy.Handle<SqlException>().
|
return GetPredefinedCardTypes();
|
||||||
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);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
|
||||||
{
|
|
||||||
public interface IIdentityService
|
|
||||||
{
|
|
||||||
string GetUserIdentity();
|
|
||||||
|
|
||||||
string GetUserName();
|
public interface IIdentityService
|
||||||
}
|
{
|
||||||
|
string GetUserIdentity();
|
||||||
|
|
||||||
|
string GetUserName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
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)
|
public string GetUserIdentity()
|
||||||
{
|
{
|
||||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
return _context.HttpContext.User.FindFirst("sub").Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUserIdentity()
|
public string GetUserName()
|
||||||
{
|
{
|
||||||
return _context.HttpContext.User.FindFirst("sub").Value;
|
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;
|
var configuration = GetConfiguration();
|
||||||
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();
|
|
||||||
|
|
||||||
Log.Logger = CreateSerilogLogger(configuration);
|
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;
|
public Startup(IConfiguration configuration)
|
||||||
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)
|
Configuration = 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class CustomExtensionsMethods
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
public virtual IServiceProvider ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration)
|
services
|
||||||
{
|
.AddGrpc(options =>
|
||||||
services.AddApplicationInsightsTelemetry(configuration);
|
{
|
||||||
services.AddApplicationInsightsKubernetesEnricher();
|
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)
|
app.UseSwagger()
|
||||||
{
|
.UseSwaggerUI(c =>
|
||||||
// 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",
|
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Ordering.API V1");
|
||||||
builder => builder
|
c.OAuthClientId("orderingswaggerui");
|
||||||
.SetIsOriginAllowed((host) => true)
|
c.OAuthAppName("Ordering Swagger UI");
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowCredentials());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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();
|
endpoints.MapGrpcService<OrderingService>();
|
||||||
|
endpoints.MapDefaultControllerRoute();
|
||||||
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
|
endpoints.MapControllers();
|
||||||
|
endpoints.MapGet("/_proto/", async ctx =>
|
||||||
hcBuilder
|
|
||||||
.AddSqlServer(
|
|
||||||
configuration["ConnectionString"],
|
|
||||||
name: "OrderingDB-check",
|
|
||||||
tags: new string[] { "orderingdb" });
|
|
||||||
|
|
||||||
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
|
|
||||||
{
|
{
|
||||||
hcBuilder
|
ctx.Response.ContentType = "text/plain";
|
||||||
.AddAzureServiceBusTopic(
|
using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read);
|
||||||
configuration["EventBusConnection"],
|
using var sr = new StreamReader(fs);
|
||||||
topicName: "eshop_event_bus",
|
while (!sr.EndOfStream)
|
||||||
name: "ordering-servicebus-check",
|
{
|
||||||
tags: new string[] { "servicebus" });
|
var line = await sr.ReadLineAsync();
|
||||||
}
|
if (line != "/* >>" || line != "<< */")
|
||||||
else
|
{
|
||||||
{
|
await ctx.Response.WriteAsync(line);
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddCustomSwagger(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.AddSwaggerGen(options =>
|
|
||||||
{
|
{
|
||||||
options.DescribeAllEnumsAsStrings();
|
Predicate = _ => true,
|
||||||
options.SwaggerDoc("v1", new OpenApiInfo
|
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||||
{
|
|
||||||
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>();
|
|
||||||
});
|
});
|
||||||
|
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
|
||||||
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 =>
|
Predicate = r => r.Name.Contains("self")
|
||||||
{
|
|
||||||
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;
|
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,
|
private void ConfigureEventBus(IApplicationBuilder app)
|
||||||
eventBusSubcriptionsManager, iLifetimeScope);
|
{
|
||||||
});
|
var eventBus = app.ApplicationServices.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>();
|
||||||
}
|
|
||||||
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;
|
eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>();
|
||||||
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
|
eventBus.Subscribe<GracePeriodConfirmedIntegrationEvent, IIntegrationEventHandler<GracePeriodConfirmedIntegrationEvent>>();
|
||||||
{
|
eventBus.Subscribe<OrderStockConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>>();
|
||||||
retryCount = int.Parse(configuration["EventBusRetryCount"]);
|
eventBus.Subscribe<OrderStockRejectedIntegrationEvent, IIntegrationEventHandler<OrderStockRejectedIntegrationEvent>>();
|
||||||
}
|
eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>();
|
||||||
|
eventBus.Subscribe<OrderPaymentSucceededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSucceededIntegrationEvent>>();
|
||||||
|
}
|
||||||
|
|
||||||
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
|
protected virtual void ConfigureAuth(IApplicationBuilder app)
|
||||||
});
|
{
|
||||||
}
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
|
}
|
||||||
|
}
|
||||||
return services;
|
|
||||||
}
|
static class CustomExtensionsMethods
|
||||||
|
{
|
||||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
// prevent from mapping "sub" claim to nameidentifier.
|
services.AddApplicationInsightsTelemetry(configuration);
|
||||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
|
services.AddApplicationInsightsKubernetesEnricher();
|
||||||
|
|
||||||
var identityUrl = configuration.GetValue<string>("IdentityUrl");
|
return services;
|
||||||
|
}
|
||||||
services.AddAuthentication(options =>
|
|
||||||
{
|
public static IServiceCollection AddCustomMvc(this IServiceCollection services)
|
||||||
options.DefaultAuthenticateScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;
|
{
|
||||||
options.DefaultChallengeScheme = AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme;
|
// Add framework services.
|
||||||
|
services.AddControllers(options =>
|
||||||
}).AddJwtBearer(options =>
|
{
|
||||||
{
|
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
|
||||||
options.Authority = identityUrl;
|
})
|
||||||
options.RequireHttpsMetadata = false;
|
// Added for functional tests
|
||||||
options.Audience = "orders";
|
.AddApplicationPart(typeof(OrdersController).Assembly)
|
||||||
});
|
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true)
|
||||||
|
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
|
||||||
return services;
|
|
||||||
}
|
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