Skip to content

Commit

Permalink
feat: introduce price update strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
dhhyi committed Mar 30, 2022
1 parent 638609e commit 34a1be2
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 81 deletions.
8 changes: 8 additions & 0 deletions src/app/core/configurations/injection-keys.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { InjectionToken } from '@angular/core';

import { CookieConsentOptions } from 'ish-core/models/cookies/cookies.model';
import { PriceUpdateType } from 'ish-core/models/price/price.model';
import { ViewType } from 'ish-core/models/viewtype/viewtype.types';
import { DataRetentionPolicy } from 'ish-core/utils/meta-reducers';

Expand Down Expand Up @@ -58,6 +59,13 @@ export const DATA_RETENTION_POLICY = new InjectionToken<DataRetentionPolicy>('da
factory: () => environment.dataRetention,
});

/**
* the configured price update policy for the application
*/
export const PRICE_UPDATE = new InjectionToken<PriceUpdateType>('priceUpdate', {
factory: () => environment.priceUpdate,
});

/**
* the configured theme color
*/
Expand Down
15 changes: 13 additions & 2 deletions src/app/core/facades/product-context.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,20 @@ export class ProductContextFacade extends RxState<ProductContext> {
case 'prices':
wrap(
'prices',
combineLatest([this.select('displayProperties', 'price'), this.select('product', 'sku')]).pipe(
combineLatest([
this.select('displayProperties', 'price'),
this.select('product').pipe(
filter(p => !!p && !p.failed),
mapToProperty('sku'),
distinctUntilChanged()
),
this.select('requiredCompletenessLevel').pipe(
map(completeness => completeness === true),
distinctUntilChanged()
),
]).pipe(
filter(([visible]) => !!visible),
switchMap(([, ids]) => this.shoppingFacade.productPrices$(ids))
switchMap(([, sku, fresh]) => this.shoppingFacade.productPrices$(sku, fresh))
)
);
break;
Expand Down
24 changes: 19 additions & 5 deletions src/app/core/facades/shopping.facade.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Injectable } from '@angular/core';
import { Inject, Injectable } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable, combineLatest } from 'rxjs';
import { Observable, combineLatest, identity } from 'rxjs';
import { debounce, filter, map, pairwise, startWith, switchMap, tap } from 'rxjs/operators';

