Browse Source

Included file scope namespaces for all files

migration/webmvc/net-6
Sumit Ghosh 3 years ago
parent
commit
5e7de1617e
47 changed files with 1213 additions and 1481 deletions
  1. +24
    -25
      src/Web/WebMVC/AppSettings.cs
  2. +32
    -43
      src/Web/WebMVC/Controllers/AccountController.cs
  3. +56
    -66
      src/Web/WebMVC/Controllers/CartController.cs
  4. +26
    -34
      src/Web/WebMVC/Controllers/CatalogController.cs
  5. +3
    -6
      src/Web/WebMVC/Controllers/ErrorController.cs
  6. +55
    -62
      src/Web/WebMVC/Controllers/OrderController.cs
  7. +22
    -31
      src/Web/WebMVC/Controllers/OrderManagementController.cs
  8. +36
    -45
      src/Web/WebMVC/Controllers/TestController.cs
  9. +19
    -26
      src/Web/WebMVC/Extensions/HttpClientExtensions.cs
  10. +1
    -5
      src/Web/WebMVC/Extensions/SessionExtensions.cs
  11. +60
    -61
      src/Web/WebMVC/Infrastructure/API.cs
  12. +28
    -37
      src/Web/WebMVC/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs
  13. +14
    -20
      src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs
  14. +56
    -68
      src/Web/WebMVC/Infrastructure/WebContextSeed.cs
  15. +1
    -10
      src/Web/WebMVC/Program.cs
  16. +89
    -99
      src/Web/WebMVC/Services/BasketService.cs
  17. +55
    -66
      src/Web/WebMVC/Services/CatalogService.cs
  18. +10
    -14
      src/Web/WebMVC/Services/IBasketService.cs
  19. +5
    -11
      src/Web/WebMVC/Services/ICatalogService.cs
  20. +3
    -6
      src/Web/WebMVC/Services/IIdentityParser.cs
  21. +10
    -15
      src/Web/WebMVC/Services/IOrderingService.cs
  22. +24
    -33
      src/Web/WebMVC/Services/IdentityParser.cs
  23. +23
    -28
      src/Web/WebMVC/Services/ModelDTOs/BasketDTO.cs
  24. +5
    -6
      src/Web/WebMVC/Services/ModelDTOs/LocationDTO.cs
  25. +5
    -8
      src/Web/WebMVC/Services/ModelDTOs/OrderDTO.cs
  26. +13
    -14
      src/Web/WebMVC/Services/ModelDTOs/OrderProcessAction.cs
  27. +107
    -116
      src/Web/WebMVC/Services/OrderingService.cs
  28. +144
    -166
      src/Web/WebMVC/Startup.cs
  29. +19
    -26
      src/Web/WebMVC/ViewComponents/Cart.cs
  30. +16
    -23
      src/Web/WebMVC/ViewComponents/CartList.cs
  31. +19
    -23
      src/Web/WebMVC/ViewModels/Annotations/CardExpiration.cs
  32. +11
    -15
      src/Web/WebMVC/ViewModels/Annotations/LatitudeCoordinate.cs
  33. +11
    -15
      src/Web/WebMVC/ViewModels/Annotations/LongitudeCoordinate.cs
  34. +21
    -25
      src/Web/WebMVC/ViewModels/ApplicationUser.cs
  35. +11
    -16
      src/Web/WebMVC/ViewModels/Basket.cs
  36. +10
    -11
      src/Web/WebMVC/ViewModels/BasketItem.cs
  37. +7
    -10
      src/Web/WebMVC/ViewModels/Campaign.cs
  38. +10
    -13
      src/Web/WebMVC/ViewModels/CampaignItem.cs
  39. +5
    -6
      src/Web/WebMVC/ViewModels/CartViewModels/IndexViewModel.cs
  40. +6
    -9
      src/Web/WebMVC/ViewModels/Catalog.cs
  41. +13
    -14
      src/Web/WebMVC/ViewModels/CatalogItem.cs
  42. +8
    -13
      src/Web/WebMVC/ViewModels/CatalogViewModels/IndexViewModel.cs
  43. +17
    -25
      src/Web/WebMVC/ViewModels/Converters/NumberToStringConverter.cs
  44. +6
    -7
      src/Web/WebMVC/ViewModels/Header.cs
  45. +79
    -89
      src/Web/WebMVC/ViewModels/Order.cs
  46. +9
    -10
      src/Web/WebMVC/ViewModels/OrderItem.cs
  47. +9
    -10
      src/Web/WebMVC/ViewModels/Pagination/PaginationInfo.cs

