@ -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)] | |||
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 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 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); | |||
} | |||
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); | |||
} | |||
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; } | |||
} |