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