+ 24
- 25
src/Web/WebMVC/AppSettings.cs View File

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

+ 32
- 43
src/Web/WebMVC/Controllers/AccountController.cs View File

@ -1,53 +1,42 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
namespace Microsoft.eShopOnContainers.WebMVC.Controllers;
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
public class AccountController : Controller
{ {
private readonly ILogger<AccountController> _logger;
public AccountController(ILogger<AccountController> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] [Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
public class AccountController : Controller
public async Task<IActionResult> SignIn(string returnUrl)
{ {
private readonly ILogger<AccountController> _logger;
var user = User as ClaimsPrincipal;
var token = await HttpContext.GetTokenAsync("access_token");
public AccountController(ILogger<AccountController> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
_logger.LogInformation("----- User {@User} authenticated into {AppName}", user, Program.AppName);
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
public async Task<IActionResult> SignIn(string returnUrl)
if (token != null)
{ {
var user = User as ClaimsPrincipal;
var token = await HttpContext.GetTokenAsync("access_token");
_logger.LogInformation("----- User {@User} authenticated into {AppName}", user, Program.AppName);
if (token != null)
{
ViewData["access_token"] = token;
}
// "Catalog" because UrlHelper doesn't support nameof() for controllers
// https://github.com/aspnet/Mvc/issues/5853
return RedirectToAction(nameof(CatalogController.Index), "Catalog");
ViewData["access_token"] = token;
} }
public async Task<IActionResult> Signout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
// "Catalog" because UrlHelper doesn't support nameof() for controllers
// https://github.com/aspnet/Mvc/issues/5853
var homeUrl = Url.Action(nameof(CatalogController.Index), "Catalog");
return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme,
new AspNetCore.Authentication.AuthenticationProperties { RedirectUri = homeUrl });
}
// "Catalog" because UrlHelper doesn't support nameof() for controllers
// https://github.com/aspnet/Mvc/issues/5853
return RedirectToAction(nameof(CatalogController.Index), "Catalog");
}
public async Task<IActionResult> Signout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
// "Catalog" because UrlHelper doesn't support nameof() for controllers
// https://github.com/aspnet/Mvc/issues/5853
var homeUrl = Url.Action(nameof(CatalogController.Index), "Catalog");
return new SignOutResult(OpenIdConnectDefaults.AuthenticationScheme,
new AspNetCore.Authentication.AuthenticationProperties { RedirectUri = homeUrl });
} }
} }

+ 56
- 66
src/Web/WebMVC/Controllers/CartController.cs View File

@ -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}";
} }
} }

+ 26
- 34
src/Web/WebMVC/Controllers/CatalogController.cs View File

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

+ 3
- 6
src/Web/WebMVC/Controllers/ErrorController.cs View File

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

+ 55
- 62
src/Web/WebMVC/Controllers/OrderController.cs View File

