diff --git a/src/ApiGateways/ApiGw-Base/Dockerfile b/src/ApiGateways/ApiGw-Base/Dockerfile index 7f0cf43a6..7fdd5f073 100644 --- a/src/ApiGateways/ApiGw-Base/Dockerfile +++ b/src/ApiGateways/ApiGw-Base/Dockerfile @@ -1,8 +1,8 @@ -FROM microsoft/aspnetcore:2.0 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY src/ApiGateways/ApiGw-Base/OcelotApiGw.csproj src/ApiGateways/ApiGw-Base/ RUN dotnet restore src/ApiGateways/ApiGw-Base/ diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile index 273743cee..7787dd159 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/ApiGateways/Mobile.Bff.Shopping/aggregator +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs new file mode 100644 index 000000000..967a8c826 --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +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 _httpContextAccesor; + + public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor) + { + _httpContextAccesor = httpContextAccesor; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var authorizationHeader = _httpContextAccesor.HttpContext + .Request.Headers["Authorization"]; + + if (!string.IsNullOrEmpty(authorizationHeader)) + { + request.Headers.Add("Authorization", new List() { authorizationHeader }); + } + + var token = await GetToken(); + + if (token != null) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + + return await base.SendAsync(request, cancellationToken); + } + + async Task GetToken() + { + const string ACCESS_TOKEN = "access_token"; + + return await _httpContextAccesor.HttpContext + .GetTokenAsync(ACCESS_TOKEN); + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs index b00fbb52c..8339ee44b 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs @@ -1,53 +1,41 @@ -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.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; +using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; +using System.Net.Http; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services { public class BasketService : IBasketService { - private readonly IHttpClient _apiClient; + private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly UrlsConfig _urls; - private readonly IHttpContextAccessor _httpContextAccessor; - public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger logger, IOptionsSnapshot config) + public BasketService(HttpClient httpClient, ILogger logger, IOptions config) { - _apiClient = httpClient; + _httpClient = 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 data = await _httpClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id)); + 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; - } + var basketContent = new StringContent(JsonConvert.SerializeObject(currentBasket), System.Text.Encoding.UTF8, "application/json"); - async Task GetUserTokenAsync() - { - var context = _httpContextAccessor.HttpContext; - return await context.GetTokenAsync("access_token"); + var data = await _httpClient.PostAsync(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), basketContent); } } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs index d37b67679..6c59f0c49 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/CatalogService.cs @@ -1,43 +1,41 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; +using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services { public class CatalogService : ICatalogService { - - private readonly IHttpClient _apiClient; + private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly UrlsConfig _urls; - public CatalogService(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + public CatalogService(HttpClient httpClient, ILogger logger, IOptions config) { - _apiClient = httpClient; + _httpClient = 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; + var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemById(id)); + var catalogItem = JsonConvert.DeserializeObject(stringContent); + + return catalogItem; } public async Task> GetCatalogItems(IEnumerable ids) { - var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); - var item = JsonConvert.DeserializeObject(data); - return item; + var stringContent = await _httpClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids)); + var catalogItems = JsonConvert.DeserializeObject(stringContent); + return catalogItems; } } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs index 2659e11cc..03644c110 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -1,24 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; +using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; +using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; -using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; +using System.Net.Http; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services { public class OrderApiClient : IOrderApiClient { - - private readonly IHttpClient _apiClient; + private readonly HttpClient _apiClient; private readonly ILogger _logger; private readonly UrlsConfig _urls; - public OrderApiClient(IHttpClient httpClient, ILogger logger, IOptionsSnapshot config) + public OrderApiClient(HttpClient httpClient, ILogger logger, IOptions config) { _apiClient = httpClient; _logger = logger; @@ -27,11 +23,15 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services public async Task GetOrderDraftFromBasket(BasketData basket) { - var url = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); - var response = await _apiClient.PostAsync(url, basket); + var uri = _urls.Orders + UrlsConfig.OrdersOperations.GetOrderDraft(); + var content = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json"); + var response = await _apiClient.PostAsync(uri, content); + response.EnsureSuccessStatusCode(); - var jsonResponse = await response.Content.ReadAsStringAsync(); - return JsonConvert.DeserializeObject(jsonResponse); + + var ordersDraftResponse = await response.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject(ordersDraftResponse); } } } diff --git a/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json b/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json index 870690ed4..0fd6d9024 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json +++ b/src/ApiGateways/Mobile.Bff.Shopping/apigw/configuration.json @@ -96,18 +96,6 @@ "UpstreamPathTemplate": "/catalog-api/{everything}", "UpstreamHttpMethod": [] }, - { - "DownstreamPathTemplate": "/{everything}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "marketing.api", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/marketing-api/{everything}", - "UpstreamHttpMethod": [] - }, { "DownstreamPathTemplate": "/{everything}", "DownstreamScheme": "http", @@ -119,18 +107,6 @@ ], "UpstreamPathTemplate": "/payment-api/{everything}", "UpstreamHttpMethod": [] - }, - { - "DownstreamPathTemplate": "/{everything}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "locations.api", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/location-api/{everything}", - "UpstreamHttpMethod": [] } ], diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index b6dd5a4b0..ccb81d25f 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -36,7 +36,7 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers var basket = await _repository.GetBasketAsync(id); if (basket == null) { - return NotFound(); + return Ok(new CustomerBasket(id) { }); } return Ok(basket); diff --git a/src/Services/Basket/Basket.API/Dockerfile b/src/Services/Basket/Basket.API/Dockerfile index 03a967a72..7027cfc2d 100644 --- a/src/Services/Basket/Basket.API/Dockerfile +++ b/src/Services/Basket/Basket.API/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Basket/Basket.API +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index 71f700762..20d1b1215 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -15,6 +15,7 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { [Route("api/v1/[controller]")] + [ApiController] public class CatalogController : ControllerBase { private readonly CatalogContext _catalogContext; @@ -25,8 +26,8 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { _catalogContext = context ?? throw new ArgumentNullException(nameof(context)); _catalogIntegrationEventService = catalogIntegrationEventService ?? throw new ArgumentNullException(nameof(catalogIntegrationEventService)); - _settings = settings.Value; + ((DbContext)context).ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } diff --git a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs index f8e986e50..c7af86eed 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/PicController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/PicController.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; // For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers -{ +{ public class PicController : Controller { private readonly IHostingEnvironment _env; diff --git a/src/Services/Catalog/Catalog.API/Dockerfile b/src/Services/Catalog/Catalog.API/Dockerfile index a1279db8c..4cbc9c8aa 100644 --- a/src/Services/Catalog/Catalog.API/Dockerfile +++ b/src/Services/Catalog/Catalog.API/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Catalog/Catalog.API +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 41d3f08e1..4c4c746a6 100644 --- a/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Catalog/Catalog.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -1,6 +1,7 @@ using Catalog.API.Infrastructure.ActionResults; using Catalog.API.Infrastructure.Exceptions; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; @@ -27,12 +28,16 @@ namespace Catalog.API.Infrastructure.Filters if (context.Exception.GetType() == typeof(CatalogDomainException)) { - var json = new JsonErrorResponse + var problemDetails = new ValidationProblemDetails() { - Messages = new[] { context.Exception.Message } + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." }; - context.Result = new BadRequestObjectResult(json); + problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); + + context.Result = new BadRequestObjectResult(problemDetails); context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; } else diff --git a/src/Services/Identity/Identity.API/AppSettings.cs b/src/Services/Identity/Identity.API/AppSettings.cs index 1f45763fe..3c1f270dc 100644 --- a/src/Services/Identity/Identity.API/AppSettings.cs +++ b/src/Services/Identity/Identity.API/AppSettings.cs @@ -1,9 +1,9 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API { - public class AppSettings - { - public string MvcClient { get; set; } + public class AppSettings + { + public string MvcClient { get; set; } - public bool UseCustomizationData { get; set; } - } + public bool UseCustomizationData { get; set; } + } } diff --git a/src/Services/Identity/Identity.API/Configuration/Config.cs b/src/Services/Identity/Identity.API/Configuration/Config.cs index 2f6e2d21e..e1ce9d2d7 100644 --- a/src/Services/Identity/Identity.API/Configuration/Config.cs +++ b/src/Services/Identity/Identity.API/Configuration/Config.cs @@ -4,252 +4,256 @@ using System.Collections.Generic; namespace Microsoft.eShopOnContainers.Services.Identity.API.Configuration { - public class Config - { - // ApiResources define the apis in your system - public static IEnumerable GetApis() - { - return new List - { - new ApiResource("orders", "Orders Service"), - new ApiResource("basket", "Basket Service"), - new ApiResource("marketing", "Marketing Service"), - new ApiResource("locations", "Locations Service"), - new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"), - new ApiResource("webshoppingagg", "Web Shopping Aggregator"), - new ApiResource("orders.signalrhub", "Ordering Signalr Hub") - }; - } - - // Identity resources are data like user ID, name, or email address of a user - // see: http://docs.identityserver.io/en/release/configuration/resources.html - public static IEnumerable GetResources() - { - return new List - { - new IdentityResources.OpenId(), - new IdentityResources.Profile() - }; - } - - // client want to access resources (aka scopes) - public static IEnumerable GetClients(Dictionary clientsUrl) - { - return new List - { - // JavaScript Client - new Client - { - ClientId = "js", - ClientName = "eShop SPA OpenId Client", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - RedirectUris = { $"{clientsUrl["Spa"]}/" }, - RequireConsent = false, - PostLogoutRedirectUris = { $"{clientsUrl["Spa"]}/" }, - AllowedCorsOrigins = { $"{clientsUrl["Spa"]}" }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "orders", - "basket", - "locations", - "marketing", - "webshoppingagg", - "orders.signalrhub" - } - }, - new Client - { - ClientId = "xamarin", - ClientName = "eShop Xamarin OpenId Client", - AllowedGrantTypes = GrantTypes.Hybrid, - //Used to retrieve the access token on the back channel. - ClientSecrets = - { - new Secret("secret".Sha256()) - }, - RedirectUris = { clientsUrl["Xamarin"] }, - RequireConsent = false, - RequirePkce = true, - PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" }, - AllowedCorsOrigins = { "http://eshopxamarin" }, - AllowedScopes = new List - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - IdentityServerConstants.StandardScopes.OfflineAccess, - "orders", - "basket", - "locations", - "marketing", - "mobileshoppingagg" - }, - //Allow requesting refresh tokens for long lived API access - AllowOfflineAccess = true, - AllowAccessTokensViaBrowser = true - }, - new Client - { - ClientId = "mvc", - ClientName = "MVC Client", - ClientSecrets = new List - { - new Secret("secret".Sha256()) - }, - ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client - AllowedGrantTypes = GrantTypes.Hybrid, - AllowAccessTokensViaBrowser = false, - RequireConsent = false, - AllowOfflineAccess = true, - AlwaysIncludeUserClaimsInIdToken = true, - RedirectUris = new List - { - $"{clientsUrl["Mvc"]}/signin-oidc" - }, - PostLogoutRedirectUris = new List - { - $"{clientsUrl["Mvc"]}/signout-callback-oidc" - }, - AllowedScopes = new List - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - IdentityServerConstants.StandardScopes.OfflineAccess, - "orders", - "basket", - "locations", - "marketing", - "webshoppingagg", - "orders.signalrhub" - }, - }, - new Client - { - ClientId = "mvctest", - ClientName = "MVC Client Test", - ClientSecrets = new List - { - new Secret("secret".Sha256()) - }, - ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client - AllowedGrantTypes = GrantTypes.Hybrid, - AllowAccessTokensViaBrowser = true, - RequireConsent = false, - AllowOfflineAccess = true, - RedirectUris = new List - { - $"{clientsUrl["Mvc"]}/signin-oidc" - }, - PostLogoutRedirectUris = new List - { - $"{clientsUrl["Mvc"]}/signout-callback-oidc" - }, - AllowedScopes = new List - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - IdentityServerConstants.StandardScopes.OfflineAccess, - "orders", - "basket", - "locations", - "marketing", - "webshoppingagg" - }, - }, - new Client - { - ClientId = "locationsswaggerui", - ClientName = "Locations Swagger UI", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - - RedirectUris = { $"{clientsUrl["LocationsApi"]}/swagger/o2c.html" }, - PostLogoutRedirectUris = { $"{clientsUrl["LocationsApi"]}/swagger/" }, - - AllowedScopes = - { - "locations" - } - }, - new Client - { - ClientId = "marketingswaggerui", - ClientName = "Marketing Swagger UI", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - - RedirectUris = { $"{clientsUrl["MarketingApi"]}/swagger/o2c.html" }, - PostLogoutRedirectUris = { $"{clientsUrl["MarketingApi"]}/swagger/" }, - - AllowedScopes = - { - "marketing" - } - }, - new Client - { - ClientId = "basketswaggerui", - ClientName = "Basket Swagger UI", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - - RedirectUris = { $"{clientsUrl["BasketApi"]}/swagger/o2c.html" }, - PostLogoutRedirectUris = { $"{clientsUrl["BasketApi"]}/swagger/" }, - - AllowedScopes = - { - "basket" - } - }, - new Client - { - ClientId = "orderingswaggerui", - ClientName = "Ordering Swagger UI", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - - RedirectUris = { $"{clientsUrl["OrderingApi"]}/swagger/o2c.html" }, - PostLogoutRedirectUris = { $"{clientsUrl["OrderingApi"]}/swagger/" }, - - AllowedScopes = - { - "orders" - } - }, - new Client - { - ClientId = "mobileshoppingaggswaggerui", - ClientName = "Mobile Shopping Aggregattor Swagger UI", - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - - RedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/o2c.html" }, - PostLogoutRedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/" }, - - AllowedScopes = - { - "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" - } - } - - }; - } - } + public class Config + { + // ApiResources define the apis in your system + public static IEnumerable GetApis() + { + return new List + { + new ApiResource("orders", "Orders Service"), + new ApiResource("basket", "Basket Service"), + new ApiResource("marketing", "Marketing Service"), + new ApiResource("locations", "Locations Service"), + new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"), + new ApiResource("webshoppingagg", "Web Shopping Aggregator"), + new ApiResource("orders.signalrhub", "Ordering SignalR Hub") + }; + } + + // Identity resources are data like user ID, name, or email address of a user + // see: http://docs.identityserver.io/en/release/configuration/resources.html + public static IEnumerable GetResources() + { + return new List + { + new IdentityResources.OpenId(), + new IdentityResources.Profile() + }; + } + + // client want to access resources (aka scopes) + public static IEnumerable GetClients(Dictionary clientsUrl) + { + return new List + { + // JavaScript Client + new Client + { + ClientId = "js", + ClientName = "eShop SPA OpenId Client", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + RedirectUris = { $"{clientsUrl["Spa"]}/" }, + RequireConsent = false, + PostLogoutRedirectUris = { $"{clientsUrl["Spa"]}/" }, + AllowedCorsOrigins = { $"{clientsUrl["Spa"]}" }, + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "orders", + "basket", + "locations", + "marketing", + "webshoppingagg", + "orders.signalrhub" + } + }, + new Client + { + ClientId = "xamarin", + ClientName = "eShop Xamarin OpenId Client", + AllowedGrantTypes = GrantTypes.Hybrid, + //Used to retrieve the access token on the back channel. + ClientSecrets = + { + new Secret("secret".Sha256()) + }, + RedirectUris = { clientsUrl["Xamarin"] }, + RequireConsent = false, + RequirePkce = true, + PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" }, + AllowedCorsOrigins = { "http://eshopxamarin" }, + AllowedScopes = new List + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + IdentityServerConstants.StandardScopes.OfflineAccess, + "orders", + "basket", + "locations", + "marketing", + "mobileshoppingagg" + }, + //Allow requesting refresh tokens for long lived API access + AllowOfflineAccess = true, + AllowAccessTokensViaBrowser = true + }, + new Client + { + ClientId = "mvc", + ClientName = "MVC Client", + ClientSecrets = new List + { + new Secret("secret".Sha256()) + }, + ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client + AllowedGrantTypes = GrantTypes.Hybrid, + AllowAccessTokensViaBrowser = false, + RequireConsent = false, + AllowOfflineAccess = true, + AlwaysIncludeUserClaimsInIdToken = true, + RedirectUris = new List + { + $"{clientsUrl["Mvc"]}/signin-oidc" + }, + PostLogoutRedirectUris = new List + { + $"{clientsUrl["Mvc"]}/signout-callback-oidc" + }, + AllowedCorsOrigins = new List + { + $"{clientsUrl["Mvc"]}" + }, + AllowedScopes = new List + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + IdentityServerConstants.StandardScopes.OfflineAccess, + "orders", + "basket", + "locations", + "marketing", + "webshoppingagg", + "orders.signalrhub" + }, + }, + new Client + { + ClientId = "mvctest", + ClientName = "MVC Client Test", + ClientSecrets = new List + { + new Secret("secret".Sha256()) + }, + ClientUri = $"{clientsUrl["Mvc"]}", // public uri of the client + AllowedGrantTypes = GrantTypes.Hybrid, + AllowAccessTokensViaBrowser = true, + RequireConsent = false, + AllowOfflineAccess = true, + RedirectUris = new List + { + $"{clientsUrl["Mvc"]}/signin-oidc" + }, + PostLogoutRedirectUris = new List + { + $"{clientsUrl["Mvc"]}/signout-callback-oidc" + }, + AllowedScopes = new List + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + IdentityServerConstants.StandardScopes.OfflineAccess, + "orders", + "basket", + "locations", + "marketing", + "webshoppingagg" + }, + }, + new Client + { + ClientId = "locationsswaggerui", + ClientName = "Locations Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["LocationsApi"]}/swagger/oauth2-redirect.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["LocationsApi"]}/swagger/" }, + + AllowedScopes = + { + "locations" + } + }, + new Client + { + ClientId = "marketingswaggerui", + ClientName = "Marketing Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["MarketingApi"]}/swagger/oauth2-redirect.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["MarketingApi"]}/swagger/" }, + + AllowedScopes = + { + "marketing" + } + }, + new Client + { + ClientId = "basketswaggerui", + ClientName = "Basket Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["BasketApi"]}/swagger/oauth2-redirect.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["BasketApi"]}/swagger/" }, + + AllowedScopes = + { + "basket" + } + }, + new Client + { + ClientId = "orderingswaggerui", + ClientName = "Ordering Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["OrderingApi"]}/swagger/oauth2-redirect.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["OrderingApi"]}/swagger/" }, + + AllowedScopes = + { + "orders" + } + }, + new Client + { + ClientId = "mobileshoppingaggswaggerui", + ClientName = "Mobile Shopping Aggregattor Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/oauth2-redirect.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["MobileShoppingAgg"]}/swagger/" }, + + AllowedScopes = + { + "mobileshoppingagg" + } + }, + new Client + { + ClientId = "webshoppingaggswaggerui", + ClientName = "Web Shopping Aggregattor Swagger UI", + AllowedGrantTypes = GrantTypes.Implicit, + AllowAccessTokensViaBrowser = true, + + RedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/oauth2-redirect.html" }, + PostLogoutRedirectUris = { $"{clientsUrl["WebShoppingAgg"]}/swagger/" }, + + AllowedScopes = + { + "webshoppingagg" + } + } + + }; + } + } } \ No newline at end of file diff --git a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs index 3dbf28171..c70fadb5f 100644 --- a/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ApplicationDbContextSeed.cs @@ -16,220 +16,220 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data { - public class ApplicationDbContextSeed - { - private readonly IPasswordHasher _passwordHasher = new PasswordHasher(); - - public async Task SeedAsync(ApplicationDbContext context,IHostingEnvironment env, - ILogger logger, IOptions settings,int? retry = 0) - { - int retryForAvaiability = retry.Value; - - try - { - var useCustomizationData = settings.Value.UseCustomizationData; - var contentRootPath = env.ContentRootPath; - var webroot = env.WebRootPath; - - if (!context.Users.Any()) - { - context.Users.AddRange(useCustomizationData - ? GetUsersFromFile(contentRootPath, logger) - : GetDefaultUser()); - - await context.SaveChangesAsync(); - } - - if (useCustomizationData) - { - GetPreconfiguredImages(contentRootPath, webroot, logger); - } - } - catch (Exception ex) - { - if (retryForAvaiability < 10) - { - retryForAvaiability++; - - logger.LogError(ex.Message,$"There is an error migrating data for ApplicationDbContext"); - - await SeedAsync(context,env,logger,settings, retryForAvaiability); - } - } - } - - private IEnumerable GetUsersFromFile(string contentRootPath, ILogger logger) - { - string csvFileUsers = Path.Combine(contentRootPath, "Setup", "Users.csv"); - - if (!File.Exists(csvFileUsers)) - { - return GetDefaultUser(); - } - - string[] csvheaders; - try - { - string[] requiredHeaders = { - "cardholdername", "cardnumber", "cardtype", "city", "country", - "email", "expiration", "lastname", "name", "phonenumber", - "username", "zipcode", "state", "street", "securitynumber", - "normalizedemail", "normalizedusername", "password" - }; - csvheaders = GetHeaders(requiredHeaders, csvFileUsers); - } - catch (Exception ex) - { - logger.LogError(ex.Message); - - return GetDefaultUser(); - } - - List users = File.ReadAllLines(csvFileUsers) - .Skip(1) // skip header column - .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") ) - .SelectTry(column => CreateApplicationUser(column, csvheaders)) - .OnCaughtException(ex => { logger.LogError(ex.Message); return null; }) - .Where(x => x != null) - .ToList(); - - return users; - } - - private ApplicationUser CreateApplicationUser(string[] column, string[] headers) - { - if (column.Count() != headers.Count()) - { - throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); - } - - string cardtypeString = column[Array.IndexOf(headers, "cardtype")].Trim('"').Trim(); - if (!int.TryParse(cardtypeString, out int cardtype)) - { - throw new Exception($"cardtype='{cardtypeString}' is not a number"); - } - - var user = new ApplicationUser - { - CardHolderName = column[Array.IndexOf(headers, "cardholdername")].Trim('"').Trim(), - CardNumber = column[Array.IndexOf(headers, "cardnumber")].Trim('"').Trim(), - CardType = cardtype, - City = column[Array.IndexOf(headers, "city")].Trim('"').Trim(), - Country = column[Array.IndexOf(headers, "country")].Trim('"').Trim(), - Email = column[Array.IndexOf(headers, "email")].Trim('"').Trim(), - Expiration = column[Array.IndexOf(headers, "expiration")].Trim('"').Trim(), - Id = Guid.NewGuid().ToString(), - LastName = column[Array.IndexOf(headers, "lastname")].Trim('"').Trim(), - Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(), - PhoneNumber = column[Array.IndexOf(headers, "phonenumber")].Trim('"').Trim(), - UserName = column[Array.IndexOf(headers, "username")].Trim('"').Trim(), - ZipCode = column[Array.IndexOf(headers, "zipcode")].Trim('"').Trim(), - State = column[Array.IndexOf(headers, "state")].Trim('"').Trim(), - Street = column[Array.IndexOf(headers, "street")].Trim('"').Trim(), - SecurityNumber = column[Array.IndexOf(headers, "securitynumber")].Trim('"').Trim(), - NormalizedEmail = column[Array.IndexOf(headers, "normalizedemail")].Trim('"').Trim(), - NormalizedUserName = column[Array.IndexOf(headers, "normalizedusername")].Trim('"').Trim(), - SecurityStamp = Guid.NewGuid().ToString("D"), - PasswordHash = column[Array.IndexOf(headers, "password")].Trim('"').Trim(), // Note: This is the password - }; - - user.PasswordHash = _passwordHasher.HashPassword(user, user.PasswordHash); - - return user; - } - - private IEnumerable GetDefaultUser() - { - var user = - new ApplicationUser() - { - CardHolderName = "DemoUser", - CardNumber = "4012888888881881", - CardType = 1, - City = "Redmond", - Country = "U.S.", - Email = "demouser@microsoft.com", - Expiration = "12/20", - Id = Guid.NewGuid().ToString(), - LastName = "DemoLastName", - Name = "DemoUser", - PhoneNumber = "1234567890", - UserName = "demouser@microsoft.com", - ZipCode = "98052", - State = "WA", - Street = "15703 NE 61st Ct", - SecurityNumber = "535", - NormalizedEmail = "DEMOUSER@MICROSOFT.COM", - NormalizedUserName = "DEMOUSER@MICROSOFT.COM", - SecurityStamp = Guid.NewGuid().ToString("D"), - }; - - user.PasswordHash = _passwordHasher.HashPassword(user, "Pass@word1"); - - return new List() - { - user - }; - } - - static string[] GetHeaders(string[] requiredHeaders, string csvfile) - { - string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); - - if (csvheaders.Count() != requiredHeaders.Count()) - { - throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); - } - - foreach (var requiredHeader in requiredHeaders) - { - if (!csvheaders.Contains(requiredHeader)) - { - throw new Exception($"does not contain required header '{requiredHeader}'"); - } - } - - return csvheaders; - } - - static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger logger) - { - try - { - string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); - if (!File.Exists(imagesZipFile)) - { - logger.LogError($" zip file '{imagesZipFile}' does not exists."); - return; - } - - string imagePath = Path.Combine(webroot, "images"); - string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); - - using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) - { - foreach (ZipArchiveEntry entry in zip.Entries) - { - if (imageFiles.Contains(entry.Name)) - { - string destinationFilename = Path.Combine(imagePath, entry.Name); - if (File.Exists(destinationFilename)) - { - File.Delete(destinationFilename); - } - entry.ExtractToFile(destinationFilename); - } - else - { - logger.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); - } - } - } - } - catch (Exception ex) - { - logger.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}"); - } - } - } + public class ApplicationDbContextSeed + { + private readonly IPasswordHasher _passwordHasher = new PasswordHasher(); + + public async Task SeedAsync(ApplicationDbContext context, IHostingEnvironment env, + ILogger logger, IOptions settings, int? retry = 0) + { + int retryForAvaiability = retry.Value; + + try + { + var useCustomizationData = settings.Value.UseCustomizationData; + var contentRootPath = env.ContentRootPath; + var webroot = env.WebRootPath; + + if (!context.Users.Any()) + { + context.Users.AddRange(useCustomizationData + ? GetUsersFromFile(contentRootPath, logger) + : GetDefaultUser()); + + await context.SaveChangesAsync(); + } + + if (useCustomizationData) + { + GetPreconfiguredImages(contentRootPath, webroot, logger); + } + } + catch (Exception ex) + { + if (retryForAvaiability < 10) + { + retryForAvaiability++; + + logger.LogError(ex.Message, $"There is an error migrating data for ApplicationDbContext"); + + await SeedAsync(context, env, logger, settings, retryForAvaiability); + } + } + } + + private IEnumerable GetUsersFromFile(string contentRootPath, ILogger logger) + { + string csvFileUsers = Path.Combine(contentRootPath, "Setup", "Users.csv"); + + if (!File.Exists(csvFileUsers)) + { + return GetDefaultUser(); + } + + string[] csvheaders; + try + { + string[] requiredHeaders = { + "cardholdername", "cardnumber", "cardtype", "city", "country", + "email", "expiration", "lastname", "name", "phonenumber", + "username", "zipcode", "state", "street", "securitynumber", + "normalizedemail", "normalizedusername", "password" + }; + csvheaders = GetHeaders(requiredHeaders, csvFileUsers); + } + catch (Exception ex) + { + logger.LogError(ex.Message); + + return GetDefaultUser(); + } + + List users = File.ReadAllLines(csvFileUsers) + .Skip(1) // skip header column + .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")) + .SelectTry(column => CreateApplicationUser(column, csvheaders)) + .OnCaughtException(ex => { logger.LogError(ex.Message); return null; }) + .Where(x => x != null) + .ToList(); + + return users; + } + + private ApplicationUser CreateApplicationUser(string[] column, string[] headers) + { + if (column.Count() != headers.Count()) + { + throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); + } + + string cardtypeString = column[Array.IndexOf(headers, "cardtype")].Trim('"').Trim(); + if (!int.TryParse(cardtypeString, out int cardtype)) + { + throw new Exception($"cardtype='{cardtypeString}' is not a number"); + } + + var user = new ApplicationUser + { + CardHolderName = column[Array.IndexOf(headers, "cardholdername")].Trim('"').Trim(), + CardNumber = column[Array.IndexOf(headers, "cardnumber")].Trim('"').Trim(), + CardType = cardtype, + City = column[Array.IndexOf(headers, "city")].Trim('"').Trim(), + Country = column[Array.IndexOf(headers, "country")].Trim('"').Trim(), + Email = column[Array.IndexOf(headers, "email")].Trim('"').Trim(), + Expiration = column[Array.IndexOf(headers, "expiration")].Trim('"').Trim(), + Id = Guid.NewGuid().ToString(), + LastName = column[Array.IndexOf(headers, "lastname")].Trim('"').Trim(), + Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(), + PhoneNumber = column[Array.IndexOf(headers, "phonenumber")].Trim('"').Trim(), + UserName = column[Array.IndexOf(headers, "username")].Trim('"').Trim(), + ZipCode = column[Array.IndexOf(headers, "zipcode")].Trim('"').Trim(), + State = column[Array.IndexOf(headers, "state")].Trim('"').Trim(), + Street = column[Array.IndexOf(headers, "street")].Trim('"').Trim(), + SecurityNumber = column[Array.IndexOf(headers, "securitynumber")].Trim('"').Trim(), + NormalizedEmail = column[Array.IndexOf(headers, "normalizedemail")].Trim('"').Trim(), + NormalizedUserName = column[Array.IndexOf(headers, "normalizedusername")].Trim('"').Trim(), + SecurityStamp = Guid.NewGuid().ToString("D"), + PasswordHash = column[Array.IndexOf(headers, "password")].Trim('"').Trim(), // Note: This is the password + }; + + user.PasswordHash = _passwordHasher.HashPassword(user, user.PasswordHash); + + return user; + } + + private IEnumerable GetDefaultUser() + { + var user = + new ApplicationUser() + { + CardHolderName = "DemoUser", + CardNumber = "4012888888881881", + CardType = 1, + City = "Redmond", + Country = "U.S.", + Email = "demouser@microsoft.com", + Expiration = "12/20", + Id = Guid.NewGuid().ToString(), + LastName = "DemoLastName", + Name = "DemoUser", + PhoneNumber = "1234567890", + UserName = "demouser@microsoft.com", + ZipCode = "98052", + State = "WA", + Street = "15703 NE 61st Ct", + SecurityNumber = "535", + NormalizedEmail = "DEMOUSER@MICROSOFT.COM", + NormalizedUserName = "DEMOUSER@MICROSOFT.COM", + SecurityStamp = Guid.NewGuid().ToString("D"), + }; + + user.PasswordHash = _passwordHasher.HashPassword(user, "Pass@word1"); + + return new List() + { + user + }; + } + + static string[] GetHeaders(string[] requiredHeaders, string csvfile) + { + string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); + + if (csvheaders.Count() != requiredHeaders.Count()) + { + throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); + } + + foreach (var requiredHeader in requiredHeaders) + { + if (!csvheaders.Contains(requiredHeader)) + { + throw new Exception($"does not contain required header '{requiredHeader}'"); + } + } + + return csvheaders; + } + + static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger logger) + { + try + { + string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); + if (!File.Exists(imagesZipFile)) + { + logger.LogError($" zip file '{imagesZipFile}' does not exists."); + return; + } + + string imagePath = Path.Combine(webroot, "images"); + string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); + + using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) + { + foreach (ZipArchiveEntry entry in zip.Entries) + { + if (imageFiles.Contains(entry.Name)) + { + string destinationFilename = Path.Combine(imagePath, entry.Name); + if (File.Exists(destinationFilename)) + { + File.Delete(destinationFilename); + } + entry.ExtractToFile(destinationFilename); + } + else + { + logger.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); + } + } + } + } + catch (Exception ex) + { + logger.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}"); + } + } + } } diff --git a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs index a5a4383bd..3547c053a 100644 --- a/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs +++ b/src/Services/Identity/Identity.API/Data/ConfigurationDbContextSeed.cs @@ -1,7 +1,10 @@ using IdentityServer4.EntityFramework.DbContexts; +using IdentityServer4.EntityFramework.Entities; using IdentityServer4.EntityFramework.Mappers; +using Microsoft.EntityFrameworkCore; using Microsoft.eShopOnContainers.Services.Identity.API.Configuration; using Microsoft.Extensions.Configuration; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -30,16 +33,37 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data { foreach (var client in Config.GetClients(clientUrls)) { - await context.Clients.AddAsync(client.ToEntity()); + context.Clients.Add(client.ToEntity()); } await context.SaveChangesAsync(); } + // Checking always for old redirects to fix existing deployments + // to use new swagger-ui redirect uri as of v3.0.0 + // There should be no problem for new ones + // ref: https://github.com/dotnet-architecture/eShopOnContainers/issues/586 + else + { + List oldRedirects = (await context.Clients.Include(c => c.RedirectUris).ToListAsync()) + .SelectMany(c => c.RedirectUris) + .Where(ru => ru.RedirectUri.EndsWith("/o2c.html")) + .ToList(); + + if (oldRedirects.Any()) + { + foreach (var ru in oldRedirects) + { + ru.RedirectUri = ru.RedirectUri.Replace("/o2c.html", "/oauth2-redirect.html"); + context.Update(ru.Client); + } + await context.SaveChangesAsync(); + } + } if (!context.IdentityResources.Any()) { foreach (var resource in Config.GetResources()) { - await context.IdentityResources.AddAsync(resource.ToEntity()); + context.IdentityResources.Add(resource.ToEntity()); } await context.SaveChangesAsync(); } @@ -48,7 +72,7 @@ namespace Microsoft.eShopOnContainers.Services.Identity.API.Data { foreach (var api in Config.GetApis()) { - await context.ApiResources.AddAsync(api.ToEntity()); + context.ApiResources.Add(api.ToEntity()); } await context.SaveChangesAsync(); diff --git a/src/Services/Identity/Identity.API/Dockerfile b/src/Services/Identity/Identity.API/Dockerfile index ddc884cc2..167b2bb25 100644 --- a/src/Services/Identity/Identity.API/Dockerfile +++ b/src/Services/Identity/Identity.API/Dockerfile @@ -1,12 +1,26 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS sdk-with-node +ENV NODE_VERSION 8.11.1 +ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60 +RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \ + && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \ + && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \ + && rm nodejs.tar.gz \ + && ln -s /usr/local/bin/node /usr/local/bin/nodejs + +FROM sdk-with-node AS updated-npm +RUN npm i -g npm + + +FROM updated-npm as build +RUN npm install -g bower@1.8.4 WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Identity/Identity.API +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Services/Identity/Identity.API/appsettings.json b/src/Services/Identity/Identity.API/appsettings.json index 6b251be41..0820508ab 100644 --- a/src/Services/Identity/Identity.API/appsettings.json +++ b/src/Services/Identity/Identity.API/appsettings.json @@ -1,19 +1,20 @@ { - "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;", - "IsClusterEnv": "False", - "MvcClient": "http://localhost:5100", - "SpaClient": "http://localhost:5104", - "XamarinCallback": "http://localhost:5105/xamarincallback", - "UseCustomizationData": false, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" - } - }, - "ApplicationInsights": { - "InstrumentationKey": "" - } + "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.IdentityDb;User Id=sa;Password=Pass@word;", + "IsClusterEnv": "False", + "IdentityServer": "http://localhost:5105", + "MvcClient": "http://localhost:5100", + "SpaClient": "http://localhost:5104", + "XamarinCallback": "http://localhost:5105/xamarincallback", + "UseCustomizationData": false, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Trace", + "System": "Information", + "Microsoft": "Information" + } + }, + "ApplicationInsights": { + "InstrumentationKey": "" + } } diff --git a/src/Services/Location/Locations.API/Dockerfile b/src/Services/Location/Locations.API/Dockerfile index e2b64ba2e..12ac0e442 100644 --- a/src/Services/Location/Locations.API/Dockerfile +++ b/src/Services/Location/Locations.API/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Location/Locations.API +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Services/Marketing/Marketing.API/Dockerfile b/src/Services/Marketing/Marketing.API/Dockerfile index b30986406..75d698bfe 100644 --- a/src/Services/Marketing/Marketing.API/Dockerfile +++ b/src/Services/Marketing/Marketing.API/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Marketing/Marketing.API +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs index 24a8245d6..f8a7b06e5 100644 --- a/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderCancelled/OrderCancelledDomainEventHandler.cs @@ -28,6 +28,7 @@ namespace Ordering.API.Application.DomainEventHandlers.OrderCancelled _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); + _orderingIntegrationEventService = orderingIntegrationEventService; } public async Task Handle(OrderCancelledDomainEvent orderCancelledDomainEvent, CancellationToken cancellationToken) diff --git a/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs b/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs index 10aaf3415..89cf496e3 100644 --- a/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs +++ b/src/Services/Ordering/Ordering.API/Application/Queries/IOrderQueries.cs @@ -1,5 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries { + using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -7,7 +8,7 @@ { Task GetOrderAsync(int id); - Task> GetOrdersAsync(); + Task> GetOrdersFromUserAsync(Guid userId); Task> GetCardTypesAsync(); } diff --git a/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs b/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs index 59cc8a823..8509e10d3 100644 --- a/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs +++ b/src/Services/Ordering/Ordering.API/Application/Queries/OrderQueries.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; public class OrderQueries - :IOrderQueries + : IOrderQueries { private string _connectionString = string.Empty; @@ -42,18 +42,20 @@ } } - public async Task> GetOrdersAsync() + public async Task> GetOrdersFromUserAsync(Guid userId) { using (var connection = new SqlConnection(_connectionString)) { connection.Open(); - return await connection.QueryAsync(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status],SUM(oi.units*oi.unitprice) as total + return await connection.QueryAsync(@"SELECT o.[Id] as ordernumber,o.[OrderDate] as [date],os.[Name] as [status], SUM(oi.units*oi.unitprice) as total FROM [ordering].[Orders] o LEFT JOIN[ordering].[orderitems] oi ON o.Id = oi.orderid LEFT JOIN[ordering].[orderstatus] os on o.OrderStatusId = os.Id + LEFT JOIN[ordering].[buyers] ob on o.BuyerId = ob.Id + WHERE ob.IdentityGuid = @userId GROUP BY o.[Id], o.[OrderDate], os.[Name] - ORDER BY o.[Id]"); + ORDER BY o.[Id]", new { userId }); } } diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index ac179c97f..5542caf11 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -5,7 +5,6 @@ using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; using Ordering.API.Application.Commands; -using Ordering.API.Application.Models; using System; using System.Collections.Generic; using System.Net; @@ -15,6 +14,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers { [Route("api/v1/[controller]")] [Authorize] + [ApiController] public class OrdersController : Controller { private readonly IMediator _mediator; @@ -87,8 +87,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] public async Task GetOrders() { - var orders = await _orderQueries.GetOrdersAsync(); - + var userid = _identityService.GetUserIdentity(); + var orders = await _orderQueries.GetOrdersFromUserAsync(Guid.Parse(userid)); return Ok(orders); } diff --git a/src/Services/Ordering/Ordering.API/Dockerfile b/src/Services/Ordering/Ordering.API/Dockerfile index 6dc5f8122..78c153641 100644 --- a/src/Services/Ordering/Ordering.API/Dockerfile +++ b/src/Services/Ordering/Ordering.API/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Ordering/Ordering.API +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 69faf8e42..a4ce415ea 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -3,6 +3,7 @@ using AspNetCore.Mvc; using global::Ordering.Domain.Exceptions; using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.ActionResults; using Microsoft.Extensions.Logging; @@ -25,16 +26,18 @@ context.Exception, context.Exception.Message); - if (context.Exception.GetType() == typeof(OrderingDomainException)) + if (context.Exception.GetType() == typeof(OrderingDomainException)) { - var json = new JsonErrorResponse + var problemDetails = new ValidationProblemDetails() { - Messages = new[] { context.Exception.Message } + Instance = context.HttpContext.Request.Path, + Status = StatusCodes.Status400BadRequest, + Detail = "Please refer to the errors property for additional details." }; - // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 - //It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information - context.Result = new BadRequestObjectResult(json); + problemDetails.Errors.Add("DomainValidations", new string[] { context.Exception.Message.ToString() }); + + context.Result = new BadRequestObjectResult(problemDetails); context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; } else @@ -52,7 +55,7 @@ // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information context.Result = new InternalServerErrorObjectResult(json); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } context.ExceptionHandled = true; } diff --git a/src/Services/Ordering/Ordering.API/OrderingSettings.cs b/src/Services/Ordering/Ordering.API/OrderingSettings.cs index bfe4d0793..af38823ae 100644 --- a/src/Services/Ordering/Ordering.API/OrderingSettings.cs +++ b/src/Services/Ordering/Ordering.API/OrderingSettings.cs @@ -7,8 +7,6 @@ public string EventBusConnection { get; set; } - public int GracePeriodTime { get; set; } - public int CheckUpdateTime { get; set; } } } diff --git a/src/Services/Ordering/Ordering.API/settings.json b/src/Services/Ordering/Ordering.API/settings.json index 679cd23f4..3e0d224ec 100644 --- a/src/Services/Ordering/Ordering.API/settings.json +++ b/src/Services/Ordering/Ordering.API/settings.json @@ -1,22 +1,21 @@ { - "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", - "IdentityUrl": "http://localhost:5105", - "UseCustomizationData": false, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" - } - }, - "AzureServiceBusEnabled": false, - "SubscriptionClientName": "Ordering", - "GracePeriodTime": "1", - "CheckUpdateTime": "30000", - "ApplicationInsights": { - "InstrumentationKey": "" - }, - "EventBusRetryCount": 5, - "EventBusConnection": "localhost" + "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", + "IdentityUrl": "http://localhost:5105", + "UseCustomizationData": false, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Trace", + "System": "Information", + "Microsoft": "Information" + } + }, + "AzureServiceBusEnabled": false, + "SubscriptionClientName": "Ordering", + "CheckUpdateTime": "30000", + "ApplicationInsights": { + "InstrumentationKey": "" + }, + "EventBusRetryCount": 5, + "EventBusConnection": "localhost" } diff --git a/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile b/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile index a675d59ef..0c01dcb96 100644 --- a/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile +++ b/src/Services/Ordering/Ordering.BackgroundTasks/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Ordering/Ordering.BackgroundTasks +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Services/Ordering/Ordering.SignalrHub/Dockerfile b/src/Services/Ordering/Ordering.SignalrHub/Dockerfile index 005eea8cf..367b8db36 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/Dockerfile +++ b/src/Services/Ordering/Ordering.SignalrHub/Dockerfile @@ -1,18 +1,16 @@ -FROM microsoft/aspnetcore:2.0 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src -COPY eShopOnContainers-ServicesAndWebApps.sln ./ -COPY src/Services/Ordering/Ordering.SignalrHub/Ordering.SignalrHub.csproj src/Services/Ordering/Ordering.SignalrHub/ -RUN dotnet restore -nowarn:msb3202,nu1503 COPY . . WORKDIR /src/src/Services/Ordering/Ordering.SignalrHub -RUN dotnet build Ordering.SignalrHub.csproj -c Release -o /app +RUN dotnet restore -nowarn:msb3202,nu1503 +RUN dotnet build --no-restore Ordering.SignalrHub.csproj -c Release -o /app FROM build AS publish -RUN dotnet publish Ordering.SignalrHub.csproj -c Release -o /app +RUN dotnet publish --no-restore Ordering.SignalrHub.csproj -c Release -o /app FROM base AS final WORKDIR /app diff --git a/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs b/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs index 8a4c1c90f..5fd6f3eb9 100644 --- a/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs +++ b/src/Services/Ordering/Ordering.SignalrHub/NotificationHub.cs @@ -13,13 +13,13 @@ namespace Ordering.SignalrHub public override async Task OnConnectedAsync() { - await Groups.AddAsync(Context.ConnectionId, Context.User.Identity.Name); + await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception ex) { - await Groups.RemoveAsync(Context.ConnectionId, Context.User.Identity.Name); + await Groups.AddToGroupAsync(Context.ConnectionId, Context.User.Identity.Name); await base.OnDisconnectedAsync(ex); } } diff --git a/src/Services/Payment/Payment.API/Dockerfile b/src/Services/Payment/Payment.API/Dockerfile index 7b19579c1..d644417c6 100644 --- a/src/Services/Payment/Payment.API/Dockerfile +++ b/src/Services/Payment/Payment.API/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Services/Payment/Payment.API +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Web/WebMVC/AppSettings.cs b/src/Web/WebMVC/AppSettings.cs index 30403aa9d..a705c5a90 100644 --- a/src/Web/WebMVC/AppSettings.cs +++ b/src/Web/WebMVC/AppSettings.cs @@ -7,9 +7,8 @@ namespace Microsoft.eShopOnContainers.WebMVC { public class AppSettings { - public Connectionstrings ConnectionStrings { get; set; } + //public Connectionstrings ConnectionStrings { get; set; } public string MarketingUrl { get; set; } - public string PurchaseUrl { get; set; } public string SignalrHubUrl { get; set; } public bool ActivateCampaignDetailFunction { get; set; } diff --git a/src/Web/WebMVC/Controllers/CartController.cs b/src/Web/WebMVC/Controllers/CartController.cs index 660da1d56..30ac77e8b 100644 --- a/src/Web/WebMVC/Controllers/CartController.cs +++ b/src/Web/WebMVC/Controllers/CartController.cs @@ -71,7 +71,6 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers { var user = _appUserParser.Parse(HttpContext.User); await _basketSvc.AddItemToBasket(user, productDetails.Id); - //await _basketSvc.AddItemToBasket(user, product); } return RedirectToAction("Index", "Catalog"); } diff --git a/src/Web/WebMVC/Controllers/OrderController.cs b/src/Web/WebMVC/Controllers/OrderController.cs index a5bf4785e..cb5234e3c 100644 --- a/src/Web/WebMVC/Controllers/OrderController.cs +++ b/src/Web/WebMVC/Controllers/OrderController.cs @@ -1,14 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.eShopOnContainers.WebMVC.ViewModels; -using Microsoft.AspNetCore.Authorization; -using System.Net.Http; using Polly.CircuitBreaker; -using WebMVC.Models; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.WebMVC.Controllers { @@ -52,11 +47,12 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers return RedirectToAction("Index"); } } - catch(BrokenCircuitException) + catch (BrokenCircuitException) { ModelState.AddModelError("Error", "It was not possible to create a new order, please try later on. (Business Msg Due to Circuit-Breaker)"); } - return View("Create", model); + + return View("Create", model); } public async Task Cancel(string orderId) diff --git a/src/Web/WebMVC/Controllers/TestController.cs b/src/Web/WebMVC/Controllers/TestController.cs index 1b35d5d2b..2cf053126 100644 --- a/src/Web/WebMVC/Controllers/TestController.cs +++ b/src/Web/WebMVC/Controllers/TestController.cs @@ -1,12 +1,9 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.eShopOnContainers.WebMVC.ViewModels; -using System; -using System.Collections.Generic; -using System.Linq; +using Newtonsoft.Json; +using System.Net.Http; using System.Threading.Tasks; namespace WebMVC.Controllers @@ -14,6 +11,7 @@ namespace WebMVC.Controllers class TestPayload { public int CatalogItemId { get; set; } + public string BasketId { get; set; } public int Quantity { get; set; } @@ -22,9 +20,10 @@ namespace WebMVC.Controllers [Authorize] public class TestController : Controller { - private readonly IHttpClient _client; + private readonly IHttpClientFactory _client; private readonly IIdentityParser _appUserParser; - public TestController(IHttpClient client, IIdentityParser identityParser) + + public TestController(IHttpClientFactory client, IIdentityParser identityParser) { _client = client; _appUserParser = identityParser; @@ -33,18 +32,24 @@ namespace WebMVC.Controllers public async Task Ocelot() { var url = "http://apigw/shopping/api/v1/basket/items"; + var payload = new TestPayload() { CatalogItemId = 1, Quantity = 1, BasketId = _appUserParser.Parse(User).Id }; - var token = await HttpContext.GetTokenAsync("access_token"); - var response = await _client.PostAsync(url, payload, token); - + + var content = new StringContent(JsonConvert.SerializeObject(payload), System.Text.Encoding.UTF8, "application/json"); + + + var response = await _client.CreateClient(nameof(IBasketService)) + .PostAsync(url, content); + if (response.IsSuccessStatusCode) { var str = await response.Content.ReadAsStringAsync(); + return Ok(str); } else diff --git a/src/Web/WebMVC/Dockerfile b/src/Web/WebMVC/Dockerfile index a903387c8..bf104e83b 100644 --- a/src/Web/WebMVC/Dockerfile +++ b/src/Web/WebMVC/Dockerfile @@ -1,12 +1,25 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS sdk-with-node +ENV NODE_VERSION 8.11.1 +ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60 +RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \ + && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \ + && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \ + && rm nodejs.tar.gz \ + && ln -s /usr/local/bin/node /usr/local/bin/nodejs + +FROM sdk-with-node AS updated-npm +RUN npm i -g npm + +FROM updated-npm as build +RUN npm install -g bower@1.8.4 WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Web/WebMVC +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Web/WebMVC/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs b/src/Web/WebMVC/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs new file mode 100644 index 000000000..255bd1649 --- /dev/null +++ b/src/Web/WebMVC/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace WebMVC.Infrastructure +{ + public class HttpClientAuthorizationDelegatingHandler + : DelegatingHandler + { + private readonly IHttpContextAccessor _httpContextAccesor; + + public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccesor) + { + _httpContextAccesor = httpContextAccesor; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var authorizationHeader = _httpContextAccesor.HttpContext + .Request.Headers["Authorization"]; + + if (!string.IsNullOrEmpty(authorizationHeader)) + { + request.Headers.Add("Authorization", new List() { authorizationHeader }); + } + + var token = await GetToken(); + + if (token != null) + { + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + + return await base.SendAsync(request, cancellationToken); + } + + async Task GetToken() + { + const string ACCESS_TOKEN = "access_token"; + + return await _httpContextAccesor.HttpContext + .GetTokenAsync(ACCESS_TOKEN); + } + } +} diff --git a/src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs b/src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs new file mode 100644 index 000000000..17e4591a1 --- /dev/null +++ b/src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs @@ -0,0 +1,26 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace WebMVC.Infrastructure +{ + public class HttpClientRequestIdDelegatingHandler + : DelegatingHandler + { + + public HttpClientRequestIdDelegatingHandler() + { + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put) + { + request.Headers.Add("x-requestid", Guid.NewGuid().ToString()); + } + + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs b/src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs deleted file mode 100644 index aa1d31c9b..000000000 --- a/src/Web/WebMVC/Infrastructure/IResilientHttpClientFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; -using System; - -namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure -{ - public interface IResilientHttpClientFactory - { - ResilientHttpClient CreateResilientHttpClient(); - } -} \ No newline at end of file diff --git a/src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs b/src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs deleted file mode 100644 index 43eac7b30..000000000 --- a/src/Web/WebMVC/Infrastructure/ResilientHttpClientFactory.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; -using Microsoft.Extensions.Logging; -using Polly; -using System; -using System.Net.Http; - -namespace Microsoft.eShopOnContainers.WebMVC.Infrastructure -{ - public class ResilientHttpClientFactory : IResilientHttpClientFactory - { - private readonly ILogger _logger; - private readonly int _retryCount; - private readonly int _exceptionsAllowedBeforeBreaking; - private readonly IHttpContextAccessor _httpContextAccessor; - - public ResilientHttpClientFactory(ILogger logger, IHttpContextAccessor httpContextAccessor, int exceptionsAllowedBeforeBreaking = 5, int retryCount = 6) - { - _logger = logger; - _exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; - _retryCount = retryCount; - _httpContextAccessor = httpContextAccessor; - } - - - public ResilientHttpClient CreateResilientHttpClient() - => new ResilientHttpClient((origin) => CreatePolicies(), _logger, _httpContextAccessor); - - private Policy[] CreatePolicies() - => new Policy[] - { - Policy.Handle() - .WaitAndRetryAsync( - // number of retries - _retryCount, - // exponential backofff - retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), - // on retry - (exception, timeSpan, retryCount, context) => - { - var msg = $"Retry {retryCount} implemented with Polly's RetryPolicy " + - $"of {context.PolicyKey} " + - $"at {context.ExecutionKey}, " + - $"due to: {exception}."; - _logger.LogWarning(msg); - _logger.LogDebug(msg); - }), - Policy.Handle() - .CircuitBreakerAsync( - // number of exceptions before breaking circuit - _exceptionsAllowedBeforeBreaking, - // time circuit opened before retry - TimeSpan.FromMinutes(1), - (exception, duration) => - { - // on circuit opened - _logger.LogTrace("Circuit breaker opened"); - }, - () => - { - // on circuit closed - _logger.LogTrace("Circuit breaker reset"); - }) - }; - } -} diff --git a/src/Web/WebMVC/Infrastructure/WebContextSeed.cs b/src/Web/WebMVC/Infrastructure/WebContextSeed.cs index 65063e34f..99a07135a 100644 --- a/src/Web/WebMVC/Infrastructure/WebContextSeed.cs +++ b/src/Web/WebMVC/Infrastructure/WebContextSeed.cs @@ -17,8 +17,8 @@ namespace WebMVC.Infrastructure { var log = loggerFactory.CreateLogger("WebMVC seed"); - var settings = (AppSettings)applicationBuilder - .ApplicationServices.GetRequiredService>().Value; + var settings = applicationBuilder + .ApplicationServices.GetRequiredService>().Value; var useCustomizationData = settings.UseCustomizationData; var contentRootPath = env.ContentRootPath; @@ -66,9 +66,9 @@ namespace WebMVC.Infrastructure string imagePath = Path.Combine(webroot, "images"); string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); - using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) + using (var zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) { - foreach (ZipArchiveEntry entry in zip.Entries) + foreach (var entry in zip.Entries) { if (imageFiles.Contains(entry.Name)) { diff --git a/src/Web/WebMVC/Properties/launchSettings.json b/src/Web/WebMVC/Properties/launchSettings.json index bf529db40..2b02cc23c 100644 --- a/src/Web/WebMVC/Properties/launchSettings.json +++ b/src/Web/WebMVC/Properties/launchSettings.json @@ -12,7 +12,8 @@ "commandName": "IISExpress", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "use64Bit": true }, "Microsoft.eShopOnContainers.WebMVC": { "commandName": "Project", diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index 54287a796..7dffd934b 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -1,11 +1,9 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; -using Microsoft.eShopOnContainers.WebMVC.ViewModels; +using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.Extensions.Options; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; using WebMVC.Models; @@ -14,42 +12,40 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { public class BasketService : IBasketService { - private readonly IOptionsSnapshot _settings; - private readonly IHttpClient _apiClient; + private readonly IOptions _settings; + private readonly HttpClient _apiClient; private readonly string _basketByPassUrl; private readonly string _purchaseUrl; - private readonly IHttpContextAccessor _httpContextAccesor; private readonly string _bffUrl; - public BasketService(IOptionsSnapshot settings, - IHttpContextAccessor httpContextAccesor, IHttpClient httpClient) + public BasketService(HttpClient httpClient, IOptions settings) { + _apiClient = httpClient; _settings = settings; + _basketByPassUrl = $"{_settings.Value.PurchaseUrl}/api/v1/b/basket"; _purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1"; - _httpContextAccesor = httpContextAccesor; - _apiClient = httpClient; } public async Task GetBasket(ApplicationUser user) { - var token = await GetUserTokenAsync(); - var getBasketUri = API.Basket.GetBasket(_basketByPassUrl, user.Id); + var uri = API.Basket.GetBasket(_basketByPassUrl, user.Id); - var dataString = await _apiClient.GetStringAsync(getBasketUri, token); + var responseString = await _apiClient.GetStringAsync(uri); - return string.IsNullOrEmpty(dataString) ? - new Basket() { BuyerId = user.Id} : - JsonConvert.DeserializeObject(dataString); + return string.IsNullOrEmpty(responseString) ? + new Basket() { BuyerId = user.Id } : + JsonConvert.DeserializeObject(responseString); } public async Task UpdateBasket(Basket basket) { - var token = await GetUserTokenAsync(); - var updateBasketUri = API.Basket.UpdateBasket(_basketByPassUrl); + var uri = API.Basket.UpdateBasket(_basketByPassUrl); + + var basketContent = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json"); - var response = await _apiClient.PostAsync(updateBasketUri, basket, token); + var response = await _apiClient.PostAsync(uri, basketContent); response.EnsureSuccessStatusCode(); @@ -58,65 +54,64 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public async Task Checkout(BasketDTO basket) { - var token = await GetUserTokenAsync(); - var updateBasketUri = API.Basket.CheckoutBasket(_basketByPassUrl); + var uri = API.Basket.CheckoutBasket(_basketByPassUrl); + var basketContent = new StringContent(JsonConvert.SerializeObject(basket), System.Text.Encoding.UTF8, "application/json"); - var response = await _apiClient.PostAsync(updateBasketUri, basket, token); + var response = await _apiClient.PostAsync(uri, basketContent); response.EnsureSuccessStatusCode(); } public async Task SetQuantities(ApplicationUser user, Dictionary quantities) { + var uri = API.Purchase.UpdateBasketItem(_purchaseUrl); - var token = await GetUserTokenAsync(); - var updateBasketUri = API.Purchase.UpdateBasketItem(_purchaseUrl); - var userId = user.Id; - - var response = await _apiClient.PutAsync(updateBasketUri, new + var basketUpdate = new { - BasketId = userId, + BasketId = user.Id, Updates = quantities.Select(kvp => new { BasketItemId = kvp.Key, NewQty = kvp.Value }).ToArray() - }, token); + }; + + var basketContent = new StringContent(JsonConvert.SerializeObject(basketUpdate), System.Text.Encoding.UTF8, "application/json"); + + var response = await _apiClient.PutAsync(uri, basketContent); response.EnsureSuccessStatusCode(); + var jsonResponse = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(jsonResponse); } public async Task GetOrderDraft(string basketId) { - var token = await GetUserTokenAsync(); - var draftOrderUri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId); - var json = await _apiClient.GetStringAsync(draftOrderUri, token); - return JsonConvert.DeserializeObject(json); - } + var uri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId); + var responseString = await _apiClient.GetStringAsync(uri); + var response = JsonConvert.DeserializeObject(responseString); + + return response; + } public async Task AddItemToBasket(ApplicationUser user, int productId) { - var token = await GetUserTokenAsync(); - var updateBasketUri = API.Purchase.AddItemToBasket(_purchaseUrl); - var userId = user.Id; + var uri = API.Purchase.AddItemToBasket(_purchaseUrl); - var response = await _apiClient.PostAsync(updateBasketUri, new + var newItem = new { CatalogItemId = productId, - BasketId = userId, + BasketId = user.Id, Quantity = 1 - }, token); + }; - } + var basketContent = new StringContent(JsonConvert.SerializeObject(newItem), System.Text.Encoding.UTF8, "application/json"); - async Task GetUserTokenAsync() - { - var context = _httpContextAccesor.HttpContext; - return await context.GetTokenAsync("access_token"); + var response = await _apiClient.PostAsync(uri, basketContent); } } } diff --git a/src/Web/WebMVC/Services/CampaignService.cs b/src/Web/WebMVC/Services/CampaignService.cs index 7d61c9e7a..6521fd4ae 100644 --- a/src/Web/WebMVC/Services/CampaignService.cs +++ b/src/Web/WebMVC/Services/CampaignService.cs @@ -1,69 +1,49 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { using global::WebMVC.Infrastructure; - using AspNetCore.Authentication; - using AspNetCore.Http; - using BuildingBlocks.Resilience.Http; - using ViewModels; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; - using System; + using System.Net.Http; using System.Threading.Tasks; + using ViewModels; public class CampaignService : ICampaignService { - private readonly IOptionsSnapshot _settings; - private readonly IHttpClient _apiClient; + private readonly IOptions _settings; + private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly string _remoteServiceBaseUrl; - private readonly IHttpContextAccessor _httpContextAccesor; - public CampaignService(IOptionsSnapshot settings, IHttpClient httpClient, - ILogger logger, IHttpContextAccessor httpContextAccesor) + public CampaignService(IOptions settings, HttpClient httpClient, ILogger logger) { _settings = settings; - _apiClient = httpClient; + _httpClient = httpClient; _logger = logger; _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/m/campaigns/"; - _httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor)); } public async Task GetCampaigns(int pageSize, int pageIndex) { - var allCampaignItemsUri = API.Marketing.GetAllCampaigns(_remoteServiceBaseUrl, - pageSize, pageIndex); + var uri = API.Marketing.GetAllCampaigns(_remoteServiceBaseUrl, pageSize, pageIndex); - var authorizationToken = await GetUserTokenAsync(); - var dataString = await _apiClient.GetStringAsync(allCampaignItemsUri, authorizationToken); + var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonConvert.DeserializeObject(dataString); + var response = JsonConvert.DeserializeObject(responseString); return response; } public async Task GetCampaignById(int id) { - var campaignByIdItemUri = API.Marketing.GetAllCampaignById(_remoteServiceBaseUrl, id); + var uri = API.Marketing.GetAllCampaignById(_remoteServiceBaseUrl, id); - var authorizationToken = await GetUserTokenAsync(); - var dataString = await _apiClient.GetStringAsync(campaignByIdItemUri, authorizationToken); + var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonConvert.DeserializeObject(dataString); + var response = JsonConvert.DeserializeObject(responseString); return response; } - - private string GetUserIdentity() - { - return _httpContextAccesor.HttpContext.User.FindFirst("sub").Value; - } - - private async Task GetUserTokenAsync() - { - var context = _httpContextAccesor.HttpContext; - return await context.GetTokenAsync("access_token"); - } } } \ No newline at end of file diff --git a/src/Web/WebMVC/Services/CatalogService.cs b/src/Web/WebMVC/Services/CatalogService.cs index 5b6fbe26b..d4899b453 100644 --- a/src/Web/WebMVC/Services/CatalogService.cs +++ b/src/Web/WebMVC/Services/CatalogService.cs @@ -1,11 +1,11 @@ using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; @@ -13,16 +13,16 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { public class CatalogService : ICatalogService { - private readonly IOptionsSnapshot _settings; - private readonly IHttpClient _apiClient; + private readonly IOptions _settings; + private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly string _remoteServiceBaseUrl; - public CatalogService(IOptionsSnapshot settings, IHttpClient httpClient, ILogger logger) + public CatalogService(HttpClient httpClient, ILogger logger, IOptions settings) { + _httpClient = httpClient; _settings = settings; - _apiClient = httpClient; _logger = logger; _remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1/c/catalog/"; @@ -30,25 +30,26 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public async Task GetCatalogItems(int page, int take, int? brand, int? type) { - var allcatalogItemsUri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type); + var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type); - var dataString = await _apiClient.GetStringAsync(allcatalogItemsUri); + var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonConvert.DeserializeObject(dataString); + var catalog = JsonConvert.DeserializeObject(responseString); - return response; + return catalog; } public async Task> GetBrands() { - var getBrandsUri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl); + var uri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl); - var dataString = await _apiClient.GetStringAsync(getBrandsUri); + var responseString = await _httpClient.GetStringAsync(uri); var items = new List(); + items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); - var brands = JArray.Parse(dataString); + var brands = JArray.Parse(responseString); foreach (var brand in brands.Children()) { @@ -64,14 +65,14 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services public async Task> GetTypes() { - var getTypesUri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl); + var uri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl); - var dataString = await _apiClient.GetStringAsync(getTypesUri); + var responseString = await _httpClient.GetStringAsync(uri); var items = new List(); items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); - var brands = JArray.Parse(dataString); + var brands = JArray.Parse(responseString); foreach (var brand in brands.Children()) { items.Add(new SelectListItem() @@ -80,6 +81,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services Text = brand.Value("type") }); } + return items; } } diff --git a/src/Web/WebMVC/Services/IdentityParser.cs b/src/Web/WebMVC/Services/IdentityParser.cs index 70e82e53b..b56393d4a 100644 --- a/src/Web/WebMVC/Services/IdentityParser.cs +++ b/src/Web/WebMVC/Services/IdentityParser.cs @@ -8,37 +8,36 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.WebMVC.Services { - public class IdentityParser:IIdentityParser - { - public ApplicationUser Parse(IPrincipal principal) - { - // Pattern matching 'is' expression - // assigns "claims" if "principal" is a "ClaimsPrincipal" - if (principal is ClaimsPrincipal claims) - { - return new ApplicationUser - { - - CardHolderName = claims.Claims.FirstOrDefault(x => x.Type == "card_holder")?.Value ?? "", - CardNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_number")?.Value ?? "", - Expiration = claims.Claims.FirstOrDefault(x => x.Type == "card_expiration")?.Value ?? "", - CardType = int.Parse(claims.Claims.FirstOrDefault(x => x.Type == "missing")?.Value ?? "0"), - City = claims.Claims.FirstOrDefault(x => x.Type == "address_city")?.Value ?? "", - Country = claims.Claims.FirstOrDefault(x => x.Type == "address_country")?.Value ?? "", - Email = claims.Claims.FirstOrDefault(x => x.Type == "email")?.Value ?? "", - Id = claims.Claims.FirstOrDefault(x => x.Type == "sub")?.Value ?? "", - LastName = claims.Claims.FirstOrDefault(x => x.Type == "last_name")?.Value ?? "", - Name = claims.Claims.FirstOrDefault(x => x.Type == "name")?.Value ?? "", - PhoneNumber = claims.Claims.FirstOrDefault(x => x.Type == "phone_number")?.Value ?? "", - SecurityNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_security_number")?.Value ?? "", - State = claims.Claims.FirstOrDefault(x => x.Type == "address_state")?.Value ?? "", - Street = claims.Claims.FirstOrDefault(x => x.Type == "address_street")?.Value ?? "", - ZipCode = claims.Claims.FirstOrDefault(x => x.Type == "address_zip_code")?.Value ?? "" - }; - } - throw new ArgumentException(message: "The principal must be a ClaimsPrincipal", paramName: nameof(principal)); - } - } + public class IdentityParser : IIdentityParser + { + public ApplicationUser Parse(IPrincipal principal) + { + // Pattern matching 'is' expression + // assigns "claims" if "principal" is a "ClaimsPrincipal" + if (principal is ClaimsPrincipal claims) + { + return new ApplicationUser + { + CardHolderName = claims.Claims.FirstOrDefault(x => x.Type == "card_holder")?.Value ?? "", + CardNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_number")?.Value ?? "", + Expiration = claims.Claims.FirstOrDefault(x => x.Type == "card_expiration")?.Value ?? "", + CardType = int.Parse(claims.Claims.FirstOrDefault(x => x.Type == "missing")?.Value ?? "0"), + City = claims.Claims.FirstOrDefault(x => x.Type == "address_city")?.Value ?? "", + Country = claims.Claims.FirstOrDefault(x => x.Type == "address_country")?.Value ?? "", + Email = claims.Claims.FirstOrDefault(x => x.Type == "email")?.Value ?? "", + Id = claims.Claims.FirstOrDefault(x => x.Type == "sub")?.Value ?? "", + LastName = claims.Claims.FirstOrDefault(x => x.Type == "last_name")?.Value ?? "", + Name = claims.Claims.FirstOrDefault(x => x.Type == "name")?.Value ?? "", + PhoneNumber = claims.Claims.FirstOrDefault(x => x.Type == "phone_number")?.Value ?? "", + SecurityNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_security_number")?.Value ?? "", + State = claims.Claims.FirstOrDefault(x => x.Type == "address_state")?.Value ?? "", + Street = claims.Claims.FirstOrDefault(x => x.Type == "address_street")?.Value ?? "", + ZipCode = claims.Claims.FirstOrDefault(x => x.Type == "address_zip_code")?.Value ?? "" + }; + } + throw new ArgumentException(message: "The principal must be a ClaimsPrincipal", paramName: nameof(principal)); + } + } } diff --git a/src/Web/WebMVC/Services/LocationService.cs b/src/Web/WebMVC/Services/LocationService.cs index 8bbdf743a..3e58ef125 100644 --- a/src/Web/WebMVC/Services/LocationService.cs +++ b/src/Web/WebMVC/Services/LocationService.cs @@ -1,11 +1,9 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; -using Microsoft.eShopOnContainers.WebMVC; +using Microsoft.eShopOnContainers.WebMVC; using Microsoft.eShopOnContainers.WebMVC.Services; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; +using Newtonsoft.Json; +using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; using WebMVC.Models; @@ -14,36 +12,27 @@ namespace WebMVC.Services { public class LocationService : ILocationService { - private readonly IOptionsSnapshot _settings; - private readonly IHttpClient _apiClient; + private readonly IOptions _settings; + private readonly HttpClient _httpClient; private readonly ILogger _logger; private readonly string _remoteServiceBaseUrl; - private readonly IHttpContextAccessor _httpContextAccesor; - public LocationService(IOptionsSnapshot settings, IHttpClient httpClient, - ILogger logger, IHttpContextAccessor httpContextAccesor) + public LocationService(HttpClient httpClient, IOptions settings, ILogger logger) { + _httpClient = httpClient; _settings = settings; - _apiClient = httpClient; _logger = logger; _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/l/locations/"; - _httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor)); } public async Task CreateOrUpdateUserLocation(LocationDTO location) { - var createOrUpdateUserLocationUri = API.Locations.CreateOrUpdateUserLocation(_remoteServiceBaseUrl); + var uri = API.Locations.CreateOrUpdateUserLocation(_remoteServiceBaseUrl); + var locationContent = new StringContent(JsonConvert.SerializeObject(location), System.Text.Encoding.UTF8, "application/json"); - var authorizationToken = await GetUserTokenAsync(); - var response = await _apiClient.PostAsync(createOrUpdateUserLocationUri, location, authorizationToken); + var response = await _httpClient.PostAsync(uri, locationContent); response.EnsureSuccessStatusCode(); - } - - private async Task GetUserTokenAsync() - { - var context = _httpContextAccesor.HttpContext; - return await context.GetTokenAsync("access_token"); } } } diff --git a/src/Web/WebMVC/Services/OrderingService.cs b/src/Web/WebMVC/Services/OrderingService.cs index ec9d2e8fd..e3d24422f 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -1,11 +1,9 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; -using Microsoft.eShopOnContainers.WebMVC.ViewModels; +using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.Extensions.Options; using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; using WebMVC.Infrastructure; using WebMVC.Models; @@ -14,69 +12,54 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services { public class OrderingService : IOrderingService { - private IHttpClient _apiClient; + private HttpClient _httpClient; private readonly string _remoteServiceBaseUrl; - private readonly IOptionsSnapshot _settings; - private readonly IHttpContextAccessor _httpContextAccesor; + private readonly IOptions _settings; - public OrderingService(IOptionsSnapshot settings, IHttpContextAccessor httpContextAccesor, IHttpClient httpClient) + + public OrderingService(HttpClient httpClient, IOptions settings) { - _remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/api/v1/o/orders"; + _httpClient = httpClient; _settings = settings; - _httpContextAccesor = httpContextAccesor; - _apiClient = httpClient; + + _remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/api/v1/o/orders"; } async public Task GetOrder(ApplicationUser user, string id) { - var token = await GetUserTokenAsync(); - var getOrderUri = API.Order.GetOrder(_remoteServiceBaseUrl, id); + var uri = API.Order.GetOrder(_remoteServiceBaseUrl, id); - var dataString = await _apiClient.GetStringAsync(getOrderUri, token); + var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonConvert.DeserializeObject(dataString); + var response = JsonConvert.DeserializeObject(responseString); return response; } async public Task> GetMyOrders(ApplicationUser user) { - var token = await GetUserTokenAsync(); - var allMyOrdersUri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl); + var uri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl); + + var responseString = await _httpClient.GetStringAsync(uri); - var dataString = await _apiClient.GetStringAsync(allMyOrdersUri, token); - var response = JsonConvert.DeserializeObject>(dataString); + var response = JsonConvert.DeserializeObject>(responseString); return response; } - public Order MapUserInfoIntoOrder(ApplicationUser user, Order order) - { - order.City = user.City; - order.Street = user.Street; - order.State = user.State; - order.Country = user.Country; - order.ZipCode = user.ZipCode; - - order.CardNumber = user.CardNumber; - order.CardHolderName = user.CardHolderName; - order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1); - order.CardSecurityNumber = user.SecurityNumber; - return order; - } async public Task CancelOrder(string orderId) { - var token = await GetUserTokenAsync(); var order = new OrderDTO() { OrderNumber = orderId }; - var cancelOrderUri = API.Order.CancelOrder(_remoteServiceBaseUrl); - - var response = await _apiClient.PutAsync(cancelOrderUri, order, token, Guid.NewGuid().ToString()); + var uri = API.Order.CancelOrder(_remoteServiceBaseUrl); + var orderContent = new StringContent(JsonConvert.SerializeObject(order), System.Text.Encoding.UTF8, "application/json"); + + var response = await _httpClient.PutAsync(uri, orderContent); if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) { @@ -88,15 +71,15 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services async public Task ShipOrder(string orderId) { - var token = await GetUserTokenAsync(); var order = new OrderDTO() { OrderNumber = orderId }; - var shipOrderUri = API.Order.ShipOrder(_remoteServiceBaseUrl); + var uri = API.Order.ShipOrder(_remoteServiceBaseUrl); + var orderContent = new StringContent(JsonConvert.SerializeObject(order), System.Text.Encoding.UTF8, "application/json"); - var response = await _apiClient.PutAsync(shipOrderUri, order, token, Guid.NewGuid().ToString()); + var response = await _httpClient.PutAsync(uri, orderContent); if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) { @@ -120,6 +103,22 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services destination.CardSecurityNumber = original.CardSecurityNumber; } + public Order MapUserInfoIntoOrder(ApplicationUser user, Order order) + { + order.City = user.City; + order.Street = user.Street; + order.State = user.State; + order.Country = user.Country; + order.ZipCode = user.ZipCode; + + order.CardNumber = user.CardNumber; + order.CardHolderName = user.CardHolderName; + order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1); + order.CardSecurityNumber = user.SecurityNumber; + + return order; + } + public BasketDTO MapOrderToBasket(Order order) { order.CardExpirationApiFormat(); @@ -140,18 +139,5 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services RequestId = order.RequestId }; } - - void SetFakeIdToProducts(Order order) - { - var id = 1; - order.OrderItems.ForEach(x => { x.ProductId = id; id++; }); - } - - async Task GetUserTokenAsync() - { - var context = _httpContextAccesor.HttpContext; - - return await context.GetTokenAsync("access_token"); - } } } diff --git a/src/Web/WebMVC/ViewModels/Annotations/CardExpiration.cs b/src/Web/WebMVC/ViewModels/Annotations/CardExpiration.cs index d60150a44..b7ef9fcf4 100644 --- a/src/Web/WebMVC/ViewModels/Annotations/CardExpiration.cs +++ b/src/Web/WebMVC/ViewModels/Annotations/CardExpiration.cs @@ -6,28 +6,29 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Annotations { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] - public class CardExpirationAttribute : ValidationAttribute - { - public override bool IsValid(object value) - { - if (value == null) - return false; + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] + public class CardExpirationAttribute : ValidationAttribute + { + public override bool IsValid(object value) + { + if (value == null) + return false; - var monthString = value.ToString().Split('/')[0]; - var yearString = $"20{value.ToString().Split('/')[1]}"; - // Use the 'out' variable initializer to simplify - // the logic of validating the expiration date - if ((int.TryParse(monthString, out var month)) && - (int.TryParse(yearString, out var year))) - { - DateTime d = new DateTime(year, month, 1); + var monthString = value.ToString().Split('/')[0]; + var yearString = $"20{value.ToString().Split('/')[1]}"; + // Use the 'out' variable initializer to simplify + // the logic of validating the expiration date + if ((int.TryParse(monthString, out var month)) && + (int.TryParse(yearString, out var year))) + { + DateTime d = new DateTime(year, month, 1); - return d > DateTime.UtcNow; - } else - { - return false; - } - } - } + return d > DateTime.UtcNow; + } + else + { + return false; + } + } + } } diff --git a/src/Web/WebMVC/ViewModels/Annotations/LatitudeCoordinate.cs b/src/Web/WebMVC/ViewModels/Annotations/LatitudeCoordinate.cs index 5731b8895..15ec5bebd 100644 --- a/src/Web/WebMVC/ViewModels/Annotations/LatitudeCoordinate.cs +++ b/src/Web/WebMVC/ViewModels/Annotations/LatitudeCoordinate.cs @@ -3,20 +3,20 @@ using System.ComponentModel.DataAnnotations; namespace WebMVC.ViewModels.Annotations { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] - public class LatitudeCoordinate : ValidationAttribute - { - protected override ValidationResult - IsValid(object value, ValidationContext validationContext) - { - double coordinate; - if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -90 || coordinate > 90)) - { - return new ValidationResult - ("Latitude must be between -90 and 90 degrees inclusive."); - } + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] + public class LatitudeCoordinate : ValidationAttribute + { + protected override ValidationResult + IsValid(object value, ValidationContext validationContext) + { + double coordinate; + if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -90 || coordinate > 90)) + { + return new ValidationResult + ("Latitude must be between -90 and 90 degrees inclusive."); + } - return ValidationResult.Success; - } - } + return ValidationResult.Success; + } + } } diff --git a/src/Web/WebMVC/ViewModels/Annotations/LongitudeCoordinate.cs b/src/Web/WebMVC/ViewModels/Annotations/LongitudeCoordinate.cs index de2266711..48baecce9 100644 --- a/src/Web/WebMVC/ViewModels/Annotations/LongitudeCoordinate.cs +++ b/src/Web/WebMVC/ViewModels/Annotations/LongitudeCoordinate.cs @@ -3,20 +3,20 @@ using System.ComponentModel.DataAnnotations; namespace WebMVC.ViewModels.Annotations { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] - public class LongitudeCoordinate : ValidationAttribute - { - protected override ValidationResult - IsValid(object value, ValidationContext validationContext) - { - double coordinate; - if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -180 || coordinate > 180)) - { - return new ValidationResult - ("Longitude must be between -180 and 180 degrees inclusive."); - } + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] + public class LongitudeCoordinate : ValidationAttribute + { + protected override ValidationResult + IsValid(object value, ValidationContext validationContext) + { + double coordinate; + if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -180 || coordinate > 180)) + { + return new ValidationResult + ("Longitude must be between -180 and 180 degrees inclusive."); + } - return ValidationResult.Success; - } - } + return ValidationResult.Success; + } + } } diff --git a/src/Web/WebMVC/appsettings.json b/src/Web/WebMVC/appsettings.json index 161e247a6..ba12727bc 100644 --- a/src/Web/WebMVC/appsettings.json +++ b/src/Web/WebMVC/appsettings.json @@ -1,27 +1,27 @@ { - "CatalogUrl": "http://localhost:5101", - "OrderingUrl": "http://localhost:5102", - "BasketUrl": "http://localhost:5103", - "MarketingUrl": "http://localhost:5110", - "IdentityUrl": "http://localhost:5105", - "CallBackUrl": "http://localhost:5100/", - "LocationsUrl": "http://localhost:5109/", - "IsClusterEnv": "False", - "UseResilientHttp": "True", - "UseLoadTest": false, - "ActivateCampaignDetailFunction": "False", - "UseCustomizationData": false, - "Logging": { - "IncludeScopes": false, - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Information" - } - }, - "ApplicationInsights": { - "InstrumentationKey": "" - }, - "HttpClientRetryCount": 8, - "HttpClientExceptionsAllowedBeforeBreaking": 7 + "CatalogUrl": "http://localhost:5101", + "OrderingUrl": "http://localhost:5102", + "BasketUrl": "http://localhost:5103", + "MarketingUrl": "http://localhost:5110", + "IdentityUrl": "http://localhost:5105", + "CallBackUrl": "http://localhost:5100/", + "LocationsUrl": "http://localhost:5109/", + "IsClusterEnv": "False", + "UseResilientHttp": "True", + "UseLoadTest": false, + "ActivateCampaignDetailFunction": "False", + "UseCustomizationData": false, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Trace", + "System": "Information", + "Microsoft": "Information" + } + }, + "ApplicationInsights": { + "InstrumentationKey": "" + }, + "HttpClientRetryCount": 8, + "HttpClientExceptionsAllowedBeforeBreaking": 7 } \ No newline at end of file diff --git a/src/Web/WebSPA/Dockerfile b/src/Web/WebSPA/Dockerfile index 7b7cd47d4..68cf0988b 100644 --- a/src/Web/WebSPA/Dockerfile +++ b/src/Web/WebSPA/Dockerfile @@ -1,12 +1,24 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS sdk-with-node +ENV NODE_VERSION 8.11.1 +ENV NODE_DOWNLOAD_SHA 0e20787e2eda4cc31336d8327556ebc7417e8ee0a6ba0de96a09b0ec2b841f60 +RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \ + && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \ + && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \ + && rm nodejs.tar.gz \ + && ln -s /usr/local/bin/node /usr/local/bin/nodejs + +FROM sdk-with-node AS updated-npm +RUN npm i -g npm + +FROM updated-npm as build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Web/WebSPA +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Web/WebSPA/Properties/launchSettings.json b/src/Web/WebSPA/Properties/launchSettings.json index eff752f63..a79d8ba00 100644 --- a/src/Web/WebSPA/Properties/launchSettings.json +++ b/src/Web/WebSPA/Properties/launchSettings.json @@ -2,8 +2,12 @@ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, + "iis": { + "applicationUrl": "http://localhost/WebSPA", + "sslPort": 0 + }, "iisExpress": { - "applicationUrl": "http://localhost:58018/", + "applicationUrl": "http://localhost:5104", "sslPort": 0 } }, @@ -11,8 +15,9 @@ "IIS Express": { "commandName": "IISExpress", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Production" - } + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "use64Bit": true } } } \ No newline at end of file diff --git a/src/Web/WebSPA/WebSPA.csproj b/src/Web/WebSPA/WebSPA.csproj index 1390b7bcf..65f67d378 100644 --- a/src/Web/WebSPA/WebSPA.csproj +++ b/src/Web/WebSPA/WebSPA.csproj @@ -90,7 +90,7 @@ - + diff --git a/src/Web/WebStatus/Dockerfile b/src/Web/WebStatus/Dockerfile index 16d4cbc61..10cfa25e5 100644 --- a/src/Web/WebStatus/Dockerfile +++ b/src/Web/WebStatus/Dockerfile @@ -1,12 +1,12 @@ -FROM microsoft/aspnetcore:2.0.5 AS base +FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 -FROM microsoft/aspnetcore-build:2.0.5-2.1.4 AS build +FROM microsoft/dotnet:2.1-sdk AS build WORKDIR /src COPY . . -RUN dotnet restore -nowarn:msb3202,nu1503 WORKDIR /src/src/Web/WebStatus +RUN dotnet restore -nowarn:msb3202,nu1503 RUN dotnet build --no-restore -c Release -o /app FROM build AS publish diff --git a/src/Web/WebStatus/appsettings.json b/src/Web/WebStatus/appsettings.json index bf413fcbf..2ab5ad818 100644 --- a/src/Web/WebStatus/appsettings.json +++ b/src/Web/WebStatus/appsettings.json @@ -8,6 +8,7 @@ } }, "OrderingUrl": "http://localhost:5102/hc", + "OrderingBackgroundTasksUrl": "http://localhost:5111/hc", "BasketUrl": "http://localhost:5103/hc", "CatalogUrl": "http://localhost:5101/hc", "IdentityUrl": "http://localhost:5105/hc",