import { PRICE_UPDATE } from 'ish-core/configurations/injection-keys';
import { PriceItemHelper } from 'ish-core/models/price-item/price-item.helper';
import { PriceUpdateType } from 'ish-core/models/price/price.model';
import { ProductListingID } from 'ish-core/models/product-listing/product-listing.model';
import { ProductCompletenessLevel, ProductHelper } from 'ish-core/models/product/product.model';
import { selectRouteParam } from 'ish-core/store/core/router';
Expand Down Expand Up @@ -32,6 +34,7 @@ import {
getProductListingViewType,
loadMoreProducts,
} from 'ish-core/store/shopping/product-listing';
import { loadProductPrices } from 'ish-core/store/shopping/product-prices';
import { getProductPrice } from 'ish-core/store/shopping/product-prices/product-prices.selectors';
import {
getProduct,
Expand All @@ -51,7 +54,7 @@ import { whenFalsy, whenTruthy } from 'ish-core/utils/operators';
/* eslint-disable @typescript-eslint/member-ordering */
@Injectable({ providedIn: 'root' })
export class ShoppingFacade {
constructor(private store: Store) {}
constructor(private store: Store, @Inject(PRICE_UPDATE) private priceUpdate: PriceUpdateType) {}

// CATEGORY

Expand Down Expand Up @@ -113,11 +116,22 @@ export class ShoppingFacade {
);
}

productPrices$(sku: string | Observable<string>) {
productPrices$(sku: string | Observable<string>, fresh = false) {
return toObservable(sku).pipe(
whenTruthy(),
switchMap(plainSKU =>
combineLatest([
this.store.pipe(select(getProductPrice(plainSKU))),
this.store.pipe(
select(getProductPrice(plainSKU)),
// reset state when updates are forced
this.priceUpdate === 'always' || fresh ? startWith(undefined) : identity,
tap(prices => {
if (!prices) {
this.store.dispatch(loadProductPrices({ skus: [plainSKU] }));
}
}),
whenTruthy()
),
this.store.pipe(select(getPriceDisplayType)),
]).pipe(map(args => PriceItemHelper.selectPricing(...args)))
)
Expand Down
2 changes: 2 additions & 0 deletions src/app/core/models/price/price.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export type PriceUpdateType = 'stable' | 'always';

export interface Price {
type: 'Money';
value: number;
Expand Down
15 changes: 0 additions & 15 deletions src/app/core/store/shopping/products/products.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ import { ProductPriceDetails } from 'ish-core/models/product-prices/product-pric
import { Product, VariationProductMaster } from 'ish-core/models/product/product.model';
import { ProductsService } from 'ish-core/services/products/products.service';
import { CoreStoreModule } from 'ish-core/store/core/core-store.module';
import { CustomerStoreModule } from 'ish-core/store/customer/customer-store.module';
import { personalizationStatusDetermined } from 'ish-core/store/customer/user/user.actions';
import { loadCategory } from 'ish-core/store/shopping/categories';
import { setProductListingPageSize } from 'ish-core/store/shopping/product-listing';
import { loadProductPricesSuccess } from 'ish-core/store/shopping/product-prices';
import { loadProductPrices } from 'ish-core/store/shopping/product-prices/product-prices.actions';
import { ShoppingStoreModule } from 'ish-core/store/shopping/shopping-store.module';
import { makeHttpError } from 'ish-core/utils/dev/api-service-utils';
import { HttpStatusCodeService } from 'ish-core/utils/http-status-code/http-status-code.service';
Expand Down Expand Up @@ -66,7 +64,6 @@ describe('Products Effects', () => {
TestBed.configureTestingModule({
imports: [
CoreStoreModule.forTesting(['router', 'serverConfig']),
CustomerStoreModule.forTesting('user'),
RouterTestingModule.withRoutes([
{ path: 'category/:categoryUniqueId/product/:sku', children: [] },
{ path: 'product/:sku', children: [] },
Expand Down Expand Up @@ -189,18 +186,6 @@ describe('Products Effects', () => {
}));
});

describe('loadProductPricesAfterProductSuccess$', () => {
it('should trigger action to load product prices after successful load product action', () => {
const sku = 'sku123';
const action = loadProductSuccess({ product: { sku } as Product });
const completion = loadProductPrices({ skus: [sku] });
actions$ = hot('-a-a-a', { a: action });
const expected$ = cold('-c-c-c', { c: completion });

expect(effects.loadProductPricesAfterProductSuccess$).toBeObservable(expected$);
});
});

describe('loadProductsForCategory$', () => {
it('should call service for SKU list', done => {
actions$ = of(loadProductsForCategory({ categoryId: '123', sorting: 'name-asc' }));
Expand Down
11 changes: 0 additions & 11 deletions src/app/core/store/shopping/products/products.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { setBreadcrumbData } from 'ish-core/store/core/viewconf';
import { personalizationStatusDetermined } from 'ish-core/store/customer/user';
import { loadCategory } from 'ish-core/store/shopping/categories';
import { getProductListingItemsPerPage, setProductListingPages } from 'ish-core/store/shopping/product-listing';
import { loadProductPrices } from 'ish-core/store/shopping/product-prices';
import { HttpStatusCodeService } from 'ish-core/utils/http-status-code/http-status-code.service';
import {
delayUntil,
Expand Down Expand Up @@ -109,16 +108,6 @@ export class ProductsEffects {
)
);

loadProductPricesAfterProductSuccess$ = createEffect(() =>
this.actions$.pipe(
ofType(loadProductSuccess),
mapToPayloadProperty('product'),
mapToProperty('sku'),
whenTruthy(),
map(sku => loadProductPrices({ skus: [sku] }))
)
);

/**
* retrieve products for category incremental respecting paging
*/
Expand Down
48 changes: 0 additions & 48 deletions src/app/core/store/shopping/shopping-store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { ProductsService } from 'ish-core/services/products/products.service';
import { PromotionsService } from 'ish-core/services/promotions/promotions.service';
import { SuggestService } from 'ish-core/services/suggest/suggest.service';
import { CoreStoreModule } from 'ish-core/store/core/core-store.module';
import { CustomerStoreModule } from 'ish-core/store/customer/customer-store.module';
import { personalizationStatusDetermined } from 'ish-core/store/customer/user';
import { makeHttpError } from 'ish-core/utils/dev/api-service-utils';
import { StoreWithSnapshots, provideStoreSnapshots } from 'ish-core/utils/dev/ngrx-testing';
Expand Down Expand Up @@ -145,7 +144,6 @@ describe('Shopping Store', () => {
TestBed.configureTestingModule({
imports: [
CoreStoreModule.forTesting(['router', 'configuration', 'serverConfig'], true),
CustomerStoreModule.forTesting('user'),
RouterTestingModule.withRoutes([
{
path: 'home',
Expand Down Expand Up @@ -319,10 +317,6 @@ describe('Shopping Store', () => {
sortableAttributes: []
[Filter API] Load Filter Success:
filterNavigation: {}
[Product Price Internal] Load Product Prices:
skus: ["P2"]
[Products API] Load Product Prices Success:
prices: []
`);
}));

Expand All @@ -341,11 +335,7 @@ describe('Shopping Store', () => {
sku: "P2"
[Products API] Load Product Success:
product: {"sku":"P2","name":"nP2"}
[Product Price Internal] Load Product Prices:
skus: ["P2"]
@ngrx/router-store/navigated: /product/P2
[Products API] Load Product Prices Success:
prices: []
`);
}));
});
Expand Down Expand Up @@ -470,12 +460,6 @@ describe('Shopping Store', () => {
sortableAttributes: []
[Filter API] Load Filter Success:
filterNavigation: {}
[Product Price Internal] Load Product Prices:
skus: ["P1"]
[Product Price Internal] Load Product Prices:
skus: ["P2"]
[Products API] Load Product Prices Success:
prices: []
`);
}));

Expand All @@ -494,11 +478,7 @@ describe('Shopping Store', () => {
sku: "P1"
[Products API] Load Product Success:
product: {"sku":"P1","name":"nP1"}
[Product Price Internal] Load Product Prices:
skus: ["P1"]
@ngrx/router-store/navigated: /category/A.123.456/product/P1
[Products API] Load Product Prices Success:
prices: []
`);
}));

Expand Down Expand Up @@ -565,10 +545,6 @@ describe('Shopping Store', () => {
sortableAttributes: []
[Filter API] Load Filter Success:
filterNavigation: {}
[Product Price Internal] Load Product Prices:
skus: ["P2"]
[Products API] Load Product Prices Success:
prices: []
`);
}));

Expand Down Expand Up @@ -609,17 +585,11 @@ describe('Shopping Store', () => {
sortableAttributes: []
[Filter API] Load Filter Success:
filterNavigation: {}
[Product Price Internal] Load Product Prices:
skus: ["P1"]
[Product Price Internal] Load Product Prices:
skus: ["P2"]
@ngrx/router-store/navigated: /category/A.123.456
[Product Listing] Load More Products:
id: {"type":"category","value":"A.123.456"}
[Viewconf Internal] Set Breadcrumb Data:
breadcrumbData: [{"text":"nA","link":"/nA-catA"},{"text":"nA123","link":"/nA...
[Products API] Load Product Prices Success:
prices: []
`);
}));
});
Expand Down Expand Up @@ -688,11 +658,7 @@ describe('Shopping Store', () => {
sku: "P1"
[Products API] Load Product Success:
product: {"sku":"P1","name":"nP1"}
[Product Price Internal] Load Product Prices:
skus: ["P1"]
@ngrx/router-store/navigated: /category/A.123.456/product/P1
[Products API] Load Product Prices Success:
prices: []
`);
}));

Expand Down Expand Up @@ -744,17 +710,11 @@ describe('Shopping Store', () => {
sortableAttributes: []
[Filter API] Load Filter Success:
filterNavigation: {}
[Product Price Internal] Load Product Prices:
skus: ["P1"]
[Product Price Internal] Load Product Prices:
skus: ["P2"]
@ngrx/router-store/navigated: /category/A.123.456
[Product Listing] Load More Products:
id: {"type":"category","value":"A.123.456"}
[Viewconf Internal] Set Breadcrumb Data:
breadcrumbData: [{"text":"nA","link":"/nA-catA"},{"text":"nA123","link":"/nA...
[Products API] Load Product Prices Success:
prices: []
`);
}));
});
Expand Down Expand Up @@ -812,11 +772,7 @@ describe('Shopping Store', () => {
sku: "P1"
[Products API] Load Product Success:
product: {"sku":"P1","name":"nP1"}
[Product Price Internal] Load Product Prices:
skus: ["P1"]
@ngrx/router-store/navigated: /product/P1
[Products API] Load Product Prices Success:
prices: []
`);
}));

Expand Down Expand Up @@ -964,10 +920,6 @@ describe('Shopping Store', () => {
sortableAttributes: []
[Filter API] Load Filter Success:
filterNavigation: {}
[Product Price Internal] Load Product Prices:
skus: ["P2"]
[Products API] Load Product Prices Success:
prices: []
`);
}));
});
Expand Down
8 changes: 8 additions & 0 deletions src/environments/environment.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Auth0Config } from 'ish-core/identity-provider/auth0.identity-provider';
import { CookieConsentOptions } from 'ish-core/models/cookies/cookies.model';
import { PriceUpdateType } from 'ish-core/models/price/price.model';
import { DeviceType, ViewType } from 'ish-core/models/viewtype/viewtype.types';
import { DataRetentionPolicy } from 'ish-core/utils/meta-reducers';
import { MultiSiteLocaleMap } from 'ish-core/utils/multi-site/multi-site.service';
Expand Down Expand Up @@ -112,6 +113,12 @@ export interface Environment {

// enable and configure data persistence for specific stores (compare, recently, tacton)
dataRetention: DataRetentionPolicy;

/** Price update mechanism:
* - 'always': fetch fresh price information all the time
* - 'stable': only fetch prices once per application lifetime
*/
priceUpdate: PriceUpdateType;
}

export const ENVIRONMENT_DEFAULTS: Omit<Environment, 'icmChannel'> = {
Expand Down Expand Up @@ -170,4 +177,5 @@ export const ENVIRONMENT_DEFAULTS: Omit<Environment, 'icmChannel'> = {
recently: 60 * 24 * 7, // 1 week
tacton: 'forever',
},
priceUpdate: 'always',
};

1 comment on commit 34a1be2

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Azure Demo Servers are available:

Please sign in to comment.