Browse Source

Moved "AddItemToBasket" from MVC Client to a single call in PurchaseBFF

pull/565/head^2
Eduard Tomàs 7 years ago
parent
commit
1c6431d503
23 changed files with 106 additions and 76 deletions
  1. +1
    -0
      docker-compose.override.yml
  2. +2
    -2
      src/Apigw/OcelotApiGw/Properties/launchSettings.json
  3. +9
    -0
      src/Apigw/OcelotApiGw/Startup.cs
  4. +13
    -1
      src/Apigw/OcelotApiGw/configuration/configuration.json
  5. +1
    -0
      src/BFFs/PurchaseBff/Config/UrlsConfig.cs
  6. +4
    -3
      src/BFFs/PurchaseBff/Controllers/BasketController.cs
  7. +21
    -2
      src/BFFs/PurchaseBff/Services/BasketService.cs
  8. +1
    -0
      src/BFFs/PurchaseBff/Services/IBasketService.cs
  9. +6
    -0
      src/BFFs/PurchaseBff/Startup.cs
  10. +1
    -1
      src/Services/Basket/Basket.API/Properties/launchSettings.json
  11. +1
    -1
      src/Services/Catalog/Catalog.API/Properties/launchSettings.json
  12. +3
    -3
      src/Services/Location/Locations.API/Properties/launchSettings.json
  13. +2
    -0
      src/Web/WebMVC/AppSettings.cs
  14. +3
    -11
      src/Web/WebMVC/Controllers/CartController.cs
  15. +11
    -20
      src/Web/WebMVC/Infrastructure/API.cs
  16. +18
    -16
      src/Web/WebMVC/Services/BasketService.cs
  17. +1
    -1
      src/Web/WebMVC/Services/IBasketService.cs
  18. +1
    -0
      src/Web/WebMVC/Startup.cs
  19. +1
    -1
      src/Web/WebMVC/ViewModels/CatalogItem.cs
  20. +0
    -8
      src/Web/WebMVC/Views/Catalog/_product.cshtml
  21. +1
    -1
      src/Web/WebSPA/Properties/launchSettings.json
  22. +2
    -2
      test/Services/UnitTest/Basket/Application/CartControllerTest.cs
  23. +3
    -3
      test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs

+ 1
- 0
docker-compose.override.yml View File

@ -137,6 +137,7 @@ services:
- CatalogUrl=http://catalog.api - CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api - OrderingUrl=http://ordering.api
- BasketUrl=http://basket.api - BasketUrl=http://basket.api
- PurchaseUrl=http://apigw/purchase-bff
- LocationsUrl=http://locations.api - LocationsUrl=http://locations.api
- IdentityUrl=http://10.0.75.1:5105 # Local Mac: Use http://docker.for.mac.localhost:5105 || Local Windows: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. || #Remote access: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser. - IdentityUrl=http://10.0.75.1:5105 # Local Mac: Use http://docker.for.mac.localhost:5105 || Local Windows: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. || #Remote access: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.
- MarketingUrl=http://marketing.api - MarketingUrl=http://marketing.api


+ 2
- 2
src/Apigw/OcelotApiGw/Properties/launchSettings.json View File

@ -3,7 +3,7 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:64020/",
"applicationUrl": "http://localhost:56755/",
"sslPort": 0 "sslPort": 0
} }
}, },
@ -24,4 +24,4 @@
"applicationUrl": "http://localhost:64021/" "applicationUrl": "http://localhost:64021/"
} }
} }
}
}

+ 9
- 0
src/Apigw/OcelotApiGw/Startup.cs View File

