Browse Source

Moved namespaces to globalusing file

pull/1770/head
Sumit Ghosh 3 years ago
parent
commit
a902b00a3c
27 changed files with 648 additions and 809 deletions
  1. +21
    -24
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs
  2. +115
    -125
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs
  3. +6
    -9
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs
  4. +26
    -34
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs
  5. +1
    -8
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs
  6. +24
    -30
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs
  7. +32
    -42
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs
  8. +9
    -10
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs
  9. +10
    -15
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs
  10. +10
    -13
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketDataItem.cs
  11. +7
    -8
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs
  12. +21
    -27
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs
  13. +9
    -12
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs
  14. +8
    -11
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs
  15. +7
    -12
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs
  16. +5
    -10
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs
  17. +6
    -9
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs
  18. +1
    -7
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs
  19. +62
    -69
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs
  20. +27
    -34
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs
  21. +4
    -8
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs
  22. +4
    -9
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs
  23. +3
    -7
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs
  24. +4
    -8
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderingService.cs
  25. +22
    -31
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs
  26. +54
    -61
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs
  27. +150
    -176
      src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs

+ 21
- 24
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Config/UrlsConfig.cs View File

@ -1,38 +1,35 @@
using System.Collections.Generic;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config
public class UrlsConfig
{ {
public class UrlsConfig
public class CatalogOperations
{ {
public class CatalogOperations
{
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}";
}
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}";
}
public class BasketOperations
{
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
public class BasketOperations
{
public static string GetItemById(string id) => $"/api/v1/basket/{id}";
public static string UpdateBasket() => "/api/v1/basket";
}
public static string UpdateBasket() => "/api/v1/basket";
}
public class OrdersOperations
{
public static string GetOrderDraft() => "/api/v1/orders/draft";
}
public class OrdersOperations
{
public static string GetOrderDraft() => "/api/v1/orders/draft";
}
public string Basket { get; set; }
public string Basket { get; set; }
public string Catalog { get; set; }
public string Catalog { get; set; }
public string Orders { get; set; }
public string Orders { get; set; }
public string GrpcBasket { get; set; }
public string GrpcBasket { get; set; }
public string GrpcCatalog { get; set; }
public string GrpcCatalog { get; set; }
public string GrpcOrdering { get; set; }
}
public string GrpcOrdering { get; set; }
} }

+ 115
- 125
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/BasketController.cs View File