@ -1,82 +1,75 @@
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.WebMVC.Services;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers;
using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using System;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
public class OrderController : Controller
{ {
[Authorize(AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)]
public class OrderController : Controller
private IOrderingService _orderSvc;
private IBasketService _basketSvc;
private readonly IIdentityParser<ApplicationUser> _appUserParser;
public OrderController(IOrderingService orderSvc, IBasketService basketSvc, IIdentityParser<ApplicationUser> appUserParser)
{ {
private IOrderingService _orderSvc;
private IBasketService _basketSvc;
private readonly IIdentityParser<ApplicationUser> _appUserParser;
public OrderController(IOrderingService orderSvc, IBasketService basketSvc, IIdentityParser<ApplicationUser> appUserParser)
{
_appUserParser = appUserParser;
_orderSvc = orderSvc;
_basketSvc = basketSvc;
}
_appUserParser = appUserParser;
_orderSvc = orderSvc;
_basketSvc = basketSvc;
}
public async Task<IActionResult> Create()
{
public async Task<IActionResult> Create()
{
var user = _appUserParser.Parse(HttpContext.User);
var order = await _basketSvc.GetOrderDraft(user.Id);
var vm = _orderSvc.MapUserInfoIntoOrder(user, order);
vm.CardExpirationShortFormat();
var user = _appUserParser.Parse(HttpContext.User);
var order = await _basketSvc.GetOrderDraft(user.Id);
var vm = _orderSvc.MapUserInfoIntoOrder(user, order);
vm.CardExpirationShortFormat();
return View(vm);
}
return View(vm);
}
[HttpPost]
public async Task<IActionResult> Checkout(Order model)
[HttpPost]
public async Task<IActionResult> Checkout(Order model)
{
try
{ {
try
if (ModelState.IsValid)
{ {
if (ModelState.IsValid)
{
var user = _appUserParser.Parse(HttpContext.User);
var basket = _orderSvc.MapOrderToBasket(model);
var user = _appUserParser.Parse(HttpContext.User);
var basket = _orderSvc.MapOrderToBasket(model);
await _basketSvc.Checkout(basket);
await _basketSvc.Checkout(basket);
//Redirect to historic list.
return RedirectToAction("Index");
}
}
catch (Exception ex)
{
ModelState.AddModelError("Error", $"It was not possible to create a new order, please try later on ({ex.GetType().Name} - {ex.Message})");
//Redirect to historic list.
return RedirectToAction("Index");
} }
return View("Create", model);
} }
public async Task<IActionResult> Cancel(string orderId)
catch (Exception ex)
{ {
await _orderSvc.CancelOrder(orderId);
//Redirect to historic list.
return RedirectToAction("Index");
ModelState.AddModelError("Error", $"It was not possible to create a new order, please try later on ({ex.GetType().Name} - {ex.Message})");
} }
public async Task<IActionResult> Detail(string orderId)
{
var user = _appUserParser.Parse(HttpContext.User);
return View("Create", model);
}
var order = await _orderSvc.GetOrder(user, orderId);
return View(order);
}
public async Task<IActionResult> Cancel(string orderId)
{
await _orderSvc.CancelOrder(orderId);
public async Task<IActionResult> Index(Order item)
{
var user = _appUserParser.Parse(HttpContext.User);
var vm = await _orderSvc.GetMyOrders(user);
return View(vm);
}
//Redirect to historic list.
return RedirectToAction("Index");
}
public async Task<IActionResult> Detail(string orderId)
{
var user = _appUserParser.Parse(HttpContext.User);
var order = await _orderSvc.GetOrder(user, orderId);
return View(order);
}
public async Task<IActionResult> Index(Order item)
{
var user = _appUserParser.Parse(HttpContext.User);
var vm = await _orderSvc.GetMyOrders(user);
return View(vm);
} }
}
}

+ 22
- 31
src/Web/WebMVC/Controllers/OrderManagementController.cs View File

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

+ 36
- 45
src/Web/WebMVC/Controllers/TestController.cs View File

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

+ 19
- 26
src/Web/WebMVC/Extensions/HttpClientExtensions.cs View File

@ -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
- 5
src/Web/WebMVC/Extensions/SessionExtensions.cs View File

@ -1,8 +1,4 @@
using Microsoft.AspNetCore.Http;
using System.Text.Json;
public static class SessionExtensions
public static class SessionExtensions
{ {
public static void SetObject(this ISession session, string key, object value) => public static void SetObject(this ISession session, string key, object value) =>
session.SetString(key,JsonSerializer.Serialize(value)); session.SetString(key,JsonSerializer.Serialize(value));


+ 60
- 61
src/Web/WebMVC/Infrastructure/API.cs View File

@ -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";
} }
} }
}
}

+ 28
- 37
src/Web/WebMVC/Infrastructure/HttpClientAuthorizationDelegatingHandler.cs View File

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

+ 14
- 20
src/Web/WebMVC/Infrastructure/HttpClientRequestIdDelegatingHandler.cs View File

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

+ 56
- 68
src/Web/WebMVC/Infrastructure/WebContextSeed.cs View File

