Browse Source

Updating SPA for working with PurchaseBFF.

pull/565/head^2
Eduard Tomàs 7 years ago
parent
commit
8edd736196
20 changed files with 138 additions and 36 deletions
  1. +1
    -3
      docker-compose.override.yml
  2. +11
    -0
      src/Apigw/OcelotApiGw/Startup.cs
  3. +1
    -0
      src/BFFs/PurchaseBff/Config/UrlsConfig.cs
  4. +43
    -0
      src/BFFs/PurchaseBff/Controllers/BasketController.cs
  5. +21
    -0
      src/BFFs/PurchaseBff/Models/UpdateBasketRequest.cs
  6. +1
    -1
      src/BFFs/PurchaseBff/Properties/launchSettings.json
  7. +8
    -0
      src/BFFs/PurchaseBff/Services/CatalogService.cs
  8. +1
    -0
      src/BFFs/PurchaseBff/Services/ICatalogService.cs
  9. +1
    -1
      src/Services/Basket/Basket.API/Properties/launchSettings.json
  10. +25
    -3
      src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
  11. +1
    -1
      src/Services/Catalog/Catalog.API/Properties/launchSettings.json
  12. +4
    -4
      src/Web/WebSPA/AppSettings.cs
  13. +8
    -5
      src/Web/WebSPA/Client/modules/basket/basket.service.ts
  14. +3
    -3
      src/Web/WebSPA/Client/modules/catalog/catalog.service.ts
  15. +4
    -4
      src/Web/WebSPA/Client/modules/orders/orders.service.ts
  16. +1
    -3
      src/Web/WebSPA/Client/modules/shared/models/configuration.model.ts
  17. +1
    -3
      src/Web/WebSPA/Client/modules/shared/services/configuration.service.ts
  18. +1
    -1
      src/Web/WebSPA/Client/modules/shared/services/security.service.ts
  19. +1
    -1
      src/Web/WebSPA/Properties/launchSettings.json
  20. +1
    -3
      src/Web/WebSPA/appsettings.json

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

@ -112,12 +112,10 @@ services:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://0.0.0.0:80 - ASPNETCORE_URLS=http://0.0.0.0:80
- CatalogUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101
- OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102
- IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110 - MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110
- LocationsUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5109 - LocationsUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5109
- PurchaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5200
- CatalogUrlHC=http://catalog.api/hc - CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc - OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser. - IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.


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

@ -29,6 +29,15 @@ namespace OcelotApiGw
var identityUrl = _cfg.GetValue<string>("IdentityUrl"); var identityUrl = _cfg.GetValue<string>("IdentityUrl");
var authenticationProviderKey = "IdentityApiKey"; var authenticationProviderKey = "IdentityApiKey";
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddAuthentication() services.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, x => .AddJwtBearer(authenticationProviderKey, x =>
{ {
@ -68,6 +77,8 @@ namespace OcelotApiGw
loggerFactory.AddConsole(_cfg.GetSection("Logging")); loggerFactory.AddConsole(_cfg.GetSection("Logging"));
app.UseCors("CorsPolicy");
app.UseOcelot().Wait(); app.UseOcelot().Wait();
} }
} }


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

@ -10,6 +10,7 @@ namespace PurchaseBff.Config
public class CatalogOperations public class CatalogOperations
{ {
public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}"; public static string GetItemById(int id) => $"/api/v1/catalog/items/{id}";
public static string GetItemsById(IEnumerable<int> ids) => $"/api/v1/catalog/items?ids={string.Join(',', ids)}";
} }
public class BasketOperations public class BasketOperations


+ 43
- 0
src/BFFs/PurchaseBff/Controllers/BasketController.cs View File

@ -21,6 +21,49 @@ namespace PurchaseBff.Controllers
_basket = basketService; _basket = basketService;
} }
[HttpPost]
[HttpPut]
public async Task<IActionResult> UpdateAllBasket([FromBody] UpdateBasketRequest data)
{
if (data.Items == null || !data.Items.Any())
{
return BadRequest("Need to pass at least one basket line");
}
// Retrieve the current basket
var currentBasket = await _basket.GetById(data.BuyerId);
if (currentBasket == null)
{
currentBasket = new BasketData(data.BuyerId);
}
var catalogItems = await _catalog.GetCatalogItems(data.Items.Select(x => x.ProductId));
var newBasket = new BasketData(data.BuyerId);
foreach (var bitem in data.Items)
{
var catalogItem = catalogItems.SingleOrDefault(ci => ci.Id == bitem.ProductId);
if (catalogItem == null)
{
return BadRequest($"Basket refers to a non-existing catalog item ({bitem.ProductId})");
}
newBasket.Items.Add(new BasketDataItem()
{
Id = bitem.Id,
ProductId = catalogItem.Id.ToString(),
ProductName = catalogItem.Name,
PictureUrl = catalogItem.PictureUri,
UnitPrice = catalogItem.Price,
Quantity = bitem.Quantity
});
}
await _basket.Update(newBasket);
return Ok(newBasket);
}
[HttpPut] [HttpPut]
[Route("items")] [Route("items")]
public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data) public async Task<IActionResult> UpdateQuantities([FromBody] UpdateBasketItemsRequest data)