@ -1,156 +1,146 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers;
[Route("api/v1/[controller]")]
[Authorize]
[ApiController]
public class BasketController : ControllerBase
{ {
[Route("api/v1/[controller]")]
[Authorize]
[ApiController]
public class BasketController : ControllerBase
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
public BasketController(ICatalogService catalogService, IBasketService basketService)
{ {
private readonly ICatalogService _catalog;
private readonly IBasketService _basket;
_catalog = catalogService;
_basket = basketService;
}
public BasketController(ICatalogService catalogService, IBasketService basketService)
[HttpPost]
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{ {
_catalog = catalogService;
_basket = basketService;
return BadRequest("Need to pass at least one basket line");
} }
[HttpPost]
[HttpPut]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateAllBasketAsync([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId);
// Retrieve the current basket
var basket = await _basket.GetById(data.BuyerId) ?? new BasketData(data.BuyerId);
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
// group by product id to avoid duplicates
var itemsCalculated = data
.Items
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i })
.Select(groupedItem =>
{
var item = groupedItem.items.First();
item.Quantity = groupedItem.items.Sum(i => i.Quantity);
return item;
});
foreach (var bitem in itemsCalculated)
{
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
var catalogItems = await _catalog.GetCatalogItemsAsync(data.Items.Select(x => x.ProductId));
// group by product id to avoid duplicates
var itemsCalculated = data
.Items
.GroupBy(x => x.ProductId, x => x, (k, i) => new { productId = k, items = i })
.Select(groupedItem =>
{ {
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
}
var item = groupedItem.items.First();
item.Quantity = groupedItem.items.Sum(i => i.Quantity);
return item;
});
var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId);
if (itemInBasket == null)
{
basket.Items.Add(new BasketDataItem()
{
Id = bitem.Id,
ProductId = catalogItem.Id,
ProductName = catalogItem.Name,
PictureUrl = catalogItem.PictureUri,
UnitPrice = catalogItem.Price,
Quantity = bitem.Quantity
});
}
else
{
itemInBasket.Quantity = bitem.Quantity;
}
}
await _basket.UpdateAsync(basket);
return basket;
}
[HttpPut]
[Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
foreach (var bitem in itemsCalculated)
{ {
if (!data.Updates.Any())
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
{ {
return BadRequest("No updates sent");
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
} }
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
if (currentBasket == null)
var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == bitem.ProductId);
if (itemInBasket == null)
{ {
return BadRequest($"Basket with id {data.BasketId} not found.");
basket.Items.Add(new BasketDataItem()
{
Id = bitem.Id,
ProductId = catalogItem.Id,
ProductName = catalogItem.Name,
PictureUrl = catalogItem.PictureUri,
UnitPrice = catalogItem.Price,
Quantity = bitem.Quantity
});
} }
// Update with new quantities
foreach (var update in data.Updates)
else
{ {
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
itemInBasket.Quantity = bitem.Quantity;
}
}
if (basketItem == null)
{
return BadRequest($"Basket item with id {update.BasketItemId} not found");
}
await _basket.UpdateAsync(basket);
basketItem.Quantity = update.NewQty;
}
return basket;
}
// Save the updated basket
await _basket.UpdateAsync(currentBasket);
[HttpPut]
[Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(BasketData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<BasketData>> UpdateQuantitiesAsync([FromBody] UpdateBasketItemsRequest data)
{
if (!data.Updates.Any())
{
return BadRequest("No updates sent");
}
return currentBasket;
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BasketId);
if (currentBasket == null)
{
return BadRequest($"Basket with id {data.BasketId} not found.");
} }
[HttpPost]
[Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
// Update with new quantities
foreach (var update in data.Updates)
{ {
if (data == null || data.Quantity == 0)
var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId);
if (basketItem == null)
{ {
return BadRequest("Invalid payload");
return BadRequest($"Basket item with id {update.BasketItemId} not found");
} }
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
basketItem.Quantity = update.NewQty;
}
// Save the updated basket
await _basket.UpdateAsync(currentBasket);
//item.PictureUri =
return currentBasket;
}
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id,
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
// Step 4: Update basket
await _basket.UpdateAsync(currentBasket);
return Ok();
[HttpPost]
[Route("items")]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.OK)]
public async Task<ActionResult> AddBasketItemAsync([FromBody] AddBasketItemRequest data)
{
if (data == null || data.Quantity == 0)
{
return BadRequest("Invalid payload");
} }
// Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItemAsync(data.CatalogItemId);
//item.PictureUri =
// Step 2: Get current basket status
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem()
{
UnitPrice = item.Price,
PictureUrl = item.PictureUri,
ProductId = item.Id,
ProductName = item.Name,
Quantity = data.Quantity,
Id = Guid.NewGuid().ToString()
});
// Step 4: Update basket
await _basket.UpdateAsync(currentBasket);
return Ok();
} }
} }

+ 6
- 9
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/HomeController.cs View File

@ -1,14 +1,11 @@
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[Route("")]
public class HomeController : Controller
{ {
[Route("")]
public class HomeController : Controller
[HttpGet()]
public IActionResult Index()
{ {
[HttpGet()]
public IActionResult Index()
{
return new RedirectResult("~/swagger");
}
return new RedirectResult("~/swagger");
} }
} }

+ 26
- 34
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Controllers/OrderController.cs View File

@ -1,45 +1,37 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using System.Net;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Controllers
[Route("api/v1/[controller]")]
[Authorize]
[ApiController]
public class OrderController : ControllerBase
{ {
[Route("api/v1/[controller]")]
[Authorize]
[ApiController]
public class OrderController : ControllerBase
private readonly IBasketService _basketService;
private readonly IOrderingService _orderingService;
public OrderController(IBasketService basketService, IOrderingService orderingService)
{ {
private readonly IBasketService _basketService;
private readonly IOrderingService _orderingService;
_basketService = basketService;
_orderingService = orderingService;
}
public OrderController(IBasketService basketService, IOrderingService orderingService)
[Route("draft/{basketId}")]
[HttpGet]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
{
if (string.IsNullOrEmpty(basketId))
{ {
_basketService = basketService;
_orderingService = orderingService;
return BadRequest("Need a valid basketid");
} }
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
[Route("draft/{basketId}")]
[HttpGet]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(OrderData), (int)HttpStatusCode.OK)]
public async Task<ActionResult<OrderData>> GetOrderDraftAsync(string basketId)
if (basket == null)
{ {
if (string.IsNullOrEmpty(basketId))
{
return BadRequest("Need a valid basketid");
}
// Get the basket data and build a order draft based on it
var basket = await _basketService.GetById(basketId);
if (basket == null)
{
return BadRequest($"No basket found for id {basketId}");
}
return await _orderingService.GetOrderDraftAsync(basket);
return BadRequest($"No basket found for id {basketId}");
} }
return await _orderingService.GetOrderDraftAsync(basket);
} }
} }