@ -1,97 +1,85 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.eShopOnContainers.WebMVC;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace WebMVC.Infrastructure;
using Serilog; using Serilog;
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace WebMVC.Infrastructure
public class WebContextSeed
{ {
public class WebContextSeed
public static void Seed(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
{ {
public static void Seed(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
{
var log = Serilog.Log.Logger;
var log = Serilog.Log.Logger;
var settings = (AppSettings)applicationBuilder
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value;
var settings = (AppSettings)applicationBuilder
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value;
var useCustomizationData = settings.UseCustomizationData;
var contentRootPath = env.ContentRootPath;
var webroot = env.WebRootPath;
var useCustomizationData = settings.UseCustomizationData;
var contentRootPath = env.ContentRootPath;
var webroot = env.WebRootPath;
if (useCustomizationData)
{
GetPreconfiguredImages(contentRootPath, webroot, log);
if (useCustomizationData)
{
GetPreconfiguredImages(contentRootPath, webroot, log);
GetPreconfiguredCSS(contentRootPath, webroot, log);
}
GetPreconfiguredCSS(contentRootPath, webroot, log);
} }
}
static void GetPreconfiguredCSS(string contentRootPath, string webroot, Serilog.ILogger log)
static void GetPreconfiguredCSS(string contentRootPath, string webroot, ILogger log)
{
try
{ {
try
string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css");
if (!File.Exists(overrideCssFile))
{ {
string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css");
if (!File.Exists(overrideCssFile))
{
log.Error("Override css file '{FileName}' does not exists.", overrideCssFile);
return;
}
string destinationFilename = Path.Combine(webroot, "css", "override.css");
File.Copy(overrideCssFile, destinationFilename, true);
}
catch (Exception ex)
{
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message);
log.Error("Override css file '{FileName}' does not exists.", overrideCssFile);
return;
} }
string destinationFilename = Path.Combine(webroot, "css", "override.css");
File.Copy(overrideCssFile, destinationFilename, true);
} }
catch (Exception ex)
{
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message);
}
}
static void GetPreconfiguredImages(string contentRootPath, string webroot, Serilog.ILogger log)
static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log)
{
try
{ {
try
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
if (!File.Exists(imagesZipFile))
{ {
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
if (!File.Exists(imagesZipFile))
{
log.Error("Zip file '{ZipFileName}' does not exists.", imagesZipFile);
return;
}
log.Error("Zip file '{ZipFileName}' does not exists.", imagesZipFile);
return;
}
string imagePath = Path.Combine(webroot, "images");
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray();
string imagePath = Path.Combine(webroot, "images");
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray();
using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read))
using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in zip.Entries)
{ {
foreach (ZipArchiveEntry entry in zip.Entries)
if (imageFiles.Contains(entry.Name))
{ {
if (imageFiles.Contains(entry.Name))
{
string destinationFilename = Path.Combine(imagePath, entry.Name);
if (File.Exists(destinationFilename))
{
File.Delete(destinationFilename);
}
entry.ExtractToFile(destinationFilename);
}
else
string destinationFilename = Path.Combine(imagePath, entry.Name);
if (File.Exists(destinationFilename))
{ {
log.Warning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile);
File.Delete(destinationFilename);
} }
entry.ExtractToFile(destinationFilename);
}
else
{
log.Warning("Skipped file '{FileName}' in zipfile '{ZipFileName}'", entry.Name, imagesZipFile);
} }
} }
} }
catch (Exception ex)
{
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message);
}
} }
catch (Exception ex)
{
log.Error(ex, "EXCEPTION ERROR: {Message}", ex.Message);
}
} }
}
}

+ 1
- 10
src/Web/WebMVC/Program.cs View File

@ -1,13 +1,4 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.eShopOnContainers.WebMVC;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.IO;
var configuration = GetConfiguration();
var configuration = GetConfiguration();
Log.Logger = CreateSerilogLogger(configuration); Log.Logger = CreateSerilogLogger(configuration);


+ 89
- 99
src/Web/WebMVC/Services/BasketService.cs View File

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

+ 55
- 66
src/Web/WebMVC/Services/CatalogService.cs View File

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

+ 10
- 14
src/Web/WebMVC/Services/IBasketService.cs View File

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

+ 5
- 11
src/Web/WebMVC/Services/ICatalogService.cs View File

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

+ 3
- 6
src/Web/WebMVC/Services/IIdentityParser.cs View File

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

+ 10
- 15
src/Web/WebMVC/Services/IOrderingService.cs View File

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

+ 24
- 33
src/Web/WebMVC/Services/IdentityParser.cs View File

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

+ 23
- 28
src/Web/WebMVC/Services/ModelDTOs/BasketDTO.cs View File

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

+ 5
- 6
src/Web/WebMVC/Services/ModelDTOs/LocationDTO.cs View File

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

+ 5
- 8
src/Web/WebMVC/Services/ModelDTOs/OrderDTO.cs View File

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

+ 13
- 14
src/Web/WebMVC/Services/ModelDTOs/OrderProcessAction.cs View File

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

+ 107
- 116
src/Web/WebMVC/Services/OrderingService.cs View File

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

+ 144
- 166
src/Web/WebMVC/Startup.cs View File

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

+ 19
- 26
src/Web/WebMVC/ViewComponents/Cart.cs View File