+ 21
- 0
src/BFFs/PurchaseBff/Models/UpdateBasketRequest.cs View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace PurchaseBff.Models
{
public class UpdateBasketRequest
{
public string BuyerId { get; set; }
public IEnumerable<UpdateBasketRequestItemData> Items { get; set; }
}
public class UpdateBasketRequestItemData
{
public string Id { get; set; } // Basket id
public int ProductId { get; set; } // Catalog item id
public int Quantity { get; set; } // Quantity
}
}

+ 1
- 1
src/BFFs/PurchaseBff/Properties/launchSettings.json View File

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


+ 8
- 0
src/BFFs/PurchaseBff/Services/CatalogService.cs View File

@ -31,5 +31,13 @@ namespace PurchaseBff.Services
var item = JsonConvert.DeserializeObject<CatalogItem>(data); var item = JsonConvert.DeserializeObject<CatalogItem>(data);
return item; return item;
} }
public async Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids)
{
var data = await _apiClient.GetStringAsync(_urls.Catalog + UrlsConfig.CatalogOperations.GetItemsById(ids));
var item = JsonConvert.DeserializeObject<CatalogItem[]>(data);
return item;
}
} }
} }

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

@ -9,5 +9,6 @@ namespace PurchaseBff.Services
public interface ICatalogService public interface ICatalogService
{ {
Task<CatalogItem> GetCatalogItem(int id); Task<CatalogItem> GetCatalogItem(int id);
Task<IEnumerable<CatalogItem>> GetCatalogItems(IEnumerable<int> ids);
} }
} }

+ 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:50920/",
"applicationUrl": "http://localhost:57423/",
"sslPort": 0 "sslPort": 0
} }
}, },


+ 25
- 3
src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs View File