+ 1
- 8
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Filters/AuthorizeCheckOperationFilter.cs View File

@ -1,12 +1,5 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters
{ {
namespace Basket.API.Infrastructure.Filters namespace Basket.API.Infrastructure.Filters
{ {
public class AuthorizeCheckOperationFilter : IOperationFilter public class AuthorizeCheckOperationFilter : IOperationFilter


+ 24
- 30
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs View File

@ -1,41 +1,35 @@
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure
public class GrpcExceptionInterceptor : Interceptor
{ {
public class GrpcExceptionInterceptor : Interceptor
private readonly ILogger<GrpcExceptionInterceptor> _logger;
public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
{ {
private readonly ILogger<GrpcExceptionInterceptor> _logger;
_logger = logger;
}
public GrpcExceptionInterceptor(ILogger<GrpcExceptionInterceptor> logger)
{
_logger = logger;
}
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var call = continuation(request, context);
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
}
return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
{
try
{
var response = await t;
return response;
} }
private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
catch (RpcException e)
{ {
try
{
var response = await t;
return response;
}
catch (RpcException e)
{
_logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message);
return default;
}
_logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message);
return default;
} }
} }
} }

+ 32
- 42
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs View File

@ -1,54 +1,44 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure
{
public class HttpClientAuthorizationDelegatingHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<HttpClientAuthorizationDelegatingHandler> _logger;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor, ILogger<HttpClientAuthorizationDelegatingHandler> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public class HttpClientAuthorizationDelegatingHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<HttpClientAuthorizationDelegatingHandler> _logger;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Version = new System.Version(2, 0);
request.Method = HttpMethod.Get;
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor, ILogger<HttpClientAuthorizationDelegatingHandler> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
var authorizationHeader = _httpContextAccessor.HttpContext
.Request.Headers["Authorization"];
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Version = new System.Version(2, 0);
request.Method = HttpMethod.Get;
if (!string.IsNullOrEmpty(authorizationHeader))
{
request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
}
var authorizationHeader = _httpContextAccessor.HttpContext
.Request.Headers["Authorization"];
var token = await GetToken();
if (!string.IsNullOrEmpty(authorizationHeader))
{
request.Headers.Add("Authorization", new List<string>() { authorizationHeader });
}
if (token != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
var token = await GetToken();
return await base.SendAsync(request, cancellationToken);
if (token != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
} }
async Task<string> GetToken()
{
const string ACCESS_TOKEN = "access_token";
return await base.SendAsync(request, cancellationToken);
}
return await _httpContextAccessor.HttpContext
.GetTokenAsync(ACCESS_TOKEN);
}
async Task<string> GetToken()
{
const string ACCESS_TOKEN = "access_token";
return await _httpContextAccessor.HttpContext
.GetTokenAsync(ACCESS_TOKEN);
} }
} }

+ 9
- 10
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/AddBasketItemRequest.cs View File

@ -1,16 +1,15 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
public class AddBasketItemRequest
{ {
public class AddBasketItemRequest
{
public int CatalogItemId { get; set; }
public int CatalogItemId { get; set; }
public string BasketId { get; set; }
public string BasketId { get; set; }
public int Quantity { get; set; }
public int Quantity { get; set; }
public AddBasketItemRequest()
{
Quantity = 1;
}
public AddBasketItemRequest()
{
Quantity = 1;
} }
} }

+ 10
- 15
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketData.cs View File

@ -1,22 +1,17 @@
using System.Collections.Generic;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class BasketData
{ {
public string BuyerId { get; set; }
public class BasketData
{
public string BuyerId { get; set; }
public List<BasketDataItem> Items { get; set; } = new List<BasketDataItem>();
public BasketData()
{
}
public List<BasketDataItem> Items { get; set; } = new List<BasketDataItem>();
public BasketData(string buyerId)
{
BuyerId = buyerId;
}
public BasketData()
{
} }
public BasketData(string buyerId)
{
BuyerId = buyerId;
}
} }

