@ -1,30 +1,29 @@ | |||||
namespace Microsoft.eShopOnContainers.WebMVC | |||||
namespace Microsoft.eShopOnContainers.WebMVC; | |||||
public class AppSettings | |||||
{ | { | ||||
public class AppSettings | |||||
{ | |||||
//public Connectionstrings ConnectionStrings { get; set; } | |||||
public string PurchaseUrl { get; set; } | |||||
public string SignalrHubUrl { get; set; } | |||||
public bool ActivateCampaignDetailFunction { get; set; } | |||||
public Logging Logging { get; set; } | |||||
public bool UseCustomizationData { get; set; } | |||||
} | |||||
//public Connectionstrings ConnectionStrings { get; set; } | |||||
public string PurchaseUrl { get; set; } | |||||
public string SignalrHubUrl { get; set; } | |||||
public bool ActivateCampaignDetailFunction { get; set; } | |||||
public Logging Logging { get; set; } | |||||
public bool UseCustomizationData { get; set; } | |||||
} | |||||
public class Connectionstrings | |||||
{ | |||||
public string DefaultConnection { get; set; } | |||||
} | |||||
public class Connectionstrings | |||||
{ | |||||
public string DefaultConnection { get; set; } | |||||
} | |||||
public class Logging | |||||
{ | |||||
public bool IncludeScopes { get; set; } | |||||
public Loglevel LogLevel { get; set; } | |||||
} | |||||
public class Logging | |||||
{ | |||||
public bool IncludeScopes { get; set; } | |||||
public Loglevel LogLevel { get; set; } | |||||
} | |||||
public class Loglevel | |||||
{ | |||||
public string Default { get; set; } | |||||
public string System { get; set; } | |||||
public string Microsoft { get; set; } | |||||
} | |||||
public class Loglevel | |||||
{ | |||||
public string Default { get; set; } | |||||
public string System { get; set; } | |||||
public string Microsoft { get; set; } | |||||
} | } |
@ -1,53 +1,42 @@ | |||||
using Microsoft.AspNetCore.Authentication; | |||||
using Microsoft.AspNetCore.Authentication.Cookies; | |||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.Extensions.Logging; | |||||
using System; | |||||
using System.Security.Claims; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers; | |||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public class AccountController : Controller | |||||
{ | { | ||||
private readonly ILogger<AccountController> _logger; | |||||
public AccountController(ILogger<AccountController> logger) | |||||
{ | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
} | |||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | [Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | ||||
public class AccountController : Controller | |||||
public async Task<IActionResult> SignIn(string returnUrl) | |||||
{ | { | ||||
private readonly ILogger<AccountController> _logger; | |||||
var user = User as ClaimsPrincipal; | |||||
var token = await HttpContext.GetTokenAsync("access_token"); | |||||
public AccountController(ILogger<AccountController> logger) | |||||
{ | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | |||||
} | |||||
_logger.LogInformation("----- User {@User} authenticated into {AppName}", user, Program.AppName); | |||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public async Task<IActionResult> SignIn(string returnUrl) | |||||
if (token != null) | |||||
{ | { | ||||
var user = User as ClaimsPrincipal; | |||||
var token = await HttpContext.GetTokenAsync("access_token"); | |||||
_logger.LogInformation("----- User {@User} authenticated into {AppName}", user, Program.AppName); | |||||
if (token != null) | |||||
{ | |||||
ViewData["access_token"] = token; | |||||
} | |||||
// "Catalog" because UrlHelper doesn't support nameof() for controllers | |||||
// https://github.com/aspnet/Mvc/issues/5853 | |||||
return RedirectToAction(nameof(CatalogController.Index), "Catalog"); | |||||
ViewData["access_token"] = token; | |||||
} | } | ||||
public async Task<IActionResult> Signout() | |||||
{ | |||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); | |||||
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); | |||||
// "Catalog" because UrlHelper doesn't support nameof() for controllers | |||||
// https://github.com/aspnet/Mvc/issues/5853 | |||||
var homeUrl = Url.Action(nameof(CatalogController.Index), "Catalog"); | |||||
return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme, | |||||
new AspNetCore.Authentication.AuthenticationProperties { RedirectUri = homeUrl }); | |||||
} | |||||
// "Catalog" because UrlHelper doesn't support nameof() for controllers | |||||
// https://github.com/aspnet/Mvc/issues/5853 | |||||
return RedirectToAction(nameof(CatalogController.Index), "Catalog"); | |||||
} | |||||
public async Task<IActionResult> Signout() | |||||
{ | |||||
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); | |||||
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); | |||||
// "Catalog" because UrlHelper doesn't support nameof() for controllers | |||||
// https://github.com/aspnet/Mvc/issues/5853 | |||||
var homeUrl = Url.Action(nameof(CatalogController.Index), "Catalog"); | |||||
return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme, | |||||
new AspNetCore.Authentication.AuthenticationProperties { RedirectUri = homeUrl }); | |||||
} | } | ||||
} | } |
@ -1,89 +1,79 @@ | |||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers | |||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public class CartController : Controller | |||||
{ | { | ||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public class CartController : Controller | |||||
private readonly IBasketService _basketSvc; | |||||
private readonly ICatalogService _catalogSvc; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
public CartController(IBasketService basketSvc, ICatalogService catalogSvc, IIdentityParser<ApplicationUser> appUserParser) | |||||
{ | { | ||||
private readonly IBasketService _basketSvc; | |||||
private readonly ICatalogService _catalogSvc; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
_basketSvc = basketSvc; | |||||
_catalogSvc = catalogSvc; | |||||
_appUserParser = appUserParser; | |||||
} | |||||
public CartController(IBasketService basketSvc, ICatalogService catalogSvc, IIdentityParser<ApplicationUser> appUserParser) | |||||
public async Task<IActionResult> Index() | |||||
{ | |||||
try | |||||
{ | { | ||||
_basketSvc = basketSvc; | |||||
_catalogSvc = catalogSvc; | |||||
_appUserParser = appUserParser; | |||||
} | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var vm = await _basketSvc.GetBasket(user); | |||||
public async Task<IActionResult> Index() | |||||
return View(vm); | |||||
} | |||||
catch (Exception ex) | |||||
{ | { | ||||
try | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var vm = await _basketSvc.GetBasket(user); | |||||
return View(vm); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
HandleException(ex); | |||||
} | |||||
return View(); | |||||
HandleException(ex); | |||||
} | } | ||||
return View(); | |||||
} | |||||
[HttpPost] | |||||
public async Task<IActionResult> Index(Dictionary<string, int> quantities, string action) | |||||
[HttpPost] | |||||
public async Task<IActionResult> Index(Dictionary<string, int> quantities, string action) | |||||
{ | |||||
try | |||||
{ | { | ||||
try | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var basket = await _basketSvc.SetQuantities(user, quantities); | |||||
if (action == "[ Checkout ]") | |||||
{ | { | ||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var basket = await _basketSvc.SetQuantities(user, quantities); | |||||
if (action == "[ Checkout ]") | |||||
{ | |||||
return RedirectToAction("Create", "Order"); | |||||
} | |||||
return RedirectToAction("Create", "Order"); | |||||
} | } | ||||
catch (Exception ex) | |||||
{ | |||||
HandleException(ex); | |||||
} | |||||
return View(); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
HandleException(ex); | |||||
} | } | ||||
public async Task<IActionResult> AddToCart(CatalogItem productDetails) | |||||
return View(); | |||||
} | |||||
public async Task<IActionResult> AddToCart(CatalogItem productDetails) | |||||
{ | |||||
try | |||||
{ | { | ||||
try | |||||
{ | |||||
if (productDetails?.Id != null) | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
await _basketSvc.AddItemToBasket(user, productDetails.Id); | |||||
} | |||||
return RedirectToAction("Index", "Catalog"); | |||||
} | |||||
catch (Exception ex) | |||||
if (productDetails?.Id != null) | |||||
{ | { | ||||
// Catch error when Basket.api is in circuit-opened mode | |||||
HandleException(ex); | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
await _basketSvc.AddItemToBasket(user, productDetails.Id); | |||||
} | } | ||||
return RedirectToAction("Index", "Catalog", new { errorMsg = ViewBag.BasketInoperativeMsg }); | |||||
return RedirectToAction("Index", "Catalog"); | |||||
} | } | ||||
private void HandleException(Exception ex) | |||||
catch (Exception ex) | |||||
{ | { | ||||
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative {ex.GetType().Name} - {ex.Message}"; | |||||
// Catch error when Basket.api is in circuit-opened mode | |||||
HandleException(ex); | |||||
} | } | ||||
return RedirectToAction("Index", "Catalog", new { errorMsg = ViewBag.BasketInoperativeMsg }); | |||||
} | |||||
private void HandleException(Exception ex) | |||||
{ | |||||
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative {ex.GetType().Name} - {ex.Message}"; | |||||
} | } | ||||
} | } |
@ -1,45 +1,37 @@ | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels.CatalogViewModels; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels.Pagination; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers | |||||
public class CatalogController : Controller | |||||
{ | { | ||||
public class CatalogController : Controller | |||||
{ | |||||
private ICatalogService _catalogSvc; | |||||
private ICatalogService _catalogSvc; | |||||
public CatalogController(ICatalogService catalogSvc) => | |||||
_catalogSvc = catalogSvc; | |||||
public CatalogController(ICatalogService catalogSvc) => | |||||
_catalogSvc = catalogSvc; | |||||
public async Task<IActionResult> Index(int? BrandFilterApplied, int? TypesFilterApplied, int? page, [FromQuery] string errorMsg) | |||||
public async Task<IActionResult> Index(int? BrandFilterApplied, int? TypesFilterApplied, int? page, [FromQuery] string errorMsg) | |||||
{ | |||||
var itemsPage = 9; | |||||
var catalog = await _catalogSvc.GetCatalogItems(page ?? 0, itemsPage, BrandFilterApplied, TypesFilterApplied); | |||||
var vm = new IndexViewModel() | |||||
{ | { | ||||
var itemsPage = 9; | |||||
var catalog = await _catalogSvc.GetCatalogItems(page ?? 0, itemsPage, BrandFilterApplied, TypesFilterApplied); | |||||
var vm = new IndexViewModel() | |||||
CatalogItems = catalog.Data, | |||||
Brands = await _catalogSvc.GetBrands(), | |||||
Types = await _catalogSvc.GetTypes(), | |||||
BrandFilterApplied = BrandFilterApplied ?? 0, | |||||
TypesFilterApplied = TypesFilterApplied ?? 0, | |||||
PaginationInfo = new PaginationInfo() | |||||
{ | { | ||||
CatalogItems = catalog.Data, | |||||
Brands = await _catalogSvc.GetBrands(), | |||||
Types = await _catalogSvc.GetTypes(), | |||||
BrandFilterApplied = BrandFilterApplied ?? 0, | |||||
TypesFilterApplied = TypesFilterApplied ?? 0, | |||||
PaginationInfo = new PaginationInfo() | |||||
{ | |||||
ActualPage = page ?? 0, | |||||
ItemsPerPage = catalog.Data.Count, | |||||
TotalItems = catalog.Count, | |||||
TotalPages = (int)Math.Ceiling(((decimal)catalog.Count / itemsPage)) | |||||
} | |||||
}; | |||||
ActualPage = page ?? 0, | |||||
ItemsPerPage = catalog.Data.Count, | |||||
TotalItems = catalog.Count, | |||||
TotalPages = (int)Math.Ceiling(((decimal)catalog.Count / itemsPage)) | |||||
} | |||||
}; | |||||
vm.PaginationInfo.Next = (vm.PaginationInfo.ActualPage == vm.PaginationInfo.TotalPages - 1) ? "is-disabled" : ""; | |||||
vm.PaginationInfo.Previous = (vm.PaginationInfo.ActualPage == 0) ? "is-disabled" : ""; | |||||
vm.PaginationInfo.Next = (vm.PaginationInfo.ActualPage == vm.PaginationInfo.TotalPages - 1) ? "is-disabled" : ""; | |||||
vm.PaginationInfo.Previous = (vm.PaginationInfo.ActualPage == 0) ? "is-disabled" : ""; | |||||
ViewBag.BasketInoperativeMsg = errorMsg; | |||||
ViewBag.BasketInoperativeMsg = errorMsg; | |||||
return View(vm); | |||||
} | |||||
return View(vm); | |||||
} | } | ||||
} | } |
@ -1,9 +1,6 @@ | |||||
using Microsoft.AspNetCore.Mvc; | |||||
namespace WebMVC.Controllers; | |||||
namespace WebMVC.Controllers | |||||
public class ErrorController : Controller | |||||
{ | { | ||||
public class ErrorController : Controller | |||||
{ | |||||
public IActionResult Error() => View(); | |||||
} | |||||
public IActionResult Error() => View(); | |||||
} | } |
@ -1,82 +1,75 @@ | |||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | using Microsoft.eShopOnContainers.WebMVC.ViewModels; | ||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Controllers | |||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public class OrderController : Controller | |||||
{ | { | ||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public class OrderController : Controller | |||||
private IOrderingService _orderSvc; | |||||
private IBasketService _basketSvc; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
public OrderController(IOrderingService orderSvc, IBasketService basketSvc, IIdentityParser<ApplicationUser> appUserParser) | |||||
{ | { | ||||
private IOrderingService _orderSvc; | |||||
private IBasketService _basketSvc; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
public OrderController(IOrderingService orderSvc, IBasketService basketSvc, IIdentityParser<ApplicationUser> appUserParser) | |||||
{ | |||||
_appUserParser = appUserParser; | |||||
_orderSvc = orderSvc; | |||||
_basketSvc = basketSvc; | |||||
} | |||||
_appUserParser = appUserParser; | |||||
_orderSvc = orderSvc; | |||||
_basketSvc = basketSvc; | |||||
} | |||||
public async Task<IActionResult> Create() | |||||
{ | |||||
public async Task<IActionResult> Create() | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var order = await _basketSvc.GetOrderDraft(user.Id); | |||||
var vm = _orderSvc.MapUserInfoIntoOrder(user, order); | |||||
vm.CardExpirationShortFormat(); | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var order = await _basketSvc.GetOrderDraft(user.Id); | |||||
var vm = _orderSvc.MapUserInfoIntoOrder(user, order); | |||||
vm.CardExpirationShortFormat(); | |||||
return View(vm); | |||||
} | |||||
return View(vm); | |||||
} | |||||
[HttpPost] | |||||
public async Task<IActionResult> Checkout(Order model) | |||||
[HttpPost] | |||||
public async Task<IActionResult> Checkout(Order model) | |||||
{ | |||||
try | |||||
{ | { | ||||
try | |||||
if (ModelState.IsValid) | |||||
{ | { | ||||
if (ModelState.IsValid) | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var basket = _orderSvc.MapOrderToBasket(model); | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var basket = _orderSvc.MapOrderToBasket(model); | |||||
await _basketSvc.Checkout(basket); | |||||
await _basketSvc.Checkout(basket); | |||||
//Redirect to historic list. | |||||
return RedirectToAction("Index"); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
ModelState.AddModelError("Error", $"It was not possible to create a new order, please try later on ({ex.GetType().Name} - {ex.Message})"); | |||||
//Redirect to historic list. | |||||
return RedirectToAction("Index"); | |||||
} | } | ||||
return View("Create", model); | |||||
} | } | ||||
public async Task<IActionResult> Cancel(string orderId) | |||||
catch (Exception ex) | |||||
{ | { | ||||
await _orderSvc.CancelOrder(orderId); | |||||
//Redirect to historic list. | |||||
return RedirectToAction("Index"); | |||||
ModelState.AddModelError("Error", $"It was not possible to create a new order, please try later on ({ex.GetType().Name} - {ex.Message})"); | |||||
} | } | ||||
public async Task<IActionResult> Detail(string orderId) | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
return View("Create", model); | |||||
} | |||||
var order = await _orderSvc.GetOrder(user, orderId); | |||||
return View(order); | |||||
} | |||||
public async Task<IActionResult> Cancel(string orderId) | |||||
{ | |||||
await _orderSvc.CancelOrder(orderId); | |||||
public async Task<IActionResult> Index(Order item) | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var vm = await _orderSvc.GetMyOrders(user); | |||||
return View(vm); | |||||
} | |||||
//Redirect to historic list. | |||||
return RedirectToAction("Index"); | |||||
} | |||||
public async Task<IActionResult> Detail(string orderId) | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var order = await _orderSvc.GetOrder(user, orderId); | |||||
return View(order); | |||||
} | |||||
public async Task<IActionResult> Index(Order item) | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var vm = await _orderSvc.GetMyOrders(user); | |||||
return View(vm); | |||||
} | } | ||||
} | |||||
} |
@ -1,41 +1,32 @@ | |||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect; | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System.Threading.Tasks; | |||||
using WebMVC.Services.ModelDTOs; | |||||
namespace WebMVC.Controllers; | |||||
namespace WebMVC.Controllers | |||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public class OrderManagementController : Controller | |||||
{ | { | ||||
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] | |||||
public class OrderManagementController : Controller | |||||
private IOrderingService _orderSvc; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
public OrderManagementController(IOrderingService orderSvc, IIdentityParser<ApplicationUser> appUserParser) | |||||
{ | { | ||||
private IOrderingService _orderSvc; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
public OrderManagementController(IOrderingService orderSvc, IIdentityParser<ApplicationUser> appUserParser) | |||||
{ | |||||
_appUserParser = appUserParser; | |||||
_orderSvc = orderSvc; | |||||
} | |||||
_appUserParser = appUserParser; | |||||
_orderSvc = orderSvc; | |||||
} | |||||
public async Task<IActionResult> Index() | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var vm = await _orderSvc.GetMyOrders(user); | |||||
public async Task<IActionResult> Index() | |||||
{ | |||||
var user = _appUserParser.Parse(HttpContext.User); | |||||
var vm = await _orderSvc.GetMyOrders(user); | |||||
return View(vm); | |||||
} | |||||
return View(vm); | |||||
} | |||||
[HttpPost] | |||||
public async Task<IActionResult> OrderProcess(string orderId, string actionCode) | |||||
[HttpPost] | |||||
public async Task<IActionResult> OrderProcess(string orderId, string actionCode) | |||||
{ | |||||
if (OrderProcessAction.Ship.Code == actionCode) | |||||
{ | { | ||||
if (OrderProcessAction.Ship.Code == actionCode) | |||||
{ | |||||
await _orderSvc.ShipOrder(orderId); | |||||
} | |||||
return RedirectToAction("Index"); | |||||
await _orderSvc.ShipOrder(orderId); | |||||
} | } | ||||
return RedirectToAction("Index"); | |||||
} | } | ||||
} | } |
@ -1,61 +1,52 @@ | |||||
using Microsoft.AspNetCore.Authorization; | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System.Net.Http; | |||||
using System.Threading.Tasks; | |||||
using System.Text.Json; | |||||
namespace WebMVC.Controllers | |||||
namespace WebMVC.Controllers; | |||||
class TestPayload | |||||
{ | { | ||||
class TestPayload | |||||
{ | |||||
public int CatalogItemId { get; set; } | |||||
public int CatalogItemId { get; set; } | |||||
public string BasketId { get; set; } | |||||
public string BasketId { get; set; } | |||||
public int Quantity { get; set; } | |||||
} | |||||
public int Quantity { get; set; } | |||||
} | |||||
[Authorize] | |||||
public class TestController : Controller | |||||
[Authorize] | |||||
public class TestController : Controller | |||||
{ | |||||
private readonly IHttpClientFactory _client; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
public TestController(IHttpClientFactory client, IIdentityParser<ApplicationUser> identityParser) | |||||
{ | { | ||||
private readonly IHttpClientFactory _client; | |||||
private readonly IIdentityParser<ApplicationUser> _appUserParser; | |||||
_client = client; | |||||
_appUserParser = identityParser; | |||||
} | |||||
public TestController(IHttpClientFactory client, IIdentityParser<ApplicationUser> identityParser) | |||||
{ | |||||
_client = client; | |||||
_appUserParser = identityParser; | |||||
} | |||||
public async Task<IActionResult> Ocelot() | |||||
{ | |||||
var url = "http://apigw/shopping/api/v1/basket/items"; | |||||
public async Task<IActionResult> Ocelot() | |||||
var payload = new TestPayload() | |||||
{ | { | ||||
var url = "http://apigw/shopping/api/v1/basket/items"; | |||||
var payload = new TestPayload() | |||||
{ | |||||
CatalogItemId = 1, | |||||
Quantity = 1, | |||||
BasketId = _appUserParser.Parse(User).Id | |||||
}; | |||||
CatalogItemId = 1, | |||||
Quantity = 1, | |||||
BasketId = _appUserParser.Parse(User).Id | |||||
}; | |||||
var content = new StringContent(JsonSerializer.Serialize(payload), System.Text.Encoding.UTF8, "application/json"); | |||||
var content = new StringContent(JsonSerializer.Serialize(payload), System.Text.Encoding.UTF8, "application/json"); | |||||
var response = await _client.CreateClient(nameof(IBasketService)) | |||||
.PostAsync(url, content); | |||||
var response = await _client.CreateClient(nameof(IBasketService)) | |||||
.PostAsync(url, content); | |||||
if (response.IsSuccessStatusCode) | |||||
{ | |||||
var str = await response.Content.ReadAsStringAsync(); | |||||
if (response.IsSuccessStatusCode) | |||||
{ | |||||
var str = await response.Content.ReadAsStringAsync(); | |||||
return Ok(str); | |||||
} | |||||
else | |||||
{ | |||||
return Ok(new { response.StatusCode, response.ReasonPhrase }); | |||||
} | |||||
return Ok(str); | |||||
} | |||||
else | |||||
{ | |||||
return Ok(new { response.StatusCode, response.ReasonPhrase }); | |||||
} | } | ||||
} | } | ||||
} | } |
@ -1,35 +1,28 @@ | |||||
using System; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
using System.Net.Http; | |||||
using System.Net.Http.Headers; | |||||
using System.Text; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Extensions; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Extensions | |||||
public static class HttpClientExtensions | |||||
{ | { | ||||
public static class HttpClientExtensions | |||||
{ | |||||
public static void SetBasicAuthentication(this HttpClient client, string userName, string password) => | |||||
client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(userName, password); | |||||
public static void SetBasicAuthentication(this HttpClient client, string userName, string password) => | |||||
client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(userName, password); | |||||
public static void SetToken(this HttpClient client, string scheme, string token) => | |||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, token); | |||||
public static void SetToken(this HttpClient client, string scheme, string token) => | |||||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(scheme, token); | |||||
public static void SetBearerToken(this HttpClient client, string token) => | |||||
client.SetToken(JwtConstants.TokenType, token); | |||||
} | |||||
public static void SetBearerToken(this HttpClient client, string token) => | |||||
client.SetToken(JwtConstants.TokenType, token); | |||||
} | |||||
public class BasicAuthenticationHeaderValue : AuthenticationHeaderValue | |||||
{ | |||||
public BasicAuthenticationHeaderValue(string userName, string password) | |||||
: base("Basic", EncodeCredential(userName, password)) | |||||
{ } | |||||
public class BasicAuthenticationHeaderValue : AuthenticationHeaderValue | |||||
{ | |||||
public BasicAuthenticationHeaderValue(string userName, string password) | |||||
: base("Basic", EncodeCredential(userName, password)) | |||||
{ } | |||||
private static string EncodeCredential(string userName, string password) | |||||
{ | |||||
Encoding encoding = Encoding.GetEncoding("iso-8859-1"); | |||||
string credential = String.Format("{0}:{1}", userName, password); | |||||
private static string EncodeCredential(string userName, string password) | |||||
{ | |||||
Encoding encoding = Encoding.GetEncoding("iso-8859-1"); | |||||
string credential = String.Format("{0}:{1}", userName, password); | |||||
return Convert.ToBase64String(encoding.GetBytes(credential)); | |||||
} | |||||
return Convert.ToBase64String(encoding.GetBytes(credential)); | |||||
} | } | ||||
} | } |
@ -1,86 +1,85 @@ | |||||
namespace WebMVC.Infrastructure | |||||
namespace WebMVC.Infrastructure; | |||||
public static class API | |||||
{ | { | ||||
public static class API | |||||
public static class Purchase | |||||
{ | |||||
public static string AddItemToBasket(string baseUri) => $"{baseUri}/basket/items"; | |||||
public static string UpdateBasketItem(string baseUri) => $"{baseUri}/basket/items"; | |||||
public static string GetOrderDraft(string baseUri, string basketId) => $"{baseUri}/order/draft/{basketId}"; | |||||
} | |||||
public static class Basket | |||||
{ | { | ||||
public static string GetBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}"; | |||||
public static string UpdateBasket(string baseUri) => baseUri; | |||||
public static string CheckoutBasket(string baseUri) => $"{baseUri}/checkout"; | |||||
public static string CleanBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}"; | |||||
} | |||||
public static class Purchase | |||||
public static class Order | |||||
{ | |||||
public static string GetOrder(string baseUri, string orderId) | |||||
{ | { | ||||
public static string AddItemToBasket(string baseUri) => $"{baseUri}/basket/items"; | |||||
public static string UpdateBasketItem(string baseUri) => $"{baseUri}/basket/items"; | |||||
return $"{baseUri}/{orderId}"; | |||||
} | |||||
public static string GetOrderDraft(string baseUri, string basketId) => $"{baseUri}/order/draft/{basketId}"; | |||||
public static string GetAllMyOrders(string baseUri) | |||||
{ | |||||
return baseUri; | |||||
} | } | ||||
public static class Basket | |||||
public static string AddNewOrder(string baseUri) | |||||
{ | { | ||||
public static string GetBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}"; | |||||
public static string UpdateBasket(string baseUri) => baseUri; | |||||
public static string CheckoutBasket(string baseUri) => $"{baseUri}/checkout"; | |||||
public static string CleanBasket(string baseUri, string basketId) => $"{baseUri}/{basketId}"; | |||||
return $"{baseUri}/new"; | |||||
} | } | ||||
public static class Order | |||||
public static string CancelOrder(string baseUri) | |||||
{ | { | ||||
public static string GetOrder(string baseUri, string orderId) | |||||
{ | |||||
return $"{baseUri}/{orderId}"; | |||||
} | |||||
return $"{baseUri}/cancel"; | |||||
} | |||||
public static string GetAllMyOrders(string baseUri) | |||||
{ | |||||
return baseUri; | |||||
} | |||||
public static string ShipOrder(string baseUri) | |||||
{ | |||||
return $"{baseUri}/ship"; | |||||
} | |||||
} | |||||
public static string AddNewOrder(string baseUri) | |||||
public static class Catalog | |||||
{ | |||||
public static string GetAllCatalogItems(string baseUri, int page, int take, int? brand, int? type) | |||||
{ | |||||
var filterQs = ""; | |||||
if (type.HasValue) | |||||
{ | { | ||||
return $"{baseUri}/new"; | |||||
} | |||||
var brandQs = (brand.HasValue) ? brand.Value.ToString() : string.Empty; | |||||
filterQs = $"/type/{type.Value}/brand/{brandQs}"; | |||||
public static string CancelOrder(string baseUri) | |||||
} | |||||
else if (brand.HasValue) | |||||
{ | { | ||||
return $"{baseUri}/cancel"; | |||||
var brandQs = (brand.HasValue) ? brand.Value.ToString() : string.Empty; | |||||
filterQs = $"/type/all/brand/{brandQs}"; | |||||
} | } | ||||
public static string ShipOrder(string baseUri) | |||||
else | |||||
{ | { | ||||
return $"{baseUri}/ship"; | |||||
filterQs = string.Empty; | |||||
} | } | ||||
return $"{baseUri}items{filterQs}?pageIndex={page}&pageSize={take}"; | |||||
} | } | ||||
public static class Catalog | |||||
public static string GetAllBrands(string baseUri) | |||||
{ | { | ||||
public static string GetAllCatalogItems(string baseUri, int page, int take, int? brand, int? type) | |||||
{ | |||||
var filterQs = ""; | |||||
if (type.HasValue) | |||||
{ | |||||
var brandQs = (brand.HasValue) ? brand.Value.ToString() : string.Empty; | |||||
filterQs = $"/type/{type.Value}/brand/{brandQs}"; | |||||
} | |||||
else if (brand.HasValue) | |||||
{ | |||||
var brandQs = (brand.HasValue) ? brand.Value.ToString() : string.Empty; | |||||
filterQs = $"/type/all/brand/{brandQs}"; | |||||
} | |||||
else | |||||
{ | |||||
filterQs = string.Empty; | |||||
} | |||||
return $"{baseUri}items{filterQs}?pageIndex={page}&pageSize={take}"; | |||||
} | |||||
public static string GetAllBrands(string baseUri) | |||||
{ | |||||
return $"{baseUri}catalogBrands"; | |||||
} | |||||
return $"{baseUri}catalogBrands"; | |||||
} | |||||
public static string GetAllTypes(string baseUri) | |||||
{ | |||||
return $"{baseUri}catalogTypes"; | |||||
} | |||||
public static string GetAllTypes(string baseUri) | |||||
{ | |||||
return $"{baseUri}catalogTypes"; | |||||
} | } | ||||
} | } | ||||
} | |||||
} |
@ -1,49 +1,40 @@ | |||||
using Microsoft.AspNetCore.Authentication; | |||||
using Microsoft.AspNetCore.Http; | |||||
using System.Collections.Generic; | |||||
using System.Net.Http; | |||||
using System.Net.Http.Headers; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace WebMVC.Infrastructure | |||||
namespace WebMVC.Infrastructure; | |||||
public class HttpClientAuthorizationDelegatingHandler | |||||
: DelegatingHandler | |||||
{ | { | ||||
public class HttpClientAuthorizationDelegatingHandler | |||||
: DelegatingHandler | |||||
private readonly IHttpContextAccessor _httpContextAccessor; | |||||
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor) | |||||
{ | { | ||||
private readonly IHttpContextAccessor _httpContextAccessor; | |||||
_httpContextAccessor = httpContextAccessor; | |||||
} | |||||
public HttpClientAuthorizationDelegatingHandler(IHttpContextAccessor httpContextAccessor) | |||||
{ | |||||
_httpContextAccessor = httpContextAccessor; | |||||
} | |||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |||||
{ | |||||
var authorizationHeader = _httpContextAccessor.HttpContext | |||||
.Request.Headers["Authorization"]; | |||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |||||
if (!string.IsNullOrEmpty(authorizationHeader)) | |||||
{ | { | ||||
var authorizationHeader = _httpContextAccessor.HttpContext | |||||
.Request.Headers["Authorization"]; | |||||
if (!string.IsNullOrEmpty(authorizationHeader)) | |||||
{ | |||||
request.Headers.Add("Authorization", new List<string>() { authorizationHeader }); | |||||
} | |||||
var token = await GetToken(); | |||||
request.Headers.Add("Authorization", new List<string>() { authorizationHeader }); | |||||
} | |||||
if (token != null) | |||||
{ | |||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); | |||||
} | |||||
var token = await GetToken(); | |||||
return await base.SendAsync(request, cancellationToken); | |||||
if (token != null) | |||||
{ | |||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); | |||||
} | } | ||||
async Task<string> GetToken() | |||||
{ | |||||
const string ACCESS_TOKEN = "access_token"; | |||||
return await base.SendAsync(request, cancellationToken); | |||||
} | |||||
return await _httpContextAccessor.HttpContext | |||||
.GetTokenAsync(ACCESS_TOKEN); | |||||
} | |||||
async Task<string> GetToken() | |||||
{ | |||||
const string ACCESS_TOKEN = "access_token"; | |||||
return await _httpContextAccessor.HttpContext | |||||
.GetTokenAsync(ACCESS_TOKEN); | |||||
} | } | ||||
} | } |
@ -1,29 +1,23 @@ | |||||
using System; | |||||
using System.Net.Http; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace WebMVC.Infrastructure; | |||||
namespace WebMVC.Infrastructure | |||||
public class HttpClientRequestIdDelegatingHandler | |||||
: DelegatingHandler | |||||
{ | { | ||||
public class HttpClientRequestIdDelegatingHandler | |||||
: DelegatingHandler | |||||
{ | |||||
public HttpClientRequestIdDelegatingHandler() | |||||
{ | |||||
} | |||||
public HttpClientRequestIdDelegatingHandler() | |||||
{ | |||||
} | |||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |||||
{ | |||||
if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put) | |||||
{ | { | ||||
if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put) | |||||
if (!request.Headers.Contains("x-requestid")) | |||||
{ | { | ||||
if (!request.Headers.Contains("x-requestid")) | |||||
{ | |||||
request.Headers.Add("x-requestid", Guid.NewGuid().ToString()); | |||||
} | |||||
request.Headers.Add("x-requestid", Guid.NewGuid().ToString()); | |||||
} | } | ||||
return await base.SendAsync(request, cancellationToken); | |||||
} | } | ||||
return await base.SendAsync(request, cancellationToken); | |||||
} | } | ||||
} | |||||
} |
@ -1,97 +1,85 @@ | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.eShopOnContainers.WebMVC; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Options; | |||||
namespace WebMVC.Infrastructure; | |||||
using Serilog; | using Serilog; | ||||
using System; | |||||
using System.IO; | |||||
using System.IO.Compression; | |||||
using System.Linq; | |||||
namespace WebMVC.Infrastructure | |||||
public class WebContextSeed | |||||
{ | { | ||||
public class WebContextSeed | |||||
public static void Seed(IApplicationBuilder applicationBuilder, IWebHostEnvironment env) | |||||
{ | { | ||||
public static void Seed(IApplicationBuilder applicationBuilder, IWebHostEnvironment env) | |||||
{ | |||||
var log = Serilog.Log.Logger; | |||||
var log = Serilog.Log.Logger; | |||||
var settings = (AppSettings)applicationBuilder | |||||
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value; | |||||
var settings = (AppSettings)applicationBuilder | |||||
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value; | |||||
var useCustomizationData = settings.UseCustomizationData; | |||||
var contentRootPath = env.ContentRootPath; | |||||
var webroot = env.WebRootPath; | |||||
var useCustomizationData = settings.UseCustomizationData; | |||||
var contentRootPath = env.ContentRootPath; | |||||
var webroot = env.WebRootPath; | |||||
if (useCustomizationData) | |||||
{ | |||||
GetPreconfiguredImages(contentRootPath, webroot, log); | |||||
if (useCustomizationData) | |||||
{ | |||||
GetPreconfiguredImages(contentRootPath, webroot, log); | |||||
GetPreconfiguredCSS(contentRootPath, webroot, log); | |||||
} | |||||
GetPreconfiguredCSS(contentRootPath, webroot, log); | |||||
} | } | ||||
} | |||||
static void GetPreconfiguredCSS(string contentRootPath, string webroot, Serilog.ILogger log) | |||||
static void GetPreconfiguredCSS(string contentRootPath, string webroot, ILogger log) | |||||
{ | |||||
try | |||||
{ | { | ||||
try | |||||
string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css"); | |||||
if (!File.Exists(overrideCssFile)) | |||||
{ | { | ||||
string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css"); | |||||
if (!File.Exists(overrideCssFile)) | |||||
{ | |||||
log.Error("Override css file '{FileName}' does not exists.", overrideCssFile); | |||||
return; | |||||
} | |||||
string destinationFilename = Path.Combine(webroot, "css", "override.css"); | |||||
File.Copy(overrideCssFile, destinationFilename, true); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message); | |||||
log.Error("Override css file '{FileName}' does not exists.", overrideCssFile); | |||||
return; | |||||
} | } | ||||
string destinationFilename = Path.Combine(webroot, "css", "override.css"); | |||||
File.Copy(overrideCssFile, destinationFilename, true); | |||||
} | } | ||||
catch (Exception ex) | |||||
{ | |||||
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message); | |||||
} | |||||
} | |||||
static void GetPreconfiguredImages(string contentRootPath, string webroot, Serilog.ILogger log) | |||||
static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log) | |||||
{ | |||||
try | |||||
{ | { | ||||
try | |||||
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); | |||||
if (!File.Exists(imagesZipFile)) | |||||
{ | { | ||||
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); | |||||
if (!File.Exists(imagesZipFile)) | |||||
{ | |||||
log.Error("Zip file '{ZipFileName}' does not exists.", imagesZipFile); | |||||
return; | |||||
} | |||||
log.Error("Zip file '{ZipFileName}' does not exists.", imagesZipFile); | |||||
return; | |||||
} | |||||
string imagePath = Path.Combine(webroot, "images"); | |||||
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); | |||||
string imagePath = Path.Combine(webroot, "images"); | |||||
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); | |||||
using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) | |||||
using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) | |||||
{ | |||||
foreach (ZipArchiveEntry entry in zip.Entries) | |||||
{ | { | ||||
foreach (ZipArchiveEntry entry in zip.Entries) | |||||
if (imageFiles.Contains(entry.Name)) | |||||
{ | { | ||||
if (imageFiles.Contains(entry.Name)) | |||||
{ | |||||
string destinationFilename = Path.Combine(imagePath, entry.Name); | |||||
if (File.Exists(destinationFilename)) | |||||
{ | |||||
File.Delete(destinationFilename); | |||||
} | |||||
entry.ExtractToFile(destinationFilename); | |||||
} | |||||
else | |||||
string destinationFilename = Path.Combine(imagePath, entry.Name); | |||||
if (File.Exists(destinationFilename)) | |||||
{ | { | ||||
log.Warning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile); | |||||
File.Delete(destinationFilename); | |||||
} | } | ||||
entry.ExtractToFile(destinationFilename); | |||||
} | |||||
else | |||||
{ | |||||
log.Warning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
catch (Exception ex) | |||||
{ | |||||
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message); | |||||
} | |||||
} | } | ||||
catch (Exception ex) | |||||
{ | |||||
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message); | |||||
} | |||||
} | } | ||||
} | |||||
} |
@ -1,130 +1,120 @@ | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net.Http; | |||||
using System.Threading.Tasks; | |||||
using WebMVC.Infrastructure; | |||||
using WebMVC.Services.ModelDTOs; | |||||
using System.Text.Json; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public class BasketService : IBasketService | |||||
{ | { | ||||
public class BasketService : IBasketService | |||||
private readonly IOptions<AppSettings> _settings; | |||||
private readonly HttpClient _apiClient; | |||||
private readonly ILogger<BasketService> _logger; | |||||
private readonly string _basketByPassUrl; | |||||
private readonly string _purchaseUrl; | |||||
public BasketService(HttpClient httpClient, IOptions<AppSettings> settings, ILogger<BasketService> logger) | |||||
{ | { | ||||
private readonly IOptions<AppSettings> _settings; | |||||
private readonly HttpClient _apiClient; | |||||
private readonly ILogger<BasketService> _logger; | |||||
private readonly string _basketByPassUrl; | |||||
private readonly string _purchaseUrl; | |||||
_apiClient = httpClient; | |||||
_settings = settings; | |||||
_logger = logger; | |||||
public BasketService(HttpClient httpClient, IOptions<AppSettings> settings, ILogger<BasketService> logger) | |||||
{ | |||||
_apiClient = httpClient; | |||||
_settings = settings; | |||||
_logger = logger; | |||||
_basketByPassUrl = $"{_settings.Value.PurchaseUrl}/b/api/v1/basket"; | |||||
_purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1"; | |||||
} | |||||
_basketByPassUrl = $"{_settings.Value.PurchaseUrl}/b/api/v1/basket"; | |||||
_purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1"; | |||||
} | |||||
public async Task<Basket> GetBasket(ApplicationUser user) | |||||
{ | |||||
var uri = API.Basket.GetBasket(_basketByPassUrl, user.Id); | |||||
_logger.LogDebug("[GetBasket] -> Calling {Uri} to get the basket", uri); | |||||
var response = await _apiClient.GetAsync(uri); | |||||
_logger.LogDebug("[GetBasket] -> response code {StatusCode}", response.StatusCode); | |||||
var responseString = await response.Content.ReadAsStringAsync(); | |||||
return string.IsNullOrEmpty(responseString) ? | |||||
new Basket() { BuyerId = user.Id } : | |||||
JsonSerializer.Deserialize<Basket>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
} | |||||
public async Task<Basket> GetBasket(ApplicationUser user) | |||||
{ | |||||
var uri = API.Basket.GetBasket(_basketByPassUrl, user.Id); | |||||
_logger.LogDebug("[GetBasket] -> Calling {Uri} to get the basket", uri); | |||||
var response = await _apiClient.GetAsync(uri); | |||||
_logger.LogDebug("[GetBasket] -> response code {StatusCode}", response.StatusCode); | |||||
var responseString = await response.Content.ReadAsStringAsync(); | |||||
return string.IsNullOrEmpty(responseString) ? | |||||
new Basket() { BuyerId = user.Id } : | |||||
JsonSerializer.Deserialize<Basket>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
} | |||||
public async Task<Basket> UpdateBasket(Basket basket) | |||||
{ | |||||
var uri = API.Basket.UpdateBasket(_basketByPassUrl); | |||||
public async Task<Basket> UpdateBasket(Basket basket) | |||||
{ | |||||
var uri = API.Basket.UpdateBasket(_basketByPassUrl); | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); | |||||
var response = await _apiClient.PostAsync(uri, basketContent); | |||||
var response = await _apiClient.PostAsync(uri, basketContent); | |||||
response.EnsureSuccessStatusCode(); | |||||
response.EnsureSuccessStatusCode(); | |||||
return basket; | |||||
} | |||||
return basket; | |||||
} | |||||
public async Task Checkout(BasketDTO basket) | |||||
{ | |||||
var uri = API.Basket.CheckoutBasket(_basketByPassUrl); | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); | |||||
public async Task Checkout(BasketDTO basket) | |||||
{ | |||||
var uri = API.Basket.CheckoutBasket(_basketByPassUrl); | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(basket), System.Text.Encoding.UTF8, "application/json"); | |||||
_logger.LogInformation("Uri chechout {uri}", uri); | |||||
_logger.LogInformation("Uri chechout {uri}", uri); | |||||
var response = await _apiClient.PostAsync(uri, basketContent); | |||||
var response = await _apiClient.PostAsync(uri, basketContent); | |||||
response.EnsureSuccessStatusCode(); | |||||
} | |||||
response.EnsureSuccessStatusCode(); | |||||
} | |||||
public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities) | |||||
{ | |||||
var uri = API.Purchase.UpdateBasketItem(_purchaseUrl); | |||||
public async Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities) | |||||
{ | |||||
var uri = API.Purchase.UpdateBasketItem(_purchaseUrl); | |||||
var basketUpdate = new | |||||
var basketUpdate = new | |||||
{ | |||||
BasketId = user.Id, | |||||
Updates = quantities.Select(kvp => new | |||||
{ | { | ||||
BasketId = user.Id, | |||||
Updates = quantities.Select(kvp => new | |||||
{ | |||||
BasketItemId = kvp.Key, | |||||
NewQty = kvp.Value | |||||
}).ToArray() | |||||
}; | |||||
BasketItemId = kvp.Key, | |||||
NewQty = kvp.Value | |||||
}).ToArray() | |||||
}; | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(basketUpdate), System.Text.Encoding.UTF8, "application/json"); | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(basketUpdate), System.Text.Encoding.UTF8, "application/json"); | |||||
var response = await _apiClient.PutAsync(uri, basketContent); | |||||
var response = await _apiClient.PutAsync(uri, basketContent); | |||||
response.EnsureSuccessStatusCode(); | |||||
response.EnsureSuccessStatusCode(); | |||||
var jsonResponse = await response.Content.ReadAsStringAsync(); | |||||
var jsonResponse = await response.Content.ReadAsStringAsync(); | |||||
return JsonSerializer.Deserialize<Basket>(jsonResponse, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
} | |||||
public async Task<Order> GetOrderDraft(string basketId) | |||||
return JsonSerializer.Deserialize<Basket>(jsonResponse, new JsonSerializerOptions | |||||
{ | { | ||||
var uri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId); | |||||
var responseString = await _apiClient.GetStringAsync(uri); | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
} | |||||
var response = JsonSerializer.Deserialize<Order>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
public async Task<Order> GetOrderDraft(string basketId) | |||||
{ | |||||
var uri = API.Purchase.GetOrderDraft(_purchaseUrl, basketId); | |||||
return response; | |||||
} | |||||
var responseString = await _apiClient.GetStringAsync(uri); | |||||
public async Task AddItemToBasket(ApplicationUser user, int productId) | |||||
var response = JsonSerializer.Deserialize<Order>(responseString, new JsonSerializerOptions | |||||
{ | { | ||||
var uri = API.Purchase.AddItemToBasket(_purchaseUrl); | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
var newItem = new | |||||
{ | |||||
CatalogItemId = productId, | |||||
BasketId = user.Id, | |||||
Quantity = 1 | |||||
}; | |||||
return response; | |||||
} | |||||
public async Task AddItemToBasket(ApplicationUser user, int productId) | |||||
{ | |||||
var uri = API.Purchase.AddItemToBasket(_purchaseUrl); | |||||
var newItem = new | |||||
{ | |||||
CatalogItemId = productId, | |||||
BasketId = user.Id, | |||||
Quantity = 1 | |||||
}; | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(newItem), System.Text.Encoding.UTF8, "application/json"); | |||||
var basketContent = new StringContent(JsonSerializer.Serialize(newItem), System.Text.Encoding.UTF8, "application/json"); | |||||
var response = await _apiClient.PostAsync(uri, basketContent); | |||||
} | |||||
var response = await _apiClient.PostAsync(uri, basketContent); | |||||
} | } | ||||
} | } |
@ -1,91 +1,80 @@ | |||||
using Microsoft.AspNetCore.Mvc.Rendering; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using Microsoft.Extensions.Logging; | |||||
using Microsoft.Extensions.Options; | |||||
using System.Collections.Generic; | |||||
using System.Net.Http; | |||||
using System.Threading.Tasks; | |||||
using WebMVC.Infrastructure; | |||||
using System.Text.Json; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
public class CatalogService : ICatalogService | |||||
{ | { | ||||
public class CatalogService : ICatalogService | |||||
{ | |||||
private readonly IOptions<AppSettings> _settings; | |||||
private readonly HttpClient _httpClient; | |||||
private readonly ILogger<CatalogService> _logger; | |||||
private readonly IOptions<AppSettings> _settings; | |||||
private readonly HttpClient _httpClient; | |||||
private readonly ILogger<CatalogService> _logger; | |||||
private readonly string _remoteServiceBaseUrl; | |||||
private readonly string _remoteServiceBaseUrl; | |||||
public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<AppSettings> settings) | |||||
{ | |||||
_httpClient = httpClient; | |||||
_settings = settings; | |||||
_logger = logger; | |||||
public CatalogService(HttpClient httpClient, ILogger<CatalogService> logger, IOptions<AppSettings> settings) | |||||
{ | |||||
_httpClient = httpClient; | |||||
_settings = settings; | |||||
_logger = logger; | |||||
_remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/c/api/v1/catalog/"; | |||||
} | |||||
_remoteServiceBaseUrl = $"{_settings.Value.PurchaseUrl}/c/api/v1/catalog/"; | |||||
} | |||||
public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type) | |||||
{ | |||||
var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type); | |||||
public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type) | |||||
{ | |||||
var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type); | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
var catalog = JsonSerializer.Deserialize<Catalog>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
var catalog = JsonSerializer.Deserialize<Catalog>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
return catalog; | |||||
} | |||||
return catalog; | |||||
} | |||||
public async Task<IEnumerable<SelectListItem>> GetBrands() | |||||
{ | |||||
var uri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl); | |||||
public async Task<IEnumerable<SelectListItem>> GetBrands() | |||||
{ | |||||
var uri = API.Catalog.GetAllBrands(_remoteServiceBaseUrl); | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
var items = new List<SelectListItem>(); | |||||
var items = new List<SelectListItem>(); | |||||
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); | |||||
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); | |||||
using var brands = JsonDocument.Parse(responseString); | |||||
using var brands = JsonDocument.Parse(responseString); | |||||
foreach (JsonElement brand in brands.RootElement.EnumerateArray()) | |||||
foreach (JsonElement brand in brands.RootElement.EnumerateArray()) | |||||
{ | |||||
items.Add(new SelectListItem() | |||||
{ | { | ||||
items.Add(new SelectListItem() | |||||
{ | |||||
Value = brand.GetProperty("id").ToString(), | |||||
Text = brand.GetProperty("brand").ToString() | |||||
}); | |||||
} | |||||
return items; | |||||
Value = brand.GetProperty("id").ToString(), | |||||
Text = brand.GetProperty("brand").ToString() | |||||
}); | |||||
} | } | ||||
public async Task<IEnumerable<SelectListItem>> GetTypes() | |||||
{ | |||||
var uri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl); | |||||
return items; | |||||
} | |||||
public async Task<IEnumerable<SelectListItem>> GetTypes() | |||||
{ | |||||
var uri = API.Catalog.GetAllTypes(_remoteServiceBaseUrl); | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
var items = new List<SelectListItem>(); | |||||
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); | |||||
var items = new List<SelectListItem>(); | |||||
items.Add(new SelectListItem() { Value = null, Text = "All", Selected = true }); | |||||
using var catalogTypes = JsonDocument.Parse(responseString); | |||||
using var catalogTypes = JsonDocument.Parse(responseString); | |||||
foreach (JsonElement catalogType in catalogTypes.RootElement.EnumerateArray()) | |||||
foreach (JsonElement catalogType in catalogTypes.RootElement.EnumerateArray()) | |||||
{ | |||||
items.Add(new SelectListItem() | |||||
{ | { | ||||
items.Add(new SelectListItem() | |||||
{ | |||||
Value = catalogType.GetProperty("id").ToString(), | |||||
Text = catalogType.GetProperty("type").ToString() | |||||
}); | |||||
} | |||||
return items; | |||||
Value = catalogType.GetProperty("id").ToString(), | |||||
Text = catalogType.GetProperty("type").ToString() | |||||
}); | |||||
} | } | ||||
return items; | |||||
} | } | ||||
} | } |
@ -1,17 +1,13 @@ | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System.Collections.Generic; | |||||
using System.Threading.Tasks; | |||||
using WebMVC.Services.ModelDTOs; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public interface IBasketService | |||||
{ | { | ||||
public interface IBasketService | |||||
{ | |||||
Task<Basket> GetBasket(ApplicationUser user); | |||||
Task AddItemToBasket(ApplicationUser user, int productId); | |||||
Task<Basket> UpdateBasket(Basket basket); | |||||
Task Checkout(BasketDTO basket); | |||||
Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities); | |||||
Task<Order> GetOrderDraft(string basketId); | |||||
} | |||||
Task<Basket> GetBasket(ApplicationUser user); | |||||
Task AddItemToBasket(ApplicationUser user, int productId); | |||||
Task<Basket> UpdateBasket(Basket basket); | |||||
Task Checkout(BasketDTO basket); | |||||
Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities); | |||||
Task<Order> GetOrderDraft(string basketId); | |||||
} | } |
@ -1,14 +1,8 @@ | |||||
using Microsoft.AspNetCore.Mvc.Rendering; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System.Collections.Generic; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
public interface ICatalogService | |||||
{ | { | ||||
public interface ICatalogService | |||||
{ | |||||
Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type); | |||||
Task<IEnumerable<SelectListItem>> GetBrands(); | |||||
Task<IEnumerable<SelectListItem>> GetTypes(); | |||||
} | |||||
Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type); | |||||
Task<IEnumerable<SelectListItem>> GetBrands(); | |||||
Task<IEnumerable<SelectListItem>> GetTypes(); | |||||
} | } |
@ -1,9 +1,6 @@ | |||||
using System.Security.Principal; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
public interface IIdentityParser<T> | |||||
{ | { | ||||
public interface IIdentityParser<T> | |||||
{ | |||||
T Parse(IPrincipal principal); | |||||
} | |||||
T Parse(IPrincipal principal); | |||||
} | } |
@ -1,18 +1,13 @@ | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System.Collections.Generic; | |||||
using System.Threading.Tasks; | |||||
using WebMVC.Services.ModelDTOs; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
public interface IOrderingService | |||||
{ | { | ||||
public interface IOrderingService | |||||
{ | |||||
Task<List<Order>> GetMyOrders(ApplicationUser user); | |||||
Task<Order> GetOrder(ApplicationUser user, string orderId); | |||||
Task CancelOrder(string orderId); | |||||
Task ShipOrder(string orderId); | |||||
Order MapUserInfoIntoOrder(ApplicationUser user, Order order); | |||||
BasketDTO MapOrderToBasket(Order order); | |||||
void OverrideUserInfoIntoOrder(Order original, Order destination); | |||||
} | |||||
Task<List<Order>> GetMyOrders(ApplicationUser user); | |||||
Task<Order> GetOrder(ApplicationUser user, string orderId); | |||||
Task CancelOrder(string orderId); | |||||
Task ShipOrder(string orderId); | |||||
Order MapUserInfoIntoOrder(ApplicationUser user, Order order); | |||||
BasketDTO MapOrderToBasket(Order order); | |||||
void OverrideUserInfoIntoOrder(Order original, Order destination); | |||||
} | } |
@ -1,42 +1,33 @@ | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System; | |||||
using System.Linq; | |||||
using System.Security.Claims; | |||||
using System.Security.Principal; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
public class IdentityParser : IIdentityParser<ApplicationUser> | |||||
{ | { | ||||
public class IdentityParser : IIdentityParser<ApplicationUser> | |||||
public ApplicationUser Parse(IPrincipal principal) | |||||
{ | { | ||||
public ApplicationUser Parse(IPrincipal principal) | |||||
// Pattern matching 'is' expression | |||||
// assigns "claims" if "principal" is a "ClaimsPrincipal" | |||||
if (principal is ClaimsPrincipal claims) | |||||
{ | { | ||||
// Pattern matching 'is' expression | |||||
// assigns "claims" if "principal" is a "ClaimsPrincipal" | |||||
if (principal is ClaimsPrincipal claims) | |||||
return new ApplicationUser | |||||
{ | { | ||||
return new ApplicationUser | |||||
{ | |||||
CardHolderName = claims.Claims.FirstOrDefault(x => x.Type == "card_holder")?.Value ?? "", | |||||
CardNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_number")?.Value ?? "", | |||||
Expiration = claims.Claims.FirstOrDefault(x => x.Type == "card_expiration")?.Value ?? "", | |||||
CardType = int.Parse(claims.Claims.FirstOrDefault(x => x.Type == "missing")?.Value ?? "0"), | |||||
City = claims.Claims.FirstOrDefault(x => x.Type == "address_city")?.Value ?? "", | |||||
Country = claims.Claims.FirstOrDefault(x => x.Type == "address_country")?.Value ?? "", | |||||
Email = claims.Claims.FirstOrDefault(x => x.Type == "email")?.Value ?? "", | |||||
Id = claims.Claims.FirstOrDefault(x => x.Type == "sub")?.Value ?? "", | |||||
LastName = claims.Claims.FirstOrDefault(x => x.Type == "last_name")?.Value ?? "", | |||||
Name = claims.Claims.FirstOrDefault(x => x.Type == "name")?.Value ?? "", | |||||
PhoneNumber = claims.Claims.FirstOrDefault(x => x.Type == "phone_number")?.Value ?? "", | |||||
SecurityNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_security_number")?.Value ?? "", | |||||
State = claims.Claims.FirstOrDefault(x => x.Type == "address_state")?.Value ?? "", | |||||
Street = claims.Claims.FirstOrDefault(x => x.Type == "address_street")?.Value ?? "", | |||||
ZipCode = claims.Claims.FirstOrDefault(x => x.Type == "address_zip_code")?.Value ?? "" | |||||
}; | |||||
} | |||||
throw new ArgumentException(message: "The principal must be a ClaimsPrincipal", paramName: nameof(principal)); | |||||
CardHolderName = claims.Claims.FirstOrDefault(x => x.Type == "card_holder")?.Value ?? "", | |||||
CardNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_number")?.Value ?? "", | |||||
Expiration = claims.Claims.FirstOrDefault(x => x.Type == "card_expiration")?.Value ?? "", | |||||
CardType = int.Parse(claims.Claims.FirstOrDefault(x => x.Type == "missing")?.Value ?? "0"), | |||||
City = claims.Claims.FirstOrDefault(x => x.Type == "address_city")?.Value ?? "", | |||||
Country = claims.Claims.FirstOrDefault(x => x.Type == "address_country")?.Value ?? "", | |||||
Email = claims.Claims.FirstOrDefault(x => x.Type == "email")?.Value ?? "", | |||||
Id = claims.Claims.FirstOrDefault(x => x.Type == "sub")?.Value ?? "", | |||||
LastName = claims.Claims.FirstOrDefault(x => x.Type == "last_name")?.Value ?? "", | |||||
Name = claims.Claims.FirstOrDefault(x => x.Type == "name")?.Value ?? "", | |||||
PhoneNumber = claims.Claims.FirstOrDefault(x => x.Type == "phone_number")?.Value ?? "", | |||||
SecurityNumber = claims.Claims.FirstOrDefault(x => x.Type == "card_security_number")?.Value ?? "", | |||||
State = claims.Claims.FirstOrDefault(x => x.Type == "address_state")?.Value ?? "", | |||||
Street = claims.Claims.FirstOrDefault(x => x.Type == "address_street")?.Value ?? "", | |||||
ZipCode = claims.Claims.FirstOrDefault(x => x.Type == "address_zip_code")?.Value ?? "" | |||||
}; | |||||
} | } | ||||
throw new ArgumentException(message: "The principal must be a ClaimsPrincipal", paramName: nameof(principal)); | |||||
} | } | ||||
} | } | ||||
@ -1,37 +1,32 @@ | |||||
using System; | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace WebMVC.Services.ModelDTOs; | |||||
namespace WebMVC.Services.ModelDTOs | |||||
public record BasketDTO | |||||
{ | { | ||||
public record BasketDTO | |||||
{ | |||||
[Required] | |||||
public string City { get; init; } | |||||
[Required] | |||||
public string Street { get; init; } | |||||
[Required] | |||||
public string State { get; init; } | |||||
[Required] | |||||
public string Country { get; init; } | |||||
[Required] | |||||
public string City { get; init; } | |||||
[Required] | |||||
public string Street { get; init; } | |||||
[Required] | |||||
public string State { get; init; } | |||||
[Required] | |||||
public string Country { get; init; } | |||||
public string ZipCode { get; init; } | |||||
[Required] | |||||
public string CardNumber { get; init; } | |||||
[Required] | |||||
public string CardHolderName { get; init; } | |||||
public string ZipCode { get; init; } | |||||
[Required] | |||||
public string CardNumber { get; init; } | |||||
[Required] | |||||
public string CardHolderName { get; init; } | |||||
[Required] | |||||
public DateTime CardExpiration { get; init; } | |||||
[Required] | |||||
public DateTime CardExpiration { get; init; } | |||||
[Required] | |||||
public string CardSecurityNumber { get; init; } | |||||
[Required] | |||||
public string CardSecurityNumber { get; init; } | |||||
public int CardTypeId { get; init; } | |||||
public int CardTypeId { get; init; } | |||||
public string Buyer { get; init; } | |||||
public string Buyer { get; init; } | |||||
[Required] | |||||
public Guid RequestId { get; init; } | |||||
} | |||||
[Required] | |||||
public Guid RequestId { get; init; } | |||||
} | } | ||||
@ -1,8 +1,7 @@ | |||||
namespace WebMVC.Services.ModelDTOs | |||||
namespace WebMVC.Services.ModelDTOs; | |||||
public record LocationDTO | |||||
{ | { | ||||
public record LocationDTO | |||||
{ | |||||
public double Longitude { get; init; } | |||||
public double Latitude { get; init; } | |||||
} | |||||
public double Longitude { get; init; } | |||||
public double Latitude { get; init; } | |||||
} | } |
@ -1,10 +1,7 @@ | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace WebMVC.Services.ModelDTOs; | |||||
namespace WebMVC.Services.ModelDTOs | |||||
public record OrderDTO | |||||
{ | { | ||||
public record OrderDTO | |||||
{ | |||||
[Required] | |||||
public string OrderNumber { get; init; } | |||||
} | |||||
} | |||||
[Required] | |||||
public string OrderNumber { get; init; } | |||||
} |
@ -1,20 +1,19 @@ | |||||
namespace WebMVC.Services.ModelDTOs | |||||
namespace WebMVC.Services.ModelDTOs; | |||||
public record OrderProcessAction | |||||
{ | { | ||||
public record OrderProcessAction | |||||
{ | |||||
public string Code { get; } | |||||
public string Name { get; } | |||||
public string Code { get; } | |||||
public string Name { get; } | |||||
public static OrderProcessAction Ship = new OrderProcessAction(nameof(Ship).ToLowerInvariant(), "Ship"); | |||||
public static OrderProcessAction Ship = new OrderProcessAction(nameof(Ship).ToLowerInvariant(), "Ship"); | |||||
protected OrderProcessAction() | |||||
{ | |||||
} | |||||
protected OrderProcessAction() | |||||
{ | |||||
} | |||||
public OrderProcessAction(string code, string name) | |||||
{ | |||||
Code = code; | |||||
Name = name; | |||||
} | |||||
public OrderProcessAction(string code, string name) | |||||
{ | |||||
Code = code; | |||||
Name = name; | |||||
} | } | ||||
} | } |
@ -1,149 +1,140 @@ | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using Microsoft.Extensions.Options; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Net.Http; | |||||
using System.Threading.Tasks; | |||||
using WebMVC.Infrastructure; | |||||
using WebMVC.Services.ModelDTOs; | |||||
using System.Text.Json; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services | |||||
{ | |||||
public class OrderingService : IOrderingService | |||||
{ | |||||
private HttpClient _httpClient; | |||||
private readonly string _remoteServiceBaseUrl; | |||||
private readonly IOptions<AppSettings> _settings; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public OrderingService(HttpClient httpClient, IOptions<AppSettings> settings) | |||||
{ | |||||
_httpClient = httpClient; | |||||
_settings = settings; | |||||
public class OrderingService : IOrderingService | |||||
{ | |||||
private HttpClient _httpClient; | |||||
private readonly string _remoteServiceBaseUrl; | |||||
private readonly IOptions<AppSettings> _settings; | |||||
_remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/o/api/v1/orders"; | |||||
} | |||||
async public Task<Order> GetOrder(ApplicationUser user, string id) | |||||
{ | |||||
var uri = API.Order.GetOrder(_remoteServiceBaseUrl, id); | |||||
public OrderingService(HttpClient httpClient, IOptions<AppSettings> settings) | |||||
{ | |||||
_httpClient = httpClient; | |||||
_settings = settings; | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
_remoteServiceBaseUrl = $"{settings.Value.PurchaseUrl}/o/api/v1/orders"; | |||||
} | |||||
var response = JsonSerializer.Deserialize<Order>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
async public Task<Order> GetOrder(ApplicationUser user, string id) | |||||
{ | |||||
var uri = API.Order.GetOrder(_remoteServiceBaseUrl, id); | |||||
return response; | |||||
} | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
async public Task<List<Order>> GetMyOrders(ApplicationUser user) | |||||
var response = JsonSerializer.Deserialize<Order>(responseString, new JsonSerializerOptions | |||||
{ | { | ||||
var uri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl); | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
return response; | |||||
} | |||||
var response = JsonSerializer.Deserialize<List<Order>>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
async public Task<List<Order>> GetMyOrders(ApplicationUser user) | |||||
{ | |||||
var uri = API.Order.GetAllMyOrders(_remoteServiceBaseUrl); | |||||
return response; | |||||
} | |||||
var responseString = await _httpClient.GetStringAsync(uri); | |||||
var response = JsonSerializer.Deserialize<List<Order>>(responseString, new JsonSerializerOptions | |||||
{ | |||||
PropertyNameCaseInsensitive = true | |||||
}); | |||||
return response; | |||||
} | |||||
async public Task CancelOrder(string orderId) | |||||
{ | |||||
var order = new OrderDTO() | |||||
{ | |||||
OrderNumber = orderId | |||||
}; | |||||
var uri = API.Order.CancelOrder(_remoteServiceBaseUrl); | |||||
var orderContent = new StringContent(JsonSerializer.Serialize(order), System.Text.Encoding.UTF8, "application/json"); | |||||
var response = await _httpClient.PutAsync(uri, orderContent); | |||||
async public Task CancelOrder(string orderId) | |||||
{ | |||||
var order = new OrderDTO() | |||||
{ | |||||
OrderNumber = orderId | |||||
}; | |||||
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) | |||||
{ | |||||
throw new Exception("Error cancelling order, try later."); | |||||
} | |||||
var uri = API.Order.CancelOrder(_remoteServiceBaseUrl); | |||||
var orderContent = new StringContent(JsonSerializer.Serialize(order), System.Text.Encoding.UTF8, "application/json"); | |||||
response.EnsureSuccessStatusCode(); | |||||
} | |||||
var response = await _httpClient.PutAsync(uri, orderContent); | |||||
async public Task ShipOrder(string orderId) | |||||
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) | |||||
{ | { | ||||
var order = new OrderDTO() | |||||
{ | |||||
OrderNumber = orderId | |||||
}; | |||||
throw new Exception("Error cancelling order, try later."); | |||||
} | |||||
var uri = API.Order.ShipOrder(_remoteServiceBaseUrl); | |||||
var orderContent = new StringContent(JsonSerializer.Serialize(order), System.Text.Encoding.UTF8, "application/json"); | |||||
response.EnsureSuccessStatusCode(); | |||||
} | |||||
var response = await _httpClient.PutAsync(uri, orderContent); | |||||
async public Task ShipOrder(string orderId) | |||||
{ | |||||
var order = new OrderDTO() | |||||
{ | |||||
OrderNumber = orderId | |||||
}; | |||||
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) | |||||
{ | |||||
throw new Exception("Error in ship order process, try later."); | |||||
} | |||||
var uri = API.Order.ShipOrder(_remoteServiceBaseUrl); | |||||
var orderContent = new StringContent(JsonSerializer.Serialize(order), System.Text.Encoding.UTF8, "application/json"); | |||||
response.EnsureSuccessStatusCode(); | |||||
} | |||||
var response = await _httpClient.PutAsync(uri, orderContent); | |||||
public void OverrideUserInfoIntoOrder(Order original, Order destination) | |||||
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) | |||||
{ | { | ||||
destination.City = original.City; | |||||
destination.Street = original.Street; | |||||
destination.State = original.State; | |||||
destination.Country = original.Country; | |||||
destination.ZipCode = original.ZipCode; | |||||
destination.CardNumber = original.CardNumber; | |||||
destination.CardHolderName = original.CardHolderName; | |||||
destination.CardExpiration = original.CardExpiration; | |||||
destination.CardSecurityNumber = original.CardSecurityNumber; | |||||
throw new Exception("Error in ship order process, try later."); | |||||
} | } | ||||
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order) | |||||
{ | |||||
order.City = user.City; | |||||
order.Street = user.Street; | |||||
order.State = user.State; | |||||
order.Country = user.Country; | |||||
order.ZipCode = user.ZipCode; | |||||
order.CardNumber = user.CardNumber; | |||||
order.CardHolderName = user.CardHolderName; | |||||
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1); | |||||
order.CardSecurityNumber = user.SecurityNumber; | |||||
return order; | |||||
} | |||||
response.EnsureSuccessStatusCode(); | |||||
} | |||||
public BasketDTO MapOrderToBasket(Order order) | |||||
public void OverrideUserInfoIntoOrder(Order original, Order destination) | |||||
{ | |||||
destination.City = original.City; | |||||
destination.Street = original.Street; | |||||
destination.State = original.State; | |||||
destination.Country = original.Country; | |||||
destination.ZipCode = original.ZipCode; | |||||
destination.CardNumber = original.CardNumber; | |||||
destination.CardHolderName = original.CardHolderName; | |||||
destination.CardExpiration = original.CardExpiration; | |||||
destination.CardSecurityNumber = original.CardSecurityNumber; | |||||
} | |||||
public Order MapUserInfoIntoOrder(ApplicationUser user, Order order) | |||||
{ | |||||
order.City = user.City; | |||||
order.Street = user.Street; | |||||
order.State = user.State; | |||||
order.Country = user.Country; | |||||
order.ZipCode = user.ZipCode; | |||||
order.CardNumber = user.CardNumber; | |||||
order.CardHolderName = user.CardHolderName; | |||||
order.CardExpiration = new DateTime(int.Parse("20" + user.Expiration.Split('/')[1]), int.Parse(user.Expiration.Split('/')[0]), 1); | |||||
order.CardSecurityNumber = user.SecurityNumber; | |||||
return order; | |||||
} | |||||
public BasketDTO MapOrderToBasket(Order order) | |||||
{ | |||||
order.CardExpirationApiFormat(); | |||||
return new BasketDTO() | |||||
{ | { | ||||
order.CardExpirationApiFormat(); | |||||
return new BasketDTO() | |||||
{ | |||||
City = order.City, | |||||
Street = order.Street, | |||||
State = order.State, | |||||
Country = order.Country, | |||||
ZipCode = order.ZipCode, | |||||
CardNumber = order.CardNumber, | |||||
CardHolderName = order.CardHolderName, | |||||
CardExpiration = order.CardExpiration, | |||||
CardSecurityNumber = order.CardSecurityNumber, | |||||
CardTypeId = 1, | |||||
Buyer = order.Buyer, | |||||
RequestId = order.RequestId | |||||
}; | |||||
} | |||||
City = order.City, | |||||
Street = order.Street, | |||||
State = order.State, | |||||
Country = order.Country, | |||||
ZipCode = order.ZipCode, | |||||
CardNumber = order.CardNumber, | |||||
CardHolderName = order.CardHolderName, | |||||
CardExpiration = order.CardExpiration, | |||||
CardSecurityNumber = order.CardSecurityNumber, | |||||
CardTypeId = 1, | |||||
Buyer = order.Buyer, | |||||
RequestId = order.RequestId | |||||
}; | |||||
} | } | ||||
} | } |
@ -1,212 +1,190 @@ | |||||
using Devspaces.Support; | |||||
using HealthChecks.UI.Client; | |||||
using Microsoft.AspNetCore.Authentication.Cookies; | |||||
using Microsoft.AspNetCore.Authentication.JwtBearer; | |||||
using Microsoft.AspNetCore.Builder; | |||||
using Microsoft.AspNetCore.DataProtection; | |||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks; | |||||
using Microsoft.AspNetCore.Hosting; | |||||
using Microsoft.AspNetCore.Http; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using Microsoft.Extensions.Configuration; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Diagnostics.HealthChecks; | |||||
using Microsoft.Extensions.Hosting; | |||||
using Microsoft.IdentityModel.Logging; | |||||
using StackExchange.Redis; | |||||
using System; | |||||
using System.IdentityModel.Tokens.Jwt; | |||||
using WebMVC.Infrastructure; | |||||
namespace Microsoft.eShopOnContainers.WebMVC | |||||
namespace Microsoft.eShopOnContainers.WebMVC; | |||||
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 IoC container. | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddControllersWithViews() | |||||
.Services | |||||
.AddAppInsight(Configuration) | |||||
.AddHealthChecks(Configuration) | |||||
.AddCustomMvc(Configuration) | |||||
.AddDevspaces() | |||||
.AddHttpClientServices(Configuration); | |||||
// This method gets called by the runtime. Use this method to add services to the IoC container. | |||||
public void ConfigureServices(IServiceCollection services) | |||||
{ | |||||
services.AddControllersWithViews() | |||||
.Services | |||||
.AddAppInsight(Configuration) | |||||
.AddHealthChecks(Configuration) | |||||
.AddCustomMvc(Configuration) | |||||
.AddDevspaces() | |||||
.AddHttpClientServices(Configuration); | |||||
IdentityModelEventSource.ShowPII = true; // Caution! Do NOT use in production: https://aka.ms/IdentityModel/PII | |||||
IdentityModelEventSource.ShowPII = true; // Caution! Do NOT use in production: https://aka.ms/IdentityModel/PII | |||||
services.AddControllers(); | |||||
services.AddControllers(); | |||||
services.AddCustomAuthentication(Configuration); | |||||
} | |||||
services.AddCustomAuthentication(Configuration); | |||||
} | |||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) | |||||
{ | |||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | |||||
if (env.IsDevelopment()) | |||||
{ | { | ||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub"); | |||||
if (env.IsDevelopment()) | |||||
{ | |||||
app.UseDeveloperExceptionPage(); | |||||
} | |||||
else | |||||
{ | |||||
app.UseExceptionHandler("/Error"); | |||||
} | |||||
app.UseDeveloperExceptionPage(); | |||||
} | |||||
else | |||||
{ | |||||
app.UseExceptionHandler("/Error"); | |||||
} | |||||
var pathBase = Configuration["PATH_BASE"]; | |||||
var pathBase = Configuration["PATH_BASE"]; | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
if (!string.IsNullOrEmpty(pathBase)) | |||||
{ | |||||
app.UsePathBase(pathBase); | |||||
} | |||||
app.UseStaticFiles(); | |||||
app.UseSession(); | |||||
app.UseStaticFiles(); | |||||
app.UseSession(); | |||||
WebContextSeed.Seed(app, env); | |||||
WebContextSeed.Seed(app, env); | |||||
// Fix samesite issue when running eShop from docker-compose locally as by default http protocol is being used | |||||
// Refer to https://github.com/dotnet-architecture/eShopOnContainers/issues/1391 | |||||
app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = AspNetCore.Http.SameSiteMode.Lax }); | |||||
// Fix samesite issue when running eShop from docker-compose locally as by default http protocol is being used | |||||
// Refer to https://github.com/dotnet-architecture/eShopOnContainers/issues/1391 | |||||
app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = AspNetCore.Http.SameSiteMode.Lax }); | |||||
app.UseRouting(); | |||||
app.UseRouting(); | |||||
app.UseAuthentication(); | |||||
app.UseAuthorization(); | |||||
app.UseAuthentication(); | |||||
app.UseAuthorization(); | |||||
app.UseEndpoints(endpoints => | |||||
app.UseEndpoints(endpoints => | |||||
{ | |||||
endpoints.MapControllerRoute("default", "{controller=Catalog}/{action=Index}/{id?}"); | |||||
endpoints.MapControllerRoute("defaultError", "{controller=Error}/{action=Error}"); | |||||
endpoints.MapControllers(); | |||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions | |||||
{ | { | ||||
endpoints.MapControllerRoute("default", "{controller=Catalog}/{action=Index}/{id?}"); | |||||
endpoints.MapControllerRoute("defaultError", "{controller=Error}/{action=Error}"); | |||||
endpoints.MapControllers(); | |||||
endpoints.MapHealthChecks("/liveness", new HealthCheckOptions | |||||
{ | |||||
Predicate = r => r.Name.Contains("self") | |||||
}); | |||||
endpoints.MapHealthChecks("/hc", new HealthCheckOptions() | |||||
{ | |||||
Predicate = _ => true, | |||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | |||||
}); | |||||
Predicate = r => r.Name.Contains("self") | |||||
}); | }); | ||||
} | |||||
endpoints.MapHealthChecks("/hc", new HealthCheckOptions() | |||||
{ | |||||
Predicate = _ => true, | |||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse | |||||
}); | |||||
}); | |||||
} | } | ||||
} | |||||
static class ServiceCollectionExtensions | |||||
{ | |||||
static class ServiceCollectionExtensions | |||||
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) | |||||
{ | { | ||||
services.AddApplicationInsightsTelemetry(configuration); | |||||
services.AddApplicationInsightsKubernetesEnricher(); | |||||
public static IServiceCollection AddAppInsight(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddApplicationInsightsTelemetry(configuration); | |||||
services.AddApplicationInsightsKubernetesEnricher(); | |||||
return services; | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddHealthChecks() | |||||
.AddCheck("self", () => HealthCheckResult.Healthy()) | |||||
.AddUrlGroup(new Uri(configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }); | |||||
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddHealthChecks() | |||||
.AddCheck("self", () => HealthCheckResult.Healthy()) | |||||
.AddUrlGroup(new Uri(configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" }); | |||||
return services; | |||||
} | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddOptions(); | |||||
services.Configure<AppSettings>(configuration); | |||||
services.AddSession(); | |||||
services.AddDistributedMemoryCache(); | |||||
public static IServiceCollection AddCustomMvc(this IServiceCollection services, IConfiguration configuration) | |||||
if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString) | |||||
{ | { | ||||
services.AddOptions(); | |||||
services.Configure<AppSettings>(configuration); | |||||
services.AddSession(); | |||||
services.AddDistributedMemoryCache(); | |||||
if (configuration.GetValue<string>("IsClusterEnv") == bool.TrueString) | |||||
services.AddDataProtection(opts => | |||||
{ | { | ||||
services.AddDataProtection(opts => | |||||
{ | |||||
opts.ApplicationDiscriminator = "eshop.webmvc"; | |||||
}) | |||||
.PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys"); | |||||
} | |||||
return services; | |||||
opts.ApplicationDiscriminator = "eshop.webmvc"; | |||||
}) | |||||
.PersistKeysToStackExchangeRedis(ConnectionMultiplexer.Connect(configuration["DPConnectionString"]), "DataProtection-Keys"); | |||||
} | } | ||||
// Adds all Http client services | |||||
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||||
return services; | |||||
} | |||||
//register delegating handlers | |||||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); | |||||
services.AddTransient<HttpClientRequestIdDelegatingHandler>(); | |||||
// Adds all Http client services | |||||
public static IServiceCollection AddHttpClientServices(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); | |||||
//set 5 min as the lifetime for each HttpMessageHandler int the pool | |||||
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)).AddDevspacesSupport(); | |||||
//register delegating handlers | |||||
services.AddTransient<HttpClientAuthorizationDelegatingHandler>(); | |||||
services.AddTransient<HttpClientRequestIdDelegatingHandler>(); | |||||
//add http client services | |||||
services.AddHttpClient<IBasketService, BasketService>() | |||||
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes | |||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||||
.AddDevspacesSupport(); | |||||
//set 5 min as the lifetime for each HttpMessageHandler int the pool | |||||
services.AddHttpClient("extendedhandlerlifetime").SetHandlerLifetime(TimeSpan.FromMinutes(5)).AddDevspacesSupport(); | |||||
services.AddHttpClient<ICatalogService, CatalogService>() | |||||
.AddDevspacesSupport(); | |||||
//add http client services | |||||
services.AddHttpClient<IBasketService, BasketService>() | |||||
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Sample. Default lifetime is 2 minutes | |||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||||
.AddDevspacesSupport(); | |||||
services.AddHttpClient<IOrderingService, OrderingService>() | |||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||||
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>() | |||||
.AddDevspacesSupport(); | |||||
services.AddHttpClient<ICatalogService, CatalogService>() | |||||
.AddDevspacesSupport(); | |||||
services.AddHttpClient<IOrderingService, OrderingService>() | |||||
.AddHttpMessageHandler<HttpClientAuthorizationDelegatingHandler>() | |||||
.AddHttpMessageHandler<HttpClientRequestIdDelegatingHandler>() | |||||
.AddDevspacesSupport(); | |||||
//add custom application services | |||||
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); | |||||
return services; | |||||
} | |||||
//add custom application services | |||||
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); | |||||
return services; | |||||
} | |||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
var identityUrl = configuration.GetValue<string>("IdentityUrl"); | |||||
var callBackUrl = configuration.GetValue<string>("CallBackUrl"); | |||||
var sessionCookieLifetime = configuration.GetValue("SessionCookieLifetimeMinutes", 60); | |||||
// Add Authentication services | |||||
public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) | |||||
{ | |||||
var identityUrl = configuration.GetValue<string>("IdentityUrl"); | |||||
var callBackUrl = configuration.GetValue<string>("CallBackUrl"); | |||||
var sessionCookieLifetime = configuration.GetValue("SessionCookieLifetimeMinutes", 60); | |||||
services.AddAuthentication(options => | |||||
{ | |||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
}) | |||||
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime)) | |||||
.AddOpenIdConnect(options => | |||||
{ | |||||
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; | |||||
options.Authority = identityUrl.ToString(); | |||||
options.SignedOutRedirectUri = callBackUrl.ToString(); | |||||
options.ClientId = "mvc"; | |||||
options.ClientSecret = "secret"; | |||||
options.ResponseType = "code id_token"; | |||||
options.SaveTokens = true; | |||||
options.GetClaimsFromUserInfoEndpoint = true; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Scope.Add("openid"); | |||||
options.Scope.Add("profile"); | |||||
options.Scope.Add("orders"); | |||||
options.Scope.Add("basket"); | |||||
options.Scope.Add("webshoppingagg"); | |||||
options.Scope.Add("orders.signalrhub"); | |||||
}); | |||||
// Add Authentication services | |||||
return services; | |||||
} | |||||
services.AddAuthentication(options => | |||||
{ | |||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; | |||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | |||||
}) | |||||
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime)) | |||||
.AddOpenIdConnect(options => | |||||
{ | |||||
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; | |||||
options.Authority = identityUrl.ToString(); | |||||
options.SignedOutRedirectUri = callBackUrl.ToString(); | |||||
options.ClientId = "mvc"; | |||||
options.ClientSecret = "secret"; | |||||
options.ResponseType = "code id_token"; | |||||
options.SaveTokens = true; | |||||
options.GetClaimsFromUserInfoEndpoint = true; | |||||
options.RequireHttpsMetadata = false; | |||||
options.Scope.Add("openid"); | |||||
options.Scope.Add("profile"); | |||||
options.Scope.Add("orders"); | |||||
options.Scope.Add("basket"); | |||||
options.Scope.Add("webshoppingagg"); | |||||
options.Scope.Add("orders.signalrhub"); | |||||
}); | |||||
return services; | |||||
} | } | ||||
} | } |
@ -1,37 +1,30 @@ | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels.CartViewModels; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents | |||||
public class Cart : ViewComponent | |||||
{ | { | ||||
public class Cart : ViewComponent | |||||
{ | |||||
private readonly IBasketService _cartSvc; | |||||
private readonly IBasketService _cartSvc; | |||||
public Cart(IBasketService cartSvc) => _cartSvc = cartSvc; | |||||
public Cart(IBasketService cartSvc) => _cartSvc = cartSvc; | |||||
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user) | |||||
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user) | |||||
{ | |||||
var vm = new CartComponentViewModel(); | |||||
try | |||||
{ | { | ||||
var vm = new CartComponentViewModel(); | |||||
try | |||||
{ | |||||
var itemsInCart = await ItemsInCartAsync(user); | |||||
vm.ItemsCount = itemsInCart; | |||||
return View(vm); | |||||
} | |||||
catch | |||||
{ | |||||
ViewBag.IsBasketInoperative = true; | |||||
} | |||||
var itemsInCart = await ItemsInCartAsync(user); | |||||
vm.ItemsCount = itemsInCart; | |||||
return View(vm); | return View(vm); | ||||
} | } | ||||
private async Task<int> ItemsInCartAsync(ApplicationUser user) | |||||
catch | |||||
{ | { | ||||
var basket = await _cartSvc.GetBasket(user); | |||||
return basket.Items.Count; | |||||
ViewBag.IsBasketInoperative = true; | |||||
} | } | ||||
return View(vm); | |||||
} | |||||
private async Task<int> ItemsInCartAsync(ApplicationUser user) | |||||
{ | |||||
var basket = await _cartSvc.GetBasket(user); | |||||
return basket.Items.Count; | |||||
} | } | ||||
} | } |
@ -1,33 +1,26 @@ | |||||
using Microsoft.AspNetCore.Mvc; | |||||
using Microsoft.eShopOnContainers.WebMVC.Services; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
using System; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents | |||||
public class CartList : ViewComponent | |||||
{ | { | ||||
public class CartList : ViewComponent | |||||
{ | |||||
private readonly IBasketService _cartSvc; | |||||
private readonly IBasketService _cartSvc; | |||||
public CartList(IBasketService cartSvc) => _cartSvc = cartSvc; | |||||
public CartList(IBasketService cartSvc) => _cartSvc = cartSvc; | |||||
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user) | |||||
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user) | |||||
{ | |||||
var vm = new Basket(); | |||||
try | |||||
{ | { | ||||
var vm = new Basket(); | |||||
try | |||||
{ | |||||
vm = await GetItemsAsync(user); | |||||
return View(vm); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative, please try later on. ({ex.GetType().Name} - {ex.Message}))"; | |||||
} | |||||
vm = await GetItemsAsync(user); | |||||
return View(vm); | return View(vm); | ||||
} | } | ||||
catch (Exception ex) | |||||
{ | |||||
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative, please try later on. ({ex.GetType().Name} - {ex.Message}))"; | |||||
} | |||||
private Task<Basket> GetItemsAsync(ApplicationUser user) => _cartSvc.GetBasket(user); | |||||
return View(vm); | |||||
} | } | ||||
private Task<Basket> GetItemsAsync(ApplicationUser user) => _cartSvc.GetBasket(user); | |||||
} | } |
@ -1,31 +1,27 @@ | |||||
using System; | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Annotations; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Annotations | |||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] | |||||
public class CardExpirationAttribute : ValidationAttribute | |||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] | |||||
public class CardExpirationAttribute : ValidationAttribute | |||||
public override bool IsValid(object value) | |||||
{ | { | ||||
public override bool IsValid(object value) | |||||
{ | |||||
if (value == null) | |||||
return false; | |||||
if (value == null) | |||||
return false; | |||||
var monthString = value.ToString().Split('/')[0]; | |||||
var yearString = $"20{value.ToString().Split('/')[1]}"; | |||||
// Use the 'out' variable initializer to simplify | |||||
// the logic of validating the expiration date | |||||
if ((int.TryParse(monthString, out var month)) && | |||||
(int.TryParse(yearString, out var year))) | |||||
{ | |||||
DateTime d = new DateTime(year, month, 1); | |||||
var monthString = value.ToString().Split('/')[0]; | |||||
var yearString = $"20{value.ToString().Split('/')[1]}"; | |||||
// Use the 'out' variable initializer to simplify | |||||
// the logic of validating the expiration date | |||||
if ((int.TryParse(monthString, out var month)) && | |||||
(int.TryParse(yearString, out var year))) | |||||
{ | |||||
DateTime d = new DateTime(year, month, 1); | |||||
return d > DateTime.UtcNow; | |||||
} | |||||
else | |||||
{ | |||||
return false; | |||||
} | |||||
return d > DateTime.UtcNow; | |||||
} | |||||
else | |||||
{ | |||||
return false; | |||||
} | } | ||||
} | } | ||||
} | } |
@ -1,22 +1,18 @@ | |||||
using System; | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace WebMVC.ViewModels.Annotations; | |||||
namespace WebMVC.ViewModels.Annotations | |||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] | |||||
public class LatitudeCoordinate : ValidationAttribute | |||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] | |||||
public class LatitudeCoordinate : ValidationAttribute | |||||
protected override ValidationResult | |||||
IsValid(object value, ValidationContext validationContext) | |||||
{ | { | ||||
protected override ValidationResult | |||||
IsValid(object value, ValidationContext validationContext) | |||||
double coordinate; | |||||
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -90 || coordinate > 90)) | |||||
{ | { | ||||
double coordinate; | |||||
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -90 || coordinate > 90)) | |||||
{ | |||||
return new ValidationResult | |||||
("Latitude must be between -90 and 90 degrees inclusive."); | |||||
} | |||||
return ValidationResult.Success; | |||||
return new ValidationResult | |||||
("Latitude must be between -90 and 90 degrees inclusive."); | |||||
} | } | ||||
return ValidationResult.Success; | |||||
} | } | ||||
} | } |
@ -1,22 +1,18 @@ | |||||
using System; | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace WebMVC.ViewModels.Annotations; | |||||
namespace WebMVC.ViewModels.Annotations | |||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] | |||||
public class LongitudeCoordinate : ValidationAttribute | |||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] | |||||
public class LongitudeCoordinate : ValidationAttribute | |||||
protected override ValidationResult | |||||
IsValid(object value, ValidationContext validationContext) | |||||
{ | { | ||||
protected override ValidationResult | |||||
IsValid(object value, ValidationContext validationContext) | |||||
double coordinate; | |||||
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -180 || coordinate > 180)) | |||||
{ | { | ||||
double coordinate; | |||||
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -180 || coordinate > 180)) | |||||
{ | |||||
return new ValidationResult | |||||
("Longitude must be between -180 and 180 degrees inclusive."); | |||||
} | |||||
return ValidationResult.Success; | |||||
return new ValidationResult | |||||
("Longitude must be between -180 and 180 degrees inclusive."); | |||||
} | } | ||||
return ValidationResult.Success; | |||||
} | } | ||||
} | } |
@ -1,28 +1,24 @@ | |||||
using Microsoft.AspNetCore.Identity; | |||||
using System.ComponentModel.DataAnnotations; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
// Add profile data for application users by adding properties to the ApplicationUser class | |||||
public class ApplicationUser : IdentityUser | |||||
{ | { | ||||
// Add profile data for application users by adding properties to the ApplicationUser class | |||||
public class ApplicationUser : IdentityUser | |||||
{ | |||||
public string CardNumber { get; set; } | |||||
public string SecurityNumber { get; set; } | |||||
public string Expiration { get; set; } | |||||
public string CardHolderName { get; set; } | |||||
public int CardType { get; set; } | |||||
public string Street { get; set; } | |||||
public string City { get; set; } | |||||
public string State { get; set; } | |||||
public string StateCode { get; set; } | |||||
public string Country { get; set; } | |||||
public string CountryCode { get; set; } | |||||
public string ZipCode { get; set; } | |||||
public double Latitude { get; set; } | |||||
public double Longitude { get; set; } | |||||
[Required] | |||||
public string Name { get; set; } | |||||
[Required] | |||||
public string LastName { get; set; } | |||||
} | |||||
public string CardNumber { get; set; } | |||||
public string SecurityNumber { get; set; } | |||||
public string Expiration { get; set; } | |||||
public string CardHolderName { get; set; } | |||||
public int CardType { get; set; } | |||||
public string Street { get; set; } | |||||
public string City { get; set; } | |||||
public string State { get; set; } | |||||
public string StateCode { get; set; } | |||||
public string Country { get; set; } | |||||
public string CountryCode { get; set; } | |||||
public string ZipCode { get; set; } | |||||
public double Latitude { get; set; } | |||||
public double Longitude { get; set; } | |||||
[Required] | |||||
public string Name { get; set; } | |||||
[Required] | |||||
public string LastName { get; set; } | |||||
} | } |
@ -1,21 +1,16 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
public record Basket | |||||
{ | { | ||||
public record Basket | |||||
{ | |||||
// Use property initializer syntax. | |||||
// While this is often more useful for read only | |||||
// auto implemented properties, it can simplify logic | |||||
// for read/write properties. | |||||
public List<BasketItem> Items { get; init; } = new List<BasketItem>(); | |||||
public string BuyerId { get; init; } | |||||
// Use property initializer syntax. | |||||
// While this is often more useful for read only | |||||
// auto implemented properties, it can simplify logic | |||||
// for read/write properties. | |||||
public List<BasketItem> Items { get; init; } = new List<BasketItem>(); | |||||
public string BuyerId { get; init; } | |||||
public decimal Total() | |||||
{ | |||||
return Math.Round(Items.Sum(x => x.UnitPrice * x.Quantity), 2); | |||||
} | |||||
public decimal Total() | |||||
{ | |||||
return Math.Round(Items.Sum(x => x.UnitPrice * x.Quantity), 2); | |||||
} | } | ||||
} | } |
@ -1,13 +1,12 @@ | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public record BasketItem | |||||
{ | { | ||||
public record BasketItem | |||||
{ | |||||
public string Id { get; init; } | |||||
public int ProductId { get; init; } | |||||
public string ProductName { get; init; } | |||||
public decimal UnitPrice { get; init; } | |||||
public decimal OldUnitPrice { get; init; } | |||||
public int Quantity { get; init; } | |||||
public string PictureUrl { get; init; } | |||||
} | |||||
public string Id { get; init; } | |||||
public int ProductId { get; init; } | |||||
public string ProductName { get; init; } | |||||
public decimal UnitPrice { get; init; } | |||||
public decimal OldUnitPrice { get; init; } | |||||
public int Quantity { get; init; } | |||||
public string PictureUrl { get; init; } | |||||
} | } |
@ -1,12 +1,9 @@ | |||||
using System.Collections.Generic; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
public record Campaign | |||||
{ | { | ||||
public record Campaign | |||||
{ | |||||
public int PageIndex { get; init; } | |||||
public int PageSize { get; init; } | |||||
public int Count { get; init; } | |||||
public List<CampaignItem> Data { get; init; } | |||||
} | |||||
} | |||||
public int PageIndex { get; init; } | |||||
public int PageSize { get; init; } | |||||
public int Count { get; init; } | |||||
public List<CampaignItem> Data { get; init; } | |||||
} |
@ -1,20 +1,17 @@ | |||||
using System; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
public record CampaignItem | |||||
{ | { | ||||
public record CampaignItem | |||||
{ | |||||
public int Id { get; init; } | |||||
public int Id { get; init; } | |||||
public string Name { get; init; } | |||||
public string Name { get; init; } | |||||
public string Description { get; init; } | |||||
public string Description { get; init; } | |||||
public DateTime From { get; init; } | |||||
public DateTime From { get; init; } | |||||
public DateTime To { get; init; } | |||||
public DateTime To { get; init; } | |||||
public string PictureUri { get; init; } | |||||
public string DetailsUri { get; init; } | |||||
} | |||||
} | |||||
public string PictureUri { get; init; } | |||||
public string DetailsUri { get; init; } | |||||
} |
@ -1,8 +1,7 @@ | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.CartViewModels | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.CartViewModels; | |||||
public class CartComponentViewModel | |||||
{ | { | ||||
public class CartComponentViewModel | |||||
{ | |||||
public int ItemsCount { get; set; } | |||||
public string Disabled => (ItemsCount == 0) ? "is-disabled" : ""; | |||||
} | |||||
public int ItemsCount { get; set; } | |||||
public string Disabled => (ItemsCount == 0) ? "is-disabled" : ""; | |||||
} | } |
@ -1,12 +1,9 @@ | |||||
using System.Collections.Generic; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
public record Catalog | |||||
{ | { | ||||
public record Catalog | |||||
{ | |||||
public int PageIndex { get; init; } | |||||
public int PageSize { get; init; } | |||||
public int Count { get; init; } | |||||
public List<CatalogItem> Data { get; init; } | |||||
} | |||||
public int PageIndex { get; init; } | |||||
public int PageSize { get; init; } | |||||
public int Count { get; init; } | |||||
public List<CatalogItem> Data { get; init; } | |||||
} | } |
@ -1,15 +1,14 @@ | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public record CatalogItem | |||||
{ | { | ||||
public record CatalogItem | |||||
{ | |||||
public int Id { get; init; } | |||||
public string Name { get; init; } | |||||
public string Description { get; init; } | |||||
public decimal Price { get; init; } | |||||
public string PictureUri { get; init; } | |||||
public int CatalogBrandId { get; init; } | |||||
public string CatalogBrand { get; init; } | |||||
public int CatalogTypeId { get; init; } | |||||
public string CatalogType { get; init; } | |||||
} | |||||
} | |||||
public int Id { get; init; } | |||||
public string Name { get; init; } | |||||
public string Description { get; init; } | |||||
public decimal Price { get; init; } | |||||
public string PictureUri { get; init; } | |||||
public int CatalogBrandId { get; init; } | |||||
public string CatalogBrand { get; init; } | |||||
public int CatalogTypeId { get; init; } | |||||
public string CatalogType { get; init; } | |||||
} |
@ -1,16 +1,11 @@ | |||||
using Microsoft.AspNetCore.Mvc.Rendering; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels.Pagination; | |||||
using System.Collections.Generic; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.CatalogViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.CatalogViewModels | |||||
public class IndexViewModel | |||||
{ | { | ||||
public class IndexViewModel | |||||
{ | |||||
public IEnumerable<CatalogItem> CatalogItems { get; set; } | |||||
public IEnumerable<SelectListItem> Brands { get; set; } | |||||
public IEnumerable<SelectListItem> Types { get; set; } | |||||
public int? BrandFilterApplied { get; set; } | |||||
public int? TypesFilterApplied { get; set; } | |||||
public PaginationInfo PaginationInfo { get; set; } | |||||
} | |||||
public IEnumerable<CatalogItem> CatalogItems { get; set; } | |||||
public IEnumerable<SelectListItem> Brands { get; set; } | |||||
public IEnumerable<SelectListItem> Types { get; set; } | |||||
public int? BrandFilterApplied { get; set; } | |||||
public int? TypesFilterApplied { get; set; } | |||||
public PaginationInfo PaginationInfo { get; set; } | |||||
} | } |
@ -1,34 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text.Json; | |||||
using System.Text.Json.Serialization; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
public class NumberToStringConverter : JsonConverter<string> | |||||
{ | { | ||||
public class NumberToStringConverter : JsonConverter<string> | |||||
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | |||||
{ | { | ||||
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | |||||
if (reader.TokenType == JsonTokenType.Number) | |||||
{ | { | ||||
if (reader.TokenType == JsonTokenType.Number) | |||||
{ | |||||
var numberValue = reader.GetInt32(); | |||||
return numberValue.ToString(); | |||||
} | |||||
else if (reader.TokenType == JsonTokenType.String) | |||||
{ | |||||
return reader.GetString(); | |||||
} | |||||
else | |||||
{ | |||||
throw new JsonException(); | |||||
} | |||||
var numberValue = reader.GetInt32(); | |||||
return numberValue.ToString(); | |||||
} | } | ||||
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) | |||||
else if (reader.TokenType == JsonTokenType.String) | |||||
{ | |||||
return reader.GetString(); | |||||
} | |||||
else | |||||
{ | { | ||||
writer.WriteStringValue(value); | |||||
throw new JsonException(); | |||||
} | } | ||||
} | } | ||||
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) | |||||
{ | |||||
writer.WriteStringValue(value); | |||||
} | |||||
} | } |
@ -1,8 +1,7 @@ | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public record Header | |||||
{ | { | ||||
public record Header | |||||
{ | |||||
public string Controller { get; init; } | |||||
public string Text { get; init; } | |||||
} | |||||
} | |||||
public string Controller { get; init; } | |||||
public string Text { get; init; } | |||||
} |
@ -1,101 +1,91 @@ | |||||
using Microsoft.AspNetCore.Mvc.Rendering; | |||||
using Microsoft.eShopOnContainers.WebMVC.ViewModels.Annotations; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel; | |||||
using System.ComponentModel.DataAnnotations; | |||||
using System.Text.Json.Serialization; | |||||
using WebMVC.Services.ModelDTOs; | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
{ | |||||
public class Order | |||||
{ | |||||
[JsonConverter(typeof(NumberToStringConverter))] | |||||
public string OrderNumber { get; set; } | |||||
public DateTime Date { get; set; } | |||||
public string Status { get; set; } | |||||
public decimal Total { get; set; } | |||||
public string Description { get; set; } | |||||
[Required] | |||||
public string City { get; set; } | |||||
[Required] | |||||
public string Street { get; set; } | |||||
[Required] | |||||
public string State { get; set; } | |||||
[Required] | |||||
public string Country { get; set; } | |||||
public string ZipCode { get; set; } | |||||
[Required] | |||||
[DisplayName("Card number")] | |||||
public string CardNumber { get; set; } | |||||
[Required] | |||||
[DisplayName("Cardholder name")] | |||||
public string CardHolderName { get; set; } | |||||
public DateTime CardExpiration { get; set; } | |||||
[RegularExpression(@"(0[1-9]|1[0-2])\/[0-9]{2}", ErrorMessage = "Expiration should match a valid MM/YY value")] | |||||
[CardExpiration(ErrorMessage = "The card is expired"), Required] | |||||
[DisplayName("Card expiration")] | |||||
public string CardExpirationShort { get; set; } | |||||
[Required] | |||||
[DisplayName("Card security number")] | |||||
public string CardSecurityNumber { get; set; } | |||||
public int CardTypeId { get; set; } | |||||
public string Buyer { get; set; } | |||||
public List<SelectListItem> ActionCodeSelectList => | |||||
GetActionCodesByCurrentState(); | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public class Order | |||||
{ | |||||
[JsonConverter(typeof(NumberToStringConverter))] | |||||
public string OrderNumber { get; set; } | |||||
public DateTime Date { get; set; } | |||||
public string Status { get; set; } | |||||
public decimal Total { get; set; } | |||||
public string Description { get; set; } | |||||
[Required] | |||||
public string City { get; set; } | |||||
[Required] | |||||
public string Street { get; set; } | |||||
[Required] | |||||
public string State { get; set; } | |||||
[Required] | |||||
public string Country { get; set; } | |||||
public string ZipCode { get; set; } | |||||
[Required] | |||||
[DisplayName("Card number")] | |||||
public string CardNumber { get; set; } | |||||
[Required] | |||||
[DisplayName("Cardholder name")] | |||||
public string CardHolderName { get; set; } | |||||
public DateTime CardExpiration { get; set; } | |||||
[RegularExpression(@"(0[1-9]|1[0-2])\/[0-9]{2}", ErrorMessage = "Expiration should match a valid MM/YY value")] | |||||
[CardExpiration(ErrorMessage = "The card is expired"), Required] | |||||
[DisplayName("Card expiration")] | |||||
public string CardExpirationShort { get; set; } | |||||
[Required] | |||||
[DisplayName("Card security number")] | |||||
public string CardSecurityNumber { get; set; } | |||||
public int CardTypeId { get; set; } | |||||
public string Buyer { get; set; } | |||||
public List<SelectListItem> ActionCodeSelectList => | |||||
GetActionCodesByCurrentState(); | |||||
public List<OrderItem> OrderItems { get; set; } | |||||
public List<OrderItem> OrderItems { get; set; } | |||||
[Required] | |||||
public Guid RequestId { get; set; } | |||||
[Required] | |||||
public Guid RequestId { get; set; } | |||||
public void CardExpirationShortFormat() | |||||
{ | |||||
CardExpirationShort = CardExpiration.ToString("MM/yy"); | |||||
} | |||||
public void CardExpirationShortFormat() | |||||
{ | |||||
CardExpirationShort = CardExpiration.ToString("MM/yy"); | |||||
} | |||||
public void CardExpirationApiFormat() | |||||
{ | |||||
var month = CardExpirationShort.Split('/')[0]; | |||||
var year = $"20{CardExpirationShort.Split('/')[1]}"; | |||||
public void CardExpirationApiFormat() | |||||
{ | |||||
var month = CardExpirationShort.Split('/')[0]; | |||||
var year = $"20{CardExpirationShort.Split('/')[1]}"; | |||||
CardExpiration = new DateTime(int.Parse(year), int.Parse(month), 1); | |||||
} | |||||
CardExpiration = new DateTime(int.Parse(year), int.Parse(month), 1); | |||||
} | |||||
private List<SelectListItem> GetActionCodesByCurrentState() | |||||
private List<SelectListItem> GetActionCodesByCurrentState() | |||||
{ | |||||
var actions = new List<OrderProcessAction>(); | |||||
switch (Status?.ToLower()) | |||||
{ | { | ||||
var actions = new List<OrderProcessAction>(); | |||||
switch (Status?.ToLower()) | |||||
{ | |||||
case "paid": | |||||
actions.Add(OrderProcessAction.Ship); | |||||
break; | |||||
} | |||||
var result = new List<SelectListItem>(); | |||||
actions.ForEach(action => | |||||
{ | |||||
result.Add(new SelectListItem { Text = action.Name, Value = action.Code }); | |||||
}); | |||||
return result; | |||||
case "paid": | |||||
actions.Add(OrderProcessAction.Ship); | |||||
break; | |||||
} | } | ||||
} | |||||
public enum CardType | |||||
{ | |||||
AMEX = 1 | |||||
var result = new List<SelectListItem>(); | |||||
actions.ForEach(action => | |||||
{ | |||||
result.Add(new SelectListItem { Text = action.Name, Value = action.Code }); | |||||
}); | |||||
return result; | |||||
} | } | ||||
} | } | ||||
public enum CardType | |||||
{ | |||||
AMEX = 1 | |||||
} |
@ -1,17 +1,16 @@ | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels; | |||||
public record OrderItem | |||||
{ | { | ||||
public record OrderItem | |||||
{ | |||||
public int ProductId { get; init; } | |||||
public int ProductId { get; init; } | |||||
public string ProductName { get; init; } | |||||
public string ProductName { get; init; } | |||||
public decimal UnitPrice { get; init; } | |||||
public decimal UnitPrice { get; init; } | |||||
public decimal Discount { get; init; } | |||||
public decimal Discount { get; init; } | |||||
public int Units { get; init; } | |||||
public int Units { get; init; } | |||||
public string PictureUrl { get; init; } | |||||
} | |||||
public string PictureUrl { get; init; } | |||||
} | } |
@ -1,12 +1,11 @@ | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Pagination | |||||
namespace Microsoft.eShopOnContainers.WebMVC.ViewModels.Pagination; | |||||
public class PaginationInfo | |||||
{ | { | ||||
public class PaginationInfo | |||||
{ | |||||
public int TotalItems { get; set; } | |||||
public int ItemsPerPage { get; set; } | |||||
public int ActualPage { get; set; } | |||||
public int TotalPages { get; set; } | |||||
public string Previous { get; set; } | |||||
public string Next { get; set; } | |||||
} | |||||
public int TotalItems { get; set; } | |||||
public int ItemsPerPage { get; set; } | |||||
public int ActualPage { get; set; } | |||||
public int TotalPages { get; set; } | |||||
public string Previous { get; set; } | |||||
public string Next { get; set; } | |||||
} | } |