Skip to content

Commit

Permalink
feat: switch customer REST resource depending on application and cust…
Browse files Browse the repository at this point in the history
…omer type (#302)
  • Loading branch information
SGrueber authored and dhhyi committed Jul 28, 2020
1 parent 741454c commit 04abd41
Show file tree
Hide file tree
Showing 17 changed files with 530 additions and 115 deletions.
2 changes: 2 additions & 0 deletions .createMockdata.table
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ suggest ?SearchTerm=kodak
products ?amount=9&attrs=sku,salePrice,listPrice,availability,manufacturer,image,minOrderQuantity,inStock,promotions,packingUnit,mastered,productMaster,productMasterSKU,roundedAverageRating&offset=0&returnSortKeys=true&searchTerm=kodak {elements:[.elements[]|select(.uri|test(".*/7912057"))],name,type,sortKeys,total:1,offset,amount}
countries ? . Accept:application/vnd.intershop.country.v1+json
customers/- ? . Authorization:BASIC\ cGF0cmljaWFAdGVzdC5pbnRlcnNob3AuZGU6IUludGVyU2hvcDAwIQ==
privatecustomers/- ? . Authorization:BASIC\ cGF0cmljaWFAdGVzdC5pbnRlcnNob3AuZGU6IUludGVyU2hvcDAwIQ==
cms/includes/include.homepage.content.pagelet2-Include
configurations
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export class ProfileEditDetailsPage {
}

submit() {
cy.server().route('PUT', '**/customers/**').as('customers');
cy.server()
.route('PUT', /.*\/(private)?customers\/-.*/)
.as('customers');
cy.wait(500);

cy.get(this.tag).find('button[type="submit"]').click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ export class ProfileEditEmailPage {
}

submit() {
cy.server().route('PUT', '**/customers/**').as('customers');
cy.server()
.route('PUT', /.*\/(private)?customers\/-.*/)
.as('customers');
cy.wait(500);

cy.get(this.tag).find('button[type="submit"]').click();
Expand Down
4 changes: 3 additions & 1 deletion e2e/cypress/integration/pages/account/registration.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ export class RegistrationPage {
}

submitAndObserve() {
cy.server().route('POST', '**/customers').as('customers');
cy.server()
.route('POST', /.*\/(private)?customers$/)
.as('customers');
cy.wait(500);

this.submit();
Expand Down
28 changes: 27 additions & 1 deletion src/app/core/facades/app.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { Injectable } from '@angular/core';
import { NavigationCancel, NavigationEnd, NavigationStart, Router } from '@angular/router';
import { Store, select } from '@ngrx/store';
import { combineLatest, merge, noop } from 'rxjs';
import { filter, map, mapTo, shareReplay, startWith } from 'rxjs/operators';
import { filter, map, mapTo, shareReplay, startWith, withLatestFrom } from 'rxjs/operators';

import { getAvailableLocales, getCurrentLocale, getDeviceType, getICMBaseURL } from 'ish-core/store/core/configuration';
import { getGeneralError, getGeneralErrorType } from 'ish-core/store/core/error';
import { selectPath } from 'ish-core/store/core/router';
import { getBreadcrumbData, getHeaderType, getWrapperClass, isStickyHeader } from 'ish-core/store/core/viewconf';
import { getLoggedInCustomer } from 'ish-core/store/customer/user';
import { getAllCountries, getCountriesLoading, loadCountries } from 'ish-core/store/general/countries';
import { getRegionsByCountryCode, loadRegions } from 'ish-core/store/general/regions';
import { getServerConfigParameter } from 'ish-core/store/general/server-config';

@Injectable({ providedIn: 'root' })
export class AppFacade {
Expand Down Expand Up @@ -64,6 +66,30 @@ export class AppFacade {
).pipe(startWith(true), shareReplay(1));
path$ = this.store.pipe(select(selectPath));

/**
* selects whether the current application type is 'REST'. If the application type is unknown it returns true
*/
isAppTypeREST$ = this.store.pipe(
select(
getServerConfigParameter<'intershop.REST' | 'intershop.B2CResponsive' | 'intershop.SMBResponsive'>(
'application.applicationType'
)
),
map(appType => appType === 'intershop.REST' || !appType)
);

customerRestResource$ = this.isAppTypeREST$.pipe(
withLatestFrom(this.store.pipe(select(getLoggedInCustomer))),
map(([isRest, customer]) => AppFacade.getCustomerRestResource(!customer || customer.isBusinessCustomer, isRest))
);

static getCustomerRestResource(
isBusinessCustomer: boolean,
isAppTypeREST: boolean
): 'customers' | 'privatecustomers' {
return isAppTypeREST && !isBusinessCustomer ? 'privatecustomers' : 'customers';
}

countries$() {
this.store.dispatch(loadCountries());
return this.store.pipe(select(getAllCountries));
Expand Down
12 changes: 10 additions & 2 deletions src/app/core/models/customer/customer.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export class CustomerMapper {
* Get User data for the logged in Business Customer.
*/
static mapLoginData(data: CustomerData): CustomerUserType {
return data.type === 'SMBCustomer'
return CustomerMapper.isBusinessCustomer(data)
? {
customer: CustomerMapper.fromData(data),
user: undefined,
Expand Down Expand Up @@ -36,7 +36,7 @@ export class CustomerMapper {
* Map customer data in dependence of the customer type (PrivateCustomer/SMBCustomer)
*/
static fromData(data: CustomerData): Customer {
return data.type === 'SMBCustomer'
return CustomerMapper.isBusinessCustomer(data)
? {
customerNo: data.customerNo,
isBusinessCustomer: true,
Expand All @@ -51,4 +51,12 @@ export class CustomerMapper {
isBusinessCustomer: false,
};
}

private static isBusinessCustomer(data: CustomerData): boolean {
// ToDo: #IS-30018 use the customer type for this decision
if (data.type === 'PrivateCustomer') {
return false;
}
return true;
}
}
28 changes: 26 additions & 2 deletions src/app/core/services/address/address.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { anything, instance, mock, verify, when } from 'ts-mockito';

import { AppFacade } from 'ish-core/facades/app.facade';
import { ApiService } from 'ish-core/services/api/api.service';
import { BasketMockData } from 'ish-core/utils/dev/basket-mock-data';

Expand All @@ -9,13 +11,24 @@ import { AddressService } from './address.service';
describe('Address Service', () => {
let addressService: AddressService;
let apiService: ApiService;
let appFacade: AppFacade;

beforeEach(() => {
apiService = mock(ApiService);
addressService = new AddressService(instance(apiService));
appFacade = mock(AppFacade);

TestBed.configureTestingModule({
providers: [
{ provide: ApiService, useFactory: () => instance(apiService) },
{ provide: AppFacade, useFactory: () => instance(appFacade) },
],
});
when(appFacade.customerRestResource$).thenReturn(of('customers'));

addressService = TestBed.inject(AddressService);
});

it("should get addresses data when 'getCustomerAddresses' is called", done => {
it("should get addresses data when 'getCustomerAddresses' is called for b2c/b2x applications", done => {
when(apiService.get(`customers/-/addresses`)).thenReturn(of([]));
when(apiService.resolveLinks()).thenReturn(() => of([]));

Expand All @@ -25,6 +38,17 @@ describe('Address Service', () => {
});
});

it("should get addresses data when 'getCustomerAddresses' is called for rest applications", done => {
when(apiService.get(`privatecustomers/-/addresses`)).thenReturn(of([]));
when(apiService.resolveLinks()).thenReturn(() => of([]));
when(appFacade.customerRestResource$).thenReturn(of('privatecustomers'));

addressService.getCustomerAddresses().subscribe(() => {
verify(apiService.get(`privatecustomers/-/addresses`)).once();
done();
});
});

it("should create an address when 'createCustomerAddress' is called", done => {
when(apiService.post(`customers/-/addresses`, anything())).thenReturn(
of({ type: 'Link', uri: 'site/-/customers/-/addresses/addressid' })
Expand Down
47 changes: 34 additions & 13 deletions src/app/core/services/address/address.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map, mapTo } from 'rxjs/operators';
import { concatMap, first, map, mapTo } from 'rxjs/operators';

import { AppFacade } from 'ish-core/facades/app.facade';
import { AddressMapper } from 'ish-core/models/address/address.mapper';
import { Address } from 'ish-core/models/address/address.model';
import { Link } from 'ish-core/models/link/link.model';
Expand All @@ -12,18 +13,23 @@ import { ApiService, unpackEnvelope } from 'ish-core/services/api/api.service';
*/
@Injectable({ providedIn: 'root' })
export class AddressService {
constructor(private apiService: ApiService) {}
constructor(private apiService: ApiService, private appFacade: AppFacade) {}

/**
* Gets the addresses for the given customer id. Falls back to '-' as customer id to get the addresses for the current user.
* @param customerId The customer id.
* @returns The customer's addresses.
*/
getCustomerAddresses(customerId: string = '-'): Observable<Address[]> {
return this.apiService.get(`customers/${customerId}/addresses`).pipe(
unpackEnvelope<Link>(),
this.apiService.resolveLinks<Address>(),
map(addressesData => addressesData.map(AddressMapper.fromData))
return this.appFacade.customerRestResource$.pipe(
first(),
concatMap(restResource =>
this.apiService.get(`${restResource}/${customerId}/addresses`).pipe(
unpackEnvelope<Link>(),
this.apiService.resolveLinks<Address>(),
map(addressesData => addressesData.map(AddressMapper.fromData))
)
)
);
}

Expand All @@ -39,9 +45,14 @@ export class AddressService {
mainDivision: address.mainDivisionCode,
};

return this.apiService
.post(`customers/${customerId}/addresses`, customerAddress)
.pipe(this.apiService.resolveLink<Address>(), map(AddressMapper.fromData));
return this.appFacade.customerRestResource$.pipe(
first(),
concatMap(restResource =>
this.apiService
.post(`${restResource}/${customerId}/addresses`, customerAddress)
.pipe(this.apiService.resolveLink<Address>(), map(AddressMapper.fromData))
)
);
}

/**
Expand All @@ -55,9 +66,14 @@ export class AddressService {
mainDivision: address.mainDivisionCode,
};

return this.apiService
.put(`customers/${customerId}/addresses/${address.id}`, customerAddress)
.pipe(map(AddressMapper.fromData));
return this.appFacade.customerRestResource$.pipe(
first(),
concatMap(restResource =>
this.apiService
.put(`${restResource}/${customerId}/addresses/${address.id}`, customerAddress)
.pipe(map(AddressMapper.fromData))
)
);
}

/**
Expand All @@ -67,6 +83,11 @@ export class AddressService {
* @returns The id of the deleted address.
*/
deleteCustomerAddress(customerId: string = '-', addressId: string): Observable<string> {
return this.apiService.delete(`customers/${customerId}/addresses/${addressId}`).pipe(mapTo(addressId));
return this.appFacade.customerRestResource$.pipe(
first(),
concatMap(restResource =>
this.apiService.delete(`${restResource}/${customerId}/addresses/${addressId}`).pipe(mapTo(addressId))
)
);
}
}
30 changes: 28 additions & 2 deletions src/app/core/services/payment/payment.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { provideMockStore } from '@ngrx/store/testing';
import { of } from 'rxjs';
import { anyString, anything, instance, mock, verify, when } from 'ts-mockito';

import { AppFacade } from 'ish-core/facades/app.facade';
import { Customer } from 'ish-core/models/customer/customer.model';
import { Locale } from 'ish-core/models/locale/locale.model';
import { PaymentInstrument } from 'ish-core/models/payment-instrument/payment-instrument.model';
Expand All @@ -15,6 +16,7 @@ import { PaymentService } from './payment.service';
describe('Payment Service', () => {
let paymentService: PaymentService;
let apiService: ApiService;
let appFacade: AppFacade;

const basketMock = {
data: {
Expand Down Expand Up @@ -67,12 +69,19 @@ describe('Payment Service', () => {

beforeEach(() => {
apiService = mock(ApiService);
appFacade = mock(AppFacade);

TestBed.configureTestingModule({
providers: [
{ provide: ApiService, useFactory: () => instance(apiService) },
provideMockStore({ selectors: [{ selector: getCurrentLocale, value: { lang: 'en_US' } as Locale }] }),
{ provide: AppFacade, useFactory: () => instance(appFacade) },
provideMockStore({
selectors: [{ selector: getCurrentLocale, value: { lang: 'en_US' } as Locale }],
}),
],
});
when(appFacade.customerRestResource$).thenReturn(of('customers'));

paymentService = TestBed.inject(PaymentService);
});

Expand Down Expand Up @@ -187,7 +196,7 @@ describe('Payment Service', () => {
});

describe('user payment service', () => {
it("should get a user's payment method data when 'getUserPaymentMethods' is called", done => {
it("should get a user's payment method data when 'getUserPaymentMethods' is called for b2c/b2x applications", done => {
when(apiService.get(anyString())).thenReturn(of([]));
when(apiService.resolveLinks()).thenReturn(() => of([]));
when(apiService.options(anyString())).thenReturn(of([]));
Expand All @@ -203,6 +212,23 @@ describe('Payment Service', () => {
});
});

it("should get a user's payment method data when 'getUserPaymentMethods' is called for rest applications", done => {
when(apiService.get(anyString())).thenReturn(of([]));
when(apiService.resolveLinks()).thenReturn(() => of([]));
when(apiService.options(anyString())).thenReturn(of([]));
when(appFacade.customerRestResource$).thenReturn(of('privatecustomers'));
const customer = {
customerNo: '4711',
isBusinessCustomer: false,
} as Customer;

paymentService.getUserPaymentMethods(customer).subscribe(() => {
verify(apiService.get('privatecustomers/4711/payments')).once();
verify(apiService.options('privatecustomers/4711/payments')).once();
done();
});
});

it("should create a payment instrument for the user when 'createUserPayment' is called", done => {
const customerNo = '12345';

Expand Down
Loading

0 comments on commit 04abd41

Please sign in to comment.