+ 10
- 13
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/BasketDataItem.cs View File

@ -1,21 +1,18 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class BasketDataItem
{
public string Id { get; set; }
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
public int ProductId { get; set; }
public class BasketDataItem
{
public string Id { get; set; }
public string ProductName { get; set; }
public int ProductId { get; set; }
public decimal UnitPrice { get; set; }
public string ProductName { get; set; }
public decimal OldUnitPrice { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public decimal OldUnitPrice { get; set; }
public string PictureUrl { get; set; }
}
public int Quantity { get; set; }
public string PictureUrl { get; set; }
} }

+ 7
- 8
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/CatalogItem.cs View File

@ -1,13 +1,12 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
public class CatalogItem
{ {
public class CatalogItem
{
public int Id { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public decimal Price { get; set; }
public string PictureUri { get; set; }
}
public string PictureUri { get; set; }
} }

+ 21
- 27
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderData.cs View File

@ -1,48 +1,42 @@
using System;
using System.Collections.Generic;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class OrderData
{ {
public string OrderNumber { get; set; }
public class OrderData
{
public string OrderNumber { get; set; }
public DateTime Date { get; set; }
public DateTime Date { get; set; }
public string Status { get; set; }
public string Status { get; set; }
public decimal Total { get; set; }
public decimal Total { get; set; }
public string Description { get; set; }
public string Description { get; set; }
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 bool IsDraft { get; set; }
public bool IsDraft { get; set; }
public DateTime CardExpiration { get; set; }
public DateTime CardExpiration { get; set; }
public string CardExpirationShort { get; set; }
public string CardExpirationShort { 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 List<OrderItemData> OrderItems { get; } = new List<OrderItemData>();
}
public string Buyer { get; set; }
public List<OrderItemData> OrderItems { get; } = new List<OrderItemData>();
} }

+ 9
- 12
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/OrderItemData.cs View File

@ -1,19 +1,16 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class OrderItemData
{
public int ProductId { get; set; }
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
public string ProductName { get; set; }
public class OrderItemData
{
public int ProductId { get; set; }
public decimal UnitPrice { get; set; }
public string ProductName { get; set; }
public decimal Discount { get; set; }
public decimal UnitPrice { get; set; }
public int Units { get; set; }
public decimal Discount { get; set; }
public string PictureUrl { get; set; }
}
public int Units { get; set; }
public string PictureUrl { get; set; }
} }

+ 8
- 11
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemData.cs View File

@ -1,16 +1,13 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
public class UpdateBasketItemData
{
public string BasketItemId { get; set; }
public class UpdateBasketItemData
{
public string BasketItemId { get; set; }
public int NewQty { get; set; }
public int NewQty { get; set; }
public UpdateBasketItemData()
{
NewQty = 0;
}
public UpdateBasketItemData()
{
NewQty = 0;
} }
} }

+ 7
- 12
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketItemsRequest.cs View File

@ -1,19 +1,14 @@
using System.Collections.Generic;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class UpdateBasketItemsRequest
{ {
public class UpdateBasketItemsRequest
{
public string BasketId { get; set; }
public string BasketId { get; set; }
public ICollection<UpdateBasketItemData> Updates { get; set; }
public ICollection<UpdateBasketItemData> Updates { get; set; }
public UpdateBasketItemsRequest()
{
Updates = new List<UpdateBasketItemData>();
}
public UpdateBasketItemsRequest()
{
Updates = new List<UpdateBasketItemData>();
} }
} }

+ 5
- 10
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequest.cs View File

@ -1,13 +1,8 @@
using System.Collections.Generic;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
public class UpdateBasketRequest
{ {
public string BuyerId { get; set; }
public class UpdateBasketRequest
{
public string BuyerId { get; set; }
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
}
}
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
}

+ 6
- 9
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Models/UpdateBasketRequestItemData.cs View File

@ -1,13 +1,10 @@
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models
{
public class UpdateBasketRequestItemData
{
public string Id { get; set; } // Basket id
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
public int ProductId { get; set; } // Catalog item id
public class UpdateBasketRequestItemData
{
public string Id { get; set; } // Basket id
public int Quantity { get; set; } // Quantity
}
public int ProductId { get; set; } // Catalog item id
public int Quantity { get; set; } // Quantity
} }

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

