diff --git a/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.adapter.ts b/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.adapter.ts index 6dac748e0d6..34fd3e52231 100644 --- a/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.adapter.ts +++ b/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.adapter.ts @@ -38,6 +38,16 @@ export abstract class ConfiguratorTextfieldAdapter { parameters: CommonConfigurator.ReadConfigurationFromCartEntryParameters ): Observable; + /** + * Abstract method to read a configuration for an order entry + * + * @param parameters read from order entry parameters object + * @returns Observable of configurations + */ + abstract readConfigurationForOrderEntry( + parameters: CommonConfigurator.ReadConfigurationFromOrderEntryParameters + ): Observable; + /** * Abstract method to update a configuration attached to a cart entry * diff --git a/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.spec.ts b/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.spec.ts index 041dfa68a0d..4f1b0bc646a 100644 --- a/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.spec.ts +++ b/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.spec.ts @@ -37,6 +37,10 @@ class MockConfiguratorTextfieldAdapter implements ConfiguratorTextfieldAdapter { (params: CommonConfigurator.ReadConfigurationFromCartEntryParameters) => of('readConfigurationForCartEntry' + params) ); + readConfigurationForOrderEntry = createSpy().and.callFake( + (params: CommonConfigurator.ReadConfigurationFromOrderEntryParameters) => + of('readConfigurationForOrderEntry' + params) + ); } describe('ConfiguratorTextfieldConnector', () => { @@ -88,10 +92,9 @@ describe('ConfiguratorTextfieldConnector', () => { ConfiguratorTextfieldAdapter as Type ); - const params: CommonConfigurator.ReadConfigurationFromCartEntryParameters = - { - owner: ConfiguratorModelUtils.createInitialOwner(), - }; + const params: CommonConfigurator.ReadConfigurationFromCartEntryParameters = { + owner: ConfiguratorModelUtils.createInitialOwner(), + }; let result; service .readConfigurationForCartEntry(params) @@ -100,6 +103,22 @@ describe('ConfiguratorTextfieldConnector', () => { expect(adapter.readConfigurationForCartEntry).toHaveBeenCalledWith(params); }); + it('should call adapter on readConfigurationForOrderEntry', () => { + const adapter = TestBed.inject( + ConfiguratorTextfieldAdapter as Type + ); + + const params: CommonConfigurator.ReadConfigurationFromOrderEntryParameters = { + owner: ConfiguratorModelUtils.createInitialOwner(), + }; + let result; + service + .readConfigurationForOrderEntry(params) + .subscribe((res) => (result = res)); + expect(result).toBe('readConfigurationForOrderEntry' + params); + expect(adapter.readConfigurationForOrderEntry).toHaveBeenCalledWith(params); + }); + it('should call adapter on addToCart', () => { const adapter = TestBed.inject( ConfiguratorTextfieldAdapter as Type diff --git a/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.ts b/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.ts index 9e56fb4fb9c..a7aa5bf75de 100644 --- a/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.ts +++ b/feature-libs/product-configurator/textfield/core/connectors/configurator-textfield.connector.ts @@ -31,6 +31,16 @@ export class ConfiguratorTextfieldConnector { ): Observable { return this.adapter.readConfigurationForCartEntry(parameters); } + /** + * Reads an existing configuration for an order entry + * @param parameters Attributes needed to read a product configuration for an order entry + * @returns Observable of product configurations + */ + readConfigurationForOrderEntry( + parameters: CommonConfigurator.ReadConfigurationFromOrderEntryParameters + ): Observable { + return this.adapter.readConfigurationForOrderEntry(parameters); + } /** * Updates a configuration that is attached to a cart entry * @param parameters Attributes needed to update a cart entries' configuration diff --git a/feature-libs/product-configurator/textfield/core/state/actions/configurator-textfield.action.ts b/feature-libs/product-configurator/textfield/core/state/actions/configurator-textfield.action.ts index 8baf22e5a99..e03b874c94b 100644 --- a/feature-libs/product-configurator/textfield/core/state/actions/configurator-textfield.action.ts +++ b/feature-libs/product-configurator/textfield/core/state/actions/configurator-textfield.action.ts @@ -19,6 +19,12 @@ export const READ_CART_ENTRY_CONFIGURATION_FAIL = '[Configurator] Read cart entry configuration Textfield Fail'; export const READ_CART_ENTRY_CONFIGURATION_SUCCESS = '[Configurator] Read cart entry configuration Textfield Success'; +export const READ_ORDER_ENTRY_CONFIGURATION = + '[Configurator] Read order entry configuration textfield'; +export const READ_ORDER_ENTRY_CONFIGURATION_FAIL = + '[Configurator] Read order entry configuration textfield Fail'; +export const READ_ORDER_ENTRY_CONFIGURATION_SUCCESS = + '[Configurator] Read order entry configuration textfield Success'; export const UPDATE_CART_ENTRY_CONFIGURATION = '[Configurator] Update cart entry configuration Textfield'; export const UPDATE_CART_ENTRY_CONFIGURATION_FAIL = @@ -108,6 +114,29 @@ export class ReadCartEntryConfigurationFail extends StateUtils.LoaderFailAction } } +export class ReadOrderEntryConfiguration extends StateUtils.LoaderLoadAction { + readonly type = READ_ORDER_ENTRY_CONFIGURATION; + constructor( + public payload: CommonConfigurator.ReadConfigurationFromOrderEntryParameters + ) { + super(CONFIGURATION_TEXTFIELD_DATA); + } +} + +export class ReadOrderEntryConfigurationSuccess extends StateUtils.LoaderSuccessAction { + readonly type = READ_ORDER_ENTRY_CONFIGURATION_SUCCESS; + constructor(public payload: ConfiguratorTextfield.Configuration) { + super(CONFIGURATION_TEXTFIELD_DATA); + } +} + +export class ReadOrderEntryConfigurationFail extends StateUtils.LoaderFailAction { + readonly type = READ_ORDER_ENTRY_CONFIGURATION_FAIL; + constructor(public payload: any) { + super(CONFIGURATION_TEXTFIELD_DATA, payload); + } +} + export class RemoveConfiguration extends StateUtils.LoaderResetAction { readonly type = REMOVE_CONFIGURATION; constructor() { @@ -123,5 +152,8 @@ export type ConfiguratorActions = | ReadCartEntryConfigurationFail | ReadCartEntryConfigurationSuccess | ReadCartEntryConfiguration + | ReadOrderEntryConfigurationFail + | ReadOrderEntryConfigurationSuccess + | ReadOrderEntryConfiguration | UpdateCartEntryConfiguration | RemoveConfiguration; diff --git a/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts b/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts index 18a85a42244..7ce56865f44 100644 --- a/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts +++ b/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.spec.ts @@ -48,6 +48,7 @@ const cartModification: CartModification = { describe('ConfiguratorTextfieldEffect', () => { let createMock: jasmine.Spy; let readFromCartEntryMock: jasmine.Spy; + let readFromOrderEntryMock: jasmine.Spy; let addToCartMock: jasmine.Spy; let updateCartEntryMock: jasmine.Spy; @@ -60,6 +61,9 @@ describe('ConfiguratorTextfieldEffect', () => { readFromCartEntryMock = jasmine .createSpy() .and.returnValue(of(productConfiguration)); + readFromOrderEntryMock = jasmine + .createSpy() + .and.returnValue(of(productConfiguration)); addToCartMock = jasmine.createSpy().and.returnValue(of(cartModification)); updateCartEntryMock = jasmine .createSpy() @@ -68,6 +72,7 @@ describe('ConfiguratorTextfieldEffect', () => { createConfiguration = createMock; addToCart = addToCartMock; readConfigurationForCartEntry = readFromCartEntryMock; + readConfigurationForOrderEntry = readFromOrderEntryMock; updateConfigurationForCartEntry = updateCartEntryMock; } @@ -109,10 +114,9 @@ describe('ConfiguratorTextfieldEffect', () => { payloadInput ); - const completion = - new ConfiguratorTextfieldActions.CreateConfigurationSuccess( - productConfiguration - ); + const completion = new ConfiguratorTextfieldActions.CreateConfigurationSuccess( + productConfiguration + ); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); @@ -129,10 +133,9 @@ describe('ConfiguratorTextfieldEffect', () => { payloadInput ); - const completionFailure = - new ConfiguratorTextfieldActions.CreateConfigurationFail( - normalizeHttpError(errorResponse) - ); + const completionFailure = new ConfiguratorTextfieldActions.CreateConfigurationFail( + normalizeHttpError(errorResponse) + ); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completionFailure }); @@ -140,18 +143,16 @@ describe('ConfiguratorTextfieldEffect', () => { }); it('should emit a success action with content for an action of type readConfigurationFromCart if read from cart is successful', () => { - const payloadInput: CommonConfigurator.ReadConfigurationFromCartEntryParameters = - { - owner: ConfiguratorModelUtils.createInitialOwner(), - }; + const payloadInput: CommonConfigurator.ReadConfigurationFromCartEntryParameters = { + owner: ConfiguratorModelUtils.createInitialOwner(), + }; const action = new ConfiguratorTextfieldActions.ReadCartEntryConfiguration( payloadInput ); - const completion = - new ConfiguratorTextfieldActions.ReadCartEntryConfigurationSuccess( - productConfiguration - ); + const completion = new ConfiguratorTextfieldActions.ReadCartEntryConfigurationSuccess( + productConfiguration + ); actions$ = hot('-a', { a: action }); const expectedObs = cold('-b', { b: completion }); @@ -162,18 +163,16 @@ describe('ConfiguratorTextfieldEffect', () => { it('should emit a fail action in case read from cart leads to an error', () => { readFromCartEntryMock.and.returnValue(throwError(errorResponse)); - const payloadInput: CommonConfigurator.ReadConfigurationFromCartEntryParameters = - { - owner: ConfiguratorModelUtils.createInitialOwner(), - }; + const payloadInput: CommonConfigurator.ReadConfigurationFromCartEntryParameters = { + owner: ConfiguratorModelUtils.createInitialOwner(), + }; const action = new ConfiguratorTextfieldActions.ReadCartEntryConfiguration( payloadInput ); - const completionFailure = - new ConfiguratorTextfieldActions.ReadCartEntryConfigurationFail( - normalizeHttpError(errorResponse) - ); + const completionFailure = new ConfiguratorTextfieldActions.ReadCartEntryConfigurationFail( + normalizeHttpError(errorResponse) + ); actions$ = hot('-a', { a: action }); const expectedObs = cold('-b', { b: completionFailure }); @@ -182,6 +181,45 @@ describe('ConfiguratorTextfieldEffect', () => { ); }); + it('should emit a success action with content for an action of type readOrderEntryConfiguration if read from order entry is successful', () => { + const payloadInput: CommonConfigurator.ReadConfigurationFromOrderEntryParameters = { + owner: ConfiguratorModelUtils.createInitialOwner(), + }; + const action = new ConfiguratorTextfieldActions.ReadOrderEntryConfiguration( + payloadInput + ); + + const completion = new ConfiguratorTextfieldActions.ReadOrderEntryConfigurationSuccess( + productConfiguration + ); + actions$ = cold('-a', { a: action }); + const expectedObs = cold('-b', { b: completion }); + + expect(configEffects.readConfigurationForOrderEntry$).toBeObservable( + expectedObs + ); + }); + + it('should emit a fail action in case read from order entry leads to an error', () => { + readFromOrderEntryMock.and.returnValue(throwError(errorResponse)); + const payloadInput: CommonConfigurator.ReadConfigurationFromOrderEntryParameters = { + owner: ConfiguratorModelUtils.createInitialOwner(), + }; + const action = new ConfiguratorTextfieldActions.ReadOrderEntryConfiguration( + payloadInput + ); + + const completionFailure = new ConfiguratorTextfieldActions.ReadOrderEntryConfigurationFail( + normalizeHttpError(errorResponse) + ); + actions$ = cold('-a', { a: action }); + const expectedObs = cold('-b', { b: completionFailure }); + + expect(configEffects.readConfigurationForOrderEntry$).toBeObservable( + expectedObs + ); + }); + it('createConfiguration must not emit anything in case source action is not covered', () => { const action = new ConfiguratorTextfieldActions.CreateConfigurationSuccess({ configurationInfos: [], @@ -208,8 +246,7 @@ describe('ConfiguratorTextfieldEffect', () => { userId: userId, }); - const removeConfiguration = - new ConfiguratorTextfieldActions.RemoveConfiguration(); + const removeConfiguration = new ConfiguratorTextfieldActions.RemoveConfiguration(); actions$ = hot('-a', { a: action }); const expected = cold('-(bc)', { @@ -250,17 +287,15 @@ describe('ConfiguratorTextfieldEffect', () => { cartEntryNumber: cartEntryNumber, configuration: productConfiguration, }; - const action = - new ConfiguratorTextfieldActions.UpdateCartEntryConfiguration( - payloadInput - ); + const action = new ConfiguratorTextfieldActions.UpdateCartEntryConfiguration( + payloadInput + ); const loadCart = new CartActions.LoadCart({ userId: userId, cartId: cartId, }); - const removeConfiguration = - new ConfiguratorTextfieldActions.RemoveConfiguration(); + const removeConfiguration = new ConfiguratorTextfieldActions.RemoveConfiguration(); actions$ = hot('-a', { a: action }); const expected = cold('-(bc)', { @@ -278,14 +313,12 @@ describe('ConfiguratorTextfieldEffect', () => { cartEntryNumber: cartEntryNumber, configuration: productConfiguration, }; - const action = - new ConfiguratorTextfieldActions.UpdateCartEntryConfiguration( - payloadInput - ); - const cartUpdateFail = - new ConfiguratorTextfieldActions.UpdateCartEntryConfigurationFail( - normalizeHttpError(errorResponse) - ); + const action = new ConfiguratorTextfieldActions.UpdateCartEntryConfiguration( + payloadInput + ); + const cartUpdateFail = new ConfiguratorTextfieldActions.UpdateCartEntryConfigurationFail( + normalizeHttpError(errorResponse) + ); actions$ = hot('-a', { a: action }); diff --git a/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.ts b/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.ts index 193eb160abf..635c4036c3f 100644 --- a/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.ts +++ b/feature-libs/product-configurator/textfield/core/state/effects/configurator-textfield.effect.ts @@ -135,6 +135,35 @@ export class ConfiguratorTextfieldEffects { ) ); + @Effect() + readConfigurationForOrderEntry$: Observable< + | ConfiguratorTextfieldActions.ReadOrderEntryConfigurationSuccess + | ConfiguratorTextfieldActions.ReadOrderEntryConfigurationFail + > = this.actions$.pipe( + ofType(ConfiguratorTextfieldActions.READ_ORDER_ENTRY_CONFIGURATION), + switchMap( + (action: ConfiguratorTextfieldActions.ReadOrderEntryConfiguration) => { + const parameters: CommonConfigurator.ReadConfigurationFromOrderEntryParameters = + action.payload; + + return this.configuratorTextfieldConnector + .readConfigurationForOrderEntry(parameters) + .pipe( + switchMap((result: ConfiguratorTextfield.Configuration) => [ + new ConfiguratorTextfieldActions.ReadOrderEntryConfigurationSuccess( + result + ), + ]), + catchError((error) => [ + new ConfiguratorTextfieldActions.ReadOrderEntryConfigurationFail( + normalizeHttpError(error) + ), + ]) + ); + } + ) + ); + constructor( private actions$: Actions, private configuratorTextfieldConnector: ConfiguratorTextfieldConnector diff --git a/feature-libs/product-configurator/textfield/occ/default-occ-configurator-textfield-config.ts b/feature-libs/product-configurator/textfield/occ/default-occ-configurator-textfield-config.ts index 92c051870e9..e156a77db15 100644 --- a/feature-libs/product-configurator/textfield/occ/default-occ-configurator-textfield-config.ts +++ b/feature-libs/product-configurator/textfield/occ/default-occ-configurator-textfield-config.ts @@ -13,7 +13,8 @@ export function defaultOccConfiguratorTextfieldConfigFactory(): OccConfig { readTextfieldConfigurationForCartEntry: 'users/${userId}/carts/${cartId}/entries/${cartEntryNumber}/configurator/textfield', - + readTextfieldConfigurationForOrderEntry: + 'users/${userId}/orders/${orderId}/entries/${orderEntryNumber}/configurator/textfield', updateTextfieldConfigurationForCartEntry: 'users/${userId}/carts/${cartId}/entries/${cartEntryNumber}/configurator/textfield', }, diff --git a/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.spec.ts b/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.spec.ts index d55f5d1a9a9..8221875d116 100644 --- a/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.spec.ts +++ b/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.spec.ts @@ -34,7 +34,9 @@ class MockOccEndpointsService { const productCode = 'CONF_LAPTOP'; const USER_ID = 'theUser'; const CART_ID = '98876'; +const ORDER_ID = '0001000'; const CART_ENTRY_NUMBER = '1'; +const ORDER_ENTRY_NUMBER = '10'; const PRODUCT_CODE = 'CPQ_LAPTOP'; const QUANTITY = 1; const LABEL1 = 'LABEL1'; @@ -58,20 +60,25 @@ const addToCartParameters: ConfiguratorTextfield.AddToCartParameters = { configuration: configuration, }; -const updateCartEntryParameters: ConfiguratorTextfield.UpdateCartEntryParameters = - { - userId: USER_ID, - cartId: CART_ID, - cartEntryNumber: CART_ENTRY_NUMBER, - configuration: configuration, - }; -const readParams: CommonConfigurator.ReadConfigurationFromCartEntryParameters = - { - userId: USER_ID, - cartId: CART_ID, - cartEntryNumber: '0', - owner: ConfiguratorModelUtils.createInitialOwner(), - }; +const updateCartEntryParameters: ConfiguratorTextfield.UpdateCartEntryParameters = { + userId: USER_ID, + cartId: CART_ID, + cartEntryNumber: CART_ENTRY_NUMBER, + configuration: configuration, +}; +const readParams: CommonConfigurator.ReadConfigurationFromCartEntryParameters = { + userId: USER_ID, + cartId: CART_ID, + cartEntryNumber: '0', + owner: ConfiguratorModelUtils.createInitialOwner(), +}; + +const readParamsForOrder: CommonConfigurator.ReadConfigurationFromOrderEntryParameters = { + userId: USER_ID, + orderId: ORDER_ID, + orderEntryNumber: ORDER_ENTRY_NUMBER, + owner: ConfiguratorModelUtils.createInitialOwner(), +}; describe('OccConfigurationTextfieldAdapter', () => { let occConfiguratorVariantAdapter: OccConfiguratorTextfieldAdapter; @@ -169,6 +176,36 @@ describe('OccConfigurationTextfieldAdapter', () => { ); }); + it('should call readTextfieldConfigurationForOrderEntry endpoint', () => { + occConfiguratorVariantAdapter + .readConfigurationForOrderEntry(readParamsForOrder) + .subscribe(); + + const mockReq = httpMock.expectOne((req) => { + return ( + req.method === 'GET' && + req.url === 'readTextfieldConfigurationForOrderEntry' + ); + }); + + expect(occEnpointsService.buildUrl).toHaveBeenCalledWith( + 'readTextfieldConfigurationForOrderEntry', + { + urlParams: { + userId: USER_ID, + orderId: ORDER_ID, + orderEntryNumber: ORDER_ENTRY_NUMBER, + }, + } + ); + + expect(mockReq.cancelled).toBeFalsy(); + expect(mockReq.request.responseType).toEqual('json'); + expect(converterService.pipeable).toHaveBeenCalledWith( + CONFIGURATION_TEXTFIELD_NORMALIZER + ); + }); + it('should call addConfigurationTextfieldToCart endpoint', () => { occConfiguratorVariantAdapter.addToCart(addToCartParameters).subscribe(); diff --git a/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.ts b/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.ts index 2b6275733c6..f1458ab16f2 100644 --- a/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.ts +++ b/feature-libs/product-configurator/textfield/occ/occ-configurator-textfield.adapter.ts @@ -20,8 +20,7 @@ import { OccConfiguratorTextfield } from './occ-configurator-textfield.models'; @Injectable() export class OccConfiguratorTextfieldAdapter - implements ConfiguratorTextfieldAdapter -{ + implements ConfiguratorTextfieldAdapter { constructor( protected http: HttpClient, protected occEndpointsService: OccEndpointsService, @@ -100,6 +99,32 @@ export class OccConfiguratorTextfieldAdapter }) ); } + readConfigurationForOrderEntry( + parameters: CommonConfigurator.ReadConfigurationFromOrderEntryParameters + ): Observable { + const url = this.occEndpointsService.buildUrl( + 'readTextfieldConfigurationForOrderEntry', + { + urlParams: { + userId: parameters.userId, + orderId: parameters.orderId, + orderEntryNumber: parameters.orderEntryNumber, + }, + } + ); + + return this.http.get(url).pipe( + this.converterService.pipeable(CONFIGURATION_TEXTFIELD_NORMALIZER), + map((resultConfiguration) => { + return { + ...resultConfiguration, + owner: { + ...parameters.owner, + }, + }; + }) + ); + } updateConfigurationForCartEntry( parameters: ConfiguratorTextfield.UpdateCartEntryParameters ): Observable {