@ -41,6 +41,15 @@ namespace OcelotApiGw
x.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents() x.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents()
{ {
OnAuthenticationFailed = async ctx => OnAuthenticationFailed = async ctx =>
{
int i = 0;
},
OnTokenValidated = async ctx =>
{
int i = 0;
},
OnMessageReceived = async ctx =>
{ {
int i = 0; int i = 0;
} }


+ 13
- 1
src/Apigw/OcelotApiGw/configuration/configuration.json View File

@ -8,13 +8,25 @@
"UpstreamPathTemplate": "/purchase-bff/catalog/{everything}", "UpstreamPathTemplate": "/purchase-bff/catalog/{everything}",
"UpstreamHttpMethod": [ "GET" ] "UpstreamHttpMethod": [ "GET" ]
}, },
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHost": "basket.api",
"DownstreamPort": 80,
"UpstreamPathTemplate": "/purchase-bff/basket/{everything}",
"UpstreamHttpMethod": [ "GET" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": []
}
},
{ {
"DownstreamPathTemplate": "/{everything}", "DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHost": "purchasebff", "DownstreamHost": "purchasebff",
"DownstreamPort": 80, "DownstreamPort": 80,
"UpstreamPathTemplate": "/purchase-bff/{everything}", "UpstreamPathTemplate": "/purchase-bff/{everything}",
"UpstreamHttpMethod": [],
"UpstreamHttpMethod": ["POST"],
"AuthenticationOptions": { "AuthenticationOptions": {
"AuthenticationProviderKey": "IdentityApiKey", "AuthenticationProviderKey": "IdentityApiKey",
"AllowedScopes": [] "AllowedScopes": []


+ 1
- 0
src/BFFs/PurchaseBff/Config/UrlsConfig.cs View File

@ -15,6 +15,7 @@ namespace PurchaseBff.Config
public class BasketOperations public class BasketOperations
{ {
public static string GetItemById(string id) => $"/api/v1/basket/{id}"; public static string GetItemById(string id) => $"/api/v1/basket/{id}";
public static string UpdateBasket() => $"/api/v1/basket";
} }
public string Basket { get; set; } public string Basket { get; set; }


+ 4
- 3
src/BFFs/PurchaseBff/Controllers/BasketController.cs View File

@ -32,20 +32,21 @@ namespace PurchaseBff.Controllers
// Step 1: Get the item from catalog // Step 1: Get the item from catalog
var item = await _catalog.GetCatalogItem(data.CatalogItemId); var item = await _catalog.GetCatalogItem(data.CatalogItemId);
// Step 2: Get current basket status // Step 2: Get current basket status
var currentBasket = await _basket.GetById(data.BasketId);
var currentBasket = (await _basket.GetById(data.BasketId)) ?? new BasketData(data.BasketId);
// Step 3: Merge current status with new product // Step 3: Merge current status with new product
currentBasket.Items.Add(new BasketDataItem() currentBasket.Items.Add(new BasketDataItem()
{ {
OldUnitPrice = item.Price,
UnitPrice = item.Price, UnitPrice = item.Price,
PictureUrl = item.PictureUri, PictureUrl = item.PictureUri,
ProductId = item.Id.ToString(), ProductId = item.Id.ToString(),
ProductName = item.Name, ProductName = item.Name,
Quantity = data.Quantity, Quantity = data.Quantity,
Id = item.Id.ToString()
Id = Guid.NewGuid().ToString()
}); });
// Step 4: Update basket // Step 4: Update basket
await _basket.Update(currentBasket);
return Ok(); return Ok();
} }


+ 21
- 2
src/BFFs/PurchaseBff/Services/BasketService.cs View File

@ -2,6 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -17,18 +19,35 @@ namespace PurchaseBff.Services
private readonly IHttpClient _apiClient; private readonly IHttpClient _apiClient;
private readonly ILogger<BasketService> _logger; private readonly ILogger<BasketService> _logger;
private readonly UrlsConfig _urls; private readonly UrlsConfig _urls;
public BasketService(IHttpClient httpClient, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config)
private readonly IHttpContextAccessor _httpContextAccessor;
public BasketService(IHttpClient httpClient, IHttpContextAccessor httpContextAccessor, ILogger<BasketService> logger, IOptionsSnapshot<UrlsConfig> config)
{ {
_apiClient = httpClient; _apiClient = httpClient;
_logger = logger; _logger = logger;
_urls = config.Value; _urls = config.Value;
_httpContextAccessor = httpContextAccessor;
} }
public async Task<BasketData> GetById(string id) public async Task<BasketData> GetById(string id)
{ {
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id));
var token = await GetUserTokenAsync();
var data = await _apiClient.GetStringAsync(_urls.Basket + UrlsConfig.BasketOperations.GetItemById(id), token);
var basket = JsonConvert.DeserializeObject<BasketData>(data); var basket = JsonConvert.DeserializeObject<BasketData>(data);
return basket; return basket;
} }
public async Task Update(BasketData currentBasket)
{
var token = await GetUserTokenAsync();
var data = await _apiClient.PostAsync<BasketData>(_urls.Basket + UrlsConfig.BasketOperations.UpdateBasket(), currentBasket, token);
int i = 0;
}
async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccessor.HttpContext;
return await context.GetTokenAsync("access_token");
}
} }
} }