@ -1,10 +1,4 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator;
using Serilog;
BuildWebHost(args).Run();
BuildWebHost(args).Run();
IWebHost BuildWebHost(string[] args) => IWebHost BuildWebHost(string[] args) =>
WebHost WebHost
.CreateDefaultBuilder(args) .CreateDefaultBuilder(args)


+ 62
- 69
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs View File

@ -1,90 +1,83 @@
using GrpcBasket;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public class BasketService : IBasketService
{ {
public class BasketService : IBasketService
private readonly Basket.BasketClient _basketClient;
private readonly ILogger<BasketService> _logger;
public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
{ {
private readonly Basket.BasketClient _basketClient;
private readonly ILogger<BasketService> _logger;
_basketClient = basketClient;
_logger = logger;
}
public BasketService(Basket.BasketClient basketClient, ILogger<BasketService> logger)
{
_basketClient = basketClient;
_logger = logger;
}
public async Task<BasketData> GetById(string id)
{
_logger.LogDebug("grpc client created, request = {@id}", id);
var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
_logger.LogDebug("grpc response {@response}", response);
public async Task<BasketData> GetById(string id)
{
_logger.LogDebug("grpc client created, request = {@id}", id);
var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id });
_logger.LogDebug("grpc response {@response}", response);
return MapToBasketData(response);
}
return MapToBasketData(response);
}
public async Task UpdateAsync(BasketData currentBasket)
{
_logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
var request = MapToCustomerBasketRequest(currentBasket);
_logger.LogDebug("Grpc update basket request {@request}", request);
public async Task UpdateAsync(BasketData currentBasket)
{
_logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket);
var request = MapToCustomerBasketRequest(currentBasket);
_logger.LogDebug("Grpc update basket request {@request}", request);
await _basketClient.UpdateBasketAsync(request);
}
await _basketClient.UpdateBasketAsync(request);
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)
{
if (customerBasketRequest == null)
{
return null;
} }
private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest)
var map = new BasketData
{ {
if (customerBasketRequest == null)
{
return null;
}
BuyerId = customerBasketRequest.Buyerid
};
var map = new BasketData
{
BuyerId = customerBasketRequest.Buyerid
};
customerBasketRequest.Items.ToList().ForEach(item => map.Items.Add(new BasketDataItem
{
Id = item.Id,
OldUnitPrice = (decimal)item.Oldunitprice,
PictureUrl = item.Pictureurl,
ProductId = item.Productid,
ProductName = item.Productname,
Quantity = item.Quantity,
UnitPrice = (decimal)item.Unitprice
}));
customerBasketRequest.Items.ToList().ForEach(item => map.Items.Add(new BasketDataItem
{
Id = item.Id,
OldUnitPrice = (decimal)item.Oldunitprice,
PictureUrl = item.Pictureurl,
ProductId = item.Productid,
ProductName = item.Productname,
Quantity = item.Quantity,
UnitPrice = (decimal)item.Unitprice
}));
return map;
}
return map;
private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData)
{
if (basketData == null)
{
return null;
} }
private CustomerBasketRequest MapToCustomerBasketRequest(BasketData basketData)
var map = new CustomerBasketRequest
{ {
if (basketData == null)
{
return null;
}
var map = new CustomerBasketRequest
{
Buyerid = basketData.BuyerId
};
Buyerid = basketData.BuyerId
};
basketData.Items.ToList().ForEach(item => map.Items.Add(new BasketItemResponse
{
Id = item.Id,
Oldunitprice = (double)item.OldUnitPrice,
Pictureurl = item.PictureUrl,
Productid = item.ProductId,
Productname = item.ProductName,
Quantity = item.Quantity,
Unitprice = (double)item.UnitPrice
}));
basketData.Items.ToList().ForEach(item => map.Items.Add(new BasketItemResponse
{
Id = item.Id,
Oldunitprice = (double)item.OldUnitPrice,
Pictureurl = item.PictureUrl,
Productid = item.ProductId,
Productname = item.ProductName,
Quantity = item.Quantity,
Unitprice = (double)item.UnitPrice
}));
return map;
}
return map;
} }
} }

+ 27
- 34
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs View File

