diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs new file mode 100644 index 000000000..aa1d5ce6a --- /dev/null +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs @@ -0,0 +1,43 @@ +using Grpc.Core; +using Grpc.Core.Interceptors; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Infrastructure +{ + public class GrpcExceptionInterceptor : Interceptor + { + private readonly ILogger _logger; + + public GrpcExceptionInterceptor(ILogger logger) + { + _logger = logger; + } + + public override AsyncUnaryCall AsyncUnaryCall( + TRequest request, + ClientInterceptorContext context, + AsyncUnaryCallContinuation continuation) + { + var call = continuation(request, context); + + return new AsyncUnaryCall(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose); + } + + private async Task HandleResponse(Task t) + { + try + { + var response = await t; + _logger.LogDebug($"Response received: {response}"); + return response; + } + catch (RpcException e) + { + _logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message); + return default; + } + } + } +} diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj index 770d084c3..09c238f9d 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Mobile.Shopping.HttpAggregator.csproj @@ -20,6 +20,7 @@ + diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs index 2fc86c51f..3e80a543f 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Program.cs @@ -1,55 +1,29 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; +using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator; using Serilog; -using System.IO; -namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator -{ - public class Program - { - private static IConfiguration _configuration; - public static void Main(string[] args) +BuildWebHost(args).Run(); +IWebHost BuildWebHost(string[] args) => + WebHost + .CreateDefaultBuilder(args) + .ConfigureAppConfiguration(cb => { - _configuration = GetConfiguration(); - - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost - .CreateDefaultBuilder(args) - .ConfigureAppConfiguration(cb => - { - var sources = cb.Sources; - sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() - { - Optional = true, - Path = "appsettings.localhost.json", - ReloadOnChange = false - }); - }) - .UseStartup() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) - .Build(); - - private static IConfiguration GetConfiguration() + var sources = cb.Sources; + sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() + { + Optional = true, + Path = "appsettings.localhost.json", + ReloadOnChange = false + }); + }) + .UseStartup() + .UseSerilog((builderContext, config) => { - var builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddEnvironmentVariables(); - - var config = builder.Build(); - - return builder.Build(); - } - } -} + config + .MinimumLevel.Information() + .Enrich.FromLogContext() + .WriteTo.Console(); + }) + .Build(); \ No newline at end of file diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs index 8bd4c8948..29f02e720 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/BasketService.cs @@ -1,54 +1,40 @@ -using Grpc.Net.Client; -using GrpcBasket; +using GrpcBasket; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Config; using Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator.Services { public class BasketService : IBasketService { - private readonly HttpClient _httpClient; - private readonly UrlsConfig _urls; + private readonly Basket.BasketClient _basketClient; private readonly ILogger _logger; - public BasketService(HttpClient httpClient, IOptions config, ILogger logger) + public BasketService(Basket.BasketClient basketClient, ILogger logger) { - _httpClient = httpClient; - _urls = config.Value; + _basketClient = basketClient; _logger = logger; } public async Task GetById(string id) { - return await GrpcCallerService.CallService(_urls.GrpcBasket, async channel => - { - - var client = new Basket.BasketClient(channel); - _logger.LogDebug("grpc client created, request = {@id}", id); - var response = await client.GetBasketByIdAsync(new BasketRequest { Id = id }); - _logger.LogDebug("grpc response {@response}", response); + _logger.LogDebug("grpc client created, request = {@id}", id); + var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id }); + _logger.LogDebug("grpc response {@response}", response); - return MapToBasketData(response); - }); + return MapToBasketData(response); } public async Task UpdateAsync(BasketData currentBasket) { - await GrpcCallerService.CallService(_urls.GrpcBasket, async httpClient => - { - var channel = GrpcChannel.ForAddress(_urls.GrpcBasket); - var client = new Basket.BasketClient(channel); - _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); - var request = MapToCustomerBasketRequest(currentBasket); - _logger.LogDebug("Grpc update basket request {@request}", request); + _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); + var request = MapToCustomerBasketRequest(currentBasket); + _logger.LogDebug("Grpc update basket request {@request}", request); - return await client.UpdateBasketAsync(request); - }); + await _basketClient.UpdateBasketAsync(request); } private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs index 6523cafe9..812163d29 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Startup.cs @@ -1,4 +1,5 @@ using Devspaces.Support; +using GrpcBasket; using HealthChecks.UI.Client; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; @@ -14,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using System; using System.Collections.Generic; @@ -46,7 +48,8 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator services.AddCustomMvc(Configuration) .AddCustomAuthentication(Configuration) .AddDevspaces() - .AddHttpServices(); + .AddHttpServices() + .AddGrpcServices(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -177,10 +180,6 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator services.AddSingleton(); //register http services - services - .AddHttpClient() - .AddHttpMessageHandler() - .AddDevspacesSupport(); services.AddHttpClient() .AddDevspacesSupport(); @@ -194,5 +193,20 @@ namespace Microsoft.eShopOnContainers.Mobile.Shopping.HttpAggregator return services; } + public static IServiceCollection AddGrpcServices(this IServiceCollection services) + { + services.AddSingleton(); + + services.AddScoped(); + + services.AddGrpcClient((services, options) => + { + var basketApi = services.GetRequiredService>().Value.GrpcBasket; + options.Address = new Uri(basketApi); + }).AddInterceptor(); + + return services; + } + } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile index 037105cb4..c6e7b8be6 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Dockerfile @@ -1,8 +1,8 @@ -FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build WORKDIR /src # It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs new file mode 100644 index 000000000..3120a3567 --- /dev/null +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Infrastructure/GrpcExceptionInterceptor.cs @@ -0,0 +1,43 @@ +using Grpc.Core; +using Grpc.Core.Interceptors; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Infrastructure +{ + public class GrpcExceptionInterceptor : Interceptor + { + private readonly ILogger _logger; + + public GrpcExceptionInterceptor(ILogger logger) + { + _logger = logger; + } + + public override AsyncUnaryCall AsyncUnaryCall( + TRequest request, + ClientInterceptorContext context, + AsyncUnaryCallContinuation continuation) + { + var call = continuation(request, context); + + return new AsyncUnaryCall(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose); + } + + private async Task HandleResponse(Task t) + { + try + { + var response = await t; + _logger.LogDebug($"Response received: {response}"); + return response; + } + catch (RpcException e) + { + _logger.LogError("Error calling via grpc: {Status} - {Message}", e.Status, e.Message); + return default; + } + } + } +} diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs index b601d67f2..083292f54 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Program.cs @@ -1,38 +1,29 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator; using Serilog; -namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator -{ - public class Program - { - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost - .CreateDefaultBuilder(args) - .ConfigureAppConfiguration(cb => - { - var sources = cb.Sources; - sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() - { - Optional = true, - Path = "appsettings.localhost.json", - ReloadOnChange = false - }); - }) - .UseStartup() - .UseSerilog((builderContext, config) => - { - config - .MinimumLevel.Information() - .Enrich.FromLogContext() - .WriteTo.Console(); - }) - .Build(); +BuildWebHost(args).Run(); - } -} +IWebHost BuildWebHost(string[] args) => + WebHost + .CreateDefaultBuilder(args) + .ConfigureAppConfiguration(cb => + { + var sources = cb.Sources; + sources.Insert(3, new Microsoft.Extensions.Configuration.Json.JsonConfigurationSource() + { + Optional = true, + Path = "appsettings.localhost.json", + ReloadOnChange = false + }); + }) + .UseStartup() + .UseSerilog((builderContext, config) => + { + config + .MinimumLevel.Information() + .Enrich.FromLogContext() + .WriteTo.Console(); + }) + .Build(); \ No newline at end of file diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs index 7e85132cd..7b526a5b9 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/BasketService.cs @@ -4,49 +4,38 @@ using Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Models; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator.Services { public class BasketService : IBasketService { - private readonly UrlsConfig _urls; - public readonly HttpClient _httpClient; + private readonly Basket.BasketClient _basketClient; private readonly ILogger _logger; - public BasketService(HttpClient httpClient, IOptions config, ILogger logger) + public BasketService(Basket.BasketClient basketClient, ILogger logger) { - _urls = config.Value; - _httpClient = httpClient; + _basketClient = basketClient; _logger = logger; } public async Task GetById(string id) { - return await GrpcCallerService.CallService(_urls.GrpcBasket, async channel => - { - var client = new Basket.BasketClient(channel); - _logger.LogDebug("grpc client created, request = {@id}", id); - var response = await client.GetBasketByIdAsync(new BasketRequest { Id = id }); - _logger.LogDebug("grpc response {@response}", response); + _logger.LogDebug("grpc client created, request = {@id}", id); + var response = await _basketClient.GetBasketByIdAsync(new BasketRequest { Id = id }); + _logger.LogDebug("grpc response {@response}", response); - return MapToBasketData(response); - }); + return MapToBasketData(response); } public async Task UpdateAsync(BasketData currentBasket) { - await GrpcCallerService.CallService(_urls.GrpcBasket, async channel => - { - var client = new Basket.BasketClient(channel); - _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); - var request = MapToCustomerBasketRequest(currentBasket); - _logger.LogDebug("Grpc update basket request {@request}", request); + _logger.LogDebug("Grpc update basket currentBasket {@currentBasket}", currentBasket); + var request = MapToCustomerBasketRequest(currentBasket); + _logger.LogDebug("Grpc update basket request {@request}", request); - return await client.UpdateBasketAsync(request); - }); + await _basketClient.UpdateBasketAsync(request); } private BasketData MapToBasketData(CustomerBasketResponse customerBasketRequest) diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs index 1ea6c0f4f..06af6901f 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Startup.cs @@ -1,4 +1,5 @@ using Devspaces.Support; +using GrpcBasket; using HealthChecks.UI.Client; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; @@ -14,6 +15,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using System; using System.Collections.Generic; @@ -42,11 +44,12 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator .AddUrlGroup(new Uri(Configuration["MarketingUrlHC"]), name: "marketingapi-check", tags: new string[] { "marketingapi" }) .AddUrlGroup(new Uri(Configuration["PaymentUrlHC"]), name: "paymentapi-check", tags: new string[] { "paymentapi" }) .AddUrlGroup(new Uri(Configuration["LocationUrlHC"]), name: "locationapi-check", tags: new string[] { "locationapi" }); - + services.AddCustomMvc(Configuration) .AddCustomAuthentication(Configuration) .AddDevspaces() - .AddApplicationServices(); + .AddApplicationServices() + .AddGrpcServices(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -63,7 +66,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator { app.UseDeveloperExceptionPage(); } - + app.UseHttpsRedirection(); app.UseSwagger().UseSwaggerUI(c => @@ -139,7 +142,7 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator Version = "v1", Description = "Shopping Aggregator for Web Clients" }); - + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, @@ -181,10 +184,6 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator //register http services - services.AddHttpClient() - .AddHttpMessageHandler() - .AddDevspacesSupport(); - services.AddHttpClient() .AddDevspacesSupport(); @@ -199,6 +198,19 @@ namespace Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator return services; } + public static IServiceCollection AddGrpcServices(this IServiceCollection services) + { + services.AddSingleton(); + + services.AddScoped(); + + services.AddGrpcClient((services, options) => + { + var basketApi = services.GetRequiredService>().Value.GrpcBasket; + options.Address = new Uri(basketApi); + }).AddInterceptor(); + return services; + } } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj index 2cef24f4e..8258a8c16 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Web.Shopping.HttpAggregator.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 Web.Shopping.HttpAggregator Microsoft.eShopOnContainers.Web.Shopping.HttpAggregator ..\..\..\docker-compose.dcproj @@ -15,20 +15,21 @@ - - - - - - - - + + + + + + + + + - - - + + + - + diff --git a/src/Services/Basket/Basket.API/Basket.API.csproj b/src/Services/Basket/Basket.API/Basket.API.csproj index b0e9ca4ca..4921041f2 100644 --- a/src/Services/Basket/Basket.API/Basket.API.csproj +++ b/src/Services/Basket/Basket.API/Basket.API.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; ..\..\..\..\docker-compose.dcproj false @@ -16,30 +16,30 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + - + - + diff --git a/src/Services/Basket/Basket.API/Dockerfile b/src/Services/Basket/Basket.API/Dockerfile index f04156711..53bcc38c4 100644 --- a/src/Services/Basket/Basket.API/Dockerfile +++ b/src/Services/Basket/Basket.API/Dockerfile @@ -1,8 +1,8 @@ -FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build WORKDIR /src # It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles diff --git a/src/Services/Basket/Basket.API/Model/BasketCheckout.cs b/src/Services/Basket/Basket.API/Model/BasketCheckout.cs index 410700773..70173611f 100644 --- a/src/Services/Basket/Basket.API/Model/BasketCheckout.cs +++ b/src/Services/Basket/Basket.API/Model/BasketCheckout.cs @@ -2,29 +2,29 @@ namespace Basket.API.Model { - public class BasketCheckout + public record BasketCheckout { - public string City { get; set; } + public string City { get; init; } - public string Street { get; set; } + public string Street { get; init; } - public string State { get; set; } + public string State { get; init; } - public string Country { get; set; } + public string Country { get; init; } - public string ZipCode { get; set; } + public string ZipCode { get; init; } - public string CardNumber { get; set; } + public string CardNumber { get; init; } - public string CardHolderName { get; set; } + public string CardHolderName { get; init; } - public DateTime CardExpiration { get; set; } + public DateTime CardExpiration { get; init; } - public string CardSecurityNumber { get; set; } + public string CardSecurityNumber { get; init; } - public int CardTypeId { get; set; } + public int CardTypeId { get; init; } - public string Buyer { get; set; } + public string Buyer { get; init; } public Guid RequestId { get; set; } } diff --git a/src/Services/Basket/Basket.API/Model/BasketItem.cs b/src/Services/Basket/Basket.API/Model/BasketItem.cs index 4d2e4f331..e0414884c 100644 --- a/src/Services/Basket/Basket.API/Model/BasketItem.cs +++ b/src/Services/Basket/Basket.API/Model/BasketItem.cs @@ -3,15 +3,15 @@ using System.ComponentModel.DataAnnotations; namespace Microsoft.eShopOnContainers.Services.Basket.API.Model { - public class BasketItem : IValidatableObject + public record BasketItem : IValidatableObject { - public string Id { get; set; } - public int ProductId { get; set; } - public string ProductName { get; set; } + public string Id { get; init; } + public int ProductId { get; init; } + public string ProductName { get; init; } public decimal UnitPrice { get; set; } public decimal OldUnitPrice { get; set; } - public int Quantity { get; set; } - public string PictureUrl { get; set; } + public int Quantity { get; init; } + public string PictureUrl { get; init; } public IEnumerable Validate(ValidationContext validationContext) { var results = new List(); diff --git a/src/Services/Basket/Basket.API/Model/CustomerBasket.cs b/src/Services/Basket/Basket.API/Model/CustomerBasket.cs index 9ae495d4f..e8dc14edb 100644 --- a/src/Services/Basket/Basket.API/Model/CustomerBasket.cs +++ b/src/Services/Basket/Basket.API/Model/CustomerBasket.cs @@ -2,9 +2,9 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API.Model { - public class CustomerBasket + public record CustomerBasket { - public string BuyerId { get; set; } + public string BuyerId { get; init; } public List Items { get; set; } = new List(); diff --git a/src/Services/Basket/Basket.API/Program.cs b/src/Services/Basket/Basket.API/Program.cs index 1296256aa..ef36cddfe 100644 --- a/src/Services/Basket/Basket.API/Program.cs +++ b/src/Services/Basket/Basket.API/Program.cs @@ -3,113 +3,109 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.eShopOnContainers.Services.Basket.API; using Microsoft.Extensions.Configuration; using Serilog; using System; using System.IO; using System.Net; -namespace Microsoft.eShopOnContainers.Services.Basket.API -{ - public class Program - { - public static readonly string Namespace = typeof(Program).Namespace; - public static readonly string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); +var configuration = GetConfiguration(); - public static int Main(string[] args) - { - var configuration = GetConfiguration(); +Log.Logger = CreateSerilogLogger(configuration); - Log.Logger = CreateSerilogLogger(configuration); +try +{ + Log.Information("Configuring web host ({ApplicationContext})...", Program.AppName); + var host = BuildWebHost(configuration, args); - try - { - Log.Information("Configuring web host ({ApplicationContext})...", AppName); - var host = BuildWebHost(configuration, args); + Log.Information("Starting web host ({ApplicationContext})...", Program.AppName); + host.Run(); - Log.Information("Starting web host ({ApplicationContext})...", AppName); - host.Run(); + return 0; +} +catch (Exception ex) +{ + Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", Program.AppName); + return 1; +} +finally +{ + Log.CloseAndFlush(); +} - return 0; - } - catch (Exception ex) +IWebHost BuildWebHost(IConfiguration configuration, string[] args) => + WebHost.CreateDefaultBuilder(args) + .CaptureStartupErrors(false) + .ConfigureKestrel(options => + { + var ports = GetDefinedPorts(configuration); + options.Listen(IPAddress.Any, ports.httpPort, listenOptions => { - Log.Fatal(ex, "Program terminated unexpectedly ({ApplicationContext})!", AppName); - return 1; - } - finally + listenOptions.Protocols = HttpProtocols.Http1AndHttp2; + }); + + options.Listen(IPAddress.Any, ports.grpcPort, listenOptions => { - Log.CloseAndFlush(); - } - } - - private static IWebHost BuildWebHost(IConfiguration configuration, string[] args) => - WebHost.CreateDefaultBuilder(args) - .CaptureStartupErrors(false) - .ConfigureKestrel(options => - { - var ports = GetDefinedPorts(configuration); - options.Listen(IPAddress.Any, ports.httpPort, listenOptions => - { - listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - }); - - options.Listen(IPAddress.Any, ports.grpcPort, listenOptions => - { - listenOptions.Protocols = HttpProtocols.Http2; - }); - - }) - .ConfigureAppConfiguration(x => x.AddConfiguration(configuration)) - .UseFailing(options => { - options.ConfigPath = "/Failing"; - options.NotFilteredPaths.AddRange(new[] {"/hc","/liveness"}); - }) - .UseStartup() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseSerilog() - .Build(); - - private static Serilog.ILogger CreateSerilogLogger(IConfiguration configuration) - { - var seqServerUrl = configuration["Serilog:SeqServerUrl"]; - var logstashUrl = configuration["Serilog:LogstashgUrl"]; - return new LoggerConfiguration() - .MinimumLevel.Verbose() - .Enrich.WithProperty("ApplicationContext", AppName) - .Enrich.FromLogContext() - .WriteTo.Console() - .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) - .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl) - .ReadFrom.Configuration(configuration) - .CreateLogger(); - } - - private static IConfiguration GetConfiguration() + listenOptions.Protocols = HttpProtocols.Http2; + }); + + }) + .ConfigureAppConfiguration(x => x.AddConfiguration(configuration)) + .UseFailing(options => { - var builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddEnvironmentVariables(); + options.ConfigPath = "/Failing"; + options.NotFilteredPaths.AddRange(new[] { "/hc", "/liveness" }); + }) + .UseStartup() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseSerilog() + .Build(); - var config = builder.Build(); +ILogger CreateSerilogLogger(IConfiguration configuration) +{ + var seqServerUrl = configuration["Serilog:SeqServerUrl"]; + var logstashUrl = configuration["Serilog:LogstashgUrl"]; + return new LoggerConfiguration() + .MinimumLevel.Verbose() + .Enrich.WithProperty("ApplicationContext", Program.AppName) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Seq(string.IsNullOrWhiteSpace(seqServerUrl) ? "http://seq" : seqServerUrl) + .WriteTo.Http(string.IsNullOrWhiteSpace(logstashUrl) ? "http://logstash:8080" : logstashUrl) + .ReadFrom.Configuration(configuration) + .CreateLogger(); +} - if (config.GetValue("UseVault", false)) - { - builder.AddAzureKeyVault( - $"https://{config["Vault:Name"]}.vault.azure.net/", - config["Vault:ClientId"], - config["Vault:ClientSecret"]); - } +IConfiguration GetConfiguration() +{ + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); - return builder.Build(); - } + var config = builder.Build(); - private static (int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config) - { - var grpcPort = config.GetValue("GRPC_PORT", 5001); - var port = config.GetValue("PORT", 80); - return (port, grpcPort); - } + if (config.GetValue("UseVault", false)) + { + builder.AddAzureKeyVault( + $"https://{config["Vault:Name"]}.vault.azure.net/", + config["Vault:ClientId"], + config["Vault:ClientSecret"]); } + + return builder.Build(); +} + +(int httpPort, int grpcPort) GetDefinedPorts(IConfiguration config) +{ + var grpcPort = config.GetValue("GRPC_PORT", 5001); + var port = config.GetValue("PORT", 80); + return (port, grpcPort); } + +public class Program { + + public static string Namespace = typeof(Startup).Namespace; + public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); +} \ No newline at end of file diff --git a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj index fa01363de..9da204e5f 100644 --- a/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj +++ b/src/Services/Basket/Basket.FunctionalTests/Basket.FunctionalTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 false @@ -16,11 +16,11 @@ - - - - - + + + + + all runtime; build; native; contentfiles; analyzers diff --git a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj index 920b4f1da..e97affa25 100644 --- a/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj +++ b/src/Services/Basket/Basket.UnitTests/Basket.UnitTests.csproj @@ -1,19 +1,19 @@  - netcoreapp3.1 + net5.0 false false - - - - - - - + + + + + + + all runtime; build; native; contentfiles; analyzers diff --git a/src/Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj b/src/Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj index 0239c1ade..950daac27 100644 --- a/src/Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj +++ b/src/Tests/Services/Application.FunctionalTests/Application.FunctionalTests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net5.0 true false false @@ -67,10 +67,10 @@ - - + + - + all runtime; build; native; contentfiles; analyzers