+ 1
- 0
src/BFFs/PurchaseBff/Services/IBasketService.cs View File

@ -9,5 +9,6 @@ namespace PurchaseBff.Services
public interface IBasketService public interface IBasketService
{ {
Task<BasketData> GetById(string id); Task<BasketData> GetById(string id);
Task Update(BasketData currentBasket);
} }
} }

+ 6
- 0
src/BFFs/PurchaseBff/Startup.cs View File

@ -92,6 +92,10 @@ namespace PurchaseBff
options.Events = new JwtBearerEvents() options.Events = new JwtBearerEvents()
{ {
OnAuthenticationFailed = async ctx => OnAuthenticationFailed = async ctx =>
{
int i = 0;
},
OnTokenValidated = async ctx =>
{ {
int i = 0; int i = 0;
} }
@ -117,6 +121,8 @@ namespace PurchaseBff
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
app.UseAuthentication();
app.UseMvc(); app.UseMvc();
app.UseSwagger().UseSwaggerUI(c => app.UseSwagger().UseSwaggerUI(c =>


+ 1
- 1
src/Services/Basket/Basket.API/Properties/launchSettings.json View File

@ -3,7 +3,7 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:51078/",
"applicationUrl": "http://localhost:56695/",
"sslPort": 0 "sslPort": 0
} }
}, },


+ 1
- 1
src/Services/Catalog/Catalog.API/Properties/launchSettings.json View File

@ -3,7 +3,7 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:55101",
"applicationUrl": "http://localhost:56698/",
"sslPort": 0 "sslPort": 0
} }
}, },


+ 3
- 3
src/Services/Location/Locations.API/Properties/launchSettings.json View File

@ -1,9 +1,9 @@
{
{
"iisSettings": { "iisSettings": {
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:3278/",
"applicationUrl": "http://localhost:53933/",
"sslPort": 0 "sslPort": 0
} }
}, },
@ -26,4 +26,4 @@
"applicationUrl": "http://localhost:3279" "applicationUrl": "http://localhost:3279"
} }
} }
}
}

+ 2
- 0
src/Web/WebMVC/AppSettings.cs View File

@ -13,6 +13,8 @@ namespace Microsoft.eShopOnContainers.WebMVC
public string BasketUrl { get; set; } public string BasketUrl { get; set; }
public string MarketingUrl { get; set; } public string MarketingUrl { get; set; }
public string LocationsUrl { get; set; } public string LocationsUrl { get; set; }
public string PurchaseUrl { get; set; }
public bool ActivateCampaignDetailFunction { get; set; } public bool ActivateCampaignDetailFunction { get; set; }
public Logging Logging { get; set; } public Logging Logging { get; set; }
public bool UseCustomizationData { get; set; } public bool UseCustomizationData { get; set; }


+ 3
- 11
src/Web/WebMVC/Controllers/CartController.cs View File