@ -1,43 +1,36 @@
using CatalogApi;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public class CatalogService : ICatalogService
{ {
public class CatalogService : ICatalogService
{
private readonly Catalog.CatalogClient _client;
private readonly Catalog.CatalogClient _client;
public CatalogService(Catalog.CatalogClient client)
{
_client = client;
}
public CatalogService(Catalog.CatalogClient client)
{
_client = client;
}
public async Task<CatalogItem> GetCatalogItemAsync(int id)
{
var request = new CatalogItemRequest { Id = id };
var response = await _client.GetItemByIdAsync(request);
return MapToCatalogItemResponse(response);
}
public async Task<CatalogItem> GetCatalogItemAsync(int id)
{
var request = new CatalogItemRequest { Id = id };
var response = await _client.GetItemByIdAsync(request);
return MapToCatalogItemResponse(response);
}
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
{
var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 };
var response = await _client.GetItemsByIdsAsync(request);
return response.Data.Select(MapToCatalogItemResponse);
}
public async Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids)
{
var request = new CatalogItemsRequest { Ids = string.Join(",", ids), PageIndex = 1, PageSize = 10 };
var response = await _client.GetItemsByIdsAsync(request);
return response.Data.Select(MapToCatalogItemResponse);
}
private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse)
private CatalogItem MapToCatalogItemResponse(CatalogItemResponse catalogItemResponse)
{
return new CatalogItem
{ {
return new CatalogItem
{
Id = catalogItemResponse.Id,
Name = catalogItemResponse.Name,
PictureUri = catalogItemResponse.PictureUri,
Price = (decimal)catalogItemResponse.Price
};
}
Id = catalogItemResponse.Id,
Name = catalogItemResponse.Name,
PictureUri = catalogItemResponse.PictureUri,
Price = (decimal)catalogItemResponse.Price
};
} }
} }

+ 4
- 8
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IBasketService.cs View File

@ -1,13 +1,9 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public interface IBasketService
{ {
public interface IBasketService
{
Task<BasketData> GetById(string id);
Task<BasketData> GetById(string id);
Task UpdateAsync(BasketData currentBasket);
Task UpdateAsync(BasketData currentBasket);
}
} }

+ 4
- 9
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/ICatalogService.cs View File

@ -1,13 +1,8 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public interface ICatalogService
{ {
public interface ICatalogService
{
Task<CatalogItem> GetCatalogItemAsync(int id);
Task<CatalogItem> GetCatalogItemAsync(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids);
}
Task<IEnumerable<CatalogItem>> GetCatalogItemsAsync(IEnumerable<int> ids);
} }

+ 3
- 7
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderApiClient.cs View File

@ -1,10 +1,6 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public interface IOrderApiClient
{ {
public interface IOrderApiClient
{
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket);
}
Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket);
} }

+ 4
- 8
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/IOrderingService.cs View File

@ -1,10 +1,6 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public interface IOrderingService
{ {
public interface IOrderingService
{
Task<OrderData> GetOrderDraftAsync(BasketData basketData);
}
}
Task<OrderData> GetOrderDraftAsync(BasketData basketData);
}

+ 22
- 31
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs View File

