diff --git a/docker-compose.override.yml b/docker-compose.override.yml index c3dbd0027..51284360a 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -58,6 +58,7 @@ services: - BasketApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103 - OrderingApiClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102 - MobileShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5120 + - WebShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5121 - UseCustomizationData=True - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} - OrchestratorType=${ORCHESTRATOR_TYPE} @@ -262,4 +263,14 @@ services: - urls__orders=http://ordering.api - urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. ports: - - "5120:80" \ No newline at end of file + - "5120:80" + + webshoppingagg: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - urls__basket=http://basket.api + - urls__catalog=http://catalog.api + - urls__orders=http://ordering.api + - urls__identity=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5110. + ports: + - "5121:80" \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 81deef911..1f8fc6dd9 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -65,6 +65,7 @@ services: - BasketApiClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5103 - OrderingApiClient=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5102 - MobileShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5120 + - WebShoppingAggClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5120 - UseCustomizationData=True - ApplicationInsights__InstrumentationKey=${INSTRUMENTATION_KEY} - OrchestratorType=${ORCHESTRATOR_TYPE} diff --git a/docker-compose.yml b/docker-compose.yml index 703dbfee9..ae7a7faa0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -138,3 +138,9 @@ services: context: . dockerfile: src/Aggregators/Mobile.Shopping.HttpAggregator/Dockerfile + webshoppingagg: + image: eshop/webshoppingagg + build: + context: . + dockerfile: src/Aggregators/Mobile.Shopping.HttpAggregator/Dockerfile + diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index fc72b85f6..2be8cf090 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -105,6 +105,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aggregators", "Aggregators" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mobile.Shopping.HttpAggregator", "src\Aggregators\Mobile.Shopping.HttpAggregator\Mobile.Shopping.HttpAggregator.csproj", "{6E99F232-1536-424F-A28C-91692C8FD325}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web.Shopping.HttpAggregator", "src\Aggregators\Web.Shopping.HttpAggregator\Web.Shopping.HttpAggregator.csproj", "{714CE0A1-E8BE-4CF1-8948-C1202E1526E2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1415,6 +1417,54 @@ Global {6E99F232-1536-424F-A28C-91692C8FD325}.Release|x64.Build.0 = Release|Any CPU {6E99F232-1536-424F-A28C-91692C8FD325}.Release|x86.ActiveCfg = Release|Any CPU {6E99F232-1536-424F-A28C-91692C8FD325}.Release|x86.Build.0 = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|ARM.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|iPhone.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|x64.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|x64.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|x86.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.AppStore|x86.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|ARM.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|iPhone.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|x64.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|x86.ActiveCfg = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Debug|x86.Build.0 = Debug|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|Any CPU.Build.0 = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|ARM.ActiveCfg = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|ARM.Build.0 = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|iPhone.ActiveCfg = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|iPhone.Build.0 = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|x64.ActiveCfg = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|x64.Build.0 = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|x86.ActiveCfg = Release|Any CPU + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1465,6 +1515,7 @@ Global {E0C5162E-DF26-4341-9E51-14AE800D7505} = {7A58AA20-67F3-48F3-88C8-24EBFE621792} {EA378316-9D49-4A6B-858E-D4A25F948A74} = {932D8224-11F6-4D07-B109-DA28AD288A63} {6E99F232-1536-424F-A28C-91692C8FD325} = {EA378316-9D49-4A6B-858E-D4A25F948A74} + {714CE0A1-E8BE-4CF1-8948-C1202E1526E2} = {EA378316-9D49-4A6B-858E-D4A25F948A74} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {25728519-5F0F-4973-8A64-0A81EB4EA8D9} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Config/UrlsConfig.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Config/UrlsConfig.cs new file mode 100644 index 000000000..19be27dce --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Config/UrlsConfig.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config +{ + public class UrlsConfig + { + public class CatalogOperations + { + public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; + public static string GetItemsById(IEnumerable ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}"; + } + + public class BasketOperations + { + public static string GetItemById(string id) => $"/api/v1/basket/{id}"; + public static string UpdateBasket() => "/api/v1/basket"; + } + + public class OrdersOperations + { + public static string GetOrderDraft() => "/api/v1/orders/draft"; + } + + public string Basket { get; set; } + public string Catalog { get; set; } + public string Orders { get; set; } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/BasketController.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/BasketController.cs new file mode 100644 index 000000000..bfef55726 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/BasketController.cs @@ -0,0 +1,133 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers +{ + [Route("api/v1/[controller]")] + [Authorize] + public class BasketController : Controller + { + private readonly ICatalogService _catalog; + private readonly IBasketService _basket; + public BasketController(ICatalogService catalogService, IBasketService basketService) + { + _catalog = catalogService; + _basket = basketService; + } + + [HttpPost] + [HttpPut] + public async Task UpdateAllBasket([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 currentBasket = await _basket.GetById(data.BuyerId); + if (currentBasket == null) + { + currentBasket = new BasketData(data.BuyerId); + } + + var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId)); + var newBasket = new BasketData(data.BuyerId); + + foreach (var bitem in data.Items) + { + var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId); + if (catalogItem == null) + { + return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})"); + } + + newBasket.Items.Add(new BasketDataItem() + { + Id = bitem.Id, + ProductId = catalogItem.Id.ToString(), + ProductName = catalogItem.Name, + PictureUrl = catalogItem.PictureUri, + UnitPrice = catalogItem.Price, + Quantity = bitem.Quantity + }); + } + + await _basket.Update(newBasket); + return Ok(newBasket); + } + + [HttpPut] + [Route("items")] + public async Task UpdateQuantities([FromBody] UpdateBasketItemsRequest data) + { + if (!data.Updates.Any()) + { + return BadRequest("No updates sent"); + } + + // Retrieve the current basket + var currentBasket = await _basket.GetById(data.BasketId); + if (currentBasket == null) + { + return BadRequest($"Basket with id {data.BasketId} not found."); + } + + // Update with new quantities + foreach (var update in data.Updates) + { + var basketItem = currentBasket.Items.SingleOrDefault(bitem => bitem.Id == update.BasketItemId); + if (basketItem == null) + { + return BadRequest($"Basket item with id {update.BasketItemId} not found"); + } + basketItem.Quantity = update.NewQty; + } + + // Save the updated basket + await _basket.Update(currentBasket); + return Ok(currentBasket); + } + + [HttpPost] + [Route("items")] + public async Task AddBasketItem([FromBody] AddBasketItemRequest data) + { + if (data == null || data.Quantity == 0) + { + return BadRequest("Invalid payload"); + } + + // Step 1: Get the item from catalog + var item = await _catalog.GetCatalogItem(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.ToString(), + ProductName = item.Name, + Quantity = data.Quantity, + Id = Guid.NewGuid().ToString() + }); + + // Step 4: Update basket + await _basket.Update(currentBasket); + + + return Ok(); + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/HomeController.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/HomeController.cs new file mode 100644 index 000000000..58ed48d1a --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/HomeController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers +{ + [Route("")] + public class HomeController : Controller + { + [HttpGet()] + public IActionResult Index() + { + return new RedirectResult("~/swagger"); + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/OrderController.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/OrderController.cs new file mode 100644 index 000000000..fd108ffb8 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Controllers/OrderController.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Controllers +{ + [Route("api/v1/[controller]")] + [Authorize] + public class OrderController : Controller + { + private readonly IBasketService _basketService; + private readonly IOrderApiClient _orderClient; + public OrderController(IBasketService basketService, IOrderApiClient orderClient) + { + _basketService = basketService; + _orderClient = orderClient; + } + + [Route("draft/{basketId}")] + [HttpGet] + public async Task GetOrderDraft(string basketId) + { + 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}"); + } + + var orderDraft = await _orderClient.GetOrderDraftFromBasket(basket); + return Ok(orderDraft); + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Dockerfile b/src/Aggregators/Web.Shopping.HttpAggregator/Dockerfile new file mode 100644 index 000000000..267cb233c --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Dockerfile @@ -0,0 +1,18 @@ +FROM microsoft/aspnetcore:2.0.5 AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/aspnetcore-build:2.0 AS build +WORKDIR /src +COPY . . +RUN dotnet restore -nowarn:msb3202,nu1503 +WORKDIR /src/src/Aggregators/Web.Shopping.HttpAggregator +RUN dotnet build --no-restore -c Release -o /app + +FROM build AS publish +RUN dotnet publish --no-restore -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Web.Shopping.HttpAggregator.dll"] diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Filters/AuthorizeCheckOperationFilter.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Filters/AuthorizeCheckOperationFilter.cs new file mode 100644 index 000000000..3f382e5df --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Filters/AuthorizeCheckOperationFilter.cs @@ -0,0 +1,33 @@ +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters +{ + using Microsoft.AspNetCore.Authorization; + using Swashbuckle.AspNetCore.Swagger; + using Swashbuckle.AspNetCore.SwaggerGen; + using System.Collections.Generic; + using System.Linq; + + namespace Basket.API.Infrastructure.Filters + { + public class AuthorizeCheckOperationFilter : IOperationFilter + { + public void Apply(Operation operation, OperationFilterContext context) + { + // Check for authorize attribute + var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType().Any() || + context.ApiDescription.ActionAttributes().OfType().Any(); + + if (hasAuthorize) + { + operation.Responses.Add("401", new Response { Description = "Unauthorized" }); + operation.Responses.Add("403", new Response { Description = "Forbidden" }); + + operation.Security = new List>>(); + operation.Security.Add(new Dictionary> + { + { "oauth2", new [] { "Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator" } } + }); + } + } + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Models/AddBasketItemRequest.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Models/AddBasketItemRequest.cs new file mode 100644 index 000000000..88aff245f --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Models/AddBasketItemRequest.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +{ + public class AddBasketItemRequest + { + public int CatalogItemId { get; set; } + public string BasketId { get; set; } + + public int Quantity { get; set; } + + public AddBasketItemRequest() + { + Quantity = 1; + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Models/BasketData.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Models/BasketData.cs new file mode 100644 index 000000000..01831a5c9 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Models/BasketData.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +{ + public class BasketData + { + public string BuyerId { get; set; } + public List Items { get; set; } + + public BasketData(string buyerId) + { + BuyerId = buyerId; + Items = new List(); + } + } + + public class BasketDataItem + { + public string Id { get; set; } + public string ProductId { get; set; } + public string ProductName { get; set; } + public decimal UnitPrice { get; set; } + public decimal OldUnitPrice { get; set; } + public int Quantity { get; set; } + public string PictureUrl { get; set; } + + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Models/CatalogItem.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Models/CatalogItem.cs new file mode 100644 index 000000000..c6085f934 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Models/CatalogItem.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +{ + public class CatalogItem + { + public int Id { get; set; } + + public string Name { get; set; } + + public decimal Price { get; set; } + + + public string PictureUri { get; set; } + + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Models/OrderData.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Models/OrderData.cs new file mode 100644 index 000000000..e9d5982b9 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Models/OrderData.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +{ + public class OrderData + { + public string OrderNumber { get; set; } + public DateTime Date { get; set; } + public string Status { get; set; } + public decimal Total { get; set; } + public string Description { get; set; } + public string City { get; set; } + public string Street { get; set; } + public string State { get; set; } + public string Country { get; set; } + public string ZipCode { get; set; } + public string CardNumber { get; set; } + public string CardHolderName { get; set; } + public bool IsDraft { get; set; } + public DateTime CardExpiration { get; set; } + public string CardExpirationShort { get; set; } + public string CardSecurityNumber { get; set; } + + public int CardTypeId { get; set; } + + public string Buyer { get; set; } + + public List OrderItems { get; } = new List(); + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Models/OrderItemData.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Models/OrderItemData.cs new file mode 100644 index 000000000..1a40cb8cb --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Models/OrderItemData.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +{ + public class OrderItemData + { + public int ProductId { get; set; } + public string ProductName { get; set; } + public decimal UnitPrice { get; set; } + public decimal Discount { get; set; } + public int Units { get; set; } + public string PictureUrl { get; set; } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Models/UpdateBasketItemsRequest.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Models/UpdateBasketItemsRequest.cs new file mode 100644 index 000000000..b41c069bc --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Models/UpdateBasketItemsRequest.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +{ + public class UpdateBasketItemsRequest + { + + public string BasketId { get; set; } + + public ICollection Updates { get; set; } + + public UpdateBasketItemsRequest() + { + Updates = new List(); + } + } + + public class UpdateBasketItemData + { + public string BasketItemId { get; set; } + public int NewQty { get; set; } + + public UpdateBasketItemData() + { + NewQty = 0; + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Models/UpdateBasketRequest.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Models/UpdateBasketRequest.cs new file mode 100644 index 000000000..9beeeade4 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Models/UpdateBasketRequest.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models +{ + public class UpdateBasketRequest + { + public string BuyerId { get; set; } + + public IEnumerable Items { get; set; } + } + + public class UpdateBasketRequestItemData + { + public string Id { get; set; } // Basket id + public int ProductId { get; set; } // Catalog item id + public int Quantity { get; set; } // Quantity + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Program.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Program.cs new file mode 100644 index 000000000..c865a8b3b --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Program.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator +{ + public class Program + { + public static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + public static IWebHost BuildWebHost(string[] args) => + WebHost + .CreateDefaultBuilder(args) + .ConfigureAppConfiguration(cb => + { + var sources = cb.Sources; + sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() + { + Optional = true, + Path = "appsettings.localhost.json", + ReloadOnChange = false + }); + }) + .UseStartup() + .Build(); + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Properties/launchSettings.json b/src/Aggregators/Web.Shopping.HttpAggregator/Properties/launchSettings.json new file mode 100644 index 000000000..925e70b0d --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57425/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "PurchaseForMvc": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:61632/" + } + } +} \ No newline at end of file diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Services/BasketService.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Services/BasketService.cs new file mode 100644 index 000000000..5ca89a408 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Services/BasketService.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +{ + public class BasketService : IBasketService + { + + private readonly IHttpClient _apiClient; + private readonly ILogger _logger; + private readonly UrlsConfig _urls; + private readonly IHttpContextAccessor _httpContextAccessor; + + public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + _httpContextAccessor = httpContextAccessor; + } + + public async Task GetById(string id) + { + var token = await GetUserTokenAsync(); + var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token); + var basket = !string.IsNullOrEmpty(data) ? JsonConvert.DeserializeObject(data) : null; + return basket; + } + + public async Task Update(BasketData currentBasket) + { + var token = await GetUserTokenAsync(); + var data = await _apiClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token); + int i = 0; + } + + async Task GetUserTokenAsync() + { + var context = _httpContextAccessor.HttpContext; + return await context.GetTokenAsync("access_token"); + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Services/CatalogService.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Services/CatalogService.cs new file mode 100644 index 000000000..46d895f68 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Services/CatalogService.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +{ + public class CatalogService : ICatalogService + { + + private readonly IHttpClient _apiClient; + private readonly ILogger _logger; + private readonly UrlsConfig _urls; + + public CatalogService(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } + + public async Task GetCatalogItem(int id) + { + var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); + var item = JsonConvert.DeserializeObject(data); + return item; + } + + public async Task> GetCatalogItems(IEnumerable ids) + { + var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); + var item = JsonConvert.DeserializeObject(data); + return item; + + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Services/IBasketService.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Services/IBasketService.cs new file mode 100644 index 000000000..f59c31965 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Services/IBasketService.cs @@ -0,0 +1,15 @@ +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +{ + public interface IBasketService + { + Task GetById(string id); + Task Update(BasketData currentBasket); + + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Services/ICatalogService.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Services/ICatalogService.cs new file mode 100644 index 000000000..7d192f3cc --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Services/ICatalogService.cs @@ -0,0 +1,14 @@ +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +{ + public interface ICatalogService + { + Task GetCatalogItem(int id); + Task> GetCatalogItems(IEnumerable ids); + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Services/IOrderApiClient.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Services/IOrderApiClient.cs new file mode 100644 index 000000000..c97eccbbd --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Services/IOrderApiClient.cs @@ -0,0 +1,13 @@ +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +{ + public interface IOrderApiClient + { + Task GetOrderDraftFromBasket(BasketData basket); + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Services/OrderApiClient.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Services/OrderApiClient.cs new file mode 100644 index 000000000..220e9afa9 --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Services/OrderApiClient.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services +{ + public class OrderApiClient : IOrderApiClient + { + + private readonly IHttpClient _apiClient; + private readonly ILogger _logger; + private readonly UrlsConfig _urls; + + public OrderApiClient(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + { + _apiClient = httpClient; + _logger = logger; + _urls = config.Value; + } + + public async Task GetOrderDraftFromBasket(BasketData basket) + { + var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); + var response = await _apiClient.PostAsync(url, basket); + response.EnsureSuccessStatusCode(); + var jsonResponse = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(jsonResponse); + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Startup.cs b/src/Aggregators/Web.Shopping.HttpAggregator/Startup.cs new file mode 100644 index 000000000..79b9bc77b --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Startup.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Config; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Filters.Basket.API.Infrastructure.Filters; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services; +using Swashbuckle.AspNetCore.Swagger; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + 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) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + + services.AddOptions(); + services.Configure(Configuration.GetSection("urls")); + + services.AddMvc(); + + services.AddSwaggerGen(options => + { + options.DescribeAllEnumsAsStrings(); + options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info + { + Title = "Purchase BFF API Gateway", + Version = "v1", + Description = "BFF API Gateway for Purchase features", + TermsOfService = "Terms Of Service" + }); + + options.AddSecurityDefinition("oauth2", new OAuth2Scheme + { + Type = "oauth2", + Flow = "implicit", + AuthorizationUrl = $"{Configuration.GetValue("IdentityUrlExternal")}/connect/authorize", + TokenUrl = $"{Configuration.GetValue("IdentityUrlExternal")}/connect/token", + Scopes = new Dictionary() + { + { "webshoppingagg", "Purchase BFF API Gateway" } + } + }); + + options.OperationFilter(); + }); + + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + + + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + var identityUrl = Configuration.GetValue("urls:identity"); + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + + }).AddJwtBearer(options => + { + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "webshoppingagg"; + options.Events = new JwtBearerEvents() + { + OnAuthenticationFailed = async ctx => + { + int i = 0; + }, + OnTokenValidated = async ctx => + { + int i = 0; + } + }; + }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + + var pathBase = Configuration["PATH_BASE"]; + if (!string.IsNullOrEmpty(pathBase)) + { + loggerFactory.CreateLogger("init").LogDebug($"Using PATH BASE '{pathBase}'"); + app.UsePathBase(pathBase); + } + + app.UseCors("CorsPolicy"); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseAuthentication(); + + app.UseMvc(); + + app.UseSwagger().UseSwaggerUI(c => + { + c.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Purchase BFF V1"); + c.ConfigureOAuth2("Microsoft.eShopOnContainers.Web.Shopping.HttpAggregatorwaggerui", "", "", "Purchase BFF Swagger UI"); + }); + + + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/Web.Shopping.HttpAggregator.csproj b/src/Aggregators/Web.Shopping.HttpAggregator/Web.Shopping.HttpAggregator.csproj new file mode 100644 index 000000000..fd04c8bee --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/Web.Shopping.HttpAggregator.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp2.0 + Web.Shopping.HttpAggregator + Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator + ..\..\..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + + + diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/appsettings.json b/src/Aggregators/Web.Shopping.HttpAggregator/appsettings.json new file mode 100644 index 000000000..26bb0ac7a --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Warning" + } + }, + "Console": { + "LogLevel": { + "Default": "Warning" + } + } + } +} diff --git a/src/Aggregators/Web.Shopping.HttpAggregator/appsettings.localhost.json b/src/Aggregators/Web.Shopping.HttpAggregator/appsettings.localhost.json new file mode 100644 index 000000000..57b5e894d --- /dev/null +++ b/src/Aggregators/Web.Shopping.HttpAggregator/appsettings.localhost.json @@ -0,0 +1,8 @@ +{ + "urls": { + "basket": "http://localhost:55105", + "catalog": "http://localhost:55101", + "orders": "http://localhost:55102", + "identity": "http://localhost:55105" + } +} diff --git a/src/Apigw/OcelotApiGw/Startup.cs b/src/Apigw/OcelotApiGw/Startup.cs index f0a41c172..254849d89 100644 --- a/src/Apigw/OcelotApiGw/Startup.cs +++ b/src/Apigw/OcelotApiGw/Startup.cs @@ -45,7 +45,7 @@ namespace OcelotApiGw x.RequireHttpsMetadata = false; x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { - ValidAudiences = new[] { "orders", "basket", "locations", "marketing", "mobileshoppingagg" } + ValidAudiences = new[] { "orders", "basket", "locations", "marketing", "mobileshoppingagg", "webshoppingagg" } }; x.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents() { diff --git a/src/Apigw/configs/webshoppingapigw/configuration.json b/src/Apigw/configs/webshoppingapigw/configuration.json index 801eac5d9..63aeb7752 100644 --- a/src/Apigw/configs/webshoppingapigw/configuration.json +++ b/src/Apigw/configs/webshoppingapigw/configuration.json @@ -49,7 +49,7 @@ "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { - "Host": "mobileshoppingagg", + "Host": "webshoppingagg", "Port": 80 } ], diff --git a/src/Services/Identity/Identity.API/Configuration/Config.cs b/src/Services/Identity/Identity.API/Configuration/Config.cs index 18e313bf8..6aed6120d 100644 --- a/src/Services/Identity/Identity.API/Configuration/Config.cs +++ b/src/Services/Identity/Identity.API/Configuration/Config.cs @@ -15,7 +15,8 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration new ApiResource("basket", "Basket Service"), new ApiResource("marketing", "Marketing Service"), new ApiResource("locations", "Locations Service"), - new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator") + new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"), + new ApiResource("Webshoppingagg", "Web Shopping Aggregator") }; } @@ -54,7 +55,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration "basket", "locations", "marketing", - "mobileshoppingagg" + "webshoppingagg" } }, new Client @@ -118,7 +119,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration "basket", "locations", "marketing", - "mobileshoppingagg" + "webshoppingagg" }, }, new Client @@ -151,7 +152,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration "basket", "locations", "marketing", - "mobileshoppingagg" + "webshoppingagg" }, }, new Client @@ -228,6 +229,21 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration { "mobileshoppingagg" } + }, + new Client + { + ClientId = "webshoppingaggswaggerui", + ClientName = "Web Shopping Aggregattor Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/o2c.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/" }, + + AllowedScopes = + { + "webshoppingagg" + } } }; diff --git a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs index 4757d4743..a5a4383bd 100644 --- a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs @@ -24,6 +24,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data clientUrls.Add("BasketApi", configuration.GetValue("BasketApiClient")); clientUrls.Add("OrderingApi", configuration.GetValue("OrderingApiClient")); clientUrls.Add("MobileShoppingAgg", configuration.GetValue("MobileShoppingAggClient")); + clientUrls.Add("WebShoppingAgg", configuration.GetValue("WebShoppingAggClient")); if (!context.Clients.Any()) { diff --git a/src/Web/WebMVC/Startup.cs b/src/Web/WebMVC/Startup.cs index 21986b5df..385a007c3 100644 --- a/src/Web/WebMVC/Startup.cs +++ b/src/Web/WebMVC/Startup.cs @@ -131,6 +131,7 @@ namespace Microsoft.eShopOnContainers.WebMVC options.Scope.Add("marketing"); options.Scope.Add("locations"); options.Scope.Add("mobileshoppingagg"); + options.Scope.Add("webshoppingagg"); }); }