@ -70,19 +70,11 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{ {
try try
{ {
if (productDetails.Id != null)
if (productDetails?.Id != null)
{ {
var user = _appUserParser.Parse(HttpContext.User); var user = _appUserParser.Parse(HttpContext.User);
var product = new BasketItem()
{
Id = Guid.NewGuid().ToString(),
Quantity = 1,
ProductName = productDetails.Name,
PictureUrl = productDetails.PictureUri,
UnitPrice = productDetails.Price,
ProductId = productDetails.Id
};
await _basketSvc.AddItemToBasket(user, product);
await _basketSvc.AddItemToBasket(user, productDetails.Id);
//await _basketSvc.AddItemToBasket(user, product);
} }
return RedirectToAction("Index", "Catalog"); return RedirectToAction("Index", "Catalog");
} }


+ 11
- 20
src/Web/WebMVC/Infrastructure/API.cs View File

@ -4,27 +4,18 @@ namespace WebMVC.Infrastructure
{ {
public static class API public static class API
{ {
public static class Basket
{
public static string GetBasket(string baseUri, string basketId)
{
return $"{baseUri}/{basketId}";
}
public static string UpdateBasket(string baseUri)
{
return baseUri;
}
public static string CheckoutBasket(string baseUri)
{
return $"{baseUri}/checkout";
}
public static class Purchase
{
public static string AddItemToBasket(string baseUri) => $"{baseUri}/basket/items";
}
public static string CleanBasket(string baseUri, string basketId)
{
return $"{baseUri}/{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 Order public static class Order
@ -100,7 +91,7 @@ namespace WebMVC.Infrastructure
public static string CreateOrUpdateUserLocation(string baseUri) public static string CreateOrUpdateUserLocation(string baseUri)
{ {
return baseUri; return baseUri;
}
}
} }
} }
} }

+ 18
- 16
src/Web/WebMVC/Services/BasketService.cs View File

@ -14,14 +14,19 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public class BasketService : IBasketService public class BasketService : IBasketService
{ {
private readonly IOptionsSnapshot<AppSettings> _settings; private readonly IOptionsSnapshot<AppSettings> _settings;
private IHttpClient _apiClient;
private readonly IHttpClient _apiClient;
private readonly string _remoteServiceBaseUrl; private readonly string _remoteServiceBaseUrl;
private IHttpContextAccessor _httpContextAccesor;
private readonly string _purchaseUrl;
private readonly IHttpContextAccessor _httpContextAccesor;
public BasketService(IOptionsSnapshot<AppSettings> settings, IHttpContextAccessor httpContextAccesor, IHttpClient httpClient)
private readonly string _bffUrl;
public BasketService(IOptionsSnapshot<AppSettings> settings,
IHttpContextAccessor httpContextAccesor, IHttpClient httpClient)
{ {
_settings = settings; _settings = settings;
_remoteServiceBaseUrl = $"{_settings.Value.BasketUrl}/api/v1/basket"; _remoteServiceBaseUrl = $"{_settings.Value.BasketUrl}/api/v1/basket";
_purchaseUrl = $"{_settings.Value.PurchaseUrl}/api/v1";
_httpContextAccesor = httpContextAccesor; _httpContextAccesor = httpContextAccesor;
_apiClient = httpClient; _apiClient = httpClient;
} }
@ -104,23 +109,20 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
return order; return order;
} }
public async Task AddItemToBasket(ApplicationUser user, BasketItem product)
public async Task AddItemToBasket(ApplicationUser user, int productId)
{ {
var basket = await GetBasket(user);
var token = await GetUserTokenAsync();
var updateBasketUri = API.Purchase.AddItemToBasket(_purchaseUrl);
var userId = user.Id;
if (basket == null)
var response = await _apiClient.PostAsync(updateBasketUri, new
{ {
basket = new Basket()
{
BuyerId = user.Id,
Items = new List<BasketItem>()
};
}
basket.Items.Add(product);
CatalogItemId = productId,
BasketId = userId,
Quantity = 1
}, token);
await UpdateBasket(basket);
}
}
async Task<string> GetUserTokenAsync() async Task<string> GetUserTokenAsync()
{ {


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

@ -10,7 +10,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.Services
public interface IBasketService public interface IBasketService
{ {
Task<Basket> GetBasket(ApplicationUser user); Task<Basket> GetBasket(ApplicationUser user);
Task AddItemToBasket(ApplicationUser user, BasketItem product);
Task AddItemToBasket(ApplicationUser user, int productId);
Task<Basket> UpdateBasket(Basket basket); Task<Basket> UpdateBasket(Basket basket);
Task Checkout(BasketDTO basket); Task Checkout(BasketDTO basket);
Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities); Task<Basket> SetQuantities(ApplicationUser user, Dictionary<string, int> quantities);


+ 1
- 0
src/Web/WebMVC/Startup.cs View File

@ -130,6 +130,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
options.Scope.Add("basket"); options.Scope.Add("basket");
options.Scope.Add("marketing"); options.Scope.Add("marketing");
options.Scope.Add("locations"); options.Scope.Add("locations");
options.Scope.Add("purchasebff");
}); });
} }


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