@ -1,40 +1,31 @@
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public class OrderApiClient : IOrderApiClient
{ {
public class OrderApiClient : IOrderApiClient
{
private readonly HttpClient _apiClient;
private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls;
private readonly HttpClient _apiClient;
private readonly ILogger<OrderApiClient> _logger;
private readonly UrlsConfig _urls;
public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
{
_apiClient = httpClient;
_logger = logger;
_urls = config.Value;
}
public OrderApiClient(HttpClient httpClient, ILogger<OrderApiClient> logger, IOptions<UrlsConfig> config)
{
_apiClient = httpClient;
_logger = logger;
_urls = config.Value;
}
public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket)
{
var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(uri, content);
public async Task<OrderData> GetOrderDraftFromBasketAsync(BasketData basket)
{
var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft();
var content = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json");
var response = await _apiClient.PostAsync(uri, content);
response.EnsureSuccessStatusCode();
response.EnsureSuccessStatusCode();
var ordersDraftResponse = await response.Content.ReadAsStringAsync();
var ordersDraftResponse = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<OrderData>(ordersDraftResponse, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
}
return JsonSerializer.Deserialize<OrderData>(ordersDraftResponse, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
} }
} }

+ 54
- 61
src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderingService.cs View File

@ -1,79 +1,72 @@
using GrpcOrdering;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services
public class OrderingService : IOrderingService
{ {
public class OrderingService : IOrderingService
private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
private readonly ILogger<OrderingService> _logger;
public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
{ {
private readonly OrderingGrpc.OrderingGrpcClient _orderingGrpcClient;
private readonly ILogger<OrderingService> _logger;
_orderingGrpcClient = orderingGrpcClient;
_logger = logger;
}
public OrderingService(OrderingGrpc.OrderingGrpcClient orderingGrpcClient, ILogger<OrderingService> logger)
{
_orderingGrpcClient = orderingGrpcClient;
_logger = logger;
}
public async Task<OrderData> GetOrderDraftAsync(BasketData basketData)
{
_logger.LogDebug(" grpc client created, basketData={@basketData}", basketData);
public async Task<OrderData> GetOrderDraftAsync(BasketData basketData)
{
_logger.LogDebug(" grpc client created, basketData={@basketData}", basketData);
var command = MapToOrderDraftCommand(basketData);
var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command);
_logger.LogDebug(" grpc response: {@response}", response);
var command = MapToOrderDraftCommand(basketData);
var response = await _orderingGrpcClient.CreateOrderDraftFromBasketDataAsync(command);
_logger.LogDebug(" grpc response: {@response}", response);
return MapToResponse(response, basketData);
}
return MapToResponse(response, basketData);
private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData)
{
if (orderDraft == null)
{
return null;
} }
private OrderData MapToResponse(GrpcOrdering.OrderDraftDTO orderDraft, BasketData basketData)
var data = new OrderData
{ {
if (orderDraft == null)
{
return null;
}
Buyer = basketData.BuyerId,
Total = (decimal)orderDraft.Total,
};
var data = new OrderData
{
Buyer = basketData.BuyerId,
Total = (decimal)orderDraft.Total,
};
orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData
{
Discount = (decimal)o.Discount,
PictureUrl = o.PictureUrl,
ProductId = o.ProductId,
ProductName = o.ProductName,
UnitPrice = (decimal)o.UnitPrice,
Units = o.Units,
}));
orderDraft.OrderItems.ToList().ForEach(o => data.OrderItems.Add(new OrderItemData
{
Discount = (decimal)o.Discount,
PictureUrl = o.PictureUrl,
ProductId = o.ProductId,
ProductName = o.ProductName,
UnitPrice = (decimal)o.UnitPrice,
Units = o.Units,
}));
return data;
}
return data;
}
private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
private CreateOrderDraftCommand MapToOrderDraftCommand(BasketData basketData)
{
var command = new CreateOrderDraftCommand
{ {
var command = new CreateOrderDraftCommand
{
BuyerId = basketData.BuyerId,
};
BuyerId = basketData.BuyerId,
};
basketData.Items.ForEach(i => command.Items.Add(new BasketItem
{
Id = i.Id,
OldUnitPrice = (double)i.OldUnitPrice,
PictureUrl = i.PictureUrl,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = (double)i.UnitPrice,
}));
return command;
}
basketData.Items.ForEach(i => command.Items.Add(new BasketItem
{
Id = i.Id,
OldUnitPrice = (double)i.OldUnitPrice,
PictureUrl = i.PictureUrl,
ProductId = i.ProductId,
ProductName = i.ProductName,
Quantity = i.Quantity,
UnitPrice = (double)i.UnitPrice,
}));
return command;
} }
} }

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

