Skip to content

Commit

Permalink
perf: fetching eligible basket addresses (#1567)
Browse files Browse the repository at this point in the history
  • Loading branch information
SGrueber authored Jan 12, 2024
1 parent afab8cf commit 71a93ad
Show file tree
Hide file tree
Showing 20 changed files with 254 additions and 131 deletions.
16 changes: 13 additions & 3 deletions src/app/core/facades/checkout.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { LineItemUpdate } from 'ish-core/models/line-item-update/line-item-updat
import { PaymentInstrument } from 'ish-core/models/payment-instrument/payment-instrument.model';
import { selectRouteData } from 'ish-core/store/core/router';
import { getServerConfigParameter } from 'ish-core/store/core/server-config';
import { getAllAddresses } from 'ish-core/store/customer/addresses';
import {
addMessageToMerchant,
addPromotionCodeToBasket,
Expand All @@ -25,6 +24,7 @@ import {
deleteBasketItems,
deleteBasketPayment,
deleteBasketShippingAddress,
getBasketEligibleAddresses,
getBasketEligiblePaymentMethods,
getBasketEligibleShippingMethods,
getBasketError,
Expand All @@ -38,6 +38,7 @@ import {
getCurrentBasket,
getSubmittedBasket,
isBasketInvoiceAndShippingAddressEqual,
loadBasketEligibleAddresses,
loadBasketEligiblePaymentMethods,
loadBasketEligibleShippingMethods,
loadBasketWithId,
Expand Down Expand Up @@ -283,18 +284,27 @@ export class CheckoutFacade {
select(
createSelector(
getLoggedInUser,
getAllAddresses,
getBasketEligibleAddresses,
getBasketShippingAddress,
(user, addresses, shippingAddress): boolean =>
!!shippingAddress &&
!!user &&
addresses.length > 1 &&
addresses?.length > 1 &&
(!user.preferredInvoiceToAddressUrn || user.preferredInvoiceToAddressUrn !== shippingAddress.urn) &&
(!user.preferredShipToAddressUrn || user.preferredShipToAddressUrn !== shippingAddress.urn)
)
)
);

eligibleAddresses$() {
return this.basket$.pipe(
whenTruthy(),
take(1),
tap(() => this.store.dispatch(loadBasketEligibleAddresses())),
switchMap(() => this.store.pipe(select(getBasketEligibleAddresses)))
);
}

assignBasketAddress(addressId: string, scope: 'invoice' | 'shipping' | 'any') {
this.store.dispatch(assignBasketAddress({ addressId, scope }));
}
Expand Down
9 changes: 9 additions & 0 deletions src/app/core/services/basket/basket.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,15 @@ describe('Basket Service', () => {
});
});

it("should get eligible addresses for a basket when 'getBasketEligibleAddresses' is called", done => {
when(apiService.get(anything(), anything())).thenReturn(of({ data: [] }));

basketService.getBasketEligibleAddresses().subscribe(() => {
verify(apiService.get('eligible-addresses', anything())).once();
done();
});
});

it("should get eligible shipping methods for a basket when 'getBasketEligibleShippingMethods' is called", done => {
when(apiService.get(anything(), anything())).thenReturn(of({ data: [] }));

Expand Down
59 changes: 21 additions & 38 deletions src/app/core/services/basket/basket.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { Observable, of, throwError } from 'rxjs';
import { catchError, concatMap, map } from 'rxjs/operators';

import { AddressData } from 'ish-core/models/address/address.interface';
import { AddressMapper } from 'ish-core/models/address/address.mapper';
import { Address } from 'ish-core/models/address/address.model';
import { Attribute } from 'ish-core/models/attribute/attribute.model';
Expand Down Expand Up @@ -32,41 +33,6 @@ export type BasketUpdateType =
| { invoiceToAddress: string }
| { messageToMerchant: string };

type BasketIncludeType =
| 'invoiceToAddress'
| 'commonShipToAddress'
| 'commonShippingMethod'
| 'discounts'
| 'lineItems_discounts'
| 'lineItems'
| 'payments'
| 'payments_paymentMethod'
| 'payments_paymentInstrument';

type MergeBasketIncludeType =
| 'targetBasket'
| 'targetBasket_invoiceToAddress'
| 'targetBasket_commonShipToAddress'
| 'targetBasket_commonShippingMethod'
| 'targetBasket_discounts'
| 'targetBasket_lineItems_discounts'
| 'targetBasket_lineItems'
| 'targetBasket_payments'
| 'targetBasket_payments_paymentMethod'
| 'targetBasket_payments_paymentInstrument';

type ValidationBasketIncludeType =
| 'basket'
| 'basket_invoiceToAddress'
| 'basket_commonShipToAddress'
| 'basket_commonShippingMethod'
| 'basket_discounts'
| 'basket_lineItems_discounts'
| 'basket_lineItems'
| 'basket_payments'
| 'basket_payments_paymentMethod'
| 'basket_payments_paymentInstrument';

/**
* The Basket Service handles the interaction with the 'baskets' REST API.
* Methods related to basket-items are handled in the basket-items.service.
Expand All @@ -84,7 +50,7 @@ export class BasketService {
Accept: 'application/vnd.intershop.basket.v1+json',
});

private allBasketIncludes: BasketIncludeType[] = [
private readonly allBasketIncludes = [
'invoiceToAddress',
'commonShipToAddress',
'commonShippingMethod',
Expand All @@ -96,7 +62,7 @@ export class BasketService {
'payments_paymentInstrument',
];

private allTargetBasketIncludes: MergeBasketIncludeType[] = [
private readonly allTargetBasketIncludes = [
'targetBasket',
'targetBasket_invoiceToAddress',
'targetBasket_commonShipToAddress',
Expand All @@ -109,7 +75,7 @@ export class BasketService {
'targetBasket_payments_paymentInstrument',
];

private allBasketValidationIncludes: ValidationBasketIncludeType[] = [
private readonly allBasketValidationIncludes = [
'basket',
'basket_invoiceToAddress',
'basket_commonShipToAddress',
Expand Down Expand Up @@ -309,6 +275,23 @@ export class BasketService {
});
}

/**
* Get eligible addresses for the currently used basket.
*
* @returns The eligible addresses.
*/
getBasketEligibleAddresses(): Observable<Address[]> {
return this.apiService
.currentBasketEndpoint()
.get('eligible-addresses', {
headers: this.basketHeaders,
})
.pipe(
unpackEnvelope<AddressData>('data'),
map(addressesData => addressesData.map(AddressMapper.fromData))
);
}

/**
* Create a basket address for the currently used basket of an anonymous user.
*
Expand Down
11 changes: 2 additions & 9 deletions src/app/core/store/customer/addresses/addresses.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ import { createReducer, on } from '@ngrx/store';

import { Address } from 'ish-core/models/address/address.model';
import { HttpError } from 'ish-core/models/http-error/http-error.model';
import {
createBasketAddress,
createBasketAddressSuccess,
deleteBasketShippingAddress,
updateBasketAddress,
} from 'ish-core/store/customer/basket';
import { deleteBasketShippingAddress, updateBasketAddress } from 'ish-core/store/customer/basket';
import { setErrorOn, setLoadingOn, unsetLoadingAndErrorOn } from 'ish-core/utils/ngrx-creators';

import {
Expand Down Expand Up @@ -43,7 +38,6 @@ export const addressesReducer = createReducer(
setLoadingOn(
loadAddresses,
createCustomerAddress,
createBasketAddress,
updateCustomerAddress,
updateBasketAddress,
deleteCustomerAddress,
Expand All @@ -53,12 +47,11 @@ export const addressesReducer = createReducer(
unsetLoadingAndErrorOn(
loadAddressesSuccess,
createCustomerAddressSuccess,
createBasketAddressSuccess,
updateCustomerAddressSuccess,
deleteCustomerAddressSuccess
),
on(loadAddressesSuccess, (state, action) => addressAdapter.setAll(action.payload.addresses, state)),
on(createCustomerAddressSuccess, createBasketAddressSuccess, updateCustomerAddressSuccess, (state, action) =>
on(createCustomerAddressSuccess, updateCustomerAddressSuccess, (state, action) =>
addressAdapter.upsertOne(action.payload.address, state)
),
on(deleteCustomerAddressSuccess, (state, action) => addressAdapter.removeOne(action.payload.addressId, state))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing';

import { Address } from 'ish-core/models/address/address.model';
import { CoreStoreModule } from 'ish-core/store/core/core-store.module';
import { createBasketAddress, createBasketAddressSuccess, updateBasketAddress } from 'ish-core/store/customer/basket';
import { updateBasketAddress } from 'ish-core/store/customer/basket';
import { CustomerStoreModule } from 'ish-core/store/customer/customer-store.module';
import { makeHttpError } from 'ish-core/utils/dev/api-service-utils';
import { BasketMockData } from 'ish-core/utils/dev/basket-mock-data';
Expand Down Expand Up @@ -125,46 +125,6 @@ describe('Addresses Selectors', () => {
});
});

describe('create basket addresses', () => {
const address = BasketMockData.getAddress();

beforeEach(() => {
store$.dispatch(createBasketAddress({ address, scope: 'invoice' }));
});

it('should set the state to loading', () => {
expect(getAddressesLoading(store$.state)).toBeTrue();
});

describe('and reporting success', () => {
beforeEach(() => {
store$.dispatch(createBasketAddressSuccess({ address, scope: 'invoice' }));
});

it('should set loading to false and add basket address', () => {
expect(getAddressesLoading(store$.state)).toBeFalse();
expect(getAllAddresses(store$.state)).toEqual([address]);
});
});

describe('and reporting failure', () => {
beforeEach(() => {
store$.dispatch(createCustomerAddressFail({ error: makeHttpError({ message: 'error' }) }));
});

it('should not have loaded addresses on error', () => {
expect(getAddressesLoading(store$.state)).toBeFalse();
expect(getAllAddresses(store$.state)).toBeEmpty();
expect(getAddressesError(store$.state)).toMatchInlineSnapshot(`
{
"message": "error",
"name": "HttpErrorResponse",
}
`);
});
});
});

describe('update basket addresses', () => {
const address = BasketMockData.getAddress();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import {
createBasketAddressSuccess,
deleteBasketShippingAddress,
loadBasket,
loadBasketEligibleAddresses,
loadBasketEligibleAddressesFail,
loadBasketEligibleAddressesSuccess,
resetBasketErrors,
updateBasket,
updateBasketAddress,
Expand Down Expand Up @@ -57,6 +60,45 @@ describe('Basket Addresses Effects', () => {
store = TestBed.inject(Store);
});

describe('loadBasketEligibleAddresses$', () => {
beforeEach(() => {
when(basketServiceMock.getBasketEligibleAddresses()).thenReturn(of([]));
});

it('should call the basketService for loadBasketEligibleAddresses', done => {
const action = loadBasketEligibleAddresses();
actions$ = of(action);

effects.loadBasketEligibleAddresses$.subscribe(() => {
verify(basketServiceMock.getBasketEligibleAddresses()).once();
done();
});
});

it('should map to action of type LoadBasketEligibleAddressesSuccess', () => {
const action = loadBasketEligibleAddresses();
const completion = loadBasketEligibleAddressesSuccess({ addresses: [] });

actions$ = hot('-a-a-a', { a: action });
const expected$ = cold('-c-c-c', { c: completion });

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

it('should map invalid request to action of type LoadBasketEligibleAddressesFail', () => {
when(basketServiceMock.getBasketEligibleAddresses()).thenReturn(
throwError(() => makeHttpError({ message: 'invalid' }))
);

const action = loadBasketEligibleAddresses();
const completion = loadBasketEligibleAddressesFail({ error: makeHttpError({ message: 'invalid' }) });
actions$ = hot('-a-a-a', { a: action });
const expected$ = cold('-c-c-c', { c: completion });

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

describe('createAddressForBasket$ for a logged in user', () => {
beforeEach(() => {
when(addressServiceMock.createCustomerAddress('-', anything())).thenReturn(of(BasketMockData.getAddress()));
Expand Down Expand Up @@ -212,9 +254,10 @@ describe('Basket Addresses Effects', () => {
const action = updateBasketAddress({ address });
const completion1 = updateCustomerAddressSuccess({ address });
const completion2 = loadBasket();
const completion3 = resetBasketErrors();
const completion3 = loadBasketEligibleAddresses();
const completion4 = resetBasketErrors();
actions$ = hot('-a', { a: action });
const expected$ = cold('-(cde)', { c: completion1, d: completion2, e: completion3 });
const expected$ = cold('-(cdef)', { c: completion1, d: completion2, e: completion3, f: completion4 });

expect(effects.updateBasketAddress$).toBeObservable(expected$);
});
Expand Down Expand Up @@ -298,8 +341,9 @@ describe('Basket Addresses Effects', () => {
const action = deleteBasketShippingAddress({ addressId });
const completion1 = deleteCustomerAddressSuccess({ addressId });
const completion2 = loadBasket();
const completion3 = loadBasketEligibleAddresses();
actions$ = hot('-a', { a: action });
const expected$ = cold('-(cd)', { c: completion1, d: completion2 });
const expected$ = cold('-(cde)', { c: completion1, d: completion2, e: completion3 });

expect(effects.deleteBasketShippingAddress$).toBeObservable(expected$);
});
Expand Down
Loading

0 comments on commit 71a93ad

Please sign in to comment.