@ -1,37 +1,30 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.eShopOnContainers.WebMVC.ViewModels.CartViewModels;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents;
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents
public class Cart : ViewComponent
{ {
public class Cart : ViewComponent
{
private readonly IBasketService _cartSvc;
private readonly IBasketService _cartSvc;
public Cart(IBasketService cartSvc) => _cartSvc = cartSvc;
public Cart(IBasketService cartSvc) => _cartSvc = cartSvc;
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
{
var vm = new CartComponentViewModel();
try
{ {
var vm = new CartComponentViewModel();
try
{
var itemsInCart = await ItemsInCartAsync(user);
vm.ItemsCount = itemsInCart;
return View(vm);
}
catch
{
ViewBag.IsBasketInoperative = true;
}
var itemsInCart = await ItemsInCartAsync(user);
vm.ItemsCount = itemsInCart;
return View(vm); return View(vm);
} }
private async Task<int> ItemsInCartAsync(ApplicationUser user)
catch
{ {
var basket = await _cartSvc.GetBasket(user);
return basket.Items.Count;
ViewBag.IsBasketInoperative = true;
} }
return View(vm);
}
private async Task<int> ItemsInCartAsync(ApplicationUser user)
{
var basket = await _cartSvc.GetBasket(user);
return basket.Items.Count;
} }
} }

+ 16
- 23
src/Web/WebMVC/ViewComponents/CartList.cs View File

@ -1,33 +1,26 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using System;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents;
namespace Microsoft.eShopOnContainers.WebMVC.ViewComponents
public class CartList : ViewComponent
{ {
public class CartList : ViewComponent
{
private readonly IBasketService _cartSvc;
private readonly IBasketService _cartSvc;
public CartList(IBasketService cartSvc) => _cartSvc = cartSvc;
public CartList(IBasketService cartSvc) => _cartSvc = cartSvc;
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
public async Task<IViewComponentResult> InvokeAsync(ApplicationUser user)
{
var vm = new Basket();
try
{ {
var vm = new Basket();
try
{
vm = await GetItemsAsync(user);
return View(vm);
}
catch (Exception ex)
{
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative, please try later on. ({ex.GetType().Name} - {ex.Message}))";
}
vm = await GetItemsAsync(user);
return View(vm); return View(vm);
} }
catch (Exception ex)
{
ViewBag.BasketInoperativeMsg = $"Basket Service is inoperative, please try later on. ({ex.GetType().Name} - {ex.Message}))";
}
private Task<Basket> GetItemsAsync(ApplicationUser user) => _cartSvc.GetBasket(user);
return View(vm);
} }
private Task<Basket> GetItemsAsync(ApplicationUser user) => _cartSvc.GetBasket(user);
} }

+ 19
- 23
src/Web/WebMVC/ViewModels/Annotations/CardExpiration.cs View File

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

+ 11
- 15
src/Web/WebMVC/ViewModels/Annotations/LatitudeCoordinate.cs View File

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

+ 11
- 15
src/Web/WebMVC/ViewModels/Annotations/LongitudeCoordinate.cs View File

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

+ 21
- 25
src/Web/WebMVC/ViewModels/ApplicationUser.cs View File

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

+ 11
- 16
src/Web/WebMVC/ViewModels/Basket.cs View File

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

+ 10
- 11
src/Web/WebMVC/ViewModels/BasketItem.cs View File

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

+ 7
- 10
src/Web/WebMVC/ViewModels/Campaign.cs View File

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

+ 10
- 13
src/Web/WebMVC/ViewModels/CampaignItem.cs View File

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

+ 5
- 6
src/Web/WebMVC/ViewModels/CartViewModels/IndexViewModel.cs View File

@ -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" : "";
} }

+ 6
- 9
src/Web/WebMVC/ViewModels/Catalog.cs View File

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

+ 13
- 14
src/Web/WebMVC/ViewModels/CatalogItem.cs View File

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

+ 8
- 13
src/Web/WebMVC/ViewModels/CatalogViewModels/IndexViewModel.cs View File

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

+ 17
- 25
src/Web/WebMVC/ViewModels/Converters/NumberToStringConverter.cs View File

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

+ 6
- 7
src/Web/WebMVC/ViewModels/Header.cs View File

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

+ 79
- 89
src/Web/WebMVC/ViewModels/Order.cs View File

@ -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
}

+ 9
- 10
src/Web/WebMVC/ViewModels/OrderItem.cs View File

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

+ 9
- 10
src/Web/WebMVC/ViewModels/Pagination/PaginationInfo.cs View File

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

Loading…
Cancel
Save