@ -1,222 +1,196 @@
using CatalogApi;
using Devspaces.Support;
using GrpcBasket;
using GrpcOrdering;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure;
using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator
namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator;
public class Startup
{ {
public class Startup
public Startup(IConfiguration configuration)
{ {
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" });
services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration)
.AddDevspaces()
.AddHttpServices()
.AddGrpcServices();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{ {
services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddUrlGroup(new Uri(Configuration["CatalogUrlHC"]), name: "catalogapi-check", tags: new string[] { "catalogapi" })
.AddUrlGroup(new Uri(Configuration["OrderingUrlHC"]), name: "orderingapi-check", tags: new string[] { "orderingapi" })
.AddUrlGroup(new Uri(Configuration["BasketUrlHC"]), name: "basketapi-check", tags: new string[] { "basketapi" })
.AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
.AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" });
services.AddCustomMvc(Configuration)
.AddCustomAuthentication(Configuration)
.AddDevspaces()
.AddHttpServices()
.AddGrpcServices();
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
if (env.IsDevelopment())
{ {
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
loggerFactory.CreateLogger<Startup>().LogDebug("Using PATH BASE '{pathBase}'", pathBase);
app.UsePathBase(pathBase);
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDeveloperExceptionPage();
}
app.UseSwagger().UseSwaggerUI(c =>
app.UseSwagger().UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.OAuthClientId("mobileshoppingaggswaggerui");
c.OAuthClientSecret(string.Empty);
c.OAuthRealm(string.Empty);
c.OAuthAppName("Purchase BFF Swagger UI");
});
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
{ {
c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1");
c.OAuthClientId("mobileshoppingaggswaggerui");
c.OAuthClientSecret(string.Empty);
c.OAuthRealm(string.Empty);
c.OAuthAppName("Purchase BFF Swagger UI");
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
}); });
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
{ {
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
Predicate = r => r.Name.Contains("self")
}); });
}
});
} }
}
public static class ServiceCollectionExtensions
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{ {
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration)
{
services.AddOptions();
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
services.AddOptions();
services.Configure<UrlsConfig>(configuration.GetSection("urls"));
services.AddControllers()
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
services.AddControllers()
.AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true);
services.AddSwaggerGen(options =>
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new OpenApiInfo
{ {
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Shopping Aggregator for Mobile Clients",
Version = "v1",
Description = "Shopping Aggregator for Mobile Clients"
});
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
Title = "Shopping Aggregator for Mobile Clients",
Version = "v1",
Description = "Shopping Aggregator for Mobile Clients"
});
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{ {
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
Implicit = new OpenApiOAuthFlow()
{ {
Implicit = new OpenApiOAuthFlow()
{
AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"),
TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"),
AuthorizationUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize"),
TokenUrl = new Uri($"{configuration.GetValue<string>("IdentityUrlExternal")}/connect/token"),
Scopes = new Dictionary<string, string>()
{
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" }
}
Scopes = new Dictionary<string, string>()
{
{ "mobileshoppingagg", "Shopping Aggregator for Mobile Clients" }
} }
} }
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
}
}); });
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
services.AddCors(options =>
{ {
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
var identityUrl = configuration.GetValue<string>("urls:identity");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed((host) => true)
.AllowCredentials());
});
return services;
}
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");
})
.AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "mobileshoppingagg";
});
var identityUrl = configuration.GetValue<string>("urls:identity");
return services;
}
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
public static IServiceCollection AddHttpServices(this IServiceCollection services)
})
.AddJwtBearer(options =>
{ {
//register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "mobileshoppingagg";
});
//register http services
return services;
}
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
.AddDevspacesSupport();
public static IServiceCollection AddHttpServices(this IServiceCollection services)
{
//register delegating handlers
services.AddTransient<HttpClientAuthorizationDelegatingHandler>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
return services;
}
//register http services
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
{
services.AddTransient<GrpcExceptionInterceptor>();
services.AddHttpClient<IOrderApiClient, OrderApiClient>()
.AddDevspacesSupport();
services.AddScoped<IBasketService, BasketService>();
return services;
}
services.AddGrpcClient<Basket.BasketClient>((services, options) =>
{
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket;
options.Address = new Uri(basketApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
public static IServiceCollection AddGrpcServices(this IServiceCollection services)
{
services.AddTransient<GrpcExceptionInterceptor>();
services.AddScoped<ICatalogService, CatalogService>();
services.AddScoped<IBasketService, BasketService>();
services.AddGrpcClient<Catalog.CatalogClient>((services, options) =>
{
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog;
options.Address = new Uri(catalogApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
services.AddGrpcClient<Basket.BasketClient>((services, options) =>
{
var basketApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcBasket;
options.Address = new Uri(basketApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
services.AddScoped<IOrderingService, OrderingService>();
services.AddScoped<ICatalogService, CatalogService>();
services.AddGrpcClient<OrderingGrpc.OrderingGrpcClient>((services, options) =>
{
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
options.Address = new Uri(orderingApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
services.AddGrpcClient<Catalog.CatalogClient>((services, options) =>
{
var catalogApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcCatalog;
options.Address = new Uri(catalogApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
return services;
}
services.AddScoped<IOrderingService, OrderingService>();
services.AddGrpcClient<OrderingGrpc.OrderingGrpcClient>((services, options) =>
{
var orderingApi = services.GetRequiredService<IOptions<UrlsConfig>>().Value.GrpcOrdering;
options.Address = new Uri(orderingApi);
}).AddInterceptor<GrpcExceptionInterceptor>();
return services;
} }
} }

Loading…
Cancel
Save