@ -4,7 +4,7 @@ namespace Microsoft.eShopOnContainers.WebMVC.ViewModels
{ {
public class CatalogItem public class CatalogItem
{ {
public string Id { get; set; }
public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string Description { get; set; } public string Description { get; set; }
public decimal Price { get; set; } public decimal Price { get; set; }


+ 0
- 8
src/Web/WebMVC/Views/Catalog/_product.cshtml View File

@ -12,13 +12,5 @@
<div class="esh-catalog-price"> <div class="esh-catalog-price">
<span>@Model.Price.ToString("N2")</span> <span>@Model.Price.ToString("N2")</span>
</div> </div>
<input type="hidden" asp-for="@Model.CatalogBrand" name="brand" />
<input type="hidden" asp-for="@Model.CatalogBrandId" name="brandId" />
<input type="hidden" asp-for="@Model.CatalogType" name="type" />
<input type="hidden" asp-for="@Model.CatalogTypeId" name="typeId" />
<input type="hidden" asp-for="@Model.Description" name="description" />
<input type="hidden" asp-for="@Model.Id" name="id" /> <input type="hidden" asp-for="@Model.Id" name="id" />
<input type="hidden" asp-for="@Model.Name" name="name" />
<input type="hidden" asp-for="@Model.PictureUri" name="pictureUri" />
<input type="hidden" asp-for="@Model.Price" name="price" />
</form> </form>

+ 1
- 1
src/Web/WebSPA/Properties/launchSettings.json View File

@ -3,7 +3,7 @@
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:5104/",
"applicationUrl": "http://localhost:56727/",
"sslPort": 0 "sslPort": 0
} }
}, },


+ 2
- 2
test/Services/UnitTest/Basket/Application/CartControllerTest.cs View File

@ -92,7 +92,7 @@ namespace UnitTest.Basket.Application
//Arrange //Arrange
var fakeCatalogItem = GetFakeCatalogItem(); var fakeCatalogItem = GetFakeCatalogItem();
_basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny<ApplicationUser>(), It.IsAny<BasketItem>()))
_basketServiceMock.Setup(x => x.AddItemToBasket(It.IsAny<ApplicationUser>(), It.IsAny<Int32>()))
.Returns(Task.FromResult(1)); .Returns(Task.FromResult(1));
//Act //Act
@ -118,7 +118,7 @@ namespace UnitTest.Basket.Application
{ {
return new CatalogItem() return new CatalogItem()
{ {
Id = "1",
Id = 1,
Name = "fakeName", Name = "fakeName",
CatalogBrand = "fakeBrand", CatalogBrand = "fakeBrand",
CatalogType = "fakeType", CatalogType = "fakeType",


+ 3
- 3
test/Services/UnitTest/Catalog/Application/CatalogControllerTest.cs View File

@ -67,19 +67,19 @@ namespace UnitTest.Catalog.Application
{ {
new CatalogItem() new CatalogItem()
{ {
Id = "1",
Id = 1,
Name = "fakeItemA", Name = "fakeItemA",
CatalogTypeId = 1 CatalogTypeId = 1
}, },
new CatalogItem() new CatalogItem()
{ {
Id = "2",
Id = 2,
Name = "fakeItemB", Name = "fakeItemB",
CatalogTypeId = 1 CatalogTypeId = 1
}, },
new CatalogItem() new CatalogItem()
{ {
Id = "3",
Id = 3,
Name = "fakeItemC", Name = "fakeItemC",
CatalogTypeId = 1 CatalogTypeId = 1
} }


Loading…
Cancel
Save