@ -32,11 +32,16 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
// GET api/v1/[controller]/items[?pageSize=3&pageIndex=10] // GET api/v1/[controller]/items[?pageSize=3&pageIndex=10]
[HttpGet] [HttpGet]
[Route("[action]")]
[Route("items")]
[ProducesResponseType(typeof(PaginatedItemsViewModel<CatalogItem>), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(PaginatedItemsViewModel<CatalogItem>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Items([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
[ProducesResponseType(typeof(IEnumerable<CatalogItem>), (int)HttpStatusCode.OK)]
public async Task<IActionResult> Items([FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0, [FromQuery] string ids = null)
{ {
if (!string.IsNullOrEmpty(ids))
{
return GetItemsByIds(ids);
}
var totalItems = await _catalogContext.CatalogItems var totalItems = await _catalogContext.CatalogItems
.LongCountAsync(); .LongCountAsync();
@ -54,6 +59,23 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
return Ok(model); return Ok(model);
} }
private IActionResult GetItemsByIds(string ids)
{
var numIds = ids.Split(',')
.Select(id => (Ok: int.TryParse(id, out int x), Value: x));
if (!numIds.All(nid => nid.Ok))
{
return BadRequest("ids value invalid. Must be comma-separated list of numbers");
}
var idsToSelect = numIds.Select(id => id.Value);
var items = _catalogContext.CatalogItems.Where(ci => idsToSelect.Contains(ci.Id)).ToList();
items = ChangeUriPlaceholder(items);
return Ok(items);
}
[HttpGet] [HttpGet]
[Route("items/{id:int}")] [Route("items/{id:int}")]
[ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.NotFound)]


+ 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:57623/",
"applicationUrl": "http://localhost:57424/",
"sslPort": 0 "sslPort": 0
} }
}, },


+ 4
- 4
src/Web/WebSPA/AppSettings.cs View File

@ -7,13 +7,13 @@ namespace eShopOnContainers.WebSPA
{ {
public class AppSettings public class AppSettings
{ {
public string BaseUrl { get; set; }
public string CatalogUrl { get; set; }
public string OrderingUrl { get; set; }
public string IdentityUrl { get; set; } public string IdentityUrl { get; set; }
public string BasketUrl { get; set; } public string BasketUrl { get; set; }
public string MarketingUrl { get; set; } public string MarketingUrl { get; set; }
public string PurchaseUrl { get; set; }
public string ActivateCampaignDetailFunction { get; set; } public string ActivateCampaignDetailFunction { get; set; }
public bool UseCustomizationData { get; set; }
public bool UseCustomizationData { get; set; }
} }
} }

+ 8
- 5
src/Web/WebSPA/Client/modules/basket/basket.service.ts View File

@ -23,6 +23,7 @@ import { Subject } from 'rxjs/Subject';
@Injectable() @Injectable()
export class BasketService { export class BasketService {
private basketUrl: string = ''; private basketUrl: string = '';
private purchaseUrl: string = '';
basket: IBasket = { basket: IBasket = {
buyerId: '', buyerId: '',
items: [] items: []
@ -40,12 +41,14 @@ export class BasketService {
if (this.authService.UserData) { if (this.authService.UserData) {
this.basket.buyerId = this.authService.UserData.sub; this.basket.buyerId = this.authService.UserData.sub;
if (this.configurationService.isReady) { if (this.configurationService.isReady) {
this.basketUrl = this.configurationService.serverSettings.basketUrl;
this.basketUrl = this.configurationService.serverSettings.purchaseUrl;
this.purchaseUrl = this.configurationService.serverSettings.purchaseUrl;
this.loadData(); this.loadData();
} }
else { else {
this.configurationService.settingsLoaded$.subscribe(x => { this.configurationService.settingsLoaded$.subscribe(x => {
this.basketUrl = this.configurationService.serverSettings.basketUrl;
this.basketUrl = this.configurationService.serverSettings.purchaseUrl;
this.purchaseUrl = this.configurationService.serverSettings.purchaseUrl;
this.loadData(); this.loadData();
}); });
} }
@ -63,7 +66,7 @@ export class BasketService {
} }
setBasket(basket): Observable<boolean> { setBasket(basket): Observable<boolean> {
let url = this.basketUrl + '/api/v1/basket/';
let url = this.purchaseUrl + '/purchase-bff/api/v1/basket/';
this.basket = basket; this.basket = basket;
return this.service.post(url, basket).map((response: Response) => { return this.service.post(url, basket).map((response: Response) => {
return true; return true;
@ -71,7 +74,7 @@ export class BasketService {
} }
setBasketCheckout(basketCheckout): Observable<boolean> { setBasketCheckout(basketCheckout): Observable<boolean> {
let url = this.basketUrl + '/api/v1/basket/checkout';
let url = this.basketUrl + '/purchase-bff/api/v1/b/basket/checkout';
return this.service.postWithId(url, basketCheckout).map((response: Response) => { return this.service.postWithId(url, basketCheckout).map((response: Response) => {
this.basketEvents.orderCreated(); this.basketEvents.orderCreated();
return true; return true;
@ -79,7 +82,7 @@ export class BasketService {
} }
getBasket(): Observable<IBasket> { getBasket(): Observable<IBasket> {
let url = this.basketUrl + '/api/v1/basket/' + this.basket.buyerId;
let url = this.basketUrl + '/api/v1/b/basket/' + this.basket.buyerId;
return this.service.get(url).map((response: Response) => { return this.service.get(url).map((response: Response) => {
if (response.status === 204) { if (response.status === 204) {
return null; return null;


+ 3
- 3
src/Web/WebSPA/Client/modules/catalog/catalog.service.ts View File

@ -21,9 +21,9 @@ export class CatalogService {
constructor(private service: DataService, private configurationService: ConfigurationService) { constructor(private service: DataService, private configurationService: ConfigurationService) {
this.configurationService.settingsLoaded$.subscribe(x => { this.configurationService.settingsLoaded$.subscribe(x => {
this.catalogUrl = this.configurationService.serverSettings.catalogUrl + '/api/v1/catalog/items';
this.brandUrl = this.configurationService.serverSettings.catalogUrl + '/api/v1/catalog/catalogbrands';
this.typesUrl = this.configurationService.serverSettings.catalogUrl + '/api/v1/catalog/catalogtypes';
this.catalogUrl = this.configurationService.serverSettings.purchaseUrl + '/purchase-bff/api/v1/c/catalog/items';
this.brandUrl = this.configurationService.serverSettings.purchaseUrl + '/purchase-bff/api/v1/c/catalog/catalogbrands';
this.typesUrl = this.configurationService.serverSettings.purchaseUrl + '/purchase-bff/api/v1/c/catalog/catalogtypes';
}); });
} }


+ 4
- 4
src/Web/WebSPA/Client/modules/orders/orders.service.ts View File

@ -22,14 +22,14 @@ export class OrdersService {
constructor(private service: DataService, private basketService: BasketWrapperService, private identityService: SecurityService, private configurationService: ConfigurationService) { constructor(private service: DataService, private basketService: BasketWrapperService, private identityService: SecurityService, private configurationService: ConfigurationService) {
if (this.configurationService.isReady) if (this.configurationService.isReady)
this.ordersUrl = this.configurationService.serverSettings.orderingUrl;
this.ordersUrl = this.configurationService.serverSettings.purchaseUrl;
else else
this.configurationService.settingsLoaded$.subscribe(x => this.ordersUrl = this.configurationService.serverSettings.orderingUrl);
this.configurationService.settingsLoaded$.subscribe(x => this.ordersUrl = this.configurationService.serverSettings.purchaseUrl);
} }
getOrders(): Observable<IOrder[]> { getOrders(): Observable<IOrder[]> {
let url = this.ordersUrl + '/api/v1/orders';
let url = this.ordersUrl + '/purchase-bff/api/v1/o/orders';
return this.service.get(url).map((response: Response) => { return this.service.get(url).map((response: Response) => {
return response.json(); return response.json();
@ -37,7 +37,7 @@ export class OrdersService {
} }
getOrder(id: number): Observable<IOrderDetail> { getOrder(id: number): Observable<IOrderDetail> {
let url = this.ordersUrl + '/api/v1/orders/' + id;
let url = this.ordersUrl + '/purchase-bff/api/v1/o/orders/' + id;
return this.service.get(url).map((response: Response) => { return this.service.get(url).map((response: Response) => {
return response.json(); return response.json();


+ 1
- 3
src/Web/WebSPA/Client/modules/shared/models/configuration.model.ts View File

@ -1,8 +1,6 @@
export interface IConfiguration { export interface IConfiguration {
catalogUrl: string,
orderingUrl: string,
identityUrl: string, identityUrl: string,
basketUrl: string,
marketingUrl: string, marketingUrl: string,
purchaseUrl: string,
activateCampaignDetailFunction: boolean activateCampaignDetailFunction: boolean
} }

+ 1
- 3
src/Web/WebSPA/Client/modules/shared/services/configuration.service.ts View File

@ -28,11 +28,9 @@ export class ConfigurationService {
console.log('server settings loaded'); console.log('server settings loaded');
this.serverSettings = response.json(); this.serverSettings = response.json();
console.log(this.serverSettings); console.log(this.serverSettings);
this.storageService.store('basketUrl', this.serverSettings.basketUrl);
this.storageService.store('catalogUrl', this.serverSettings.catalogUrl);
this.storageService.store('identityUrl', this.serverSettings.identityUrl); this.storageService.store('identityUrl', this.serverSettings.identityUrl);
this.storageService.store('orderingUrl', this.serverSettings.orderingUrl);
this.storageService.store('marketingUrl', this.serverSettings.marketingUrl); this.storageService.store('marketingUrl', this.serverSettings.marketingUrl);
this.storageService.store('purchaseUrl', this.serverSettings.purchaseUrl);
this.storageService.store('activateCampaignDetailFunction', this.serverSettings.activateCampaignDetailFunction); this.storageService.store('activateCampaignDetailFunction', this.serverSettings.activateCampaignDetailFunction);
this.isReady = true; this.isReady = true;
this.settingsLoadedSource.next(); this.settingsLoadedSource.next();


+ 1
- 1
src/Web/WebSPA/Client/modules/shared/services/security.service.ts View File

@ -82,7 +82,7 @@ export class SecurityService {
let client_id = 'js'; let client_id = 'js';
let redirect_uri = location.origin + '/'; let redirect_uri = location.origin + '/';
let response_type = 'id_token token'; let response_type = 'id_token token';
let scope = 'openid profile orders basket marketing locations';
let scope = 'openid profile orders basket marketing locations purchasebff';
let nonce = 'N' + Math.random() + '' + Date.now(); let nonce = 'N' + Math.random() + '' + Date.now();
let state = Date.now() + '' + Math.random(); let state = Date.now() + '' + Math.random();


+ 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:50921/",
"applicationUrl": "http://localhost:64923/",
"sslPort": 0 "sslPort": 0
} }
}, },


+ 1
- 3
src/Web/WebSPA/appsettings.json View File

@ -1,10 +1,8 @@
{ {
"CatalogUrl": "http://localhost:5101",
"OrderingUrl": "http://localhost:5102",
"BasketUrl": "http://localhost:5103",
"IdentityUrl": "http://localhost:5105", "IdentityUrl": "http://localhost:5105",
"MarketingUrl": "http://localhost:5110", "MarketingUrl": "http://localhost:5110",
"CallBackUrl": "http://localhost:5104/", "CallBackUrl": "http://localhost:5104/",
"PurchaseUrl": "http://localhost:5200",
"UseCustomizationData": true, "UseCustomizationData": true,
"IsClusterEnv": "False", "IsClusterEnv": "False",
"ActivateCampaignDetailFunction": true, "ActivateCampaignDetailFunction": true,


Loading…
Cancel
Save