From 76ddee775604a946eb3d087d8df3e154ca8d527b Mon Sep 17 00:00:00 2001 From: Sumit Ghosh Date: Thu, 12 Aug 2021 18:56:38 +0530 Subject: [PATCH] Use file-scoped namespaces --- ...orizationHeaderParameterOperationFilter.cs | 41 +- .../Basket/Basket.API/BasketSettings.cs | 10 +- .../Controllers/BasketController.cs | 156 +++-- .../Basket.API/Controllers/HomeController.cs | 14 +- .../Basket/Basket.API/Grpc/BasketService.cs | 151 +++-- .../InternalServerErrorObjectResult.cs | 15 +- .../Exceptions/BasketDomainException.cs | 27 +- .../FailingMiddlewareAppBuilderExtensions.cs | 27 +- .../Filters/HttpGlobalExceptionFilter.cs | 77 ++- .../Filters/JsonErrorResponse.cs | 12 +- .../Filters/ValidateModelStateFilter.cs | 38 +- .../AuthorizeCheckOperationFilter.cs | 47 +- .../Middlewares/FailingMiddleware.cs | 137 +++-- .../Middlewares/FailingOptions.cs | 14 +- .../Middlewares/FailingStartupFilter.cs | 30 +- .../FailingWebHostBuilderExtensions.cs | 20 +- .../Repositories/RedisBasketRepository.cs | 102 ++-- .../OrderStartedIntegrationEventHandler.cs | 44 +- ...ductPriceChangedIntegrationEventHandler.cs | 77 ++- .../Events/OrderStartedIntegrationEvent.cs | 20 +- .../ProductPriceChangedIntegrationEvent.cs | 30 +- .../UserCheckoutAcceptedIntegrationEvent.cs | 83 ++- .../Basket/Basket.API/Model/BasketCheckout.cs | 33 +- .../Basket/Basket.API/Model/BasketItem.cs | 36 +- .../Basket/Basket.API/Model/CustomerBasket.cs | 25 +- .../Basket.API/Model/IBasketRepository.cs | 17 +- src/Services/Basket/Basket.API/Program.cs | 16 +- .../Basket.API/Services/IIdentityService.cs | 10 +- .../Basket.API/Services/IdentityService.cs | 26 +- src/Services/Basket/Basket.API/Startup.cs | 536 ++++++++---------- .../TestHttpResponseTrailersFeature.cs | 12 +- 31 files changed, 844 insertions(+), 1039 deletions(-) diff --git a/src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs b/src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs index 3d505b4ee..d8e0fecb3 100644 --- a/src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs +++ b/src/Services/Basket/Basket.API/Auth/Server/AuthorizationHeaderParameterOperationFilter.cs @@ -1,33 +1,26 @@ -using Microsoft.AspNetCore.Mvc.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.Linq; +namespace Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server; -namespace Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server +public class AuthorizationHeaderParameterOperationFilter : IOperationFilter { - public class AuthorizationHeaderParameterOperationFilter : IOperationFilter + public void Apply(OpenApiOperation operation, OperationFilterContext context) { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors; - var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter); - var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter); + var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors; + var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter); + var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter); - if (isAuthorized && !allowAnonymous) - { - if (operation.Parameters == null) - operation.Parameters = new List(); + if (isAuthorized && !allowAnonymous) + { + if (operation.Parameters == null) + operation.Parameters = new List(); - operation.Parameters.Add(new OpenApiParameter - { - Name = "Authorization", - In = ParameterLocation.Header, - Description = "access token", - Required = true - }); - } + operation.Parameters.Add(new OpenApiParameter + { + Name = "Authorization", + In = ParameterLocation.Header, + Description = "access token", + Required = true + }); } } } diff --git a/src/Services/Basket/Basket.API/BasketSettings.cs b/src/Services/Basket/Basket.API/BasketSettings.cs index 064d615ec..9db883101 100644 --- a/src/Services/Basket/Basket.API/BasketSettings.cs +++ b/src/Services/Basket/Basket.API/BasketSettings.cs @@ -1,7 +1,7 @@ -namespace Microsoft.eShopOnContainers.Services.Basket.API +namespace Microsoft.eShopOnContainers.Services.Basket.API; + +public class BasketSettings { - public class BasketSettings - { - public string ConnectionString { get; set; } - } + public string ConnectionString { get; set; } } + diff --git a/src/Services/Basket/Basket.API/Controllers/BasketController.cs b/src/Services/Basket/Basket.API/Controllers/BasketController.cs index 0a3b5879b..5468bbc15 100644 --- a/src/Services/Basket/Basket.API/Controllers/BasketController.cs +++ b/src/Services/Basket/Basket.API/Controllers/BasketController.cs @@ -1,103 +1,89 @@ -using Basket.API.IntegrationEvents.Events; -using Basket.API.Model; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.eShopOnContainers.Services.Basket.API.Services; -using Microsoft.Extensions.Logging; -using System; -using System.Net; -using System.Security.Claims; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers; -namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers +[Route("api/v1/[controller]")] +[Authorize] +[ApiController] +public class BasketController : ControllerBase { - [Route("api/v1/[controller]")] - [Authorize] - [ApiController] - public class BasketController : ControllerBase - { - private readonly IBasketRepository _repository; - private readonly IIdentityService _identityService; - private readonly IEventBus _eventBus; - private readonly ILogger _logger; - - public BasketController( - ILogger logger, - IBasketRepository repository, - IIdentityService identityService, - IEventBus eventBus) - { - _logger = logger; - _repository = repository; - _identityService = identityService; - _eventBus = eventBus; - } + private readonly IBasketRepository _repository; + private readonly IIdentityService _identityService; + private readonly IEventBus _eventBus; + private readonly ILogger _logger; - [HttpGet("{id}")] - [ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)] - public async Task> GetBasketByIdAsync(string id) - { - var basket = await _repository.GetBasketAsync(id); - - return Ok(basket ?? new CustomerBasket(id)); - } + public BasketController( + ILogger logger, + IBasketRepository repository, + IIdentityService identityService, + IEventBus eventBus) + { + _logger = logger; + _repository = repository; + _identityService = identityService; + _eventBus = eventBus; + } - [HttpPost] - [ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)] - public async Task> UpdateBasketAsync([FromBody] CustomerBasket value) - { - return Ok(await _repository.UpdateBasketAsync(value)); - } + [HttpGet("{id}")] + [ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)] + public async Task> GetBasketByIdAsync(string id) + { + var basket = await _repository.GetBasketAsync(id); - [Route("checkout")] - [HttpPost] - [ProducesResponseType((int)HttpStatusCode.Accepted)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public async Task CheckoutAsync([FromBody] BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId) - { - var userId = _identityService.GetUserIdentity(); + return Ok(basket ?? new CustomerBasket(id)); + } - basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ? - guid : basketCheckout.RequestId; + [HttpPost] + [ProducesResponseType(typeof(CustomerBasket), (int)HttpStatusCode.OK)] + public async Task> UpdateBasketAsync([FromBody] CustomerBasket value) + { + return Ok(await _repository.UpdateBasketAsync(value)); + } - var basket = await _repository.GetBasketAsync(userId); + [Route("checkout")] + [HttpPost] + [ProducesResponseType((int)HttpStatusCode.Accepted)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public async Task CheckoutAsync([FromBody] BasketCheckout basketCheckout, [FromHeader(Name = "x-requestid")] string requestId) + { + var userId = _identityService.GetUserIdentity(); - if (basket == null) - { - return BadRequest(); - } + basketCheckout.RequestId = (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) ? + guid : basketCheckout.RequestId; - var userName = this.HttpContext.User.FindFirst(x => x.Type == ClaimTypes.Name).Value; + var basket = await _repository.GetBasketAsync(userId); - var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, userName, basketCheckout.City, basketCheckout.Street, - basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName, - basketCheckout.CardExpiration, basketCheckout.CardSecurityNumber, basketCheckout.CardTypeId, basketCheckout.Buyer, basketCheckout.RequestId, basket); + if (basket == null) + { + return BadRequest(); + } - // Once basket is checkout, sends an integration event to - // ordering.api to convert basket to order and proceeds with - // order creation process - try - { - _eventBus.Publish(eventMessage); - } - catch (Exception ex) - { - _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName); + var userName = this.HttpContext.User.FindFirst(x => x.Type == ClaimTypes.Name).Value; - throw; - } + var eventMessage = new UserCheckoutAcceptedIntegrationEvent(userId, userName, basketCheckout.City, basketCheckout.Street, + basketCheckout.State, basketCheckout.Country, basketCheckout.ZipCode, basketCheckout.CardNumber, basketCheckout.CardHolderName, + basketCheckout.CardExpiration, basketCheckout.CardSecurityNumber, basketCheckout.CardTypeId, basketCheckout.Buyer, basketCheckout.RequestId, basket); - return Accepted(); + // Once basket is checkout, sends an integration event to + // ordering.api to convert basket to order and proceeds with + // order creation process + try + { + _eventBus.Publish(eventMessage); } - - // DELETE api/values/5 - [HttpDelete("{id}")] - [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] - public async Task DeleteBasketByIdAsync(string id) + catch (Exception ex) { - await _repository.DeleteBasketAsync(id); + _logger.LogError(ex, "ERROR Publishing integration event: {IntegrationEventId} from {AppName}", eventMessage.Id, Program.AppName); + + throw; } + + return Accepted(); + } + + // DELETE api/values/5 + [HttpDelete("{id}")] + [ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)] + public async Task DeleteBasketByIdAsync(string id) + { + await _repository.DeleteBasketAsync(id); } } diff --git a/src/Services/Basket/Basket.API/Controllers/HomeController.cs b/src/Services/Basket/Basket.API/Controllers/HomeController.cs index d0c37a205..8b2b7c2e7 100644 --- a/src/Services/Basket/Basket.API/Controllers/HomeController.cs +++ b/src/Services/Basket/Basket.API/Controllers/HomeController.cs @@ -1,13 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers; -namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers +public class HomeController : Controller { - public class HomeController : Controller + // GET: // + public IActionResult Index() { - // GET: // - public IActionResult Index() - { - return new RedirectResult("~/swagger"); - } + return new RedirectResult("~/swagger"); } } + diff --git a/src/Services/Basket/Basket.API/Grpc/BasketService.cs b/src/Services/Basket/Basket.API/Grpc/BasketService.cs index 51633b326..1cc10693b 100644 --- a/src/Services/Basket/Basket.API/Grpc/BasketService.cs +++ b/src/Services/Basket/Basket.API/Grpc/BasketService.cs @@ -1,102 +1,93 @@ -using Grpc.Core; -using Microsoft.AspNetCore.Authorization; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; - -namespace GrpcBasket +namespace GrpcBasket; +public class BasketService : Basket.BasketBase { - public class BasketService : Basket.BasketBase + private readonly IBasketRepository _repository; + private readonly ILogger _logger; + + public BasketService(IBasketRepository repository, ILogger logger) + { + _repository = repository; + _logger = logger; + } + + [AllowAnonymous] + public override async Task GetBasketById(BasketRequest request, ServerCallContext context) { - private readonly IBasketRepository _repository; - private readonly ILogger _logger; + _logger.LogInformation("Begin grpc call from method {Method} for basket id {Id}", context.Method, request.Id); + + var data = await _repository.GetBasketAsync(request.Id); - public BasketService(IBasketRepository repository, ILogger logger) + if (data != null) { - _repository = repository; - _logger = logger; - } + context.Status = new Status(StatusCode.OK, $"Basket with id {request.Id} do exist"); - [AllowAnonymous] - public override async Task GetBasketById(BasketRequest request, ServerCallContext context) + return MapToCustomerBasketResponse(data); + } + else { - _logger.LogInformation("Begin grpc call from method {Method} for basket id {Id}", context.Method, request.Id); + context.Status = new Status(StatusCode.NotFound, $"Basket with id {request.Id} do not exist"); + } - var data = await _repository.GetBasketAsync(request.Id); + return new CustomerBasketResponse(); + } - if (data != null) - { - context.Status = new Status(StatusCode.OK, $"Basket with id {request.Id} do exist"); + public override async Task UpdateBasket(CustomerBasketRequest request, ServerCallContext context) + { + _logger.LogInformation("Begin grpc call BasketService.UpdateBasketAsync for buyer id {Buyerid}", request.Buyerid); - return MapToCustomerBasketResponse(data); - } - else - { - context.Status = new Status(StatusCode.NotFound, $"Basket with id {request.Id} do not exist"); - } + var customerBasket = MapToCustomerBasket(request); - return new CustomerBasketResponse(); - } + var response = await _repository.UpdateBasketAsync(customerBasket); - public override async Task UpdateBasket(CustomerBasketRequest request, ServerCallContext context) + if (response != null) { - _logger.LogInformation("Begin grpc call BasketService.UpdateBasketAsync for buyer id {Buyerid}", request.Buyerid); - - var customerBasket = MapToCustomerBasket(request); + return MapToCustomerBasketResponse(response); + } - var response = await _repository.UpdateBasketAsync(customerBasket); + context.Status = new Status(StatusCode.NotFound, $"Basket with buyer id {request.Buyerid} do not exist"); - if (response != null) - { - return MapToCustomerBasketResponse(response); - } + return null; + } - context.Status = new Status(StatusCode.NotFound, $"Basket with buyer id {request.Buyerid} do not exist"); + private CustomerBasketResponse MapToCustomerBasketResponse(CustomerBasket customerBasket) + { + var response = new CustomerBasketResponse + { + Buyerid = customerBasket.BuyerId + }; - return null; - } + customerBasket.Items.ForEach(item => response.Items.Add(new BasketItemResponse + { + Id = item.Id, + Oldunitprice = (double)item.OldUnitPrice, + Pictureurl = item.PictureUrl, + Productid = item.ProductId, + Productname = item.ProductName, + Quantity = item.Quantity, + Unitprice = (double)item.UnitPrice + })); + + return response; + } - private CustomerBasketResponse MapToCustomerBasketResponse(CustomerBasket customerBasket) + private CustomerBasket MapToCustomerBasket(CustomerBasketRequest customerBasketRequest) + { + var response = new CustomerBasket { - var response = new CustomerBasketResponse - { - Buyerid = customerBasket.BuyerId - }; - - customerBasket.Items.ForEach(item => response.Items.Add(new BasketItemResponse - { - Id = item.Id, - Oldunitprice = (double)item.OldUnitPrice, - Pictureurl = item.PictureUrl, - Productid = item.ProductId, - Productname = item.ProductName, - Quantity = item.Quantity, - Unitprice = (double)item.UnitPrice - })); - - return response; - } + BuyerId = customerBasketRequest.Buyerid + }; - private CustomerBasket MapToCustomerBasket(CustomerBasketRequest customerBasketRequest) + customerBasketRequest.Items.ToList().ForEach(item => response.Items.Add(new BasketItem { - var response = new CustomerBasket - { - BuyerId = customerBasketRequest.Buyerid - }; - - customerBasketRequest.Items.ToList().ForEach(item => response.Items.Add(new BasketItem - { - Id = item.Id, - OldUnitPrice = (decimal)item.Oldunitprice, - PictureUrl = item.Pictureurl, - ProductId = item.Productid, - ProductName = item.Productname, - Quantity = item.Quantity, - UnitPrice = (decimal)item.Unitprice - })); - - return response; - } + Id = item.Id, + OldUnitPrice = (decimal)item.Oldunitprice, + PictureUrl = item.Pictureurl, + ProductId = item.Productid, + ProductName = item.Productname, + Quantity = item.Quantity, + UnitPrice = (decimal)item.Unitprice + })); + + return response; } } diff --git a/src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs b/src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs index a0b988156..5f95e586e 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/ActionResults/InternalServerErrorObjectResult.cs @@ -1,14 +1,11 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; +namespace Basket.API.Infrastructure.ActionResults; -namespace Basket.API.Infrastructure.ActionResults +public class InternalServerErrorObjectResult : ObjectResult { - public class InternalServerErrorObjectResult : ObjectResult + public InternalServerErrorObjectResult(object error) + : base(error) { - public InternalServerErrorObjectResult(object error) - : base(error) - { - StatusCode = StatusCodes.Status500InternalServerError; - } + StatusCode = StatusCodes.Status500InternalServerError; } } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs b/src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs index e0f2df6fa..0502b7924 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Exceptions/BasketDomainException.cs @@ -1,21 +1,16 @@ -using System; +namespace Basket.API.Infrastructure.Exceptions; -namespace Basket.API.Infrastructure.Exceptions +public class BasketDomainException : Exception { - /// - /// Exception type for app exceptions - /// - public class BasketDomainException : Exception - { - public BasketDomainException() - { } + public BasketDomainException() + { } - public BasketDomainException(string message) - : base(message) - { } + public BasketDomainException(string message) + : base(message) + { } - public BasketDomainException(string message, Exception innerException) - : base(message, innerException) - { } - } + public BasketDomainException(string message, Exception innerException) + : base(message, innerException) + { } } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Exceptions/FailingMiddlewareAppBuilderExtensions.cs b/src/Services/Basket/Basket.API/Infrastructure/Exceptions/FailingMiddlewareAppBuilderExtensions.cs index a09f32f76..f60c6035a 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Exceptions/FailingMiddlewareAppBuilderExtensions.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Exceptions/FailingMiddlewareAppBuilderExtensions.cs @@ -1,20 +1,17 @@ -using Microsoft.AspNetCore.Builder; -using System; +namespace Basket.API.Infrastructure.Middlewares; -namespace Basket.API.Infrastructure.Middlewares +public static class FailingMiddlewareAppBuilderExtensions { - public static class FailingMiddlewareAppBuilderExtensions + public static IApplicationBuilder UseFailingMiddleware(this IApplicationBuilder builder) { - public static IApplicationBuilder UseFailingMiddleware(this IApplicationBuilder builder) - { - return UseFailingMiddleware(builder, null); - } - public static IApplicationBuilder UseFailingMiddleware(this IApplicationBuilder builder, Action action) - { - var options = new FailingOptions(); - action?.Invoke(options); - builder.UseMiddleware(options); - return builder; - } + return UseFailingMiddleware(builder, null); + } + public static IApplicationBuilder UseFailingMiddleware(this IApplicationBuilder builder, Action action) + { + var options = new FailingOptions(); + action?.Invoke(options); + builder.UseMiddleware(options); + return builder; } } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs index 0fd08a0a9..00b0b5195 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -1,58 +1,47 @@ -using Basket.API.Infrastructure.ActionResults; -using Basket.API.Infrastructure.Exceptions; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System.Net; +namespace Basket.API.Infrastructure.Filters; - -namespace Basket.API.Infrastructure.Filters +public partial class HttpGlobalExceptionFilter : IExceptionFilter { - public partial class HttpGlobalExceptionFilter : IExceptionFilter + private readonly IWebHostEnvironment env; + private readonly ILogger logger; + + public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) { - private readonly IWebHostEnvironment env; - private readonly ILogger logger; + this.env = env; + this.logger = logger; + } - public HttpGlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) - { - this.env = env; - this.logger = logger; - } + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); - public void OnException(ExceptionContext context) + if (context.Exception.GetType() == typeof(BasketDomainException)) { - logger.LogError(new EventId(context.Exception.HResult), - context.Exception, - context.Exception.Message); - - if (context.Exception.GetType() == typeof(BasketDomainException)) + var json = new JsonErrorResponse { - var json = new JsonErrorResponse - { - Messages = new[] { context.Exception.Message } - }; + Messages = new[] { context.Exception.Message } + }; - context.Result = new BadRequestObjectResult(json); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; - } - else + context.Result = new BadRequestObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse { - var json = new JsonErrorResponse - { - Messages = new[] { "An error occurred. Try it again." } - }; + Messages = new[] { "An error occurred. Try it again." } + }; - if (env.IsDevelopment()) - { - json.DeveloperMessage = context.Exception; - } - - context.Result = new InternalServerErrorObjectResult(json); - context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + if (env.IsDevelopment()) + { + json.DeveloperMessage = context.Exception; } - context.ExceptionHandled = true; + + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } + context.ExceptionHandled = true; } } diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs index bcadc7358..88bc02eda 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/JsonErrorResponse.cs @@ -1,9 +1,9 @@ -namespace Basket.API.Infrastructure.Filters +namespace Basket.API.Infrastructure.Filters; + +public class JsonErrorResponse { - public class JsonErrorResponse - { - public string[] Messages { get; set; } + public string[] Messages { get; set; } - public object DeveloperMessage { get; set; } - } + public object DeveloperMessage { get; set; } } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs index 3e4c3e072..5c97b85dc 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Filters/ValidateModelStateFilter.cs @@ -1,30 +1,26 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using System.Linq; +namespace Basket.API.Infrastructure.Filters; -namespace Basket.API.Infrastructure.Filters +public class ValidateModelStateFilter : ActionFilterAttribute { - public class ValidateModelStateFilter : ActionFilterAttribute + public override void OnActionExecuting(ActionExecutingContext context) { - public override void OnActionExecuting(ActionExecutingContext context) + if (context.ModelState.IsValid) { - if (context.ModelState.IsValid) - { - return; - } + return; + } - var validationErrors = context.ModelState - .Keys - .SelectMany(k => context.ModelState[k].Errors) - .Select(e => e.ErrorMessage) - .ToArray(); + var validationErrors = context.ModelState + .Keys + .SelectMany(k => context.ModelState[k].Errors) + .Select(e => e.ErrorMessage) + .ToArray(); - var json = new JsonErrorResponse - { - Messages = validationErrors - }; + var json = new JsonErrorResponse + { + Messages = validationErrors + }; - context.Result = new BadRequestObjectResult(json); - } + context.Result = new BadRequestObjectResult(json); } } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/AuthorizeCheckOperationFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/AuthorizeCheckOperationFilter.cs index 4a1c24aa6..0957614f9 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/AuthorizeCheckOperationFilter.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/AuthorizeCheckOperationFilter.cs @@ -1,36 +1,29 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System.Collections.Generic; -using System.Linq; +namespace Basket.API.Infrastructure.Filters; -namespace Basket.API.Infrastructure.Filters +public class AuthorizeCheckOperationFilter : IOperationFilter { - public class AuthorizeCheckOperationFilter : IOperationFilter + public void Apply(OpenApiOperation operation, OperationFilterContext context) { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - // Check for authorize attribute - var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || - context.MethodInfo.GetCustomAttributes(true).OfType().Any(); + // Check for authorize attribute + var hasAuthorize = context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType().Any() || + context.MethodInfo.GetCustomAttributes(true).OfType().Any(); - if (!hasAuthorize) return; + if (!hasAuthorize) return; - operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); - operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); + operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); + operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); - var oAuthScheme = new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }; + var oAuthScheme = new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } + }; - operation.Security = new List + operation.Security = new List + { + new OpenApiSecurityRequirement { - new OpenApiSecurityRequirement - { - [ oAuthScheme ] = new [] { "basketapi" } - } - }; - } + [ oAuthScheme ] = new [] { "basketapi" } + } + }; } -} \ No newline at end of file +} diff --git a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs index 0019a2049..c2a50467d 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingMiddleware.cs @@ -1,95 +1,88 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Threading.Tasks; +namespace Basket.API.Infrastructure.Middlewares; -namespace Basket.API.Infrastructure.Middlewares +public class FailingMiddleware { - public class FailingMiddleware + private readonly RequestDelegate _next; + private bool _mustFail; + private readonly FailingOptions _options; + private readonly Microsoft.Extensions.Logging.ILogger _logger; + + public FailingMiddleware(RequestDelegate next, Microsoft.Extensions.Logging.ILogger logger, FailingOptions options) { - private readonly RequestDelegate _next; - private bool _mustFail; - private readonly FailingOptions _options; - private readonly ILogger _logger; + _next = next; + _options = options; + _mustFail = false; + _logger = logger; + } - public FailingMiddleware(RequestDelegate next, ILogger logger, FailingOptions options) + public async Task Invoke(HttpContext context) + { + var path = context.Request.Path; + if (path.Equals(_options.ConfigPath, StringComparison.OrdinalIgnoreCase)) { - _next = next; - _options = options; - _mustFail = false; - _logger = logger; + await ProcessConfigRequest(context); + return; } - public async Task Invoke(HttpContext context) + if (MustFail(context)) { - var path = context.Request.Path; - if (path.Equals(_options.ConfigPath, StringComparison.OrdinalIgnoreCase)) - { - await ProcessConfigRequest(context); - return; - } - - if (MustFail(context)) - { - _logger.LogInformation("Response for path {Path} will fail.", path); - context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; - context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync("Failed due to FailingMiddleware enabled."); - } - else - { - await _next.Invoke(context); - } + _logger.LogInformation("Response for path {Path} will fail.", path); + context.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync("Failed due to FailingMiddleware enabled."); } - - private async Task ProcessConfigRequest(HttpContext context) + else { - var enable = context.Request.Query.Keys.Any(k => k == "enable"); - var disable = context.Request.Query.Keys.Any(k => k == "disable"); + await _next.Invoke(context); + } + } - if (enable && disable) - { - throw new ArgumentException("Must use enable or disable querystring values, but not both"); - } + private async Task ProcessConfigRequest(HttpContext context) + { + var enable = context.Request.Query.Keys.Any(k => k == "enable"); + var disable = context.Request.Query.Keys.Any(k => k == "disable"); - if (disable) - { - _mustFail = false; - await SendOkResponse(context, "FailingMiddleware disabled. Further requests will be processed."); - return; - } - if (enable) - { - _mustFail = true; - await SendOkResponse(context, "FailingMiddleware enabled. Further requests will return HTTP 500"); - return; - } + if (enable && disable) + { + throw new ArgumentException("Must use enable or disable querystring values, but not both"); + } - // If reach here, that means that no valid parameter has been passed. Just output status - await SendOkResponse(context, string.Format("FailingMiddleware is {0}", _mustFail ? "enabled" : "disabled")); + if (disable) + { + _mustFail = false; + await SendOkResponse(context, "FailingMiddleware disabled. Further requests will be processed."); return; } - - private async Task SendOkResponse(HttpContext context, string message) + if (enable) { - context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK; - context.Response.ContentType = "text/plain"; - await context.Response.WriteAsync(message); + _mustFail = true; + await SendOkResponse(context, "FailingMiddleware enabled. Further requests will return HTTP 500"); + return; } - private bool MustFail(HttpContext context) - { - var rpath = context.Request.Path.Value; + // If reach here, that means that no valid parameter has been passed. Just output status + await SendOkResponse(context, string.Format("FailingMiddleware is {0}", _mustFail ? "enabled" : "disabled")); + return; + } - if (_options.NotFilteredPaths.Any(p => p.Equals(rpath, StringComparison.InvariantCultureIgnoreCase))) - { - return false; - } + private async Task SendOkResponse(HttpContext context, string message) + { + context.Response.StatusCode = (int)System.Net.HttpStatusCode.OK; + context.Response.ContentType = "text/plain"; + await context.Response.WriteAsync(message); + } + + private bool MustFail(HttpContext context) + { + var rpath = context.Request.Path.Value; - return _mustFail && - (_options.EndpointPaths.Any(x => x == rpath) - || _options.EndpointPaths.Count == 0); + if (_options.NotFilteredPaths.Any(p => p.Equals(rpath, StringComparison.InvariantCultureIgnoreCase))) + { + return false; } + + return _mustFail && + (_options.EndpointPaths.Any(x => x == rpath) + || _options.EndpointPaths.Count == 0); } } diff --git a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingOptions.cs b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingOptions.cs index e8a77f1fd..7818938d2 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingOptions.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingOptions.cs @@ -1,12 +1,10 @@ -using System.Collections.Generic; +namespace Basket.API.Infrastructure.Middlewares; -namespace Basket.API.Infrastructure.Middlewares +public class FailingOptions { - public class FailingOptions - { - public string ConfigPath = "/Failing"; - public List EndpointPaths { get; set; } = new List(); + public string ConfigPath = "/Failing"; + public List EndpointPaths { get; set; } = new List(); - public List NotFilteredPaths { get; set; } = new List(); - } + public List NotFilteredPaths { get; set; } = new List(); } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingStartupFilter.cs b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingStartupFilter.cs index 7d3b2ce18..74da62b5d 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingStartupFilter.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingStartupFilter.cs @@ -1,24 +1,20 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using System; +namespace Basket.API.Infrastructure.Middlewares; -namespace Basket.API.Infrastructure.Middlewares +public class FailingStartupFilter : IStartupFilter { - public class FailingStartupFilter : IStartupFilter + private readonly Action _options; + public FailingStartupFilter(Action optionsAction) { - private readonly Action _options; - public FailingStartupFilter(Action optionsAction) - { - _options = optionsAction; - } + _options = optionsAction; + } - public Action Configure(Action next) + public Action Configure(Action next) + { + return app => { - return app => - { - app.UseFailingMiddleware(_options); - next(app); - }; - } + app.UseFailingMiddleware(_options); + next(app); + }; } } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingWebHostBuilderExtensions.cs b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingWebHostBuilderExtensions.cs index 99be1b182..8a8ba9523 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingWebHostBuilderExtensions.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Middlewares/FailingWebHostBuilderExtensions.cs @@ -1,18 +1,14 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using System; +namespace Basket.API.Infrastructure.Middlewares; -namespace Basket.API.Infrastructure.Middlewares +public static class WebHostBuildertExtensions { - public static class WebHostBuildertExtensions + public static IWebHostBuilder UseFailing(this IWebHostBuilder builder, Action options) { - public static IWebHostBuilder UseFailing(this IWebHostBuilder builder, Action options) + builder.ConfigureServices(services => { - builder.ConfigureServices(services => - { - services.AddSingleton(new FailingStartupFilter(options)); - }); - return builder; - } + services.AddSingleton(new FailingStartupFilter(options)); + }); + return builder; } } + diff --git a/src/Services/Basket/Basket.API/Infrastructure/Repositories/RedisBasketRepository.cs b/src/Services/Basket/Basket.API/Infrastructure/Repositories/RedisBasketRepository.cs index 6e724d7ca..d04d794b1 100644 --- a/src/Services/Basket/Basket.API/Infrastructure/Repositories/RedisBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Infrastructure/Repositories/RedisBasketRepository.cs @@ -1,73 +1,63 @@ -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.Extensions.Logging; -using StackExchange.Redis; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Text.Json; - -namespace Microsoft.eShopOnContainers.Services.Basket.API.Infrastructure.Repositories +namespace Microsoft.eShopOnContainers.Services.Basket.API.Infrastructure.Repositories; +public class RedisBasketRepository : IBasketRepository { - public class RedisBasketRepository : IBasketRepository + private readonly ILogger _logger; + private readonly ConnectionMultiplexer _redis; + private readonly IDatabase _database; + + public RedisBasketRepository(ILoggerFactory loggerFactory, ConnectionMultiplexer redis) { - private readonly ILogger _logger; - private readonly ConnectionMultiplexer _redis; - private readonly IDatabase _database; + _logger = loggerFactory.CreateLogger(); + _redis = redis; + _database = redis.GetDatabase(); + } - public RedisBasketRepository(ILoggerFactory loggerFactory, ConnectionMultiplexer redis) - { - _logger = loggerFactory.CreateLogger(); - _redis = redis; - _database = redis.GetDatabase(); - } + public async Task DeleteBasketAsync(string id) + { + return await _database.KeyDeleteAsync(id); + } - public async Task DeleteBasketAsync(string id) - { - return await _database.KeyDeleteAsync(id); - } + public IEnumerable GetUsers() + { + var server = GetServer(); + var data = server.Keys(); - public IEnumerable GetUsers() - { - var server = GetServer(); - var data = server.Keys(); + return data?.Select(k => k.ToString()); + } - return data?.Select(k => k.ToString()); - } + public async Task GetBasketAsync(string customerId) + { + var data = await _database.StringGetAsync(customerId); - public async Task GetBasketAsync(string customerId) + if (data.IsNullOrEmpty) { - var data = await _database.StringGetAsync(customerId); - - if (data.IsNullOrEmpty) - { - return null; - } - - return JsonSerializer.Deserialize(data, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + return null; } - public async Task UpdateBasketAsync(CustomerBasket basket) + return JsonSerializer.Deserialize(data, new JsonSerializerOptions { - var created = await _database.StringSetAsync(basket.BuyerId, JsonSerializer.Serialize(basket)); - - if (!created) - { - _logger.LogInformation("Problem occur persisting the item."); - return null; - } - - _logger.LogInformation("Basket item persisted succesfully."); + PropertyNameCaseInsensitive = true + }); + } - return await GetBasketAsync(basket.BuyerId); - } + public async Task UpdateBasketAsync(CustomerBasket basket) + { + var created = await _database.StringSetAsync(basket.BuyerId, JsonSerializer.Serialize(basket)); - private IServer GetServer() + if (!created) { - var endpoint = _redis.GetEndPoints(); - return _redis.GetServer(endpoint.First()); + _logger.LogInformation("Problem occur persisting the item."); + return null; } + + _logger.LogInformation("Basket item persisted succesfully."); + + return await GetBasketAsync(basket.BuyerId); + } + + private IServer GetServer() + { + var endpoint = _redis.GetEndPoints(); + return _redis.GetServer(endpoint.First()); } } diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs index fb2c10e9e..2c93f82fd 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/OrderStartedIntegrationEventHandler.cs @@ -1,37 +1,29 @@ -using Basket.API.IntegrationEvents.Events; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.Extensions.Logging; -using Serilog.Context; -using System; -using System.Threading.Tasks; - -namespace Basket.API.IntegrationEvents.EventHandling +namespace Basket.API.IntegrationEvents.EventHandling; + +public class OrderStartedIntegrationEventHandler : IIntegrationEventHandler { - public class OrderStartedIntegrationEventHandler : IIntegrationEventHandler - { - private readonly IBasketRepository _repository; - private readonly ILogger _logger; + private readonly IBasketRepository _repository; + private readonly ILogger _logger; - public OrderStartedIntegrationEventHandler( - IBasketRepository repository, - ILogger logger) - { - _repository = repository ?? throw new ArgumentNullException(nameof(repository)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } + public OrderStartedIntegrationEventHandler( + IBasketRepository repository, + ILogger logger) + { + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } - public async Task Handle(OrderStartedIntegrationEvent @event) + public async Task Handle(OrderStartedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - await _repository.DeleteBasketAsync(@event.UserId.ToString()); - } + await _repository.DeleteBasketAsync(@event.UserId.ToString()); } } } + diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs index 6068bdda6..b389b73d7 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/EventHandling/ProductPriceChangedIntegrationEventHandler.cs @@ -1,64 +1,53 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.Extensions.Logging; -using Serilog.Context; -using System; -using System.Linq; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling; -namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling +public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler { - public class ProductPriceChangedIntegrationEventHandler : IIntegrationEventHandler - { - private readonly ILogger _logger; - private readonly IBasketRepository _repository; + private readonly ILogger _logger; + private readonly IBasketRepository _repository; - public ProductPriceChangedIntegrationEventHandler( - ILogger logger, - IBasketRepository repository) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _repository = repository ?? throw new ArgumentNullException(nameof(repository)); - } + public ProductPriceChangedIntegrationEventHandler( + ILogger logger, + IBasketRepository repository) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + } - public async Task Handle(ProductPriceChangedIntegrationEvent @event) + public async Task Handle(ProductPriceChangedIntegrationEvent @event) + { + using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) { - using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}")) - { - _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); + _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event); - var userIds = _repository.GetUsers(); + var userIds = _repository.GetUsers(); - foreach (var id in userIds) - { - var basket = await _repository.GetBasketAsync(id); + foreach (var id in userIds) + { + var basket = await _repository.GetBasketAsync(id); - await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket); - } + await UpdatePriceInBasketItems(@event.ProductId, @event.NewPrice, @event.OldPrice, basket); } } + } - private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, decimal oldPrice, CustomerBasket basket) + private async Task UpdatePriceInBasketItems(int productId, decimal newPrice, decimal oldPrice, CustomerBasket basket) + { + var itemsToUpdate = basket?.Items?.Where(x => x.ProductId == productId).ToList(); + + if (itemsToUpdate != null) { - var itemsToUpdate = basket?.Items?.Where(x => x.ProductId == productId).ToList(); + _logger.LogInformation("----- ProductPriceChangedIntegrationEventHandler - Updating items in basket for user: {BuyerId} ({@Items})", basket.BuyerId, itemsToUpdate); - if (itemsToUpdate != null) + foreach (var item in itemsToUpdate) { - _logger.LogInformation("----- ProductPriceChangedIntegrationEventHandler - Updating items in basket for user: {BuyerId} ({@Items})", basket.BuyerId, itemsToUpdate); - - foreach (var item in itemsToUpdate) + if (item.UnitPrice == oldPrice) { - if (item.UnitPrice == oldPrice) - { - var originalPrice = item.UnitPrice; - item.UnitPrice = newPrice; - item.OldUnitPrice = originalPrice; - } + var originalPrice = item.UnitPrice; + item.UnitPrice = newPrice; + item.OldUnitPrice = originalPrice; } - await _repository.UpdateBasketAsync(basket); } + await _repository.UpdateBasketAsync(basket); } } } - diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs b/src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs index e0567b54e..2f786fecf 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs @@ -1,15 +1,13 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Basket.API.IntegrationEvents.Events; -namespace Basket.API.IntegrationEvents.Events +// Integration Events notes: +// An Event is “something that has happened in the past”, therefore its name has to be +// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. +public record OrderStartedIntegrationEvent : IntegrationEvent { - // Integration Events notes: - // An Event is “something that has happened in the past”, therefore its name has to be - // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. - public record OrderStartedIntegrationEvent : IntegrationEvent - { - public string UserId { get; init; } + public string UserId { get; init; } - public OrderStartedIntegrationEvent(string userId) - => UserId = userId; - } + public OrderStartedIntegrationEvent(string userId) + => UserId = userId; } + diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs b/src/Services/Basket/Basket.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs index c69fe0f72..ab79e6534 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs @@ -1,23 +1,21 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events; -namespace Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events +// Integration Events notes: +// An Event is “something that has happened in the past”, therefore its name has to be +// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. +public record ProductPriceChangedIntegrationEvent : IntegrationEvent { - // Integration Events notes: - // An Event is “something that has happened in the past”, therefore its name has to be - // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. - public record ProductPriceChangedIntegrationEvent : IntegrationEvent - { - public int ProductId { get; private init; } + public int ProductId { get; private init; } - public decimal NewPrice { get; private init; } + public decimal NewPrice { get; private init; } - public decimal OldPrice { get; private init; } + public decimal OldPrice { get; private init; } - public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice) - { - ProductId = productId; - NewPrice = newPrice; - OldPrice = oldPrice; - } + public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice, decimal oldPrice) + { + ProductId = productId; + NewPrice = newPrice; + OldPrice = oldPrice; } } + diff --git a/src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs b/src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs index e79e0fbdd..d57a32c25 100644 --- a/src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs +++ b/src/Services/Basket/Basket.API/IntegrationEvents/Events/UserCheckoutAcceptedIntegrationEvent.cs @@ -1,64 +1,59 @@ -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using System; +namespace Basket.API.IntegrationEvents.Events; -namespace Basket.API.IntegrationEvents.Events +public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent { - public record UserCheckoutAcceptedIntegrationEvent : IntegrationEvent - { - public string UserId { get; } - - public string UserName { get; } + public string UserId { get; } - public int OrderNumber { get; init; } + public string UserName { get; } - public string City { get; init; } + public int OrderNumber { get; init; } - public string Street { get; init; } + public string City { get; init; } - public string State { get; init; } + public string Street { get; init; } - public string Country { get; init; } + public string State { get; init; } - public string ZipCode { get; init; } + public string Country { get; init; } - public string CardNumber { get; init; } + public string ZipCode { get; init; } - public string CardHolderName { get; init; } + public string CardNumber { get; init; } - public DateTime CardExpiration { get; init; } + public string CardHolderName { get; init; } - public string CardSecurityNumber { get; init; } + public DateTime CardExpiration { get; init; } - public int CardTypeId { get; init; } + public string CardSecurityNumber { get; init; } - public string Buyer { get; init; } + public int CardTypeId { get; init; } - public Guid RequestId { get; init; } + public string Buyer { get; init; } - public CustomerBasket Basket { get; } + public Guid RequestId { get; init; } - public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street, - string state, string country, string zipCode, string cardNumber, string cardHolderName, - DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId, - CustomerBasket basket) - { - UserId = userId; - UserName = userName; - City = city; - Street = street; - State = state; - Country = country; - ZipCode = zipCode; - CardNumber = cardNumber; - CardHolderName = cardHolderName; - CardExpiration = cardExpiration; - CardSecurityNumber = cardSecurityNumber; - CardTypeId = cardTypeId; - Buyer = buyer; - Basket = basket; - RequestId = requestId; - } + public CustomerBasket Basket { get; } + public UserCheckoutAcceptedIntegrationEvent(string userId, string userName, string city, string street, + string state, string country, string zipCode, string cardNumber, string cardHolderName, + DateTime cardExpiration, string cardSecurityNumber, int cardTypeId, string buyer, Guid requestId, + CustomerBasket basket) + { + UserId = userId; + UserName = userName; + City = city; + Street = street; + State = state; + Country = country; + ZipCode = zipCode; + CardNumber = cardNumber; + CardHolderName = cardHolderName; + CardExpiration = cardExpiration; + CardSecurityNumber = cardSecurityNumber; + CardTypeId = cardTypeId; + Buyer = buyer; + Basket = basket; + RequestId = requestId; } + } diff --git a/src/Services/Basket/Basket.API/Model/BasketCheckout.cs b/src/Services/Basket/Basket.API/Model/BasketCheckout.cs index 410700773..358e3a192 100644 --- a/src/Services/Basket/Basket.API/Model/BasketCheckout.cs +++ b/src/Services/Basket/Basket.API/Model/BasketCheckout.cs @@ -1,32 +1,27 @@ -using System; - -namespace Basket.API.Model +namespace Basket.API.Model; +public class BasketCheckout { - public class BasketCheckout - { - public string City { get; set; } + public string City { get; set; } - public string Street { get; set; } + public string Street { get; set; } - public string State { get; set; } + public string State { get; set; } - public string Country { get; set; } + public string Country { get; set; } - public string ZipCode { get; set; } + public string ZipCode { get; set; } - public string CardNumber { get; set; } + public string CardNumber { get; set; } - public string CardHolderName { get; set; } + public string CardHolderName { get; set; } - public DateTime CardExpiration { get; set; } + public DateTime CardExpiration { get; set; } - public string CardSecurityNumber { get; set; } + public string CardSecurityNumber { get; set; } - public int CardTypeId { get; set; } + public int CardTypeId { get; set; } - public string Buyer { get; set; } + public string Buyer { get; set; } - public Guid RequestId { get; set; } - } + 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 f781d5a60..e2b594ac9 100644 --- a/src/Services/Basket/Basket.API/Model/BasketItem.cs +++ b/src/Services/Basket/Basket.API/Model/BasketItem.cs @@ -1,27 +1,23 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +namespace Microsoft.eShopOnContainers.Services.Basket.API.Model; -namespace Microsoft.eShopOnContainers.Services.Basket.API.Model +public class BasketItem : IValidatableObject { - public class BasketItem : IValidatableObject + public string Id { get; set; } + public int ProductId { get; set; } + public string ProductName { get; set; } + public decimal UnitPrice { get; set; } + public decimal OldUnitPrice { get; set; } + public int Quantity { get; set; } + public string PictureUrl { get; set; } + public IEnumerable Validate(ValidationContext validationContext) { - public string Id { get; set; } - public int ProductId { get; set; } - public string ProductName { get; set; } - public decimal UnitPrice { get; set; } - public decimal OldUnitPrice { get; set; } - public int Quantity { get; set; } - public string PictureUrl { get; set; } - public IEnumerable Validate(ValidationContext validationContext) - { - var results = new List(); - - if (Quantity < 1) - { - results.Add(new ValidationResult("Invalid number of units", new[] { "Quantity" })); - } + var results = new List(); - return results; + if (Quantity < 1) + { + results.Add(new ValidationResult("Invalid number of units", new[] { "Quantity" })); } + + return results; } } diff --git a/src/Services/Basket/Basket.API/Model/CustomerBasket.cs b/src/Services/Basket/Basket.API/Model/CustomerBasket.cs index 9ae495d4f..37b975f7b 100644 --- a/src/Services/Basket/Basket.API/Model/CustomerBasket.cs +++ b/src/Services/Basket/Basket.API/Model/CustomerBasket.cs @@ -1,21 +1,18 @@ -using System.Collections.Generic; - -namespace Microsoft.eShopOnContainers.Services.Basket.API.Model +namespace Microsoft.eShopOnContainers.Services.Basket.API.Model; +public class CustomerBasket { - public class CustomerBasket - { - public string BuyerId { get; set; } + public string BuyerId { get; set; } - public List Items { get; set; } = new List(); + public List Items { get; set; } = new List(); - public CustomerBasket() - { + public CustomerBasket() + { - } + } - public CustomerBasket(string customerId) - { - BuyerId = customerId; - } + public CustomerBasket(string customerId) + { + BuyerId = customerId; } } + diff --git a/src/Services/Basket/Basket.API/Model/IBasketRepository.cs b/src/Services/Basket/Basket.API/Model/IBasketRepository.cs index 850b5b637..6081919fa 100644 --- a/src/Services/Basket/Basket.API/Model/IBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Model/IBasketRepository.cs @@ -1,13 +1,10 @@ -using System.Collections.Generic; -using System.Threading.Tasks; +namespace Microsoft.eShopOnContainers.Services.Basket.API.Model; -namespace Microsoft.eShopOnContainers.Services.Basket.API.Model +public interface IBasketRepository { - public interface IBasketRepository - { - Task GetBasketAsync(string customerId); - IEnumerable GetUsers(); - Task UpdateBasketAsync(CustomerBasket basket); - Task DeleteBasketAsync(string id); - } + Task GetBasketAsync(string customerId); + IEnumerable GetUsers(); + Task UpdateBasketAsync(CustomerBasket basket); + Task DeleteBasketAsync(string id); } + diff --git a/src/Services/Basket/Basket.API/Program.cs b/src/Services/Basket/Basket.API/Program.cs index 040b115b5..ce09dfce1 100644 --- a/src/Services/Basket/Basket.API/Program.cs +++ b/src/Services/Basket/Basket.API/Program.cs @@ -1,18 +1,4 @@ -using Basket.API.Infrastructure.Middlewares; -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 Azure.Identity; -using Serilog; -using System; -using System.IO; -using System.Net; -using Azure.Core; - -var configuration = GetConfiguration(); +var configuration = GetConfiguration(); Log.Logger = CreateSerilogLogger(configuration); diff --git a/src/Services/Basket/Basket.API/Services/IIdentityService.cs b/src/Services/Basket/Basket.API/Services/IIdentityService.cs index fe84e23d5..2e4d395e9 100644 --- a/src/Services/Basket/Basket.API/Services/IIdentityService.cs +++ b/src/Services/Basket/Basket.API/Services/IIdentityService.cs @@ -1,7 +1,7 @@ -namespace Microsoft.eShopOnContainers.Services.Basket.API.Services +namespace Microsoft.eShopOnContainers.Services.Basket.API.Services; + +public interface IIdentityService { - public interface IIdentityService - { - string GetUserIdentity(); - } + string GetUserIdentity(); } + diff --git a/src/Services/Basket/Basket.API/Services/IdentityService.cs b/src/Services/Basket/Basket.API/Services/IdentityService.cs index bc6b2faf4..9dded4ea3 100644 --- a/src/Services/Basket/Basket.API/Services/IdentityService.cs +++ b/src/Services/Basket/Basket.API/Services/IdentityService.cs @@ -1,21 +1,17 @@ - -using Microsoft.AspNetCore.Http; -using System; +namespace Microsoft.eShopOnContainers.Services.Basket.API.Services; -namespace Microsoft.eShopOnContainers.Services.Basket.API.Services +public class IdentityService : IIdentityService { - public class IdentityService : IIdentityService - { - private IHttpContextAccessor _context; + private IHttpContextAccessor _context; - public IdentityService(IHttpContextAccessor context) - { - _context = context ?? throw new ArgumentNullException(nameof(context)); - } + public IdentityService(IHttpContextAccessor context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } - public string GetUserIdentity() - { - return _context.HttpContext.User.FindFirst("sub").Value; - } + public string GetUserIdentity() + { + return _context.HttpContext.User.FindFirst("sub").Value; } } + diff --git a/src/Services/Basket/Basket.API/Startup.cs b/src/Services/Basket/Basket.API/Startup.cs index 45adb5f66..b55845277 100644 --- a/src/Services/Basket/Basket.API/Startup.cs +++ b/src/Services/Basket/Basket.API/Startup.cs @@ -1,363 +1,325 @@ -using Autofac; -using Autofac.Extensions.DependencyInjection; -using Basket.API.Infrastructure.Filters; -using Basket.API.IntegrationEvents.EventHandling; -using Basket.API.IntegrationEvents.Events; -using GrpcBasket; -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Azure.ServiceBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; -using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus; -using Microsoft.eShopOnContainers.Services.Basket.API.Controllers; -using Microsoft.eShopOnContainers.Services.Basket.API.Infrastructure.Repositories; -using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.EventHandling; -using Microsoft.eShopOnContainers.Services.Basket.API.IntegrationEvents.Events; -using Microsoft.eShopOnContainers.Services.Basket.API.Model; -using Microsoft.eShopOnContainers.Services.Basket.API.Services; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using RabbitMQ.Client; -using StackExchange.Redis; -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.IO; - -namespace Microsoft.eShopOnContainers.Services.Basket.API +namespace Microsoft.eShopOnContainers.Services.Basket.API; + +public class Startup { - public class Startup + public Startup(IConfiguration configuration) { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } + Configuration = configuration; + } - public IConfiguration Configuration { get; } + public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public virtual IServiceProvider ConfigureServices(IServiceCollection services) + // This method gets called by the runtime. Use this method to add services to the container. + public virtual IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddGrpc(options => { - services.AddGrpc(options => - { - options.EnableDetailedErrors = true; - }); + options.EnableDetailedErrors = true; + }); - RegisterAppInsights(services); + RegisterAppInsights(services); - services.AddControllers(options => - { - options.Filters.Add(typeof(HttpGlobalExceptionFilter)); - options.Filters.Add(typeof(ValidateModelStateFilter)); + services.AddControllers(options => + { + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + options.Filters.Add(typeof(ValidateModelStateFilter)); - }) // Added for functional tests - .AddApplicationPart(typeof(BasketController).Assembly) - .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); + }) // Added for functional tests + .AddApplicationPart(typeof(BasketController).Assembly) + .AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true); - services.AddSwaggerGen(options => + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo { - options.DescribeAllEnumsAsStrings(); - options.SwaggerDoc("v1", new OpenApiInfo - { - Title = "eShopOnContainers - Basket HTTP API", - Version = "v1", - Description = "The Basket Service HTTP API" - }); + Title = "eShopOnContainers - Basket HTTP API", + Version = "v1", + Description = "The Basket Service HTTP API" + }); - options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OAuth2, + Flows = new OpenApiOAuthFlows() { - Type = SecuritySchemeType.OAuth2, - Flows = new OpenApiOAuthFlows() + Implicit = new OpenApiOAuthFlow() { - Implicit = new OpenApiOAuthFlow() + AuthorizationUrl = new Uri($"{Configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), + TokenUrl = new Uri($"{Configuration.GetValue("IdentityUrlExternal")}/connect/token"), + Scopes = new Dictionary() { - AuthorizationUrl = new Uri($"{Configuration.GetValue("IdentityUrlExternal")}/connect/authorize"), - TokenUrl = new Uri($"{Configuration.GetValue("IdentityUrlExternal")}/connect/token"), - Scopes = new Dictionary() - { - { "basket", "Basket API" } - } + { "basket", "Basket API" } } } - }); - - options.OperationFilter(); + } }); - ConfigureAuthService(services); + options.OperationFilter(); + }); - services.AddCustomHealthCheck(Configuration); + ConfigureAuthService(services); - services.Configure(Configuration); + services.AddCustomHealthCheck(Configuration); - //By connecting here we are making sure that our service - //cannot start until redis is ready. This might slow down startup, - //but given that there is a delay on resolving the ip address - //and then creating the connection it seems reasonable to move - //that cost to startup instead of having the first request pay the - //penalty. - services.AddSingleton(sp => - { - var settings = sp.GetRequiredService>().Value; - var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true); - - configuration.ResolveDns = true; + services.Configure(Configuration); - return ConnectionMultiplexer.Connect(configuration); - }); + //By connecting here we are making sure that our service + //cannot start until redis is ready. This might slow down startup, + //but given that there is a delay on resolving the ip address + //and then creating the connection it seems reasonable to move + //that cost to startup instead of having the first request pay the + //penalty. + services.AddSingleton(sp => + { + var settings = sp.GetRequiredService>().Value; + var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true); + configuration.ResolveDns = true; - if (Configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusConnectionString = Configuration["EventBusConnection"]; - var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - - var subscriptionClientName = Configuration["SubscriptionClientName"]; - return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); - }); - } - else - { - services.AddSingleton(sp => - { - var logger = sp.GetRequiredService>(); + return ConnectionMultiplexer.Connect(configuration); + }); - var factory = new ConnectionFactory() - { - HostName = Configuration["EventBusConnection"], - DispatchConsumersAsync = true - }; - if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) - { - factory.UserName = Configuration["EventBusUserName"]; - } + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => + { + var serviceBusConnectionString = Configuration["EventBusConnection"]; + var serviceBusConnection = new ServiceBusConnectionStringBuilder(serviceBusConnectionString); - if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) - { - factory.Password = Configuration["EventBusPassword"]; - } + var subscriptionClientName = Configuration["SubscriptionClientName"]; + return new DefaultServiceBusPersisterConnection(serviceBusConnection, subscriptionClientName); + }); + } + else + { + services.AddSingleton(sp => + { + var logger = sp.GetRequiredService>(); - var retryCount = 5; - if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(Configuration["EventBusRetryCount"]); - } + var factory = new ConnectionFactory() + { + HostName = Configuration["EventBusConnection"], + DispatchConsumersAsync = true + }; - return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); - }); - } + if (!string.IsNullOrEmpty(Configuration["EventBusUserName"])) + { + factory.UserName = Configuration["EventBusUserName"]; + } - RegisterEventBus(services); + if (!string.IsNullOrEmpty(Configuration["EventBusPassword"])) + { + factory.Password = Configuration["EventBusPassword"]; + } + var retryCount = 5; + if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) + { + retryCount = int.Parse(Configuration["EventBusRetryCount"]); + } - services.AddCors(options => - { - options.AddPolicy("CorsPolicy", - builder => builder - .SetIsOriginAllowed((host) => true) - .AllowAnyMethod() - .AllowAnyHeader() - .AllowCredentials()); + return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount); }); - services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); + } - services.AddOptions(); + RegisterEventBus(services); - var container = new ContainerBuilder(); - container.Populate(services); - return new AutofacServiceProvider(container.Build()); - } + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder + .SetIsOriginAllowed((host) => true) + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); + + services.AddOptions(); + + var container = new ContainerBuilder(); + container.Populate(services); + + return new AutofacServiceProvider(container.Build()); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + { + //loggerFactory.AddAzureWebAppDiagnostics(); + //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + var pathBase = Configuration["PATH_BASE"]; + if (!string.IsNullOrEmpty(pathBase)) { - //loggerFactory.AddAzureWebAppDiagnostics(); - //loggerFactory.AddApplicationInsights(app.ApplicationServices, LogLevel.Trace); + app.UsePathBase(pathBase); + } - var pathBase = Configuration["PATH_BASE"]; - if (!string.IsNullOrEmpty(pathBase)) + app.UseSwagger() + .UseSwaggerUI(setup => { - app.UsePathBase(pathBase); - } - - app.UseSwagger() - .UseSwaggerUI(setup => - { - setup.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Basket.API V1"); - setup.OAuthClientId("basketswaggerui"); - setup.OAuthAppName("Basket Swagger UI"); - }); + setup.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Basket.API V1"); + setup.OAuthClientId("basketswaggerui"); + setup.OAuthAppName("Basket Swagger UI"); + }); - app.UseRouting(); - app.UseCors("CorsPolicy"); - ConfigureAuth(app); + app.UseRouting(); + app.UseCors("CorsPolicy"); + ConfigureAuth(app); - app.UseStaticFiles(); + app.UseStaticFiles(); - app.UseEndpoints(endpoints => + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + endpoints.MapDefaultControllerRoute(); + endpoints.MapControllers(); + endpoints.MapGet("/_proto/", async ctx => { - endpoints.MapGrpcService(); - endpoints.MapDefaultControllerRoute(); - endpoints.MapControllers(); - endpoints.MapGet("/_proto/", async ctx => + ctx.Response.ContentType = "text/plain"; + using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read); + using var sr = new StreamReader(fs); + while (!sr.EndOfStream) { - ctx.Response.ContentType = "text/plain"; - using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Proto", "basket.proto"), FileMode.Open, FileAccess.Read); - using var sr = new StreamReader(fs); - while (!sr.EndOfStream) + var line = await sr.ReadLineAsync(); + if (line != "/* >>" || line != "<< */") { - var line = await sr.ReadLineAsync(); - if (line != "/* >>" || line != "<< */") - { - await ctx.Response.WriteAsync(line); - } + await ctx.Response.WriteAsync(line); } - }); - endpoints.MapHealthChecks("/hc", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse - }); - endpoints.MapHealthChecks("/liveness", new HealthCheckOptions - { - Predicate = r => r.Name.Contains("self") - }); + } + }); + endpoints.MapHealthChecks("/hc", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse + }); + endpoints.MapHealthChecks("/liveness", new HealthCheckOptions + { + Predicate = r => r.Name.Contains("self") }); + }); - ConfigureEventBus(app); - } + ConfigureEventBus(app); + } + + private void RegisterAppInsights(IServiceCollection services) + { + services.AddApplicationInsightsTelemetry(Configuration); + services.AddApplicationInsightsKubernetesEnricher(); + } - private void RegisterAppInsights(IServiceCollection services) + private void ConfigureAuthService(IServiceCollection services) + { + // prevent from mapping "sub" claim to nameidentifier. + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + + var identityUrl = Configuration.GetValue("IdentityUrl"); + + services.AddAuthentication(options => { - services.AddApplicationInsightsTelemetry(Configuration); - services.AddApplicationInsightsKubernetesEnricher(); - } + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - private void ConfigureAuthService(IServiceCollection services) + }).AddJwtBearer(options => { - // prevent from mapping "sub" claim to nameidentifier. - JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); + options.Authority = identityUrl; + options.RequireHttpsMetadata = false; + options.Audience = "basket"; + }); + } - var identityUrl = Configuration.GetValue("IdentityUrl"); + protected virtual void ConfigureAuth(IApplicationBuilder app) + { + app.UseAuthentication(); + app.UseAuthorization(); + } - services.AddAuthentication(options => + private void RegisterEventBus(IServiceCollection services) + { + if (Configuration.GetValue("AzureServiceBusEnabled")) + { + services.AddSingleton(sp => { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + var serviceBusPersisterConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); - }).AddJwtBearer(options => - { - options.Authority = identityUrl; - options.RequireHttpsMetadata = false; - options.Audience = "basket"; + return new EventBusServiceBus(serviceBusPersisterConnection, logger, + eventBusSubcriptionsManager, iLifetimeScope); }); } - - protected virtual void ConfigureAuth(IApplicationBuilder app) + else { - app.UseAuthentication(); - app.UseAuthorization(); - } - - private void RegisterEventBus(IServiceCollection services) - { - if (Configuration.GetValue("AzureServiceBusEnabled")) - { - services.AddSingleton(sp => - { - var serviceBusPersisterConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - return new EventBusServiceBus(serviceBusPersisterConnection, logger, - eventBusSubcriptionsManager, iLifetimeScope); - }); - } - else + services.AddSingleton(sp => { - services.AddSingleton(sp => + var subscriptionClientName = Configuration["SubscriptionClientName"]; + var rabbitMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var retryCount = 5; + if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) { - var subscriptionClientName = Configuration["SubscriptionClientName"]; - var rabbitMQPersistentConnection = sp.GetRequiredService(); - var iLifetimeScope = sp.GetRequiredService(); - var logger = sp.GetRequiredService>(); - var eventBusSubcriptionsManager = sp.GetRequiredService(); - - var retryCount = 5; - if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"])) - { - retryCount = int.Parse(Configuration["EventBusRetryCount"]); - } + retryCount = int.Parse(Configuration["EventBusRetryCount"]); + } - return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); - }); - } + return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount); + }); + } - services.AddSingleton(); + services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); - } + services.AddTransient(); + services.AddTransient(); + } - private void ConfigureEventBus(IApplicationBuilder app) - { - var eventBus = app.ApplicationServices.GetRequiredService(); + private void ConfigureEventBus(IApplicationBuilder app) + { + var eventBus = app.ApplicationServices.GetRequiredService(); - eventBus.Subscribe(); - eventBus.Subscribe(); - } + eventBus.Subscribe(); + eventBus.Subscribe(); } +} - public static class CustomExtensionMethods +public static class CustomExtensionMethods +{ + public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) { - public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) - { - var hcBuilder = services.AddHealthChecks(); + var hcBuilder = services.AddHealthChecks(); - hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); + hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy()); - hcBuilder - .AddRedis( - configuration["ConnectionString"], - name: "redis-check", - tags: new string[] { "redis" }); + hcBuilder + .AddRedis( + configuration["ConnectionString"], + name: "redis-check", + tags: new string[] { "redis" }); - if (configuration.GetValue("AzureServiceBusEnabled")) - { - hcBuilder - .AddAzureServiceBusTopic( - configuration["EventBusConnection"], - topicName: "eshop_event_bus", - name: "basket-servicebus-check", - tags: new string[] { "servicebus" }); - } - else - { - hcBuilder - .AddRabbitMQ( - $"amqp://{configuration["EventBusConnection"]}", - name: "basket-rabbitmqbus-check", - tags: new string[] { "rabbitmqbus" }); - } - - return services; + if (configuration.GetValue("AzureServiceBusEnabled")) + { + hcBuilder + .AddAzureServiceBusTopic( + configuration["EventBusConnection"], + topicName: "eshop_event_bus", + name: "basket-servicebus-check", + tags: new string[] { "servicebus" }); + } + else + { + hcBuilder + .AddRabbitMQ( + $"amqp://{configuration["EventBusConnection"]}", + name: "basket-rabbitmqbus-check", + tags: new string[] { "rabbitmqbus" }); } + + return services; } } diff --git a/src/Services/Basket/Basket.API/TestHttpResponseTrailersFeature.cs b/src/Services/Basket/Basket.API/TestHttpResponseTrailersFeature.cs index 7cab5a07b..b1cfef87b 100644 --- a/src/Services/Basket/Basket.API/TestHttpResponseTrailersFeature.cs +++ b/src/Services/Basket/Basket.API/TestHttpResponseTrailersFeature.cs @@ -1,10 +1,6 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; +namespace Microsoft.eShopOnContainers.Services.Basket.API; -namespace Microsoft.eShopOnContainers.Services.Basket.API +internal class TestHttpResponseTrailersFeature : IHttpResponseTrailersFeature { - internal class TestHttpResponseTrailersFeature : IHttpResponseTrailersFeature - { - public IHeaderDictionary Trailers { get; set; } - } -} \ No newline at end of file + public IHeaderDictionary Trailers { get; set; } +}