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:
- ASPNETCORE_ENVIRONMENT=Development
- 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.
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110
- LocationsUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5109
- PurchaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5200
- CatalogUrlHC=http://catalog.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.


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

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


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

@ -10,6 +10,7 @@ namespace PurchaseBff.Config
public class CatalogOperations
{
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


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

@ -21,6 +21,49 @@ namespace PurchaseBff.Controllers
_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]
[Route("items")]
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,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:58243/",
"applicationUrl": "http://localhost:57425/",
"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);
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
{
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,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50920/",
"applicationUrl": "http://localhost:57423/",
"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]
[HttpGet]
[Route("[action]")]
[Route("items")]
[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
.LongCountAsync();
@ -54,6 +59,23 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
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]
[Route("items/{id:int}")]
[ProducesResponseType((int)HttpStatusCode.NotFound)]


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

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


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

@ -7,13 +7,13 @@ namespace eShopOnContainers.WebSPA
{
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 BasketUrl { get; set; }
public string MarketingUrl { get; set; }
public string PurchaseUrl { 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()
export class BasketService {
private basketUrl: string = '';
private purchaseUrl: string = '';
basket: IBasket = {
buyerId: '',
items: []
@ -40,12 +41,14 @@ export class BasketService {
if (this.authService.UserData) {
this.basket.buyerId = this.authService.UserData.sub;
if (this.configurationService.isReady) {
this.basketUrl = this.configurationService.serverSettings.basketUrl;
this.basketUrl = this.configurationService.serverSettings.purchaseUrl;
this.purchaseUrl = this.configurationService.serverSettings.purchaseUrl;
this.loadData();
}
else {
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();
});
}
@ -63,7 +66,7 @@ export class BasketService {
}
setBasket(basket): Observable<boolean> {
let url = this.basketUrl + '/api/v1/basket/';
let url = this.purchaseUrl + '/purchase-bff/api/v1/basket/';
this.basket = basket;
return this.service.post(url, basket).map((response: Response) => {
return true;
@ -71,7 +74,7 @@ export class BasketService {
}
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) => {
this.basketEvents.orderCreated();
return true;
@ -79,7 +82,7 @@ export class BasketService {
}
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) => {
if (response.status === 204) {
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) {
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) {
if (this.configurationService.isReady)
this.ordersUrl = this.configurationService.serverSettings.orderingUrl;
this.ordersUrl = this.configurationService.serverSettings.purchaseUrl;
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[]> {
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 response.json();
@ -37,7 +37,7 @@ export class OrdersService {
}
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 response.json();


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

@ -1,8 +1,6 @@
export interface IConfiguration {
catalogUrl: string,
orderingUrl: string,
identityUrl: string,
basketUrl: string,
marketingUrl: string,
purchaseUrl: string,
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');
this.serverSettings = response.json();
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('orderingUrl', this.serverSettings.orderingUrl);
this.storageService.store('marketingUrl', this.serverSettings.marketingUrl);
this.storageService.store('purchaseUrl', this.serverSettings.purchaseUrl);
this.storageService.store('activateCampaignDetailFunction', this.serverSettings.activateCampaignDetailFunction);
this.isReady = true;
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 redirect_uri = location.origin + '/';
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 state = Date.now() + '' + Math.random();


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

@ -3,7 +3,7 @@
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50921/",
"applicationUrl": "http://localhost:64923/",
"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",
"MarketingUrl": "http://localhost:5110",
"CallBackUrl": "http://localhost:5104/",
"PurchaseUrl": "http://localhost:5200",
"UseCustomizationData": true,
"IsClusterEnv": "False",
"ActivateCampaignDetailFunction": true,


Loading…
Cancel
Save