using System; using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; using Amazon.S3; using Amazon.S3.Model; using Ardalis.GuardClauses; using Microsoft.eShopWeb.ApplicationCore.Entities; using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate; using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate; using Microsoft.eShopWeb.ApplicationCore.Interfaces; using Microsoft.eShopWeb.ApplicationCore.Specifications; using Newtonsoft.Json; using NServiceBus; using Polly; namespace Microsoft.eShopWeb.ApplicationCore.Services; public class OrderService : IOrderService { private readonly IRepository<Order> _db; private readonly IUriComposer _uriComposer; private readonly IMessageSession _broker; private readonly IRepository<Basket> _basketRepository; private readonly IRepository<CatalogItem> _itemRepository; public OrderService(IRepository<Basket> basketRepository, IRepository<CatalogItem> itemRepository, IRepository<Order> orderRepository, IUriComposer uriComposer, IMessageSession messageSession) { _db = orderRepository; _uriComposer = uriComposer; _broker = messageSession; _basketRepository = basketRepository; _itemRepository = itemRepository; } public async Task CreateOrderAsync(int basketId, Address shippingAddress) { var basketSpec = new BasketWithItemsSpecification(basketId); var basket = await _basketRepository.GetBySpecAsync(basketSpec); Guard.Against.NullBasket(basketId, basket); Guard.Against.EmptyBasketOnCheckout(basket.Items); var catalogItemsSpecification = new CatalogItemsSpecification(basket.Items.Select(item => item.CatalogItemId).ToArray()); var catalogItems = await _itemRepository.ListAsync(catalogItemsSpecification); var items = basket.Items.Select(basketItem => { var catalogItem = catalogItems.First(c => c.Id == basketItem.CatalogItemId); var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, _uriComposer.ComposePicUri(catalogItem.PictureUri)); var orderItem = new OrderItem(itemOrdered, basketItem.UnitPrice, basketItem.Quantity); return orderItem; }).ToList(); var order = new Order(basket.BuyerId, shippingAddress, items); await _db.AddAsync(order); await _db.SaveChangesAsync(); var orderPlacedEvent = new OrderPlacedEvent(order.Id); var retry = Policy.Handle<Exception>() .WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) }); var fallback = Policy.Handle<Exception>().FallbackAsync(async _ => { var s3Client = new AmazonS3Client(); await s3Client.PutObjectAsync(new PutObjectRequest { Key = Guid.NewGuid().ToString(), ContentBody = JsonConvert.SerializeObject(orderPlacedEvent) }); }); await fallback.WrapAsync(retry).ExecuteAsync(async () => { await _broker.Publish(orderPlacedEvent); }); } } public record OrderPlacedEvent(int OrderId) : IEvent;