From 260f4df59fe0de1a207c9bf94ba09617d5b6b73f Mon Sep 17 00:00:00 2001 From: Krzysztof Platis Date: Fri, 30 Jun 2023 12:24:42 +0200 Subject: [PATCH 01/11] fix: 'this' is undefined in ProductEffects.productLoadEffect (#17594) Previous behavior: When `/products` endpoint returned a http error, the code broke in [this line](https://github.com/SAP/spartacus/blob/ed1e1a78c488b1e1214491ffa736612287f8cf70/projects/core/src/product/store/effects/product.effect.ts#L77), complaining that `this` is undefined. Fix: Preserve the context of `this` which was lost in [this line](https://github.com/SAP/spartacus/blob/ed1e1a78c488b1e1214491ffa736612287f8cf70/projects/core/src/product/store/effects/product.effect.ts#L52) The problem was revealed only after we implemented [CXSPA-2251](https://jira.tools.sap/browse/CXSPA-2251) where we referenced `this` by adding `this.logger` to the method `ProductEffects.productLoadEffect` fixes https://jira.tools.sap/browse/CXSPA-3902 --- projects/core/src/product/store/effects/product.effect.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/core/src/product/store/effects/product.effect.ts b/projects/core/src/product/store/effects/product.effect.ts index 87582569f82..3192d8fe193 100644 --- a/projects/core/src/product/store/effects/product.effect.ts +++ b/projects/core/src/product/store/effects/product.effect.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Action } from '@ngrx/store'; -import { Observable, merge, of } from 'rxjs'; +import { merge, Observable, of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { AuthActions } from '../../../auth/user-auth/store/actions'; import { LoggerService } from '../../../logger'; @@ -49,7 +49,7 @@ export class ProductEffects { merge( ...this.productConnector .getMany(products) - .map(this.productLoadEffect) + .map((productLoad) => this.productLoadEffect(productLoad)) ) ), withdrawOn(this.contextChange$) From 0357962f195ad41eab7e604957f517d04e886b04 Mon Sep 17 00:00:00 2001 From: kpawelczak <42094017+kpawelczak@users.noreply.github.com> Date: Tue, 25 Jul 2023 12:58:42 +0200 Subject: [PATCH 02/11] feat: handle http errors in ssr (#17624) Co-authored-by: Krzysztof Platis Co-authored-by: github-actions[bot] --- projects/core/src/base-core.module.ts | 2 + .../http-error-handler.interceptor.spec.ts | 75 +++++++++++++++++++ .../http-error-handler.interceptor.ts | 45 +++++++++++ .../http-error-handler.module.ts | 26 +++++++ .../http-error-handler/index.ts | 8 ++ projects/core/src/error-handling/index.ts | 1 + 6 files changed, 157 insertions(+) create mode 100644 projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.spec.ts create mode 100644 projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.ts create mode 100644 projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts create mode 100644 projects/core/src/error-handling/http-error-handler/index.ts diff --git a/projects/core/src/base-core.module.ts b/projects/core/src/base-core.module.ts index 1cd7d76970a..d427123b4e6 100644 --- a/projects/core/src/base-core.module.ts +++ b/projects/core/src/base-core.module.ts @@ -19,9 +19,11 @@ import { MetaTagConfigModule } from './occ/config/meta-tag-config.module'; import { ProcessModule } from './process/process.module'; import { SiteContextModule } from './site-context/site-context.module'; import { StateModule } from './state/state.module'; +import { HttpErrorHandlerModule } from './error-handling'; @NgModule({ imports: [ + HttpErrorHandlerModule.forRoot(), // Import this module before any other interceptor to handle HTTP errors efficiently StateModule.forRoot(), ConfigModule.forRoot(), ConfigInitializerModule.forRoot(), diff --git a/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.spec.ts b/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.spec.ts new file mode 100644 index 00000000000..5e87c2b0bfe --- /dev/null +++ b/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.spec.ts @@ -0,0 +1,75 @@ +import { + HttpErrorResponse, + HttpEvent, + HttpHandler, + HttpRequest, +} from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { TestBed } from '@angular/core/testing'; +import { HttpErrorHandlerInterceptor } from './http-error-handler.interceptor'; +import { ErrorHandler, Injectable } from '@angular/core'; + +@Injectable() +class MockErrorHandler { + handleError(_error: any): void {} +} + +describe('HttpErrorHandlerInterceptor', () => { + let interceptor: HttpErrorHandlerInterceptor; + let errorHandler: ErrorHandler; + let request: HttpRequest; + let next: HttpHandler; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + HttpErrorHandlerInterceptor, + { provide: ErrorHandler, useClass: MockErrorHandler }, + ], + }); + + interceptor = TestBed.inject(HttpErrorHandlerInterceptor); + errorHandler = TestBed.inject(ErrorHandler); + + request = new HttpRequest('GET', 'test-url'); + next = { + handle: () => new Observable>(), + } as HttpHandler; + }); + + it('should create the interceptor', () => { + expect(interceptor).toBeTruthy(); + }); + + it('should call handleError on error', (done) => { + const error: HttpErrorResponse = new HttpErrorResponse({ + status: 400, + statusText: 'error', + }); + spyOn(errorHandler, 'handleError'); + + next.handle = () => throwError(error); + + interceptor.intercept(request, next).subscribe({ + error: (err) => { + expect(err).toEqual(error); + expect(errorHandler.handleError).toHaveBeenCalledWith(error); + done(); + }, + }); + }); + + it('should pass through the request when there is no error', (done) => { + const response: HttpEvent = { + status: 200, + statusText: 'ok', + } as HttpEvent; + next.handle = () => + new Observable>((observer) => observer.next(response)); + + interceptor.intercept(request, next).subscribe((result) => { + expect(result).toBe(response); + done(); + }); + }); +}); diff --git a/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.ts b/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.ts new file mode 100644 index 00000000000..b996f06f725 --- /dev/null +++ b/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.ts @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ErrorHandler, Injectable } from '@angular/core'; +import { + HttpErrorResponse, + HttpEvent, + HttpHandler, + HttpInterceptor, + HttpRequest, +} from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +/** + * This interceptor forwards all HTTP errors (e.g. 5xx or 4xx status response from backend) + * to Angular `ErrorHandler`. + * + * Thanks to this, in SSR, any HTTP error from backend can potentially mark the Server-Side Rendering + * as an error and therefore allow for sending an appropriate error response to a final client of SSR. + */ +@Injectable() +export class HttpErrorHandlerInterceptor implements HttpInterceptor { + constructor(protected errorHandler: ErrorHandler) {} + + intercept( + request: HttpRequest, + next: HttpHandler + ): Observable> { + return next.handle(request).pipe( + tap({ + error: (error) => { + this.handleError(error); + }, + }) + ); + } + + protected handleError(error: HttpErrorResponse): void { + this.errorHandler.handleError(error); + } +} diff --git a/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts b/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts new file mode 100644 index 00000000000..7a0d11ca3ce --- /dev/null +++ b/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { HttpErrorHandlerInterceptor } from './http-error-handler.interceptor'; + +@NgModule() +export class HttpErrorHandlerModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: HttpErrorHandlerModule, + + providers: [ + { + provide: HTTP_INTERCEPTORS, + useClass: HttpErrorHandlerInterceptor, + multi: true, + }, + ], + }; + } +} diff --git a/projects/core/src/error-handling/http-error-handler/index.ts b/projects/core/src/error-handling/http-error-handler/index.ts new file mode 100644 index 00000000000..37800d5c183 --- /dev/null +++ b/projects/core/src/error-handling/http-error-handler/index.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './http-error-handler.interceptor'; +export * from './http-error-handler.module'; diff --git a/projects/core/src/error-handling/index.ts b/projects/core/src/error-handling/index.ts index 5215bb557f2..210387276fa 100644 --- a/projects/core/src/error-handling/index.ts +++ b/projects/core/src/error-handling/index.ts @@ -6,3 +6,4 @@ export * from './cx-error-handler'; export * from './error-handling.module'; +export * from './http-error-handler'; From 038ffdbd90d2c63a2e9abb32b2d5cc6eb7c0d093 Mon Sep 17 00:00:00 2001 From: kpawelczak <42094017+kpawelczak@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:26:49 +0200 Subject: [PATCH 03/11] feat: update optimized ssr engine tests (#17728) --- .../optimized-ssr-engine.spec.ts | 289 ++++++++++++++---- 1 file changed, 235 insertions(+), 54 deletions(-) diff --git a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts index ac97df28ba5..754946a8665 100644 --- a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts +++ b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.spec.ts @@ -8,9 +8,9 @@ import { NgExpressEngineInstance } from '../engine-decorator/ng-express-engine-d import { ExpressServerLogger, ExpressServerLoggerContext } from '../logger'; import { OptimizedSsrEngine, SsrCallbackFn } from './optimized-ssr-engine'; import { + defaultSsrOptimizationOptions, RenderingStrategy, SsrOptimizationOptions, - defaultSsrOptimizationOptions, } from './ssr-optimization-options'; const defaultRenderTime = 100; @@ -38,8 +38,8 @@ class MockExpressServerLogger implements Partial { * 3. Examine renders property for the renders */ class TestEngineRunner { - /** Accumulates html output for engine runs */ - renders: string[] = []; + /** Accumulates responses produced by engine runs (either successful html or Error) */ + responses: (string | Error)[] = []; /** Accumulates response parameters for engine runs */ responseParams: object[] = []; @@ -48,7 +48,11 @@ class TestEngineRunner { optimizedSsrEngine: OptimizedSsrEngine; engineInstance: NgExpressEngineInstance; - constructor(options: SsrOptimizationOptions, renderTime?: number) { + constructor( + options: SsrOptimizationOptions, + renderTime?: number, + renderError?: boolean // SPIKE NEW + ) { // mocked engine instance that will render test output in 100 milliseconds const engineInstanceMock = ( filePath: string, @@ -56,7 +60,14 @@ class TestEngineRunner { callback: SsrCallbackFn ) => { setTimeout(() => { - callback(undefined, `${filePath}-${this.renderCount++}`); + const result = `${filePath}-${this.renderCount++}`; + + if (renderError) { + const err = new Error(result); + callback(err); + } else { + callback(undefined, result); + } }, renderTime ?? defaultRenderTime); }; @@ -96,14 +107,14 @@ class TestEngineRunner { app, connection: >{}, res: >{ - set: (key: string, value: any) => (response[key] = value), locals: {}, + set: (key: string, value: any) => (response[key] = value), }, }, }; - this.engineInstance(url, optionsMock, (_, html): void => { - this.renders.push(html ?? ''); + this.engineInstance(url, optionsMock, (err, html): void => { + this.responses.push(err ?? html ?? ''); // SPIKE TODO: change to `?? undefined` this.responseParams.push(response); }); @@ -165,35 +176,35 @@ describe('OptimizedSsrEngine', () => { const engineRunner = new TestEngineRunner({ timeout: 50 }).request('a'); tick(200); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); })); it('should return timed out render in the followup request', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 50 }).request('a'); tick(200); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); engineRunner.request('a'); - expect(engineRunner.renders[1]).toEqual('a-0'); + expect(engineRunner.responses[1]).toEqual('a-0'); })); it('should return render if rendering meets timeout', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 150 }).request('a'); tick(200); - expect(engineRunner.renders).toEqual(['a-0']); + expect(engineRunner.responses).toEqual(['a-0']); })); it('should fallback instantly if is set to 0', () => { const engineRunner = new TestEngineRunner({ timeout: 0 }).request('a'); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); }); it('should return timed out render in the followup request, also when timeout is set to 0', fakeAsync(() => { const engineRunner = new TestEngineRunner({ timeout: 0 }).request('a'); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 1, }); @@ -204,18 +215,43 @@ describe('OptimizedSsrEngine', () => { }); engineRunner.request('a'); - expect(engineRunner.renders[1]).toEqual('a-0'); + expect(engineRunner.responses[1]).toEqual('a-0'); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 0, }); })); + + it('should fallback to CSR if rendering request fails', fakeAsync(() => { + const engineRunner = new TestEngineRunner( + { timeout: 50 }, + defaultRenderTime, + true + ).request('b'); + + tick(200); + expect(engineRunner.responses).toEqual(['']); + })); + + it('should return timed out render if followup request fails', fakeAsync(() => { + const engineRunner = new TestEngineRunner( + { timeout: 50 }, + defaultRenderTime, + true + ).request('b'); + + tick(200); + expect(engineRunner.responses).toEqual(['']); + + engineRunner.request('b'); + expect(engineRunner.responses[1]).toEqual(new Error('b-0')); + })); }); describe('no-store cache control header', () => { it('should be applied for a fallback', () => { const engineRunner = new TestEngineRunner({ timeout: 0 }).request('a'); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); expect(engineRunner.responseParams).toEqual([ { 'Cache-Control': 'no-store' }, ]); @@ -225,7 +261,7 @@ describe('OptimizedSsrEngine', () => { const engineRunner = new TestEngineRunner({ timeout: 200 }).request('a'); tick(200); - expect(engineRunner.renders).toEqual(['a-0']); + expect(engineRunner.responses).toEqual(['a-0']); expect(engineRunner.responseParams).toEqual([{}]); })); it('should not be applied for a render served with next response', fakeAsync(() => { @@ -233,7 +269,7 @@ describe('OptimizedSsrEngine', () => { tick(200); engineRunner.request('a'); - expect(engineRunner.renders).toEqual(['', 'a-0']); + expect(engineRunner.responses).toEqual(['', 'a-0']); expect(engineRunner.responseParams).toEqual([ { 'Cache-Control': 'no-store' }, {}, @@ -253,7 +289,7 @@ describe('OptimizedSsrEngine', () => { tick(200); engineRunner.request('a'); tick(200); - expect(engineRunner.renders).toEqual(['a-0', 'a-1', 'a-2']); + expect(engineRunner.responses).toEqual(['a-0', 'a-1', 'a-2']); })); it('should cache requests if enabled', fakeAsync(() => { @@ -281,7 +317,7 @@ describe('OptimizedSsrEngine', () => { tick(200); - expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']); + expect(engineRunner.responses).toEqual(['a-0', 'a-0', 'a-0']); })); }); @@ -298,7 +334,7 @@ describe('OptimizedSsrEngine', () => { .request('e'); tick(200); - expect(engineRunner.renders).toEqual([ + expect(engineRunner.responses).toEqual([ '', // CSR fallback for 'd' '', // CSR fallback for 'e' 'a-0', @@ -321,7 +357,7 @@ describe('OptimizedSsrEngine', () => { engineRunner.request('f').request('g'); tick(200); - expect(engineRunner.renders).toEqual([ + expect(engineRunner.responses).toEqual([ '', // CSR fallback for 'c' 'a-0', '', // CSR fallback for 'e' @@ -334,7 +370,7 @@ describe('OptimizedSsrEngine', () => { }); describe('ttl option', () => { - it('should invalidate expired renders', fakeAsync(() => { + it('should invalidate expired responses', fakeAsync(() => { let currentDate = 100; jest.spyOn(Date, 'now').mockImplementation(() => currentDate); @@ -353,7 +389,7 @@ describe('OptimizedSsrEngine', () => { engineRunner.request('a'); tick(200); - expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-1']); + expect(engineRunner.responses).toEqual(['a-0', 'a-0', 'a-1']); })); }); @@ -418,7 +454,7 @@ describe('OptimizedSsrEngine', () => { tick(200); engineRunner.request('elu'); tick(200); - expect(engineRunner.renders).toEqual([ + expect(engineRunner.responses).toEqual([ 'ala-0', 'ala-0', 'ela-1', @@ -426,8 +462,23 @@ describe('OptimizedSsrEngine', () => { 'ela-1', ]); })); - }); + it('should apply no-store cache control header for a timed-out render when followup request fails', fakeAsync(() => { + const engineRunner = new TestEngineRunner( + { timeout: 50 }, + defaultRenderTime, + true + ).request('c'); + + tick(200); + engineRunner.request('c'); + expect(engineRunner.responses).toEqual(['', new Error('c-0')]); + expect(engineRunner.responseParams).toEqual([ + { 'Cache-Control': 'no-store' }, + {}, + ]); + })); + }); describe('renderingStrategyResolver option', () => { describe('ALWAYS_SSR', () => { it('should ignore timeout', fakeAsync(() => { @@ -438,7 +489,7 @@ describe('OptimizedSsrEngine', () => { }).request('a'); tick(200); - expect(engineRunner.renders).toEqual(['a-0']); + expect(engineRunner.responses).toEqual(['a-0']); })); it('should ignore timeout also when it is set to 0', fakeAsync(() => { @@ -451,7 +502,7 @@ describe('OptimizedSsrEngine', () => { engineRunner.request('a'); tick(200); - expect(engineRunner.renders).toEqual(['a-0']); + expect(engineRunner.responses).toEqual(['a-0']); })); it('when reuseCurrentRendering is false, it should render each request separately, even if there is already a pending render for the same rendering key', fakeAsync(() => { @@ -475,10 +526,10 @@ describe('OptimizedSsrEngine', () => { expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 2, }); - expect(engineRunner.renders).toEqual([]); + expect(engineRunner.responses).toEqual([]); tick(100); - expect(engineRunner.renders).toEqual(['a-0', 'a-1']); + expect(engineRunner.responses).toEqual(['a-0', 'a-1']); expect( engineRunner.optimizedSsrEngine['expressEngine'] ).toHaveBeenCalledTimes(2); @@ -501,7 +552,7 @@ describe('OptimizedSsrEngine', () => { tick(200); engineRunner.request('a'); tick(200); - expect(engineRunner.renders).toEqual(['', '']); + expect(engineRunner.responses).toEqual(['', '']); })); it('should not start the actual render in the background', fakeAsync(() => { @@ -516,7 +567,7 @@ describe('OptimizedSsrEngine', () => { ); engineRunner.request('a'); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); expect( engineRunner.optimizedSsrEngine['expressEngine'] @@ -535,7 +586,7 @@ describe('OptimizedSsrEngine', () => { tick(200); engineRunner.request('a'); - expect(engineRunner.renders).toEqual(['', 'a-0']); + expect(engineRunner.responses).toEqual(['', 'a-0']); })); it('when reuseCurrentRendering is false, it should fallback to CSR when there is already pending a render for the same rendering key', fakeAsync(() => { @@ -554,10 +605,10 @@ describe('OptimizedSsrEngine', () => { currentConcurrency: 1, }); - expect(engineRunner.renders).toEqual(['']); // immediate fallback to CSR for the 2nd request for the same key + expect(engineRunner.responses).toEqual(['']); // immediate fallback to CSR for the 2nd request for the same key tick(100); - expect(engineRunner.renders).toEqual(['', 'a-0']); + expect(engineRunner.responses).toEqual(['', 'a-0']); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 0, }); @@ -578,9 +629,42 @@ describe('OptimizedSsrEngine', () => { engineRunner.request('a', { httpHeaders: { 'User-Agent': 'bot' } }); tick(200); - expect(engineRunner.renders).toEqual(['', 'a-1']); + expect(engineRunner.responses).toEqual(['', 'a-1']); })); }); + + it('should render fallback to CSR when rendering request fails', fakeAsync(() => { + const engineRunner = new TestEngineRunner( + { + renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, + timeout: 50, + }, + defaultRenderTime, + true + ); + engineRunner.request('a'); + tick(200); + expect(engineRunner.responses).toEqual([new Error('a-0')]); + })); + + it('should render fallback to CSR when followup request fails', fakeAsync(() => { + const engineRunner = new TestEngineRunner( + { + renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, + timeout: 50, + }, + defaultRenderTime, + true + ); + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + tick(200); + expect(engineRunner.responses).toEqual([ + new Error('a-0'), + new Error('a-1'), + ]); + })); }); describe('forcedSsrTimeout option', () => { @@ -597,15 +681,15 @@ describe('OptimizedSsrEngine', () => { }); tick(60); - expect(engineRunner.renders).toEqual([]); + expect(engineRunner.responses).toEqual([]); tick(50); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 0, }); engineRunner.request('a'); - expect(engineRunner.renders).toEqual(['', 'a-0']); + expect(engineRunner.responses).toEqual(['', 'a-0']); expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 0, }); @@ -620,11 +704,44 @@ describe('OptimizedSsrEngine', () => { engineRunner.request('a'); tick(60); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); tick(50); engineRunner.request('a'); - expect(engineRunner.renders).toEqual(['', 'a-0']); + expect(engineRunner.responses).toEqual(['', 'a-0']); + })); + + it('should fallback to CSR when forcedSsrTimeout is exceeded for a request', fakeAsync(() => { + const engineRunner = new TestEngineRunner( + { + renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, + timeout: 50, + forcedSsrTimeout: 150, + }, + defaultRenderTime, + true + ); + engineRunner.request('a'); + tick(200); + expect(engineRunner.responses).toEqual([new Error('a-0')]); + flush(); + })); + + it('should return timed out render in the followup request when forcedSsrTimeout is exceeded', fakeAsync(() => { + const engineRunner = new TestEngineRunner( + { + renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, + timeout: 50, + forcedSsrTimeout: 80, + }, + defaultRenderTime, + true + ); + + engineRunner.request('a'); + tick(200); + engineRunner.request('a'); + expect(engineRunner.responses).toEqual(['', new Error('a-0')]); })); }); describe('maxRenderTime option', () => { @@ -756,7 +873,7 @@ describe('OptimizedSsrEngine', () => { renderTime ).request(requestUrl); jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); - expect(engineRunner.renders).toEqual([]); + expect(engineRunner.responses).toEqual([]); tick(fiveMinutes + 101); expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( @@ -764,10 +881,10 @@ describe('OptimizedSsrEngine', () => { false, { request: expect.objectContaining({ originalUrl: requestUrl }) } ); - expect(engineRunner.renders).toEqual(['']); + expect(engineRunner.responses).toEqual(['']); engineRunner.request(requestUrl); - expect(engineRunner.renders).toEqual(['']); // if the result was cached, the 2nd request would get immediately 'a-0' + expect(engineRunner.responses).toEqual(['']); // if the result was cached, the 2nd request would get immediately 'a-0' flush(); })); }); @@ -822,7 +939,7 @@ describe('OptimizedSsrEngine', () => { false, { request: expect.objectContaining({ originalUrl: requestUrl }) } ); - expect(engineRunner.renders).toEqual(['', '']); + expect(engineRunner.responses).toEqual(['', '']); flush(); })); @@ -852,7 +969,7 @@ describe('OptimizedSsrEngine', () => { tick(100); expect(engineRunner.renderCount).toEqual(1); - expect(engineRunner.renders).toEqual(['', `${requestUrl}-0`]); + expect(engineRunner.responses).toEqual(['', `${requestUrl}-0`]); flush(); })); @@ -890,7 +1007,7 @@ describe('OptimizedSsrEngine', () => { expect(renderExceedMessageCount).toBe(2); expect(engineRunner.renderCount).toEqual(0); - expect(engineRunner.renders).toEqual(['', '']); + expect(engineRunner.responses).toEqual(['', '']); flush(); })); @@ -926,7 +1043,7 @@ describe('OptimizedSsrEngine', () => { tick(200); expect(engineRunner.renderCount).toEqual(1); - expect(engineRunner.renders).toEqual([ + expect(engineRunner.responses).toEqual([ `${requestUrl}-0`, `${requestUrl}-0`, ]); @@ -976,7 +1093,7 @@ describe('OptimizedSsrEngine', () => { false, { request: expect.objectContaining({ originalUrl: requestUrl }) } ); - expect(engineRunner.renders).toEqual(['']); // the first request fallback to CSR due to timeout + expect(engineRunner.responses).toEqual(['']); // the first request fallback to CSR due to timeout expect(getCurrentConcurrency(engineRunner)).toEqual({ currentConcurrency: 1, }); // the render still continues in the background @@ -984,7 +1101,7 @@ describe('OptimizedSsrEngine', () => { // eventually the render succeeds and 2 remaining requests get the same response: tick(100); expect(engineRunner.renderCount).toEqual(1); - expect(engineRunner.renders).toEqual([ + expect(engineRunner.responses).toEqual([ '', // CSR fallback of the 1st request due to it timed out `${requestUrl}-0`, `${requestUrl}-0`, @@ -1016,7 +1133,7 @@ describe('OptimizedSsrEngine', () => { ).not.toHaveBeenCalledWith( `CSR fallback: Concurrency limit exceeded (1)` ); - expect(engineRunner.renders).toEqual(['a-0', 'a-0']); + expect(engineRunner.responses).toEqual(['a-0', 'a-0']); })); it('combined with a different request should take up two concurrency slots', fakeAsync(() => { @@ -1051,7 +1168,7 @@ describe('OptimizedSsrEngine', () => { }); tick(250); - expect(engineRunner.renders).toEqual([ + expect(engineRunner.responses).toEqual([ 'a-0', 'a-0', 'a-0', @@ -1168,13 +1285,77 @@ describe('OptimizedSsrEngine', () => { ); expect(engineRunner.renderCount).toEqual(1); - expect(engineRunner.renders).toEqual(['', '']); + expect(engineRunner.responses).toEqual(['', '']); flush(); })); }); - }); + it('should reuse the same render for subsequent requests for the same rendering key and first request should timeout', fakeAsync(() => { + const timeout = 300; + const engineRunner = new TestEngineRunner( + { timeout, reuseCurrentRendering: true }, + 400, + true + ); + jest.spyOn(engineRunner.optimizedSsrEngine as any, 'log'); + engineRunner.request(requestUrl); + tick(200); + + engineRunner.request(requestUrl); + + tick(100); + expect(engineRunner.optimizedSsrEngine['log']).toHaveBeenCalledWith( + `SSR rendering exceeded timeout ${timeout}, fallbacking to CSR for ${requestUrl}`, + false, + { request: expect.objectContaining({ originalUrl: requestUrl }) } + ); + + tick(100); + expect(engineRunner.renderCount).toEqual(1); + expect(engineRunner.responses).toEqual(['', new Error('a-0')]); + flush(); + })); + + it('should reuse the same render for subsequent requests for the same rendering key when the rendering strategy is ALWAYS_SSR', fakeAsync(() => { + const timeout = 300; + const engineRunner = new TestEngineRunner( + { + timeout, + reuseCurrentRendering: true, + renderingStrategyResolver: () => RenderingStrategy.ALWAYS_SSR, + }, + 400, + true + ); + + engineRunner.request(requestUrl); + expect(getCurrentConcurrency(engineRunner)).toEqual({ + currentConcurrency: 1, + }); + expect(getRenderCallbacksCount(engineRunner, requestUrl)).toEqual({ + renderCallbacksCount: 1, + }); + + tick(200); + engineRunner.request(requestUrl); + expect(getCurrentConcurrency(engineRunner)).toEqual({ + currentConcurrency: 1, + }); + expect(getRenderCallbacksCount(engineRunner, requestUrl)).toEqual({ + renderCallbacksCount: 2, + }); + + tick(200); + + expect(engineRunner.renderCount).toEqual(1); + expect(engineRunner.responses).toEqual([ + new Error('a-0'), + new Error('a-0'), + ]); + flush(); + })); + }); describe('logger option', () => { it('should use ExpressServerLogger if logger is true', () => { jest.useFakeTimers().setSystemTime(new Date('2023-05-26')); From 8bba7a6e663ae92218861666106f4ed96fe956d0 Mon Sep 17 00:00:00 2001 From: kpawelczak <42094017+kpawelczak@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:24:32 +0200 Subject: [PATCH 04/11] feat: created effect for handling ngrx errors (#17657) Co-authored-by: Krzysztof Platis Co-authored-by: github-actions[bot] --- .../store/actions/customer.action.spec.ts | 10 +- .../asm/core/store/actions/customer.action.ts | 10 +- .../asm/core/store/effects/customer.effect.ts | 8 +- .../core/event/cart-event.builder.spec.ts | 13 +-- .../core/facade/cart-voucher.service.spec.ts | 3 +- .../store/actions/cart-entry.action.spec.ts | 9 +- .../core/store/actions/cart-entry.action.ts | 33 +++++-- .../store/actions/cart-voucher.action.spec.ts | 6 +- .../core/store/actions/cart-voucher.action.ts | 24 ++++- .../core/store/actions/cart.action.spec.ts | 23 ++++- .../base/core/store/actions/cart.action.ts | 23 +++-- .../core/store/effects/cart-entry.effect.ts | 8 +- .../store/effects/cart-voucher.effect.spec.ts | 4 +- .../core/store/effects/cart-voucher.effect.ts | 6 +- .../core/store/effects/cart.effect.spec.ts | 4 +- .../base/core/store/effects/cart.effect.ts | 10 +- .../core/facade/saved-cart.service.spec.ts | 3 +- .../store/actions/saved-cart.action.spec.ts | 8 +- .../core/store/actions/saved-cart.action.ts | 34 +++++-- .../core/store/effects/saved-cart.effect.ts | 14 +-- .../store/actions/wish-list.action.spec.ts | 12 ++- .../core/store/actions/wish-list.action.ts | 13 ++- .../core/store/effects/wish-list.effect.ts | 8 +- ...eplenishment-order-history.service.spec.ts | 2 +- .../consignment-tracking.action.spec.ts | 4 +- .../actions/consignment-tracking.action.ts | 9 +- .../actions/order-details.action.spec.ts | 8 +- .../store/actions/order-details.action.ts | 10 +- .../order-return-request.action.spec.ts | 16 ++-- .../actions/order-return-request.action.ts | 35 +++++-- .../core/store/actions/orders.action.spec.ts | 4 +- .../order/core/store/actions/orders.action.ts | 10 +- ...replenishment-order-details.action.spec.ts | 16 ++-- .../replenishment-order-details.action.ts | 18 +++- .../replenishment-orders.action.spec.ts | 4 +- .../actions/replenishment-orders.action.ts | 10 +- .../consignment-tracking.effect.spec.ts | 7 +- .../effects/consignment-tracking.effect.ts | 4 +- .../effects/order-details.effect.spec.ts | 12 +-- .../store/effects/order-details.effect.ts | 8 +- .../order-return-request.effect.spec.ts | 26 ++---- .../effects/order-return-request.effect.ts | 10 +- .../core/store/effects/orders.effect.spec.ts | 6 +- .../order/core/store/effects/orders.effect.ts | 4 +- ...replenishment-order-details.effect.spec.ts | 6 +- .../replenishment-order-details.effect.ts | 6 +- .../replenishment-orders.effect.spec.ts | 7 +- .../effects/replenishment-orders.effect.ts | 4 +- .../store/reducers/orders.reducer.spec.ts | 2 +- ...lenishment-order-details.selectors.spec.ts | 2 +- .../replenishment-orders.selectors.spec.ts | 2 +- .../store/actions/b2b-user.action.spec.ts | 27 ++++-- .../core/store/actions/b2b-user.action.ts | 92 +++++++++++++++---- .../core/store/actions/budget.action.spec.ts | 14 +-- .../core/store/actions/budget.action.ts | 29 +++++- .../store/actions/cost-center.action.spec.ts | 17 ++-- .../core/store/actions/cost-center.action.ts | 44 +++++++-- .../store/actions/org-unit.action.spec.ts | 29 ++++-- .../core/store/actions/org-unit.action.ts | 75 ++++++++++++--- .../store/actions/permission.action.spec.ts | 15 +-- .../core/store/actions/permission.action.ts | 32 ++++++- .../store/actions/user-group.action.spec.ts | 21 +++-- .../core/store/actions/user-group.action.ts | 69 +++++++++++--- .../core/store/effects/b2b-user.effect.ts | 28 +++--- .../core/store/effects/budget.effect.ts | 10 +- .../core/store/effects/cost-center.effect.ts | 16 ++-- .../core/store/effects/org-unit.effect.ts | 36 ++++---- .../core/store/effects/permission.effect.ts | 12 +-- .../core/store/effects/user-group.effect.ts | 26 +++--- .../services/order-approval.service.spec.ts | 2 +- .../actions/order-approval.action.spec.ts | 18 ++-- .../store/actions/order-approval.action.ts | 29 +++++- .../store/effects/order-approval.effect.ts | 8 +- .../store/actions/unit-order.action.spec.ts | 8 +- .../core/store/actions/unit-order.action.ts | 20 ++-- .../store/effects/unit-order.effect.spec.ts | 10 +- .../core/store/effects/unit-order.effect.ts | 6 +- .../store/reducers/unit-order.reducer.spec.ts | 4 +- .../store/actions/pickup-location.action.ts | 4 +- .../core/store/actions/stock.action.spec.ts | 4 +- .../core/store/actions/stock.action.ts | 16 +++- .../effects/pickup-location.effect.spec.ts | 2 +- .../store/effects/pickup-location.effect.ts | 6 +- .../core/store/effects/stock.effect.ts | 4 +- .../state/actions/configurator-cart.action.ts | 6 +- .../configurator-variant.action.spec.ts | 2 +- .../actions/configurator-variant.action.ts | 4 +- .../state/actions/configurator.action.spec.ts | 9 +- .../core/state/actions/configurator.action.ts | 46 ++++++++-- .../effects/configurator-basic.effect.ts | 16 ++-- .../state/effects/configurator-cart.effect.ts | 19 ++-- .../effects/configurator-variant.effect.ts | 4 +- .../actions/configurator-textfield.action.ts | 37 +++++--- .../effects/configurator-textfield.effect.ts | 12 +-- .../store/actions/find-stores.action.spec.ts | 16 ++-- .../core/store/actions/find-stores.action.ts | 22 ++++- .../actions/view-all-stores.action.spec.ts | 8 +- .../store/actions/view-all-stores.action.ts | 9 +- .../core/store/effects/find-stores.effect.ts | 6 +- .../store/effects/view-all-stores.effect.ts | 4 +- .../actions/cdc-user-token.action.spec.ts | 3 +- .../store/actions/cdc-user-token.action.ts | 14 ++- .../store/effects/cdc-user-token.effect.ts | 4 +- .../facade/anonymous-consents.service.spec.ts | 4 +- .../actions/anonymous-consents.action.spec.ts | 3 +- .../actions/anonymous-consents.action.ts | 18 +++- .../effects/anonymous-consents.effect.ts | 6 +- ...nymous-consent-templates.selectors.spec.ts | 2 +- .../store/actions/client-token.action.spec.ts | 4 +- .../store/actions/client-token.action.ts | 8 +- .../store/effects/client-token.effect.ts | 4 +- projects/core/src/base-core.module.ts | 2 + .../store/actions/components.action.spec.ts | 1 + .../cms/store/actions/components.action.ts | 11 ++- .../navigation-entry-item.action.spec.ts | 11 +-- .../actions/navigation-entry-item.action.ts | 8 +- .../src/cms/store/actions/page.action.spec.ts | 15 ++- .../core/src/cms/store/actions/page.action.ts | 14 ++- .../store/effects/components.effect.spec.ts | 3 + .../cms/store/effects/components.effect.ts | 11 ++- .../effects/navigation-entry-item.effect.ts | 8 +- .../src/cms/store/effects/page.effect.spec.ts | 4 +- .../core/src/cms/store/effects/page.effect.ts | 4 +- .../store/reducers/page-index.reducer.spec.ts | 2 +- .../cx-error-hadnler.effect.spec.ts | 72 +++++++++++++++ .../cx-error-handler.effect.ts | 31 +++++++ .../effects-error-handler.module.ts | 22 +++++ .../effects-error-handler.service.spec.ts | 91 ++++++++++++++++++ .../effects-error-handler.service.ts | 36 ++++++++ .../effects-error-handler/index.ts | 9 ++ .../http-error-handler.module.ts | 1 - projects/core/src/error-handling/index.ts | 1 + projects/core/src/model/error-action.ts | 15 +++ projects/core/src/model/index.ts | 1 + .../store/selectors/process.selectors.spec.ts | 6 +- .../actions/product-references.action.spec.ts | 6 +- .../actions/product-references.action.ts | 5 +- .../actions/product-reviews.action.spec.ts | 6 +- .../store/actions/product-reviews.action.ts | 9 +- .../actions/product-search.action.spec.ts | 12 +-- .../store/actions/product-search.action.ts | 12 +-- .../store/actions/product.action.spec.ts | 1 + .../product/store/actions/product.action.ts | 13 ++- .../store/effects/product-reviews.effect.ts | 6 +- .../store/effects/product-search.effect.ts | 8 +- .../product/store/effects/product.effect.ts | 6 +- .../store/actions/base-site.action.spec.ts | 12 +-- .../store/actions/base-site.action.ts | 14 ++- .../store/actions/currencies.action.spec.ts | 6 +- .../store/actions/currencies.action.ts | 5 +- .../store/actions/languages.action.spec.ts | 6 +- .../store/actions/languages.action.ts | 9 +- .../store/effects/base-site.effect.ts | 6 +- .../store/effects/currencies.effect.ts | 4 +- .../store/effects/languages.effect.ts | 4 +- .../entity-loader.action.spec.ts | 6 +- .../entity-loader/entity-loader.action.ts | 15 ++- .../entity-loader.reducer.spec.ts | 12 ++- .../state/utils/loader/loader.action.spec.ts | 6 +- .../src/state/utils/loader/loader.action.ts | 12 ++- .../state/utils/loader/loader.reducer.spec.ts | 3 +- .../entity-scoped-loader.actions.spec.ts | 8 +- .../entity-scoped-loader.actions.ts | 23 +++-- .../facade/customer-coupon.service.spec.ts | 8 +- .../user/facade/user-consent.service.spec.ts | 12 ++- .../facade/user-interests.service.spec.ts | 2 +- .../actions/billing-countries.action.spec.ts | 6 +- .../store/actions/billing-countries.action.ts | 5 +- .../actions/customer-coupon.action.spec.ts | 12 +-- .../store/actions/customer-coupon.action.ts | 22 +++-- .../actions/delivery-countries.action.spec.ts | 4 +- .../actions/delivery-countries.action.ts | 8 +- .../notification-preference.action.spec.ts | 6 +- .../actions/notification-preference.action.ts | 19 +++- .../actions/payment-methods.action.spec.ts | 16 ++-- .../store/actions/payment-methods.action.ts | 22 +++-- .../actions/product-interests.actions.spec.ts | 10 +- .../actions/product-interests.actions.ts | 37 +++++--- .../user/store/actions/regions.action.spec.ts | 4 +- .../src/user/store/actions/regions.action.ts | 9 +- .../actions/user-addresses.action.spec.ts | 10 +- .../store/actions/user-addresses.action.ts | 29 ++++-- .../actions/user-consents.action.spec.ts | 15 +-- .../store/actions/user-consents.action.ts | 26 ++++-- .../actions/user-cost-center.action.spec.ts | 4 +- .../store/actions/user-cost-center.action.ts | 5 +- .../store/effects/billing-countries.effect.ts | 6 +- .../store/effects/customer-coupon.effect.ts | 10 +- .../effects/delivery-countries.effect.ts | 4 +- .../notification-preference.effect.spec.ts | 8 +- .../effects/notification-preference.effect.ts | 6 +- .../store/effects/payment-methods.effect.ts | 8 +- .../effects/product-interests.effect.spec.ts | 9 +- .../store/effects/product-interests.effect.ts | 10 +- .../src/user/store/effects/regions.effect.ts | 4 +- .../store/effects/user-addresses.effect.ts | 10 +- .../effects/user-consents.effect.spec.ts | 6 +- .../store/effects/user-consents.effect.ts | 8 +- .../store/effects/user-cost-center.effect.ts | 6 +- .../notification-preference.reducer.spec.ts | 2 +- .../selectors/user-consents.selectors.spec.ts | 2 +- projects/core/src/util/index.ts | 1 + .../src/util/try-normalize-http-error.spec.ts | 33 +++++++ .../core/src/util/try-normalize-http-error.ts | 19 ++++ 204 files changed, 1833 insertions(+), 785 deletions(-) create mode 100644 projects/core/src/error-handling/effects-error-handler/cx-error-hadnler.effect.spec.ts create mode 100644 projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts create mode 100644 projects/core/src/error-handling/effects-error-handler/effects-error-handler.module.ts create mode 100644 projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.spec.ts create mode 100644 projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts create mode 100644 projects/core/src/error-handling/effects-error-handler/index.ts create mode 100644 projects/core/src/model/error-action.ts create mode 100644 projects/core/src/util/try-normalize-http-error.spec.ts create mode 100644 projects/core/src/util/try-normalize-http-error.ts diff --git a/feature-libs/asm/core/store/actions/customer.action.spec.ts b/feature-libs/asm/core/store/actions/customer.action.spec.ts index 293df969d30..3e48cbc9f04 100644 --- a/feature-libs/asm/core/store/actions/customer.action.spec.ts +++ b/feature-libs/asm/core/store/actions/customer.action.spec.ts @@ -18,6 +18,7 @@ const mockUser: User = { const mockCustomerSearchPage: CustomerSearchPage = { entries: [mockUser], } as CustomerSearchPage; +const error = new Error('anError'); describe('Customer Actions', () => { describe('Customer Search Actions', () => { @@ -32,13 +33,12 @@ describe('Customer Actions', () => { }); it('should create the CustomerSearchFail action', () => { - const error = 'anError'; const action = new AsmActions.CustomerSearchFail(error); expect({ ...action }).toEqual({ type: AsmActions.CUSTOMER_SEARCH_FAIL, - meta: StateUtils.failMeta(CUSTOMER_SEARCH_DATA), - payload: error, + meta: StateUtils.failMeta(CUSTOMER_SEARCH_DATA, error), + error, }); }); @@ -76,13 +76,13 @@ describe('Customer Actions', () => { }); it('should create the CustomerListCustomersSearchFail action', () => { - const error = 'anError'; const action = new AsmActions.CustomerListCustomersSearchFail(error); expect({ ...action }).toEqual({ type: AsmActions.CUSTOMER_LIST_CUSTOMERS_SEARCH_FAIL, - meta: StateUtils.failMeta(CUSTOMER_LIST_CUSTOMERS_SEARCH_DATA), + meta: StateUtils.failMeta(CUSTOMER_LIST_CUSTOMERS_SEARCH_DATA, error), payload: error, + error, }); }); diff --git a/feature-libs/asm/core/store/actions/customer.action.ts b/feature-libs/asm/core/store/actions/customer.action.ts index 36cfe5ba76b..2e7793df278 100644 --- a/feature-libs/asm/core/store/actions/customer.action.ts +++ b/feature-libs/asm/core/store/actions/customer.action.ts @@ -5,7 +5,7 @@ */ import { CustomerSearchOptions, CustomerSearchPage } from '@spartacus/asm/root'; -import { StateUtils } from '@spartacus/core'; +import { StateUtils, ErrorActionType } from '@spartacus/core'; import { CUSTOMER_LIST_CUSTOMERS_SEARCH_DATA, CUSTOMER_SEARCH_DATA, @@ -34,8 +34,8 @@ export class CustomerSearch extends StateUtils.LoaderLoadAction { export class CustomerSearchFail extends StateUtils.LoaderFailAction { readonly type = CUSTOMER_SEARCH_FAIL; - constructor(public payload: any) { - super(CUSTOMER_SEARCH_DATA); + constructor(public error: ErrorActionType) { + super(CUSTOMER_SEARCH_DATA, error); } } @@ -62,8 +62,8 @@ export class CustomerListCustomersSearch extends StateUtils.LoaderLoadAction { export class CustomerListCustomersSearchFail extends StateUtils.LoaderFailAction { readonly type = CUSTOMER_LIST_CUSTOMERS_SEARCH_FAIL; - constructor(public payload: any) { - super(CUSTOMER_LIST_CUSTOMERS_SEARCH_DATA); + constructor(public payload: ErrorActionType) { + super(CUSTOMER_LIST_CUSTOMERS_SEARCH_DATA, payload); } } diff --git a/feature-libs/asm/core/store/effects/customer.effect.ts b/feature-libs/asm/core/store/effects/customer.effect.ts index 6f24d6ec6ed..f0dfd3ece01 100644 --- a/feature-libs/asm/core/store/effects/customer.effect.ts +++ b/feature-libs/asm/core/store/effects/customer.effect.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { CustomerSearchPage } from '@spartacus/asm/root'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; import { AsmConnector } from '../../connectors/asm.connector'; @@ -29,7 +29,7 @@ export class CustomerEffects { catchError((error) => of( new AsmActions.CustomerSearchFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -53,7 +53,7 @@ export class CustomerEffects { catchError((error) => of( new AsmActions.CustomerListCustomersSearchFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/cart/base/core/event/cart-event.builder.spec.ts b/feature-libs/cart/base/core/event/cart-event.builder.spec.ts index 8cf1b5e98d0..45207ccadc0 100644 --- a/feature-libs/cart/base/core/event/cart-event.builder.spec.ts +++ b/feature-libs/cart/base/core/event/cart-event.builder.spec.ts @@ -46,6 +46,7 @@ const MOCK_ACTIVE_CART: Cart = { guid: MOCK_ACTIVE_CART_ID, code: MOCK_ID, }; +const error = new Error('error'); class MockActiveCartService implements Partial { getActive = () => of(MOCK_ACTIVE_CART); getActiveCartId = () => getActiveCartIdSubject; @@ -202,11 +203,11 @@ describe('CartEventBuilder', () => { event: createFrom(CartAddEntryFailEvent, eventData), actionActive: new CartActions.CartAddEntryFail({ ...eventData, - error: 'error', + error, }), actionNotActive: new CartActions.CartAddEntryFail({ ...eventData, - error: 'error', + error, ...MOCK_NOT_ACTIVE_CART_EVENT, }), }); @@ -284,14 +285,14 @@ describe('CartEventBuilder', () => { actions$.next( new CartActions.CartRemoveEntryFail({ - error: 'remove failed', + error, entryNumber: '0', ...MOCK_ACTIVE_CART_EVENT, }) ); actions$.next( new CartActions.CartRemoveEntryFail({ - error: 'remove failed', + error, entryNumber: '0', ...MOCK_NOT_ACTIVE_CART_EVENT, }) @@ -299,7 +300,7 @@ describe('CartEventBuilder', () => { actions$.next( new CartActions.CartRemoveEntryFail({ - error: 'remove failed', + error, entryNumber: '1', ...MOCK_ACTIVE_CART_EVENT, }) @@ -361,7 +362,7 @@ describe('CartEventBuilder', () => { actions$.next( new CartActions.CartUpdateEntryFail({ - error: 'update failed', + error: new Error('update failed'), entryNumber: '0', quantity: 2, ...MOCK_ACTIVE_CART_EVENT, diff --git a/feature-libs/cart/base/core/facade/cart-voucher.service.spec.ts b/feature-libs/cart/base/core/facade/cart-voucher.service.spec.ts index 5f0d4c8ae47..1fbd5adab04 100644 --- a/feature-libs/cart/base/core/facade/cart-voucher.service.spec.ts +++ b/feature-libs/cart/base/core/facade/cart-voucher.service.spec.ts @@ -76,9 +76,10 @@ describe('CartVoucherService', () => { }); it('should return the error flag', () => { + const error = new Error('error'); store.dispatch( new CartActions.CartAddVoucherFail({ - error: 'error', + error, userId, cartId: cart.code, voucherId, diff --git a/feature-libs/cart/base/core/store/actions/cart-entry.action.spec.ts b/feature-libs/cart/base/core/store/actions/cart-entry.action.spec.ts index 4e7581a36be..85404b00412 100644 --- a/feature-libs/cart/base/core/store/actions/cart-entry.action.spec.ts +++ b/feature-libs/cart/base/core/store/actions/cart-entry.action.spec.ts @@ -45,7 +45,7 @@ describe('Cart-entry Actions', () => { describe('CartAddEntryFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const payload = { error, cartId, @@ -56,6 +56,7 @@ describe('Cart-entry Actions', () => { const action = new CartActions.CartAddEntryFail(payload); expect({ ...action }).toEqual({ + error, type: CartActions.CART_ADD_ENTRY_FAIL, payload, meta: StateUtils.entityProcessesDecrementMeta( @@ -105,10 +106,11 @@ describe('Cart-entry Actions', () => { describe('CartRemoveEntryFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const payload = { error, cartId, userId, entryNumber }; const action = new CartActions.CartRemoveEntryFail(payload); expect({ ...action }).toEqual({ + error, type: CartActions.CART_REMOVE_ENTRY_FAIL, payload, meta: StateUtils.entityProcessesDecrementMeta( @@ -164,10 +166,11 @@ describe('Cart-entry Actions', () => { describe('CartUpdateEntryFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const payload = { error, cartId, userId, entryNumber, quantity: 2 }; const action = new CartActions.CartUpdateEntryFail(payload); expect({ ...action }).toEqual({ + error, type: CartActions.CART_UPDATE_ENTRY_FAIL, payload, meta: StateUtils.entityProcessesDecrementMeta( diff --git a/feature-libs/cart/base/core/store/actions/cart-entry.action.ts b/feature-libs/cart/base/core/store/actions/cart-entry.action.ts index 1e10a64921b..512aaa0f632 100644 --- a/feature-libs/cart/base/core/store/actions/cart-entry.action.ts +++ b/feature-libs/cart/base/core/store/actions/cart-entry.action.ts @@ -5,7 +5,7 @@ */ import { OrderEntry } from '@spartacus/cart/base/root'; -import { StateUtils } from '@spartacus/core'; +import { ErrorAction, ErrorActionType, StateUtils } from '@spartacus/core'; import { MULTI_CART_DATA } from '../multi-cart-state'; export const CART_ADD_ENTRY = '[Cart-entry] Add Entry'; @@ -21,6 +21,7 @@ export const CART_UPDATE_ENTRY_FAIL = '[Cart-entry] Update Entry Fail'; export class CartAddEntry extends StateUtils.EntityProcessesIncrementAction { readonly type = CART_ADD_ENTRY; + constructor( public payload: { cartId: string; @@ -36,6 +37,7 @@ export class CartAddEntry extends StateUtils.EntityProcessesIncrementAction { export class CartAddEntrySuccess extends StateUtils.EntityProcessesDecrementAction { readonly type = CART_ADD_ENTRY_SUCCESS; + constructor( public payload: { userId: string; @@ -54,8 +56,13 @@ export class CartAddEntrySuccess extends StateUtils.EntityProcessesDecrementActi } } -export class CartAddEntryFail extends StateUtils.EntityProcessesDecrementAction { +export class CartAddEntryFail + extends StateUtils.EntityProcessesDecrementAction + implements ErrorAction +{ + error: ErrorActionType = this.payload.error; readonly type = CART_ADD_ENTRY_FAIL; + constructor( public payload: { error: any; @@ -72,6 +79,7 @@ export class CartAddEntryFail extends StateUtils.EntityProcessesDecrementAction export class CartRemoveEntry extends StateUtils.EntityProcessesIncrementAction { readonly type = CART_REMOVE_ENTRY; + constructor( public payload: { cartId: string; userId: string; entryNumber: string } ) { @@ -81,6 +89,7 @@ export class CartRemoveEntry extends StateUtils.EntityProcessesIncrementAction { export class CartRemoveEntrySuccess extends StateUtils.EntityProcessesDecrementAction { readonly type = CART_REMOVE_ENTRY_SUCCESS; + constructor( public payload: { userId: string; cartId: string; entryNumber: string } ) { @@ -88,11 +97,16 @@ export class CartRemoveEntrySuccess extends StateUtils.EntityProcessesDecrementA } } -export class CartRemoveEntryFail extends StateUtils.EntityProcessesDecrementAction { +export class CartRemoveEntryFail + extends StateUtils.EntityProcessesDecrementAction + implements ErrorAction +{ + error: ErrorActionType = this.payload.error; readonly type = CART_REMOVE_ENTRY_FAIL; + constructor( public payload: { - error: any; + error: ErrorActionType; cartId: string; userId: string; entryNumber: string; @@ -104,6 +118,7 @@ export class CartRemoveEntryFail extends StateUtils.EntityProcessesDecrementActi export class CartUpdateEntry extends StateUtils.EntityProcessesIncrementAction { readonly type = CART_UPDATE_ENTRY; + constructor( public payload: { userId: string; @@ -120,6 +135,7 @@ export class CartUpdateEntry extends StateUtils.EntityProcessesIncrementAction { export class CartUpdateEntrySuccess extends StateUtils.EntityProcessesDecrementAction { readonly type = CART_UPDATE_ENTRY_SUCCESS; + constructor( public payload: { userId: string; @@ -134,11 +150,16 @@ export class CartUpdateEntrySuccess extends StateUtils.EntityProcessesDecrementA } } -export class CartUpdateEntryFail extends StateUtils.EntityProcessesDecrementAction { +export class CartUpdateEntryFail + extends StateUtils.EntityProcessesDecrementAction + implements ErrorAction +{ + error: ErrorActionType = this.payload.error; readonly type = CART_UPDATE_ENTRY_FAIL; + constructor( public payload: { - error: any; + error: ErrorActionType; userId: string; cartId: string; entryNumber: string; diff --git a/feature-libs/cart/base/core/store/actions/cart-voucher.action.spec.ts b/feature-libs/cart/base/core/store/actions/cart-voucher.action.spec.ts index 1efdd08ac1c..c6903013dbe 100644 --- a/feature-libs/cart/base/core/store/actions/cart-voucher.action.spec.ts +++ b/feature-libs/cart/base/core/store/actions/cart-voucher.action.spec.ts @@ -29,7 +29,7 @@ describe('Cart-voucher Actions', () => { describe('AddVoucherFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const payload = { error, voucherId, @@ -39,6 +39,7 @@ describe('Cart-voucher Actions', () => { const action = new CartActions.CartAddVoucherFail(payload); expect({ ...action }).toEqual({ + error, type: CartActions.CART_ADD_VOUCHER_FAIL, payload, meta: StateUtils.entityFailMeta( @@ -105,11 +106,12 @@ describe('Cart-voucher Actions', () => { describe('RemoveVoucherFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const payload = { error, userId, cartId, voucherId }; const action = new CartActions.CartRemoveVoucherFail(payload); expect({ ...action }).toEqual({ + error, type: CartActions.CART_REMOVE_VOUCHER_FAIL, payload, meta: StateUtils.entityProcessesDecrementMeta( diff --git a/feature-libs/cart/base/core/store/actions/cart-voucher.action.ts b/feature-libs/cart/base/core/store/actions/cart-voucher.action.ts index 2a6fd631d22..907fe9e650d 100644 --- a/feature-libs/cart/base/core/store/actions/cart-voucher.action.ts +++ b/feature-libs/cart/base/core/store/actions/cart-voucher.action.ts @@ -4,7 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { PROCESS_FEATURE, StateUtils } from '@spartacus/core'; +import { + ErrorAction, + ErrorActionType, + PROCESS_FEATURE, + StateUtils, +} from '@spartacus/core'; import { ADD_VOUCHER_PROCESS_ID, MULTI_CART_DATA } from '../multi-cart-state'; export const CART_ADD_VOUCHER = '[Cart-voucher] Add Cart Vouchers'; @@ -22,6 +27,7 @@ export const CART_REMOVE_VOUCHER_SUCCESS = // Adding cart voucher actions export class CartAddVoucher extends StateUtils.EntityLoadAction { readonly type = CART_ADD_VOUCHER; + constructor( public payload: { userId: string; cartId: string; voucherId: string } ) { @@ -31,12 +37,13 @@ export class CartAddVoucher extends StateUtils.EntityLoadAction { export class CartAddVoucherFail extends StateUtils.EntityFailAction { readonly type = CART_ADD_VOUCHER_FAIL; + constructor( public payload: { userId: string; cartId: string; voucherId: string; - error: any; + error: ErrorActionType; } ) { super(PROCESS_FEATURE, ADD_VOUCHER_PROCESS_ID, payload.error); @@ -45,6 +52,7 @@ export class CartAddVoucherFail extends StateUtils.EntityFailAction { export class CartAddVoucherSuccess extends StateUtils.EntitySuccessAction { readonly type = CART_ADD_VOUCHER_SUCCESS; + constructor( public payload: { userId: string; cartId: string; voucherId: string } ) { @@ -57,6 +65,7 @@ export class CartAddVoucherSuccess extends StateUtils.EntitySuccessAction { */ export class CartResetAddVoucher extends StateUtils.EntityLoaderResetAction { readonly type = CART_RESET_ADD_VOUCHER; + constructor() { super(PROCESS_FEATURE, ADD_VOUCHER_PROCESS_ID); } @@ -65,6 +74,7 @@ export class CartResetAddVoucher extends StateUtils.EntityLoaderResetAction { // Deleting cart voucher export class CartRemoveVoucher extends StateUtils.EntityProcessesIncrementAction { readonly type = CART_REMOVE_VOUCHER; + constructor( public payload: { userId: string; cartId: string; voucherId: string } ) { @@ -72,11 +82,16 @@ export class CartRemoveVoucher extends StateUtils.EntityProcessesIncrementAction } } -export class CartRemoveVoucherFail extends StateUtils.EntityProcessesDecrementAction { +export class CartRemoveVoucherFail + extends StateUtils.EntityProcessesDecrementAction + implements ErrorAction +{ + error: ErrorActionType = this.payload.error; readonly type = CART_REMOVE_VOUCHER_FAIL; + constructor( public payload: { - error: any; + error: ErrorActionType; cartId: string; userId: string; voucherId: string; @@ -88,6 +103,7 @@ export class CartRemoveVoucherFail extends StateUtils.EntityProcessesDecrementAc export class CartRemoveVoucherSuccess extends StateUtils.EntityProcessesDecrementAction { readonly type = CART_REMOVE_VOUCHER_SUCCESS; + constructor( public payload: { userId: string; cartId: string; voucherId: string } ) { diff --git a/feature-libs/cart/base/core/store/actions/cart.action.spec.ts b/feature-libs/cart/base/core/store/actions/cart.action.spec.ts index c99fef43a24..dc997e468f0 100644 --- a/feature-libs/cart/base/core/store/actions/cart.action.spec.ts +++ b/feature-libs/cart/base/core/store/actions/cart.action.spec.ts @@ -39,12 +39,18 @@ describe('Cart Actions', () => { describe('CreateCartFail', () => { it('should create the action', () => { - const payload = { tempCartId, userId: 'userId', error: 'error' }; + const error = new Error('error'); + const payload = { + tempCartId, + userId: 'userId', + error, + }; const action = new CartActions.CreateCartFail(payload); expect({ ...action }).toEqual({ + error: payload.error, type: CartActions.CREATE_CART_FAIL, payload, - meta: StateUtils.entityFailMeta(MULTI_CART_DATA, tempCartId), + meta: StateUtils.entityFailMeta(MULTI_CART_DATA, tempCartId, error), }); }); }); @@ -82,9 +88,14 @@ describe('Cart Actions', () => { describe('LoadCartFail', () => { it('should create the action', () => { - const payload = { cartId: 'cartId', error: 'error', userId: 'userId' }; + const payload = { + cartId: 'cartId', + error: { message: 'error' }, + userId: 'userId', + }; const action = new CartActions.LoadCartFail(payload); expect({ ...action }).toEqual({ + error: payload.error, type: CartActions.LOAD_CART_FAIL, payload, meta: StateUtils.entityFailMeta( @@ -155,7 +166,7 @@ describe('Cart Actions', () => { describe('AddEmailToCartFail', () => { it('should create the action', () => { const payload = { - error: 'anError', + error: { message: 'anError' }, cartId: 'cartId', userId: 'userId', email: 'email@email.com', @@ -163,6 +174,7 @@ describe('Cart Actions', () => { const action = new CartActions.AddEmailToCartFail(payload); expect({ ...action }).toEqual({ + error: payload.error, type: CartActions.ADD_EMAIL_TO_CART_FAIL, payload, meta: StateUtils.entityProcessesDecrementMeta( @@ -262,7 +274,7 @@ describe('Cart Actions', () => { describe('DeleteCartFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const userId = 'xxx@xxx.xxx'; const cartId = 'testCartId'; const payload = { @@ -273,6 +285,7 @@ describe('Cart Actions', () => { const action = new CartActions.DeleteCartFail(payload); expect({ ...action }).toEqual({ + error, type: CartActions.DELETE_CART_FAIL, payload, }); diff --git a/feature-libs/cart/base/core/store/actions/cart.action.ts b/feature-libs/cart/base/core/store/actions/cart.action.ts index 077570b840f..04cfc92b346 100644 --- a/feature-libs/cart/base/core/store/actions/cart.action.ts +++ b/feature-libs/cart/base/core/store/actions/cart.action.ts @@ -6,7 +6,7 @@ import { Action } from '@ngrx/store'; import { Cart } from '@spartacus/cart/base/root'; -import { StateUtils } from '@spartacus/core'; +import { StateUtils, ErrorAction, ErrorActionType } from '@spartacus/core'; import { MULTI_CART_DATA } from '../multi-cart-state'; export const CREATE_CART = '[Cart] Create Cart'; @@ -55,13 +55,13 @@ export class CreateCart extends StateUtils.EntityLoadAction { } interface CreateCartFailPayload extends CreateCartPayload { - error: any; + error: ErrorActionType; } export class CreateCartFail extends StateUtils.EntityFailAction { readonly type = CREATE_CART_FAIL; constructor(public payload: CreateCartFailPayload) { - super(MULTI_CART_DATA, payload.tempCartId); + super(MULTI_CART_DATA, payload.tempCartId, payload.error); } } @@ -86,13 +86,17 @@ export class AddEmailToCart extends StateUtils.EntityProcessesIncrementAction { } } -export class AddEmailToCartFail extends StateUtils.EntityProcessesDecrementAction { +export class AddEmailToCartFail + extends StateUtils.EntityProcessesDecrementAction + implements ErrorAction +{ + error: ErrorActionType = this.payload.error; readonly type = ADD_EMAIL_TO_CART_FAIL; constructor( public payload: { userId: string; cartId: string; - error: any; + error: ErrorActionType; email: string; } ) { @@ -125,7 +129,7 @@ export class LoadCart extends StateUtils.EntityLoadAction { } interface LoadCartFailPayload extends LoadCartPayload { - error: any; + error: ErrorActionType; } export class LoadCartFail extends StateUtils.EntityFailAction { @@ -220,9 +224,12 @@ export class DeleteCartSuccess extends StateUtils.EntityRemoveAction { } } -export class DeleteCartFail implements Action { +export class DeleteCartFail implements ErrorAction { + error: ErrorActionType = this.payload.error; readonly type = DELETE_CART_FAIL; - constructor(public payload: { userId: string; cartId: string; error: any }) {} + constructor( + public payload: { userId: string; cartId: string; error: ErrorActionType } + ) {} } export type CartAction = diff --git a/feature-libs/cart/base/core/store/effects/cart-entry.effect.ts b/feature-libs/cart/base/core/store/effects/cart-entry.effect.ts index 4912c0a4b76..143662ebab5 100644 --- a/feature-libs/cart/base/core/store/effects/cart-entry.effect.ts +++ b/feature-libs/cart/base/core/store/effects/cart-entry.effect.ts @@ -10,7 +10,7 @@ import { CartModification } from '@spartacus/cart/base/root'; import { LoggerService, SiteContextActions, - normalizeHttpError, + tryNormalizeHttpError, withdrawOn, } from '@spartacus/core'; import { Observable, from } from 'rxjs'; @@ -58,7 +58,7 @@ export class CartEntryEffects { from([ new CartActions.CartAddEntryFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new CartActions.LoadCart({ cartId: payload.cartId, @@ -93,7 +93,7 @@ export class CartEntryEffects { from([ new CartActions.CartRemoveEntryFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new CartActions.LoadCart({ cartId: payload.cartId, @@ -135,7 +135,7 @@ export class CartEntryEffects { from([ new CartActions.CartUpdateEntryFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new CartActions.LoadCart({ cartId: payload.cartId, diff --git a/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts b/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts index e9ef0c4a169..97128986c43 100644 --- a/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts +++ b/feature-libs/cart/base/core/store/effects/cart-voucher.effect.spec.ts @@ -4,8 +4,8 @@ import { TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { GlobalMessageService, - normalizeHttpError, OccConfig, + tryNormalizeHttpError, } from '@spartacus/core'; import { cold, hot } from 'jasmine-marbles'; import { Observable, of, throwError } from 'rxjs'; @@ -92,7 +92,7 @@ describe('Cart Voucher effect', () => { userId, cartId, voucherId, - error: normalizeHttpError(error), + error: tryNormalizeHttpError(error), }); const completion2 = new CartActions.CartProcessesDecrement(cartId); const completion3 = new CartActions.LoadCart({ diff --git a/feature-libs/cart/base/core/store/effects/cart-voucher.effect.ts b/feature-libs/cart/base/core/store/effects/cart-voucher.effect.ts index 00603009888..e3a42c628e4 100644 --- a/feature-libs/cart/base/core/store/effects/cart-voucher.effect.ts +++ b/feature-libs/cart/base/core/store/effects/cart-voucher.effect.ts @@ -10,7 +10,7 @@ import { GlobalMessageService, GlobalMessageType, LoggerService, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, from } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; @@ -53,7 +53,7 @@ export class CartVoucherEffects { from([ new CartActions.CartAddVoucherFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new CartActions.CartProcessesDecrement(payload.cartId), new CartActions.LoadCart({ @@ -92,7 +92,7 @@ export class CartVoucherEffects { catchError((error) => from([ new CartActions.CartRemoveVoucherFail({ - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), cartId: payload.cartId, userId: payload.userId, voucherId: payload.voucherId, diff --git a/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts b/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts index d542acb507c..371ec18e2c3 100644 --- a/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts +++ b/feature-libs/cart/base/core/store/effects/cart.effect.spec.ts @@ -6,11 +6,11 @@ import { StoreModule } from '@ngrx/store'; import { Cart } from '@spartacus/cart/base/root'; import { CLIENT_AUTH_FEATURE, - normalizeHttpError, OccConfig, OCC_CART_ID_CURRENT, SiteContextActions, USER_FEATURE, + tryNormalizeHttpError, } from '@spartacus/core'; import { cold, hot } from 'jasmine-marbles'; import * as fromClientAuthReducers from 'projects/core/src/auth/client-auth/store/reducers/index'; @@ -202,7 +202,7 @@ describe('Cart effect', () => { loadMock.and.returnValue(throwError(httpError)); const removeCartCompletion = new CartActions.LoadCartFail({ ...payload, - error: normalizeHttpError(httpError), + error: tryNormalizeHttpError(httpError), }); actions$ = hot('-a', { a: action }); const expected = cold('-b', { diff --git a/feature-libs/cart/base/core/store/effects/cart.effect.ts b/feature-libs/cart/base/core/store/effects/cart.effect.ts index a81ca6989b1..6bc4f07511f 100644 --- a/feature-libs/cart/base/core/store/effects/cart.effect.ts +++ b/feature-libs/cart/base/core/store/effects/cart.effect.ts @@ -13,7 +13,7 @@ import { OCC_CART_ID_CURRENT, SiteContextActions, isNotUndefined, - normalizeHttpError, + tryNormalizeHttpError, withdrawOn, } from '@spartacus/core'; import { Observable, from, of } from 'rxjs'; @@ -132,7 +132,7 @@ export class CartEffects { return of( new CartActions.LoadCartFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ); } @@ -177,7 +177,7 @@ export class CartEffects { of( new CartActions.CreateCartFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -301,7 +301,7 @@ export class CartEffects { from([ new CartActions.AddEmailToCartFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new CartActions.LoadCart({ userId: payload.userId, @@ -332,7 +332,7 @@ export class CartEffects { from([ new CartActions.DeleteCartFail({ ...payload, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), // Error might happen in higher backend layer and cart could still be removed. // When load fail with NotFound error then RemoveCart action will kick in and clear that cart in our state. diff --git a/feature-libs/cart/saved-cart/core/facade/saved-cart.service.spec.ts b/feature-libs/cart/saved-cart/core/facade/saved-cart.service.spec.ts index 3f591c8e315..31c1a631c44 100644 --- a/feature-libs/cart/saved-cart/core/facade/saved-cart.service.spec.ts +++ b/feature-libs/cart/saved-cart/core/facade/saved-cart.service.spec.ts @@ -12,14 +12,13 @@ import { UserAccountFacade } from '@spartacus/user/account/root'; import { of } from 'rxjs'; import { SavedCartActions } from '../store/actions/index'; import { SavedCartService } from './saved-cart.service'; - import createSpy = jasmine.createSpy; const mockUserId = 'test-user'; const mockCartId = 'test-cart'; const mockCartName = 'test-cart-name'; const mockCartDescription = 'test-cart-description'; -const mockError = 'test-error'; +const mockError = new Error('test-error'); const mockUser: User = { customerId: 'test-customer', diff --git a/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.spec.ts b/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.spec.ts index 4f1b346eda9..2cebbfafa2d 100644 --- a/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.spec.ts +++ b/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.spec.ts @@ -12,7 +12,7 @@ const mockUserId = 'test-user'; const mockCartId = 'test-cart'; const mockCartName = 'test-cart-name'; const mockCartDescription = 'test-cart-description'; -const error = 'anError'; +const error = { message: 'anError' }; describe('SavedCart Actions', () => { describe('LoadSavedCart Actions', () => { @@ -54,6 +54,7 @@ describe('SavedCart Actions', () => { }); expect({ ...action }).toEqual({ + error, type: SavedCartActions.LOAD_SAVED_CART_FAIL, payload: { userId: mockUserId, cartId: mockCartId, error }, meta: StateUtils.entityFailMeta(MULTI_CART_DATA, mockCartId, error), @@ -103,6 +104,7 @@ describe('SavedCart Actions', () => { }); expect({ ...action }).toEqual({ + error, type: SavedCartActions.LOAD_SAVED_CARTS_FAIL, payload: { userId: mockUserId, error }, meta: StateUtils.entityFailMeta( @@ -175,6 +177,7 @@ describe('SavedCart Actions', () => { }); expect({ ...action }).toEqual({ + error, type: SavedCartActions.RESTORE_SAVED_CART_FAIL, payload: { userId: mockUserId, cartId: mockCartId, error }, meta: StateUtils.entityFailMeta( @@ -254,6 +257,7 @@ describe('SavedCart Actions', () => { }); expect({ ...action }).toEqual({ + error, type: SavedCartActions.SAVE_CART_FAIL, payload: { userId: mockUserId, @@ -339,6 +343,7 @@ describe('SavedCart Actions', () => { }); expect({ ...action }).toEqual({ + error, type: SavedCartActions.EDIT_SAVED_CART_FAIL, payload: { userId: mockUserId, @@ -412,6 +417,7 @@ describe('SavedCart Actions', () => { }); expect({ ...action }).toEqual({ + error, type: SavedCartActions.CLONE_SAVED_CART_FAIL, payload: { userId: mockUserId, diff --git a/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.ts b/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.ts index 7aacbabbdb9..90b3ce4dcd7 100644 --- a/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.ts +++ b/feature-libs/cart/saved-cart/core/store/actions/saved-cart.action.ts @@ -5,7 +5,7 @@ */ import { MULTI_CART_DATA } from '@spartacus/cart/base/core'; -import { PROCESS_FEATURE, StateUtils } from '@spartacus/core'; +import { ErrorActionType, PROCESS_FEATURE, StateUtils } from '@spartacus/core'; import { SAVED_CART_CLONE_CART_PROCESS_ID, SAVED_CART_LIST_PROCESS_ID, @@ -44,6 +44,7 @@ export const CLEAR_CLONE_SAVED_CART = '[Saved Cart] Clear Clone Saved Cart'; export class LoadSavedCart extends StateUtils.EntityLoadAction { readonly type = LOAD_SAVED_CART; + constructor( public payload: { userId: string; @@ -56,6 +57,7 @@ export class LoadSavedCart extends StateUtils.EntityLoadAction { export class LoadSavedCartSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_SAVED_CART_SUCCESS; + constructor( public payload: { userId: string; @@ -68,6 +70,7 @@ export class LoadSavedCartSuccess extends StateUtils.EntitySuccessAction { export class LoadSavedCartFail extends StateUtils.EntityFailAction { readonly type = LOAD_SAVED_CART_FAIL; + constructor(public payload: { userId: string; cartId: string; error: any }) { super(MULTI_CART_DATA, payload.cartId, payload?.error); } @@ -75,6 +78,7 @@ export class LoadSavedCartFail extends StateUtils.EntityFailAction { export class LoadSavedCarts extends StateUtils.EntityLoadAction { readonly type = LOAD_SAVED_CARTS; + constructor( public payload: { userId: string; @@ -86,6 +90,7 @@ export class LoadSavedCarts extends StateUtils.EntityLoadAction { export class LoadSavedCartsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_SAVED_CARTS_SUCCESS; + constructor( public payload: { userId: string; @@ -97,10 +102,11 @@ export class LoadSavedCartsSuccess extends StateUtils.EntitySuccessAction { export class LoadSavedCartsFail extends StateUtils.EntityFailAction { readonly type = LOAD_SAVED_CARTS_FAIL; + constructor( public payload: { userId: string; - error: any; + error: ErrorActionType; } ) { super(PROCESS_FEATURE, SAVED_CART_LIST_PROCESS_ID, payload.error); @@ -109,6 +115,7 @@ export class LoadSavedCartsFail extends StateUtils.EntityFailAction { export class ClearSavedCarts extends StateUtils.EntityLoaderResetAction { readonly type = CLEAR_SAVED_CARTS; + constructor() { super(PROCESS_FEATURE, SAVED_CART_LIST_PROCESS_ID); } @@ -116,6 +123,7 @@ export class ClearSavedCarts extends StateUtils.EntityLoaderResetAction { export class RestoreSavedCart extends StateUtils.EntityLoadAction { readonly type = RESTORE_SAVED_CART; + constructor( public payload: { userId: string; @@ -128,6 +136,7 @@ export class RestoreSavedCart extends StateUtils.EntityLoadAction { export class RestoreSavedCartSuccess extends StateUtils.EntitySuccessAction { readonly type = RESTORE_SAVED_CART_SUCCESS; + constructor( public payload: { userId: string; @@ -140,11 +149,12 @@ export class RestoreSavedCartSuccess extends StateUtils.EntitySuccessAction { export class RestoreSavedCartFail extends StateUtils.EntityFailAction { readonly type = RESTORE_SAVED_CART_FAIL; + constructor( public payload: { userId: string; cartId: string; - error: any; + error: ErrorActionType; } ) { super(PROCESS_FEATURE, SAVED_CART_RESTORE_CART_PROCESS_ID, payload.error); @@ -153,6 +163,7 @@ export class RestoreSavedCartFail extends StateUtils.EntityFailAction { export class ClearRestoreSavedCart extends StateUtils.EntityLoaderResetAction { readonly type = CLEAR_RESTORE_SAVED_CART; + constructor() { super(PROCESS_FEATURE, SAVED_CART_RESTORE_CART_PROCESS_ID); } @@ -160,6 +171,7 @@ export class ClearRestoreSavedCart extends StateUtils.EntityLoaderResetAction { export class SaveCart extends StateUtils.EntityLoadAction { readonly type = SAVE_CART; + constructor( public payload: { userId: string; @@ -174,6 +186,7 @@ export class SaveCart extends StateUtils.EntityLoadAction { export class SaveCartSuccess extends StateUtils.EntitySuccessAction { readonly type = SAVE_CART_SUCCESS; + constructor( public payload: { userId: string; @@ -188,13 +201,14 @@ export class SaveCartSuccess extends StateUtils.EntitySuccessAction { export class SaveCartFail extends StateUtils.EntityFailAction { readonly type = SAVE_CART_FAIL; + constructor( public payload: { userId: string; cartId: string; saveCartName?: string; saveCartDescription?: string; - error: any; + error: ErrorActionType; } ) { super(PROCESS_FEATURE, SAVED_CART_SAVE_CART_PROCESS_ID, payload.error); @@ -203,6 +217,7 @@ export class SaveCartFail extends StateUtils.EntityFailAction { export class ClearSaveCart extends StateUtils.EntityLoaderResetAction { readonly type = CLEAR_SAVE_CART; + constructor() { super(PROCESS_FEATURE, SAVED_CART_SAVE_CART_PROCESS_ID); } @@ -210,6 +225,7 @@ export class ClearSaveCart extends StateUtils.EntityLoaderResetAction { export class EditSavedCart extends StateUtils.EntityLoadAction { readonly type = EDIT_SAVED_CART; + constructor( public payload: { userId: string; @@ -224,6 +240,7 @@ export class EditSavedCart extends StateUtils.EntityLoadAction { export class EditSavedCartSuccess extends StateUtils.EntitySuccessAction { readonly type = EDIT_SAVED_CART_SUCCESS; + constructor( public payload: { userId: string; @@ -238,13 +255,14 @@ export class EditSavedCartSuccess extends StateUtils.EntitySuccessAction { export class EditSavedCartFail extends StateUtils.EntityFailAction { readonly type = EDIT_SAVED_CART_FAIL; + constructor( public payload: { userId: string; cartId: string; saveCartName?: string; saveCartDescription?: string; - error: any; + error: ErrorActionType; } ) { super(PROCESS_FEATURE, SAVED_CART_SAVE_CART_PROCESS_ID, payload.error); @@ -253,6 +271,7 @@ export class EditSavedCartFail extends StateUtils.EntityFailAction { export class CloneSavedCart extends StateUtils.EntityLoadAction { readonly type = CLONE_SAVED_CART; + constructor( public payload: { userId: string; @@ -266,6 +285,7 @@ export class CloneSavedCart extends StateUtils.EntityLoadAction { export class CloneSavedCartSuccess extends StateUtils.EntitySuccessAction { readonly type = CLONE_SAVED_CART_SUCCESS; + constructor( public payload: { userId: string; @@ -279,12 +299,13 @@ export class CloneSavedCartSuccess extends StateUtils.EntitySuccessAction { export class CloneSavedCartFail extends StateUtils.EntityFailAction { readonly type = CLONE_SAVED_CART_FAIL; + constructor( public payload: { userId: string; cartId: string; saveCartName?: string; - error: any; + error: ErrorActionType; } ) { super(PROCESS_FEATURE, SAVED_CART_CLONE_CART_PROCESS_ID, payload.error); @@ -293,6 +314,7 @@ export class CloneSavedCartFail extends StateUtils.EntityFailAction { export class ClearCloneSavedCart extends StateUtils.EntityLoaderResetAction { readonly type = CLEAR_CLONE_SAVED_CART; + constructor() { super(PROCESS_FEATURE, SAVED_CART_CLONE_CART_PROCESS_ID); } diff --git a/feature-libs/cart/saved-cart/core/store/effects/saved-cart.effect.ts b/feature-libs/cart/saved-cart/core/store/effects/saved-cart.effect.ts index ffa4435dbab..945f8db8827 100644 --- a/feature-libs/cart/saved-cart/core/store/effects/saved-cart.effect.ts +++ b/feature-libs/cart/saved-cart/core/store/effects/saved-cart.effect.ts @@ -13,7 +13,7 @@ import { GlobalMessageService, GlobalMessageType, LoggerService, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap, withLatestFrom } from 'rxjs/operators'; @@ -49,7 +49,7 @@ export class SavedCartEffects { new SavedCartActions.LoadSavedCartFail({ userId, cartId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -78,7 +78,7 @@ export class SavedCartEffects { of( new SavedCartActions.LoadSavedCartsFail({ userId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -150,7 +150,7 @@ export class SavedCartEffects { new SavedCartActions.RestoreSavedCartFail({ userId, cartId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -196,7 +196,7 @@ export class SavedCartEffects { cartId, saveCartName, saveCartDescription, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -240,7 +240,7 @@ export class SavedCartEffects { cartId, saveCartName, saveCartDescription, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -283,7 +283,7 @@ export class SavedCartEffects { userId, cartId, saveCartName, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) diff --git a/feature-libs/cart/wish-list/core/store/actions/wish-list.action.spec.ts b/feature-libs/cart/wish-list/core/store/actions/wish-list.action.spec.ts index 6a5a4525e55..6edf737cde4 100644 --- a/feature-libs/cart/wish-list/core/store/actions/wish-list.action.spec.ts +++ b/feature-libs/cart/wish-list/core/store/actions/wish-list.action.spec.ts @@ -50,13 +50,18 @@ describe('WishList Actions', () => { const payload = { userId, cartId, - error: 'anyError', + error: { message: 'anyError' }, }; const action = new WishListActions.LoadWishListFail(payload); expect({ ...action }).toEqual({ + error: payload.error, type: WishListActions.LOAD_WISH_LIST_FAIL, payload, - meta: StateUtils.entityFailMeta(MULTI_CART_DATA, cartId, 'anyError'), + meta: StateUtils.entityFailMeta( + MULTI_CART_DATA, + cartId, + payload.error + ), }); }); }); @@ -91,9 +96,10 @@ describe('WishList Actions', () => { describe('CreateWishListFail', () => { it('should create the action', () => { - const payload = { cartId, error: 'error' }; + const payload = { cartId, error: { message: 'error' } }; const action = new WishListActions.CreateWishListFail(payload); expect({ ...action }).toEqual({ + error: payload.error, type: WishListActions.CREATE_WISH_LIST_FAIL, payload, meta: StateUtils.entityFailMeta( diff --git a/feature-libs/cart/wish-list/core/store/actions/wish-list.action.ts b/feature-libs/cart/wish-list/core/store/actions/wish-list.action.ts index fa390db61bf..94bff1aa670 100644 --- a/feature-libs/cart/wish-list/core/store/actions/wish-list.action.ts +++ b/feature-libs/cart/wish-list/core/store/actions/wish-list.action.ts @@ -7,7 +7,7 @@ import { Action } from '@ngrx/store'; import { MULTI_CART_DATA } from '@spartacus/cart/base/core'; import { Cart } from '@spartacus/cart/base/root'; -import { StateUtils } from '@spartacus/core'; +import { ErrorActionType, StateUtils } from '@spartacus/core'; export const CREATE_WISH_LIST = '[Wish List] Create Wish List'; export const CREATE_WISH_LIST_FAIL = '[Wish List] Create Wish List Fail'; @@ -19,6 +19,7 @@ export const LOAD_WISH_LIST_FAIL = '[Wish List] Load Wish List Fail'; export class CreateWishList implements Action { readonly type = CREATE_WISH_LIST; + constructor( public payload: { userId: string; @@ -30,6 +31,7 @@ export class CreateWishList implements Action { export class CreateWishListSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_WISH_LIST_SUCCESS; + constructor(public payload: { cart: Cart; cartId: string }) { super(MULTI_CART_DATA, payload.cartId); } @@ -37,7 +39,8 @@ export class CreateWishListSuccess extends StateUtils.EntitySuccessAction { export class CreateWishListFail extends StateUtils.EntityFailAction { readonly type = CREATE_WISH_LIST_FAIL; - constructor(public payload: { cartId: string; error?: any }) { + + constructor(public payload: { cartId: string; error: ErrorActionType }) { super(MULTI_CART_DATA, payload.cartId, payload.error); } } @@ -56,12 +59,15 @@ interface LoadWishListPayload { */ export class LoadWishList extends StateUtils.EntityLoadAction { readonly type = LOAD_WISH_LIST; + constructor(public payload: LoadWishListPayload) { super(MULTI_CART_DATA, payload.cartId); } } + export class LoadWishListSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_WISH_LIST_SUCCESS; + constructor(public payload: { cart: Cart; cartId: string }) { super(MULTI_CART_DATA, payload.cartId); } @@ -73,11 +79,12 @@ interface LoadWishListFailPayload { * temporary cart used to track loading/error state or to normal wish list entity. */ cartId: string; - error: any; + error: ErrorActionType; } export class LoadWishListFail extends StateUtils.EntityFailAction { readonly type = LOAD_WISH_LIST_FAIL; + constructor(public payload: LoadWishListFailPayload) { super(MULTI_CART_DATA, payload.cartId, payload.error); } diff --git a/feature-libs/cart/wish-list/core/store/effects/wish-list.effect.ts b/feature-libs/cart/wish-list/core/store/effects/wish-list.effect.ts index 8d7558716db..8057f56bd5d 100644 --- a/feature-libs/cart/wish-list/core/store/effects/wish-list.effect.ts +++ b/feature-libs/cart/wish-list/core/store/effects/wish-list.effect.ts @@ -21,7 +21,7 @@ import { StateUtils, UserIdService, isNotUndefined, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { EMPTY, Observable, from } from 'rxjs'; import { @@ -65,7 +65,7 @@ export class WishListEffects { from([ new WishListActions.CreateWishListFail({ cartId: cart.code ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ) @@ -111,7 +111,7 @@ export class WishListEffects { from([ new WishListActions.LoadWishListFail({ cartId: cartId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ) @@ -148,7 +148,7 @@ export class WishListEffects { from([ new WishListActions.LoadWishListFail({ cartId: wishListId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ) diff --git a/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts b/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts index 1a6d7b48652..b9c853cfea7 100644 --- a/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts +++ b/feature-libs/order/core/facade/replenishment-order-history.service.spec.ts @@ -19,7 +19,7 @@ import { ReplenishmentOrderHistoryService } from './replenishment-order-history. const mockUserId = OCC_USER_ID_CURRENT; const mockReplenishmentOrderCode = 'test-repl-code'; -const mockError = 'test-error'; +const mockError = new Error('test-error'); const mockReplenishmentOrder: ReplenishmentOrder = { active: true, diff --git a/feature-libs/order/core/store/actions/consignment-tracking.action.spec.ts b/feature-libs/order/core/store/actions/consignment-tracking.action.spec.ts index 837637ee700..b50def955fe 100644 --- a/feature-libs/order/core/store/actions/consignment-tracking.action.spec.ts +++ b/feature-libs/order/core/store/actions/consignment-tracking.action.spec.ts @@ -23,12 +23,12 @@ describe('Consignment Tracking Actions', () => { describe('LoadConsignmentTrackingFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new fromAction.LoadConsignmentTrackingFail(error); expect({ ...action }).toEqual({ type: fromAction.LOAD_CONSIGNMENT_TRACKING_FAIL, - payload: error, + error, }); }); }); diff --git a/feature-libs/order/core/store/actions/consignment-tracking.action.ts b/feature-libs/order/core/store/actions/consignment-tracking.action.ts index 4d323497ef5..c64444f13ca 100644 --- a/feature-libs/order/core/store/actions/consignment-tracking.action.ts +++ b/feature-libs/order/core/store/actions/consignment-tracking.action.ts @@ -6,6 +6,7 @@ import { Action } from '@ngrx/store'; import { ConsignmentTracking } from '@spartacus/order/root'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const LOAD_CONSIGNMENT_TRACKING = '[Order] Load Consignment Tracking'; export const LOAD_CONSIGNMENT_TRACKING_FAIL = @@ -16,6 +17,7 @@ export const CLEAR_CONSIGNMENT_TRACKING = '[Order] Clear Consignment Tracking'; export class LoadConsignmentTracking implements Action { readonly type = LOAD_CONSIGNMENT_TRACKING; + constructor( public payload: { userId?: string; @@ -25,18 +27,21 @@ export class LoadConsignmentTracking implements Action { ) {} } -export class LoadConsignmentTrackingFail implements Action { +export class LoadConsignmentTrackingFail implements ErrorAction { readonly type = LOAD_CONSIGNMENT_TRACKING_FAIL; - constructor(public payload: any) {} + + constructor(public error: ErrorActionType) {} } export class LoadConsignmentTrackingSuccess implements Action { readonly type = LOAD_CONSIGNMENT_TRACKING_SUCCESS; + constructor(public payload: ConsignmentTracking) {} } export class ClearConsignmentTracking { readonly type = CLEAR_CONSIGNMENT_TRACKING; + constructor() { // Intentional empty constructor } diff --git a/feature-libs/order/core/store/actions/order-details.action.spec.ts b/feature-libs/order/core/store/actions/order-details.action.spec.ts index 8ff22f17c94..f1c0d085f39 100644 --- a/feature-libs/order/core/store/actions/order-details.action.spec.ts +++ b/feature-libs/order/core/store/actions/order-details.action.spec.ts @@ -25,12 +25,12 @@ describe('Order Details Actions', () => { describe('LoadOrderDetailsFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.LoadOrderDetailsFail(error); expect({ ...action }).toEqual({ type: OrderActions.LOAD_ORDER_DETAILS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(ORDER_DETAILS, error), }); }); @@ -81,12 +81,12 @@ describe('Order Details Actions', () => { describe('CancelOrderFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.CancelOrderFail(error); expect({ ...action }).toEqual({ type: OrderActions.CANCEL_ORDER_FAIL, - payload: error, + error, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, CANCEL_ORDER_PROCESS_ID, diff --git a/feature-libs/order/core/store/actions/order-details.action.ts b/feature-libs/order/core/store/actions/order-details.action.ts index 59a98da1580..64f3bce3da9 100644 --- a/feature-libs/order/core/store/actions/order-details.action.ts +++ b/feature-libs/order/core/store/actions/order-details.action.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { PROCESS_FEATURE, StateUtils } from '@spartacus/core'; +import { PROCESS_FEATURE, StateUtils, ErrorActionType } from '@spartacus/core'; import { CancellationRequestEntryInputList, Order, @@ -35,8 +35,8 @@ export class LoadOrderDetails extends StateUtils.LoaderLoadAction { export class LoadOrderDetailsFail extends StateUtils.LoaderFailAction { readonly type = LOAD_ORDER_DETAILS_FAIL; - constructor(public payload: any) { - super(ORDER_DETAILS, payload); + constructor(public error: ErrorActionType) { + super(ORDER_DETAILS, error); } } @@ -69,8 +69,8 @@ export class CancelOrder extends StateUtils.EntityLoadAction { export class CancelOrderFail extends StateUtils.EntityFailAction { readonly type = CANCEL_ORDER_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, CANCEL_ORDER_PROCESS_ID, payload); + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, CANCEL_ORDER_PROCESS_ID, error); } } diff --git a/feature-libs/order/core/store/actions/order-return-request.action.spec.ts b/feature-libs/order/core/store/actions/order-return-request.action.spec.ts index 0fd628827cb..1be2a021964 100644 --- a/feature-libs/order/core/store/actions/order-return-request.action.spec.ts +++ b/feature-libs/order/core/store/actions/order-return-request.action.spec.ts @@ -56,12 +56,12 @@ describe('Order Return Request actions', () => { describe('CreateOrderReturnRequestFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.CreateOrderReturnRequestFail(error); expect({ ...action }).toEqual({ type: OrderActions.CREATE_ORDER_RETURN_REQUEST_FAIL, - payload: error, + error, meta: StateUtils.failMeta(RETURN_REQUEST_DETAILS, error), }); }); @@ -101,12 +101,12 @@ describe('Order Return Request actions', () => { describe('LoadOrderReturnRequestFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.LoadOrderReturnRequestFail(error); expect({ ...action }).toEqual({ type: OrderActions.LOAD_ORDER_RETURN_REQUEST_FAIL, - payload: error, + error, meta: StateUtils.failMeta(RETURN_REQUEST_DETAILS, error), }); }); @@ -151,12 +151,12 @@ describe('Order Return Request actions', () => { describe('CancelOrderReturnRequestFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.CancelOrderReturnRequestFail(error); expect({ ...action }).toEqual({ type: OrderActions.CANCEL_ORDER_RETURN_REQUEST_FAIL, - payload: error, + error, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, CANCEL_RETURN_PROCESS_ID, @@ -197,12 +197,12 @@ describe('Order Return Request actions', () => { describe('LoadOrderReturnRequestListFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.LoadOrderReturnRequestListFail(error); expect({ ...action }).toEqual({ type: OrderActions.LOAD_ORDER_RETURN_REQUEST_LIST_FAIL, - payload: error, + error, meta: StateUtils.failMeta(RETURN_REQUESTS, error), }); }); diff --git a/feature-libs/order/core/store/actions/order-return-request.action.ts b/feature-libs/order/core/store/actions/order-return-request.action.ts index c335dd83a93..48cfbd4c8c4 100644 --- a/feature-libs/order/core/store/actions/order-return-request.action.ts +++ b/feature-libs/order/core/store/actions/order-return-request.action.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { PROCESS_FEATURE, StateUtils } from '@spartacus/core'; +import { ErrorActionType, PROCESS_FEATURE, StateUtils } from '@spartacus/core'; import { ReturnRequest, ReturnRequestEntryInputList, @@ -13,8 +13,8 @@ import { } from '@spartacus/order/root'; import { CANCEL_RETURN_PROCESS_ID, - RETURN_REQUESTS, RETURN_REQUEST_DETAILS, + RETURN_REQUESTS, } from '../order-state'; export const CREATE_ORDER_RETURN_REQUEST = @@ -54,6 +54,7 @@ export const RESET_CANCEL_RETURN_PROCESS = export class CreateOrderReturnRequest extends StateUtils.LoaderLoadAction { readonly type = CREATE_ORDER_RETURN_REQUEST; + constructor( public payload: { userId: string; @@ -66,13 +67,15 @@ export class CreateOrderReturnRequest extends StateUtils.LoaderLoadAction { export class CreateOrderReturnRequestFail extends StateUtils.LoaderFailAction { readonly type = CREATE_ORDER_RETURN_REQUEST_FAIL; - constructor(public payload: any) { - super(RETURN_REQUEST_DETAILS, payload); + + constructor(public error: ErrorActionType) { + super(RETURN_REQUEST_DETAILS, error); } } export class CreateOrderReturnRequestSuccess extends StateUtils.LoaderSuccessAction { readonly type = CREATE_ORDER_RETURN_REQUEST_SUCCESS; + constructor(public payload: ReturnRequest) { super(RETURN_REQUEST_DETAILS); } @@ -80,6 +83,7 @@ export class CreateOrderReturnRequestSuccess extends StateUtils.LoaderSuccessAct export class LoadOrderReturnRequest extends StateUtils.LoaderLoadAction { readonly type = LOAD_ORDER_RETURN_REQUEST; + constructor( public payload: { userId: string; @@ -92,13 +96,15 @@ export class LoadOrderReturnRequest extends StateUtils.LoaderLoadAction { export class LoadOrderReturnRequestFail extends StateUtils.LoaderFailAction { readonly type = LOAD_ORDER_RETURN_REQUEST_FAIL; - constructor(public payload: any) { - super(RETURN_REQUEST_DETAILS, payload); + + constructor(public error: ErrorActionType) { + super(RETURN_REQUEST_DETAILS, error); } } export class LoadOrderReturnRequestSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_ORDER_RETURN_REQUEST_SUCCESS; + constructor(public payload: ReturnRequest) { super(RETURN_REQUEST_DETAILS); } @@ -106,6 +112,7 @@ export class LoadOrderReturnRequestSuccess extends StateUtils.LoaderSuccessActio export class CancelOrderReturnRequest extends StateUtils.EntityLoadAction { readonly type = CANCEL_ORDER_RETURN_REQUEST; + constructor( public payload: { userId: string; @@ -119,13 +126,15 @@ export class CancelOrderReturnRequest extends StateUtils.EntityLoadAction { export class CancelOrderReturnRequestFail extends StateUtils.EntityFailAction { readonly type = CANCEL_ORDER_RETURN_REQUEST_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, CANCEL_RETURN_PROCESS_ID, payload); + + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, CANCEL_RETURN_PROCESS_ID, error); } } export class CancelOrderReturnRequestSuccess extends StateUtils.EntitySuccessAction { readonly type = CANCEL_ORDER_RETURN_REQUEST_SUCCESS; + constructor() { super(PROCESS_FEATURE, CANCEL_RETURN_PROCESS_ID); } @@ -133,6 +142,7 @@ export class CancelOrderReturnRequestSuccess extends StateUtils.EntitySuccessAct export class LoadOrderReturnRequestList extends StateUtils.LoaderLoadAction { readonly type = LOAD_ORDER_RETURN_REQUEST_LIST; + constructor( public payload: { userId: string; @@ -147,13 +157,15 @@ export class LoadOrderReturnRequestList extends StateUtils.LoaderLoadAction { export class LoadOrderReturnRequestListFail extends StateUtils.LoaderFailAction { readonly type = LOAD_ORDER_RETURN_REQUEST_LIST_FAIL; - constructor(public payload: any) { - super(RETURN_REQUESTS, payload); + + constructor(public error: ErrorActionType) { + super(RETURN_REQUESTS, error); } } export class LoadOrderReturnRequestListSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_ORDER_RETURN_REQUEST_LIST_SUCCESS; + constructor(public payload: ReturnRequestList) { super(RETURN_REQUESTS); } @@ -161,6 +173,7 @@ export class LoadOrderReturnRequestListSuccess extends StateUtils.LoaderSuccessA export class ClearOrderReturnRequest extends StateUtils.LoaderResetAction { readonly type = CLEAR_ORDER_RETURN_REQUEST; + constructor() { super(RETURN_REQUEST_DETAILS); } @@ -168,6 +181,7 @@ export class ClearOrderReturnRequest extends StateUtils.LoaderResetAction { export class ClearOrderReturnRequestList extends StateUtils.LoaderResetAction { readonly type = CLEAR_ORDER_RETURN_REQUEST_LIST; + constructor() { super(RETURN_REQUESTS); } @@ -175,6 +189,7 @@ export class ClearOrderReturnRequestList extends StateUtils.LoaderResetAction { export class ResetCancelReturnProcess extends StateUtils.EntityLoaderResetAction { readonly type = RESET_CANCEL_RETURN_PROCESS; + constructor() { super(PROCESS_FEATURE, CANCEL_RETURN_PROCESS_ID); } diff --git a/feature-libs/order/core/store/actions/orders.action.spec.ts b/feature-libs/order/core/store/actions/orders.action.spec.ts index c6a97bfb08d..dcb39e65f60 100644 --- a/feature-libs/order/core/store/actions/orders.action.spec.ts +++ b/feature-libs/order/core/store/actions/orders.action.spec.ts @@ -32,12 +32,12 @@ describe('OrdersActions', () => { describe('LoadUserOrdersFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.LoadUserOrdersFail(error); expect({ ...action }).toEqual({ type: OrderActions.LOAD_USER_ORDERS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(ORDERS, error), }); }); diff --git a/feature-libs/order/core/store/actions/orders.action.ts b/feature-libs/order/core/store/actions/orders.action.ts index 51a6cffb730..98cc6070841 100644 --- a/feature-libs/order/core/store/actions/orders.action.ts +++ b/feature-libs/order/core/store/actions/orders.action.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { StateUtils } from '@spartacus/core'; +import { ErrorActionType, StateUtils } from '@spartacus/core'; import { OrderHistoryList } from '@spartacus/order/root'; import { ORDERS } from '../order-state'; @@ -15,6 +15,7 @@ export const CLEAR_USER_ORDERS = '[Order] Clear User Orders'; export class LoadUserOrders extends StateUtils.LoaderLoadAction { readonly type = LOAD_USER_ORDERS; + constructor( public payload: { userId: string; @@ -30,13 +31,15 @@ export class LoadUserOrders extends StateUtils.LoaderLoadAction { export class LoadUserOrdersFail extends StateUtils.LoaderFailAction { readonly type = LOAD_USER_ORDERS_FAIL; - constructor(public payload: any) { - super(ORDERS, payload); + + constructor(public error: ErrorActionType) { + super(ORDERS, error); } } export class LoadUserOrdersSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_USER_ORDERS_SUCCESS; + constructor(public payload: OrderHistoryList) { super(ORDERS); } @@ -44,6 +47,7 @@ export class LoadUserOrdersSuccess extends StateUtils.LoaderSuccessAction { export class ClearUserOrders extends StateUtils.LoaderResetAction { readonly type = CLEAR_USER_ORDERS; + constructor() { super(ORDERS); } diff --git a/feature-libs/order/core/store/actions/replenishment-order-details.action.spec.ts b/feature-libs/order/core/store/actions/replenishment-order-details.action.spec.ts index 929882c90e2..1f92a03c69f 100644 --- a/feature-libs/order/core/store/actions/replenishment-order-details.action.spec.ts +++ b/feature-libs/order/core/store/actions/replenishment-order-details.action.spec.ts @@ -7,7 +7,7 @@ import { import { OrderActions } from './index'; const mockUserId = 'test-user'; const mockReplenishmentOrderCode = 'test-repl-code'; -const mockError = 'test-error'; +const error = { message: 'test-error' }; const mockReplenishmentOrder: ReplenishmentOrder = { active: true, @@ -55,15 +55,14 @@ describe('ReplenishmentOrderActions', () => { describe('LoadReplenishmentOrderDetailsFail action', () => { it('should create an action', () => { - const payload = mockError; const action = new OrderActions.LoadReplenishmentOrderDetailsFail( - payload + error ); expect({ ...action }).toEqual({ type: OrderActions.LOAD_REPLENISHMENT_ORDER_DETAILS_FAIL, - payload, - meta: StateUtils.failMeta(REPLENISHMENT_ORDER_DETAILS, payload), + error, + meta: StateUtils.failMeta(REPLENISHMENT_ORDER_DETAILS, error), }); }); }); @@ -123,16 +122,15 @@ describe('ReplenishmentOrderActions', () => { describe('CancelReplenishmentOrderFail action', () => { it('should create an action', () => { - const payload = mockError; - const action = new OrderActions.CancelReplenishmentOrderFail(payload); + const action = new OrderActions.CancelReplenishmentOrderFail(error); expect({ ...action }).toEqual({ type: OrderActions.CANCEL_REPLENISHMENT_ORDER_FAIL, - payload, + error, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, CANCEL_REPLENISHMENT_ORDER_PROCESS_ID, - payload + error ), }); }); diff --git a/feature-libs/order/core/store/actions/replenishment-order-details.action.ts b/feature-libs/order/core/store/actions/replenishment-order-details.action.ts index c9d6f4e8e14..88e00e65dfe 100644 --- a/feature-libs/order/core/store/actions/replenishment-order-details.action.ts +++ b/feature-libs/order/core/store/actions/replenishment-order-details.action.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { PROCESS_FEATURE, StateUtils } from '@spartacus/core'; +import { ErrorActionType, PROCESS_FEATURE, StateUtils } from '@spartacus/core'; import { ReplenishmentOrder } from '@spartacus/order/root'; import { CANCEL_REPLENISHMENT_ORDER_PROCESS_ID, @@ -30,6 +30,7 @@ export const CLEAR_CANCEL_REPLENISHMENT_ORDER = export class LoadReplenishmentOrderDetails extends StateUtils.LoaderLoadAction { readonly type = LOAD_REPLENISHMENT_ORDER_DETAILS; + constructor( public payload: { userId: string; @@ -42,6 +43,7 @@ export class LoadReplenishmentOrderDetails extends StateUtils.LoaderLoadAction { export class LoadReplenishmentOrderDetailsSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_REPLENISHMENT_ORDER_DETAILS_SUCCESS; + constructor(public payload: ReplenishmentOrder) { super(REPLENISHMENT_ORDER_DETAILS); } @@ -49,13 +51,15 @@ export class LoadReplenishmentOrderDetailsSuccess extends StateUtils.LoaderSucce export class LoadReplenishmentOrderDetailsFail extends StateUtils.LoaderFailAction { readonly type = LOAD_REPLENISHMENT_ORDER_DETAILS_FAIL; - constructor(public payload: any) { - super(REPLENISHMENT_ORDER_DETAILS, payload); + + constructor(public error: ErrorActionType) { + super(REPLENISHMENT_ORDER_DETAILS, error); } } export class ClearReplenishmentOrderDetails extends StateUtils.LoaderResetAction { readonly type = ClEAR_REPLENISHMENT_ORDER_DETAILS; + constructor() { super(REPLENISHMENT_ORDER_DETAILS); } @@ -63,6 +67,7 @@ export class ClearReplenishmentOrderDetails extends StateUtils.LoaderResetAction export class CancelReplenishmentOrder extends StateUtils.EntityLoadAction { readonly type = CANCEL_REPLENISHMENT_ORDER; + constructor( public payload: { userId: string; @@ -75,6 +80,7 @@ export class CancelReplenishmentOrder extends StateUtils.EntityLoadAction { export class CancelReplenishmentOrderSuccess extends StateUtils.EntitySuccessAction { readonly type = CANCEL_REPLENISHMENT_ORDER_SUCCESS; + constructor(public payload: ReplenishmentOrder) { super(PROCESS_FEATURE, CANCEL_REPLENISHMENT_ORDER_PROCESS_ID); } @@ -82,13 +88,15 @@ export class CancelReplenishmentOrderSuccess extends StateUtils.EntitySuccessAct export class CancelReplenishmentOrderFail extends StateUtils.EntityFailAction { readonly type = CANCEL_REPLENISHMENT_ORDER_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, CANCEL_REPLENISHMENT_ORDER_PROCESS_ID, payload); + + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, CANCEL_REPLENISHMENT_ORDER_PROCESS_ID, error); } } export class ClearCancelReplenishmentOrder extends StateUtils.EntityLoaderResetAction { readonly type = CLEAR_CANCEL_REPLENISHMENT_ORDER; + constructor() { super(PROCESS_FEATURE, CANCEL_REPLENISHMENT_ORDER_PROCESS_ID); } diff --git a/feature-libs/order/core/store/actions/replenishment-orders.action.spec.ts b/feature-libs/order/core/store/actions/replenishment-orders.action.spec.ts index 00e8c36b628..221d5284487 100644 --- a/feature-libs/order/core/store/actions/replenishment-orders.action.spec.ts +++ b/feature-libs/order/core/store/actions/replenishment-orders.action.spec.ts @@ -31,12 +31,12 @@ describe('Replenishment Orders Actions', () => { describe('LoadUserReplenishmentOrdersFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new OrderActions.LoadUserReplenishmentOrdersFail(error); expect({ ...action }).toEqual({ type: OrderActions.LOAD_USER_REPLENISHMENT_ORDERS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(REPLENISHMENT_ORDERS, error), }); }); diff --git a/feature-libs/order/core/store/actions/replenishment-orders.action.ts b/feature-libs/order/core/store/actions/replenishment-orders.action.ts index 5b793741973..26806d451b9 100644 --- a/feature-libs/order/core/store/actions/replenishment-orders.action.ts +++ b/feature-libs/order/core/store/actions/replenishment-orders.action.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { StateUtils } from '@spartacus/core'; +import { ErrorActionType, StateUtils } from '@spartacus/core'; import { ReplenishmentOrderList } from '@spartacus/order/root'; import { REPLENISHMENT_ORDERS } from '../order-state'; @@ -19,6 +19,7 @@ export const CLEAR_USER_REPLENISHMENT_ORDERS = export class LoadUserReplenishmentOrders extends StateUtils.LoaderLoadAction { readonly type = LOAD_USER_REPLENISHMENT_ORDERS; + constructor( public payload: { userId: string; @@ -33,13 +34,15 @@ export class LoadUserReplenishmentOrders extends StateUtils.LoaderLoadAction { export class LoadUserReplenishmentOrdersFail extends StateUtils.LoaderFailAction { readonly type = LOAD_USER_REPLENISHMENT_ORDERS_FAIL; - constructor(public payload: any) { - super(REPLENISHMENT_ORDERS, payload); + + constructor(public error: ErrorActionType) { + super(REPLENISHMENT_ORDERS, error); } } export class LoadUserReplenishmentOrdersSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_USER_REPLENISHMENT_ORDERS_SUCCESS; + constructor(public payload: ReplenishmentOrderList) { super(REPLENISHMENT_ORDERS); } @@ -47,6 +50,7 @@ export class LoadUserReplenishmentOrdersSuccess extends StateUtils.LoaderSuccess export class ClearUserReplenishmentOrders extends StateUtils.LoaderResetAction { readonly type = CLEAR_USER_REPLENISHMENT_ORDERS; + constructor() { super(REPLENISHMENT_ORDERS); } diff --git a/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts b/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts index c2216c4da47..22f38e10218 100644 --- a/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts +++ b/feature-libs/order/core/store/effects/consignment-tracking.effect.spec.ts @@ -70,17 +70,16 @@ describe('Consignment Tracking effect', () => { }); it('should handle failures for load consignment tracking', () => { + const error = new Error('error'); spyOn(orderHistoryConnector, 'getConsignmentTracking').and.returnValue( - throwError('Error') + throwError(error) ); const action = new OrderActions.LoadConsignmentTracking( mockTrackingParams ); - const completion = new OrderActions.LoadConsignmentTrackingFail( - undefined - ); + const completion = new OrderActions.LoadConsignmentTrackingFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); diff --git a/feature-libs/order/core/store/effects/consignment-tracking.effect.ts b/feature-libs/order/core/store/effects/consignment-tracking.effect.ts index 1ecd927c055..67929a5fb28 100644 --- a/feature-libs/order/core/store/effects/consignment-tracking.effect.ts +++ b/feature-libs/order/core/store/effects/consignment-tracking.effect.ts @@ -6,7 +6,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { ConsignmentTracking } from '@spartacus/order/root'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; @@ -37,7 +37,7 @@ export class ConsignmentTrackingEffects { catchError((error) => of( new OrderActions.LoadConsignmentTrackingFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/order/core/store/effects/order-details.effect.spec.ts b/feature-libs/order/core/store/effects/order-details.effect.spec.ts index 1b91b4d637b..332f9c41ab3 100644 --- a/feature-libs/order/core/store/effects/order-details.effect.spec.ts +++ b/feature-libs/order/core/store/effects/order-details.effect.spec.ts @@ -40,6 +40,8 @@ class MockUserIdService implements Partial { } } +const error = new Error('error'); + describe('Order Details effect', () => { let orderDetailsEffect: fromOrderDetailsEffect.OrderDetailsEffect; let orderHistoryConnector: OrderHistoryConnector; @@ -89,11 +91,11 @@ describe('Order Details effect', () => { }); it('should handle failures for load order details', () => { - spyOn(orderHistoryConnector, 'get').and.returnValue(throwError('Error')); + spyOn(orderHistoryConnector, 'get').and.returnValue(throwError(error)); const action = new OrderActions.LoadOrderDetails(mockOrderDetailsParams); - const completion = new OrderActions.LoadOrderDetailsFail(undefined); + const completion = new OrderActions.LoadOrderDetailsFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); @@ -117,13 +119,11 @@ describe('Order Details effect', () => { }); it('should handle failures for cancel an order', () => { - spyOn(orderHistoryConnector, 'cancel').and.returnValue( - throwError('Error') - ); + spyOn(orderHistoryConnector, 'cancel').and.returnValue(throwError(error)); const action = new OrderActions.CancelOrder(mockCancelOrderParams); - const completion = new OrderActions.CancelOrderFail(undefined); + const completion = new OrderActions.CancelOrderFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); diff --git a/feature-libs/order/core/store/effects/order-details.effect.ts b/feature-libs/order/core/store/effects/order-details.effect.ts index cc58a0a24c7..413a9a91946 100644 --- a/feature-libs/order/core/store/effects/order-details.effect.ts +++ b/feature-libs/order/core/store/effects/order-details.effect.ts @@ -13,7 +13,7 @@ import { LoggerService, SiteContextActions, UserIdService, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Order } from '@spartacus/order/root'; import { EMPTY, Observable, of } from 'rxjs'; @@ -47,7 +47,7 @@ export class OrderDetailsEffect { catchError((error) => of( new OrderActions.LoadOrderDetailsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -75,7 +75,7 @@ export class OrderDetailsEffect { return of( new OrderActions.CancelOrderFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ); }) @@ -108,7 +108,7 @@ export class OrderDetailsEffect { catchError((error) => of( new OrderActions.LoadOrderDetailsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts b/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts index 17396349c2d..2a98486686c 100644 --- a/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts +++ b/feature-libs/order/core/store/effects/order-return-request.effect.spec.ts @@ -35,6 +35,8 @@ const mockCancelReturnRequest = { returnRequestModification: {}, }; +const error = new Error('error'); + describe('Order Return Request effect', () => { let orderReturnRequestEffect: fromOrderReturnRequestEffect.OrderReturnRequestEffect; let orderHistoryConnector: OrderHistoryConnector; @@ -81,18 +83,14 @@ describe('Order Return Request effect', () => { }); it('should handle failures for create order return request', () => { - spyOn(orderHistoryConnector, 'return').and.returnValue( - throwError('Error') - ); + spyOn(orderHistoryConnector, 'return').and.returnValue(throwError(error)); const action = new OrderActions.CreateOrderReturnRequest({ userId: 'userId', returnRequestInput, }); - const completion = new OrderActions.CreateOrderReturnRequestFail( - undefined - ); + const completion = new OrderActions.CreateOrderReturnRequestFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); @@ -127,16 +125,14 @@ describe('Order Return Request effect', () => { it('should handle failures for load return request list', () => { spyOn(orderHistoryConnector, 'getReturnRequestList').and.returnValue( - throwError('Error') + throwError(error) ); const action = new OrderActions.LoadOrderReturnRequestList({ userId: 'test@sap.com', pageSize: 5, }); - const completion = new OrderActions.LoadOrderReturnRequestListFail( - undefined - ); + const completion = new OrderActions.LoadOrderReturnRequestListFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); @@ -171,7 +167,7 @@ describe('Order Return Request effect', () => { it('should handle failures for load an order return request', () => { spyOn(orderHistoryConnector, 'getReturnRequestDetail').and.returnValue( - throwError('Error') + throwError(error) ); const action = new OrderActions.LoadOrderReturnRequest({ @@ -179,7 +175,7 @@ describe('Order Return Request effect', () => { returnRequestCode: 'test', }); - const completion = new OrderActions.LoadOrderReturnRequestFail(undefined); + const completion = new OrderActions.LoadOrderReturnRequestFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); @@ -212,16 +208,14 @@ describe('Order Return Request effect', () => { it('should handle failures for cancel return request', () => { spyOn(orderHistoryConnector, 'cancelReturnRequest').and.returnValue( - throwError('Error') + throwError(error) ); const action = new OrderActions.CancelOrderReturnRequest( mockCancelReturnRequest ); - const completion = new OrderActions.CancelOrderReturnRequestFail( - undefined - ); + const completion = new OrderActions.CancelOrderReturnRequestFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); diff --git a/feature-libs/order/core/store/effects/order-return-request.effect.ts b/feature-libs/order/core/store/effects/order-return-request.effect.ts index aed69304fa3..e381c9dde48 100644 --- a/feature-libs/order/core/store/effects/order-return-request.effect.ts +++ b/feature-libs/order/core/store/effects/order-return-request.effect.ts @@ -6,7 +6,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { ReturnRequest, ReturnRequestList } from '@spartacus/order/root'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; @@ -35,7 +35,7 @@ export class OrderReturnRequestEffect { catchError((error) => of( new OrderActions.CreateOrderReturnRequestFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -60,7 +60,7 @@ export class OrderReturnRequestEffect { catchError((error) => of( new OrderActions.LoadOrderReturnRequestFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -86,7 +86,7 @@ export class OrderReturnRequestEffect { catchError((error) => of( new OrderActions.CancelOrderReturnRequestFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -120,7 +120,7 @@ export class OrderReturnRequestEffect { catchError((error) => of( new OrderActions.LoadOrderReturnRequestListFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/order/core/store/effects/orders.effect.spec.ts b/feature-libs/order/core/store/effects/orders.effect.spec.ts index 60c63ccf22f..bf0b6160777 100644 --- a/feature-libs/order/core/store/effects/orders.effect.spec.ts +++ b/feature-libs/order/core/store/effects/orders.effect.spec.ts @@ -3,7 +3,7 @@ import { TestBed } from '@angular/core/testing'; import { Actions } from '@ngrx/effects'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; -import { normalizeHttpError, SiteContextActions } from '@spartacus/core'; +import { tryNormalizeHttpError, SiteContextActions } from '@spartacus/core'; import { OrderHistoryList } from '@spartacus/order/root'; import { cold, hot } from 'jasmine-marbles'; import { Observable, of, throwError } from 'rxjs'; @@ -84,7 +84,7 @@ describe('Orders effect', () => { }); const completion = new OrderActions.LoadUserOrdersFail( - normalizeHttpError(mockError) + tryNormalizeHttpError(mockError) ); actions$ = hot('-a', { a: action }); @@ -130,7 +130,7 @@ describe('Orders effect', () => { }); const completion = new OrderActions.LoadUserOrdersFail( - normalizeHttpError(mockError) + tryNormalizeHttpError(mockError) ); actions$ = hot('-a', { a: action }); diff --git a/feature-libs/order/core/store/effects/orders.effect.ts b/feature-libs/order/core/store/effects/orders.effect.ts index 40046f9911e..eb0888c8331 100644 --- a/feature-libs/order/core/store/effects/orders.effect.ts +++ b/feature-libs/order/core/store/effects/orders.effect.ts @@ -9,7 +9,7 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { LoggerService, SiteContextActions, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { OrderHistoryList } from '@spartacus/order/root'; import { Observable, of } from 'rxjs'; @@ -58,7 +58,7 @@ export class OrdersEffect { catchError((error) => of( new OrderActions.LoadUserOrdersFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts b/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts index cdd4587ce35..77cb1004042 100644 --- a/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts +++ b/feature-libs/order/core/store/effects/replenishment-order-details.effect.spec.ts @@ -4,7 +4,7 @@ import { Action } from '@ngrx/store'; import { GlobalMessageService, GlobalMessageType, - normalizeHttpError, + tryNormalizeHttpError, Translatable, } from '@spartacus/core'; import { ReplenishmentOrder } from '@spartacus/order/root'; @@ -102,7 +102,7 @@ describe('ReplenishmentOrderDetailsEffect', () => { replenishmentOrderCode: mockReplenishmentCode, }); const completion = new OrderActions.LoadReplenishmentOrderDetailsFail( - normalizeHttpError(mockError) + tryNormalizeHttpError(mockError) ); actions$ = hot('-a', { a: action }); @@ -146,7 +146,7 @@ describe('ReplenishmentOrderDetailsEffect', () => { replenishmentOrderCode: mockReplenishmentCode, }); const completion = new OrderActions.CancelReplenishmentOrderFail( - normalizeHttpError(mockError) + tryNormalizeHttpError(mockError) ); actions$ = hot('-a', { a: action }); diff --git a/feature-libs/order/core/store/effects/replenishment-order-details.effect.ts b/feature-libs/order/core/store/effects/replenishment-order-details.effect.ts index 64429bc17b6..31ab5e31f14 100644 --- a/feature-libs/order/core/store/effects/replenishment-order-details.effect.ts +++ b/feature-libs/order/core/store/effects/replenishment-order-details.effect.ts @@ -10,7 +10,7 @@ import { GlobalMessageService, GlobalMessageType, LoggerService, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { ReplenishmentOrder } from '@spartacus/order/root'; import { Observable, of } from 'rxjs'; @@ -41,7 +41,7 @@ export class ReplenishmentOrderDetailsEffect { catchError((error) => of( new OrderActions.LoadReplenishmentOrderDetailsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -78,7 +78,7 @@ export class ReplenishmentOrderDetailsEffect { return of( new OrderActions.CancelReplenishmentOrderFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ); }) diff --git a/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts b/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts index bfd168058ab..13d8b20de9d 100644 --- a/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts +++ b/feature-libs/order/core/store/effects/replenishment-orders.effect.spec.ts @@ -3,7 +3,7 @@ import { TestBed } from '@angular/core/testing'; import { Actions } from '@ngrx/effects'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; -import { normalizeHttpError } from '@spartacus/core'; +import { tryNormalizeHttpError } from '@spartacus/core'; import { ReplenishmentOrderList } from '@spartacus/order/root'; import { cold, hot } from 'jasmine-marbles'; import { Observable, of, throwError } from 'rxjs'; @@ -66,8 +66,9 @@ describe('Replenishment Orders effect', () => { }); it('should handle failures for load user Replenishment Orders', () => { + const error = new Error('error'); spyOn(replenishmentOrderHistoryConnector, 'loadHistory').and.returnValue( - throwError('Error') + throwError(error) ); const action = new OrderActions.LoadUserReplenishmentOrders({ @@ -76,7 +77,7 @@ describe('Replenishment Orders effect', () => { }); const completion = new OrderActions.LoadUserReplenishmentOrdersFail( - normalizeHttpError('Error') + tryNormalizeHttpError(error) ); actions$ = hot('-a', { a: action }); diff --git a/feature-libs/order/core/store/effects/replenishment-orders.effect.ts b/feature-libs/order/core/store/effects/replenishment-orders.effect.ts index 38f0bd2b405..2f1fc5a6f60 100644 --- a/feature-libs/order/core/store/effects/replenishment-orders.effect.ts +++ b/feature-libs/order/core/store/effects/replenishment-orders.effect.ts @@ -6,7 +6,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { ReplenishmentOrderList } from '@spartacus/order/root'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; @@ -41,7 +41,7 @@ export class ReplenishmentOrdersEffect { catchError((error) => of( new OrderActions.LoadUserReplenishmentOrdersFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/order/core/store/reducers/orders.reducer.spec.ts b/feature-libs/order/core/store/reducers/orders.reducer.spec.ts index 4466ba846d6..1d4f27f1106 100644 --- a/feature-libs/order/core/store/reducers/orders.reducer.spec.ts +++ b/feature-libs/order/core/store/reducers/orders.reducer.spec.ts @@ -40,7 +40,7 @@ describe('Orders Reducer', () => { describe('LOAD_USER_ORDERS_FAIL action', () => { it('should return the initial state', () => { const { initialState } = fromUserOrdersReducer; - const action = new OrderActions.LoadUserOrdersFail('error'); + const action = new OrderActions.LoadUserOrdersFail(new Error('error')); const state = fromUserOrdersReducer.reducer(initialState, action); expect(state).toEqual(initialState); }); diff --git a/feature-libs/order/core/store/selectors/replenishment-order-details.selectors.spec.ts b/feature-libs/order/core/store/selectors/replenishment-order-details.selectors.spec.ts index 5eb1d6a86ca..26d00376045 100644 --- a/feature-libs/order/core/store/selectors/replenishment-order-details.selectors.spec.ts +++ b/feature-libs/order/core/store/selectors/replenishment-order-details.selectors.spec.ts @@ -114,7 +114,7 @@ describe('ReplenishmentOrderDetailsSelectors', () => { describe('getReplenishmentOrderDetailsError', () => { it('should return the boolean value from the loader state error', () => { - const mockError = 'test-error'; + const mockError = new Error('test-error'); store.dispatch( new OrderActions.LoadReplenishmentOrderDetailsFail(mockError) diff --git a/feature-libs/order/core/store/selectors/replenishment-orders.selectors.spec.ts b/feature-libs/order/core/store/selectors/replenishment-orders.selectors.spec.ts index 4d1efc39371..e336f09deb0 100644 --- a/feature-libs/order/core/store/selectors/replenishment-orders.selectors.spec.ts +++ b/feature-libs/order/core/store/selectors/replenishment-orders.selectors.spec.ts @@ -114,7 +114,7 @@ describe('ReplenishmentOrdersSelectors', () => { describe('getReplenishmentOrdersError', () => { it('should return the boolean value from the loader state error', () => { - const mockError = 'test-error'; + const mockError = new Error('test-error'); store.dispatch( new OrderActions.LoadUserReplenishmentOrdersFail(mockError) diff --git a/feature-libs/organization/administration/core/store/actions/b2b-user.action.spec.ts b/feature-libs/organization/administration/core/store/actions/b2b-user.action.spec.ts index 16d68d40e5a..4e50ea27d16 100644 --- a/feature-libs/organization/administration/core/store/actions/b2b-user.action.spec.ts +++ b/feature-libs/organization/administration/core/store/actions/b2b-user.action.spec.ts @@ -23,7 +23,7 @@ const approverId = 'approverId'; const userGroupId = 'userGroupId'; const permissionId = 'permissionId'; const selected = true; -const error = 'anError'; +const error = { message: 'anError' }; const params = { currentPage: 2 }; const query = '?pageSize=¤tPage=2&sort='; @@ -56,6 +56,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.LOAD_B2B_USER_FAIL, payload: { orgCustomerId, error }, meta: StateUtils.entityFailMeta( @@ -132,15 +133,14 @@ describe('B2BUser Actions', () => { it('should create the action', () => { const action = new B2BUserActions.LoadB2BUsersFail({ params, - error: { error }, + error, }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.LOAD_B2B_USERS_FAIL, - payload: { params, error: { error } }, - meta: StateUtils.entityFailMeta(USER_LIST, query, { - error, - }), + payload: { params, error }, + meta: StateUtils.entityFailMeta(USER_LIST, query, error), }); }); }); @@ -201,6 +201,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.CREATE_B2B_USER_FAIL, payload: { orgCustomerId, @@ -267,6 +268,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.UPDATE_B2B_USER_FAIL, payload: { orgCustomerId, @@ -323,6 +325,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.LOAD_B2B_USER_APPROVERS_FAIL, payload: { orgCustomerId, @@ -386,13 +389,14 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.ASSIGN_B2B_USER_APPROVER_FAIL, payload: { orgCustomerId, approverId, error, }, - meta: StateUtils.entityFailMeta(B2B_USER_ENTITIES, approverId), + meta: StateUtils.entityFailMeta(B2B_USER_ENTITIES, approverId, error), }); }); }); @@ -437,13 +441,14 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.UNASSIGN_B2B_USER_APPROVER_FAIL, payload: { orgCustomerId, approverId, error, }, - meta: StateUtils.entityFailMeta(B2B_USER_ENTITIES, approverId), + meta: StateUtils.entityFailMeta(B2B_USER_ENTITIES, approverId, error), }); }); }); @@ -493,6 +498,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.LOAD_B2B_USER_PERMISSIONS_FAIL, payload: { orgCustomerId, @@ -556,6 +562,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.ASSIGN_B2B_USER_PERMISSION_FAIL, payload: { orgCustomerId, @@ -611,6 +618,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.UNASSIGN_B2B_USER_PERMISSION_FAIL, payload: { orgCustomerId, @@ -671,6 +679,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.LOAD_B2B_USER_USER_GROUPS_FAIL, payload: { orgCustomerId, @@ -734,6 +743,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.ASSIGN_B2B_USER_USER_GROUP_FAIL, payload: { orgCustomerId, @@ -789,6 +799,7 @@ describe('B2BUser Actions', () => { }); expect({ ...action }).toEqual({ + error, type: B2BUserActions.UNASSIGN_B2B_USER_USER_GROUP_FAIL, payload: { orgCustomerId, diff --git a/feature-libs/organization/administration/core/store/actions/b2b-user.action.ts b/feature-libs/organization/administration/core/store/actions/b2b-user.action.ts index 6bc684debef..ed43836e31a 100644 --- a/feature-libs/organization/administration/core/store/actions/b2b-user.action.ts +++ b/feature-libs/organization/administration/core/store/actions/b2b-user.action.ts @@ -4,7 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { B2BUser, ListModel, SearchConfig, StateUtils } from '@spartacus/core'; +import { + B2BUser, + ErrorActionType, + ListModel, + SearchConfig, + StateUtils, +} from '@spartacus/core'; import { B2B_USER_APPROVERS, B2B_USER_ENTITIES, @@ -89,6 +95,7 @@ export const UNASSIGN_B2B_USER_USER_GROUP_SUCCESS = export class LoadB2BUser extends StateUtils.EntityLoadAction { readonly type = LOAD_B2B_USER; + constructor(public payload: { userId: string; orgCustomerId: string }) { super(B2B_USER_ENTITIES, payload.orgCustomerId); } @@ -96,13 +103,17 @@ export class LoadB2BUser extends StateUtils.EntityLoadAction { export class LoadB2BUserFail extends StateUtils.EntityFailAction { readonly type = LOAD_B2B_USER_FAIL; - constructor(public payload: { orgCustomerId: string; error: any }) { + + constructor( + public payload: { orgCustomerId: string; error: ErrorActionType } + ) { super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error); } } export class LoadB2BUserSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_B2B_USER_SUCCESS; + constructor(public payload: B2BUser | B2BUser[]) { super( B2B_USER_ENTITIES, @@ -115,6 +126,7 @@ export class LoadB2BUserSuccess extends StateUtils.EntitySuccessAction { export class CreateB2BUser extends StateUtils.EntityLoadAction { readonly type = CREATE_B2B_USER; + constructor(public payload: { userId: string; orgCustomer: B2BUser }) { super(B2B_USER_ENTITIES, payload.orgCustomer.customerId ?? null); } @@ -122,13 +134,17 @@ export class CreateB2BUser extends StateUtils.EntityLoadAction { export class CreateB2BUserFail extends StateUtils.EntityFailAction { readonly type = CREATE_B2B_USER_FAIL; - constructor(public payload: { orgCustomerId: string; error: any }) { + + constructor( + public payload: { orgCustomerId: string; error: ErrorActionType } + ) { super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error); } } export class CreateB2BUserSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_B2B_USER_SUCCESS; + constructor(public payload: B2BUser) { super(B2B_USER_ENTITIES, payload.customerId ?? null, payload); } @@ -136,6 +152,7 @@ export class CreateB2BUserSuccess extends StateUtils.EntitySuccessAction { export class UpdateB2BUser extends StateUtils.EntityLoadAction { readonly type = UPDATE_B2B_USER; + constructor( public payload: { userId: string; @@ -149,13 +166,17 @@ export class UpdateB2BUser extends StateUtils.EntityLoadAction { export class UpdateB2BUserFail extends StateUtils.EntityFailAction { readonly type = UPDATE_B2B_USER_FAIL; - constructor(public payload: { orgCustomerId: string; error: any }) { + + constructor( + public payload: { orgCustomerId: string; error: ErrorActionType } + ) { super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error); } } export class UpdateB2BUserSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_B2B_USER_SUCCESS; + constructor(public payload: B2BUser) { super(B2B_USER_ENTITIES, payload.customerId ?? '', payload); } @@ -163,6 +184,7 @@ export class UpdateB2BUserSuccess extends StateUtils.EntitySuccessAction { export class LoadB2BUsers extends StateUtils.EntityLoadAction { readonly type = LOAD_B2B_USERS; + constructor( public payload: { userId: string; @@ -174,8 +196,12 @@ export class LoadB2BUsers extends StateUtils.EntityLoadAction { } export class LoadB2BUsersFail extends StateUtils.EntityFailAction { + error: ErrorActionType = this.payload.error; readonly type = LOAD_B2B_USERS_FAIL; - constructor(public payload: { params: SearchConfig; error: any }) { + + constructor( + public payload: { params: SearchConfig; error: ErrorActionType } + ) { super( USER_LIST, StateUtils.serializeSearchConfig(payload.params), @@ -186,6 +212,7 @@ export class LoadB2BUsersFail extends StateUtils.EntityFailAction { export class LoadB2BUsersSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_B2B_USERS_SUCCESS; + constructor( public payload: { page: ListModel; @@ -198,6 +225,7 @@ export class LoadB2BUsersSuccess extends StateUtils.EntitySuccessAction { export class LoadB2BUserApprovers extends StateUtils.EntityLoadAction { readonly type = LOAD_B2B_USER_APPROVERS; + constructor( public payload: { userId: string; @@ -214,11 +242,12 @@ export class LoadB2BUserApprovers extends StateUtils.EntityLoadAction { export class LoadB2BUserApproversFail extends StateUtils.EntityFailAction { readonly type = LOAD_B2B_USER_APPROVERS_FAIL; + constructor( public payload: { orgCustomerId: string; params: SearchConfig; - error: any; + error: ErrorActionType; } ) { super( @@ -231,6 +260,7 @@ export class LoadB2BUserApproversFail extends StateUtils.EntityFailAction { export class LoadB2BUserApproversSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_B2B_USER_APPROVERS_SUCCESS; + constructor( public payload: { orgCustomerId: string; @@ -247,6 +277,7 @@ export class LoadB2BUserApproversSuccess extends StateUtils.EntitySuccessAction export class AssignB2BUserApprover extends StateUtils.EntityLoadAction { readonly type = ASSIGN_B2B_USER_APPROVER; + constructor( public payload: { userId: string; @@ -259,20 +290,23 @@ export class AssignB2BUserApprover extends StateUtils.EntityLoadAction { } export class AssignB2BUserApproverFail extends StateUtils.EntityFailAction { + error: ErrorActionType = this.payload.error; readonly type = ASSIGN_B2B_USER_APPROVER_FAIL; + constructor( public payload: { orgCustomerId: string; approverId: string; - error: any; + error: ErrorActionType; } ) { - super(B2B_USER_ENTITIES, payload.approverId); + super(B2B_USER_ENTITIES, payload.approverId, payload.error); } } export class AssignB2BUserApproverSuccess extends StateUtils.EntitySuccessAction { readonly type = ASSIGN_B2B_USER_APPROVER_SUCCESS; + constructor( public payload: { approverId: string; @@ -285,6 +319,7 @@ export class AssignB2BUserApproverSuccess extends StateUtils.EntitySuccessAction export class UnassignB2BUserApprover extends StateUtils.EntityLoadAction { readonly type = UNASSIGN_B2B_USER_APPROVER; + constructor( public payload: { userId: string; @@ -297,20 +332,23 @@ export class UnassignB2BUserApprover extends StateUtils.EntityLoadAction { } export class UnassignB2BUserApproverFail extends StateUtils.EntityFailAction { + error: ErrorActionType = this.payload.error; readonly type = UNASSIGN_B2B_USER_APPROVER_FAIL; + constructor( public payload: { orgCustomerId: string; approverId: string; - error: any; + error: ErrorActionType; } ) { - super(B2B_USER_ENTITIES, payload.approverId); + super(B2B_USER_ENTITIES, payload.approverId, payload.error); } } export class UnassignB2BUserApproverSuccess extends StateUtils.EntitySuccessAction { readonly type = UNASSIGN_B2B_USER_APPROVER_SUCCESS; + constructor( public payload: { approverId: string; @@ -323,6 +361,7 @@ export class UnassignB2BUserApproverSuccess extends StateUtils.EntitySuccessActi export class LoadB2BUserPermissions extends StateUtils.EntityLoadAction { readonly type = LOAD_B2B_USER_PERMISSIONS; + constructor( public payload: { userId: string; @@ -338,12 +377,14 @@ export class LoadB2BUserPermissions extends StateUtils.EntityLoadAction { } export class LoadB2BUserPermissionsFail extends StateUtils.EntityFailAction { + error: ErrorActionType = this.payload.error; readonly type = LOAD_B2B_USER_PERMISSIONS_FAIL; + constructor( public payload: { orgCustomerId: string; params: SearchConfig; - error: any; + error: ErrorActionType; } ) { super(B2B_USER_PERMISSIONS, payload.orgCustomerId, payload.error); @@ -352,6 +393,7 @@ export class LoadB2BUserPermissionsFail extends StateUtils.EntityFailAction { export class LoadB2BUserPermissionsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_B2B_USER_PERMISSIONS_SUCCESS; + constructor( public payload: { orgCustomerId: string; @@ -368,6 +410,7 @@ export class LoadB2BUserPermissionsSuccess extends StateUtils.EntitySuccessActio export class AssignB2BUserPermission extends StateUtils.EntityLoadAction { readonly type = ASSIGN_B2B_USER_PERMISSION; + constructor( public payload: { userId: string; @@ -380,12 +423,14 @@ export class AssignB2BUserPermission extends StateUtils.EntityLoadAction { } export class AssignB2BUserPermissionFail extends StateUtils.EntityFailAction { + error: ErrorActionType = this.payload.error; readonly type = ASSIGN_B2B_USER_PERMISSION_FAIL; + constructor( public payload: { orgCustomerId: string; permissionId: string; - error: any; + error: ErrorActionType; } ) { super(PERMISSION_ENTITIES, payload.permissionId, payload.error); @@ -394,6 +439,7 @@ export class AssignB2BUserPermissionFail extends StateUtils.EntityFailAction { export class AssignB2BUserPermissionSuccess extends StateUtils.EntitySuccessAction { readonly type = ASSIGN_B2B_USER_PERMISSION_SUCCESS; + constructor( public payload: { permissionId: string; @@ -406,6 +452,7 @@ export class AssignB2BUserPermissionSuccess extends StateUtils.EntitySuccessActi export class UnassignB2BUserPermission extends StateUtils.EntityLoadAction { readonly type = UNASSIGN_B2B_USER_PERMISSION; + constructor( public payload: { userId: string; @@ -418,12 +465,14 @@ export class UnassignB2BUserPermission extends StateUtils.EntityLoadAction { } export class UnassignB2BUserPermissionFail extends StateUtils.EntityFailAction { + error: ErrorActionType = this.payload.error; readonly type = UNASSIGN_B2B_USER_PERMISSION_FAIL; + constructor( public payload: { orgCustomerId: string; permissionId: string; - error: any; + error: ErrorActionType; } ) { super(PERMISSION_ENTITIES, payload.permissionId, payload.error); @@ -432,6 +481,7 @@ export class UnassignB2BUserPermissionFail extends StateUtils.EntityFailAction { export class UnassignB2BUserPermissionSuccess extends StateUtils.EntitySuccessAction { readonly type = UNASSIGN_B2B_USER_PERMISSION_SUCCESS; + constructor( public payload: { permissionId: string; @@ -444,6 +494,7 @@ export class UnassignB2BUserPermissionSuccess extends StateUtils.EntitySuccessAc export class LoadB2BUserUserGroups extends StateUtils.EntityLoadAction { readonly type = LOAD_B2B_USER_USER_GROUPS; + constructor( public payload: { userId: string; @@ -459,12 +510,14 @@ export class LoadB2BUserUserGroups extends StateUtils.EntityLoadAction { } export class LoadB2BUserUserGroupsFail extends StateUtils.EntityFailAction { + error: ErrorActionType = this.payload.error; readonly type = LOAD_B2B_USER_USER_GROUPS_FAIL; + constructor( public payload: { orgCustomerId: string; params: SearchConfig; - error: any; + error: ErrorActionType; } ) { super( @@ -477,6 +530,7 @@ export class LoadB2BUserUserGroupsFail extends StateUtils.EntityFailAction { export class LoadB2BUserUserGroupsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_B2B_USER_USER_GROUPS_SUCCESS; + constructor( public payload: { orgCustomerId: string; @@ -493,6 +547,7 @@ export class LoadB2BUserUserGroupsSuccess extends StateUtils.EntitySuccessAction export class AssignB2BUserUserGroup extends StateUtils.EntityLoadAction { readonly type = ASSIGN_B2B_USER_USER_GROUP; + constructor( public payload: { userId: string; @@ -506,11 +561,12 @@ export class AssignB2BUserUserGroup extends StateUtils.EntityLoadAction { export class AssignB2BUserUserGroupFail extends StateUtils.EntityFailAction { readonly type = ASSIGN_B2B_USER_USER_GROUP_FAIL; + constructor( public payload: { orgCustomerId: string; userGroupId: string; - error: any; + error: ErrorActionType; } ) { super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error); @@ -519,6 +575,7 @@ export class AssignB2BUserUserGroupFail extends StateUtils.EntityFailAction { export class AssignB2BUserUserGroupSuccess extends StateUtils.EntitySuccessAction { readonly type = ASSIGN_B2B_USER_USER_GROUP_SUCCESS; + constructor( public payload: { uid: string; @@ -531,6 +588,7 @@ export class AssignB2BUserUserGroupSuccess extends StateUtils.EntitySuccessActio export class UnassignB2BUserUserGroup extends StateUtils.EntityLoadAction { readonly type = UNASSIGN_B2B_USER_USER_GROUP; + constructor( public payload: { userId: string; @@ -544,11 +602,12 @@ export class UnassignB2BUserUserGroup extends StateUtils.EntityLoadAction { export class UnassignB2BUserUserGroupFail extends StateUtils.EntityFailAction { readonly type = UNASSIGN_B2B_USER_USER_GROUP_FAIL; + constructor( public payload: { orgCustomerId: string; userGroupId: string; - error: any; + error: ErrorActionType; } ) { super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error); @@ -557,6 +616,7 @@ export class UnassignB2BUserUserGroupFail extends StateUtils.EntityFailAction { export class UnassignB2BUserUserGroupSuccess extends StateUtils.EntitySuccessAction { readonly type = UNASSIGN_B2B_USER_USER_GROUP_SUCCESS; + constructor( public payload: { uid: string; diff --git a/feature-libs/organization/administration/core/store/actions/budget.action.spec.ts b/feature-libs/organization/administration/core/store/actions/budget.action.spec.ts index c31df73d604..20d4eac47f7 100644 --- a/feature-libs/organization/administration/core/store/actions/budget.action.spec.ts +++ b/feature-libs/organization/administration/core/store/actions/budget.action.spec.ts @@ -8,7 +8,7 @@ const budget: Budget = { code: budgetCode, }; const userId = 'xxx@xxx.xxx'; -const error = 'anError'; +const error = { message: 'anError' }; const params = { currentPage: 2 }; const query = '?pageSize=¤tPage=2&sort='; @@ -38,6 +38,7 @@ describe('Budget Actions', () => { const action = new BudgetActions.LoadBudgetFail({ budgetCode, error }); expect({ ...action }).toEqual({ + error, type: BudgetActions.LOAD_BUDGET_FAIL, payload: { budgetCode, error }, meta: StateUtils.entityFailMeta(BUDGET_ENTITIES, budgetCode, error), @@ -108,15 +109,14 @@ describe('Budget Actions', () => { it('should create the action', () => { const action = new BudgetActions.LoadBudgetsFail({ params, - error: { error }, + error, }); expect({ ...action }).toEqual({ + error, type: BudgetActions.LOAD_BUDGETS_FAIL, - payload: { params, error: { error } }, - meta: StateUtils.entityFailMeta(BUDGET_LIST, query, { - error, - }), + payload: { params, error }, + meta: StateUtils.entityFailMeta(BUDGET_LIST, query, error), }); }); }); @@ -158,6 +158,7 @@ describe('Budget Actions', () => { }); expect({ ...action }).toEqual({ + error, type: BudgetActions.CREATE_BUDGET_FAIL, payload: { budgetCode, @@ -206,6 +207,7 @@ describe('Budget Actions', () => { }); expect({ ...action }).toEqual({ + error, type: BudgetActions.UPDATE_BUDGET_FAIL, payload: { budgetCode, diff --git a/feature-libs/organization/administration/core/store/actions/budget.action.ts b/feature-libs/organization/administration/core/store/actions/budget.action.ts index 3fdc0483ce6..6e6bc0e1348 100644 --- a/feature-libs/organization/administration/core/store/actions/budget.action.ts +++ b/feature-libs/organization/administration/core/store/actions/budget.action.ts @@ -4,7 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ListModel, SearchConfig, StateUtils } from '@spartacus/core'; +import { + ErrorActionType, + ListModel, + SearchConfig, + StateUtils, +} from '@spartacus/core'; import { Budget } from '../../model/budget.model'; import { BUDGET_ENTITIES, BUDGET_LIST } from '../organization-state'; @@ -26,6 +31,7 @@ export const UPDATE_BUDGET_SUCCESS = '[Budget] Update Budget Success'; export class LoadBudget extends StateUtils.EntityLoadAction { readonly type = LOAD_BUDGET; + constructor(public payload: { userId: string; budgetCode: string }) { super(BUDGET_ENTITIES, payload.budgetCode); } @@ -33,13 +39,15 @@ export class LoadBudget extends StateUtils.EntityLoadAction { export class LoadBudgetFail extends StateUtils.EntityFailAction { readonly type = LOAD_BUDGET_FAIL; - constructor(public payload: { budgetCode: string; error: any }) { + + constructor(public payload: { budgetCode: string; error: ErrorActionType }) { super(BUDGET_ENTITIES, payload.budgetCode, payload.error); } } export class LoadBudgetSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_BUDGET_SUCCESS; + constructor(public payload: Budget | Budget[]) { super( BUDGET_ENTITIES, @@ -52,6 +60,7 @@ export class LoadBudgetSuccess extends StateUtils.EntitySuccessAction { export class LoadBudgets extends StateUtils.EntityLoadAction { readonly type = LOAD_BUDGETS; + constructor( public payload: { userId: string; @@ -64,7 +73,10 @@ export class LoadBudgets extends StateUtils.EntityLoadAction { export class LoadBudgetsFail extends StateUtils.EntityFailAction { readonly type = LOAD_BUDGETS_FAIL; - constructor(public payload: { params: SearchConfig; error: any }) { + + constructor( + public payload: { params: SearchConfig; error: ErrorActionType } + ) { super( BUDGET_LIST, StateUtils.serializeSearchConfig(payload.params), @@ -75,6 +87,7 @@ export class LoadBudgetsFail extends StateUtils.EntityFailAction { export class LoadBudgetsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_BUDGETS_SUCCESS; + constructor( public payload: { page: ListModel; @@ -87,6 +100,7 @@ export class LoadBudgetsSuccess extends StateUtils.EntitySuccessAction { export class CreateBudget extends StateUtils.EntityLoadAction { readonly type = CREATE_BUDGET; + constructor(public payload: { userId: string; budget: Budget }) { super(BUDGET_ENTITIES, payload.budget.code ?? null); } @@ -94,13 +108,15 @@ export class CreateBudget extends StateUtils.EntityLoadAction { export class CreateBudgetFail extends StateUtils.EntityFailAction { readonly type = CREATE_BUDGET_FAIL; - constructor(public payload: { budgetCode: string; error: any }) { + + constructor(public payload: { budgetCode: string; error: ErrorActionType }) { super(BUDGET_ENTITIES, payload.budgetCode, payload.error); } } export class CreateBudgetSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_BUDGET_SUCCESS; + constructor(public payload: Budget) { super(BUDGET_ENTITIES, payload.code ?? null, payload); } @@ -108,6 +124,7 @@ export class CreateBudgetSuccess extends StateUtils.EntitySuccessAction { export class UpdateBudget extends StateUtils.EntityLoadAction { readonly type = UPDATE_BUDGET; + constructor( public payload: { userId: string; budgetCode: string; budget: Budget } ) { @@ -117,13 +134,15 @@ export class UpdateBudget extends StateUtils.EntityLoadAction { export class UpdateBudgetFail extends StateUtils.EntityFailAction { readonly type = UPDATE_BUDGET_FAIL; - constructor(public payload: { budgetCode: string; error: any }) { + + constructor(public payload: { budgetCode: string; error: ErrorActionType }) { super(BUDGET_ENTITIES, payload.budgetCode, payload.error); } } export class UpdateBudgetSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_BUDGET_SUCCESS; + constructor(public payload: Budget) { super(BUDGET_ENTITIES, payload.code ?? '', payload); } diff --git a/feature-libs/organization/administration/core/store/actions/cost-center.action.spec.ts b/feature-libs/organization/administration/core/store/actions/cost-center.action.spec.ts index a1b63c68813..98108e9bfe9 100644 --- a/feature-libs/organization/administration/core/store/actions/cost-center.action.spec.ts +++ b/feature-libs/organization/administration/core/store/actions/cost-center.action.spec.ts @@ -13,7 +13,7 @@ const costCenter: CostCenter = { code: costCenterCode, }; const userId = 'xxx@xxx.xxx'; -const error = 'anError'; +const error = { message: 'anError' }; const params = { currentPage: 2 }; const query = '?pageSize=¤tPage=2&sort='; @@ -46,6 +46,7 @@ describe('CostCenter Actions', () => { }); expect({ ...action }).toEqual({ + error, type: CostCenterActions.LOAD_COST_CENTER_FAIL, payload: { costCenterCode, error }, meta: StateUtils.entityFailMeta( @@ -127,15 +128,14 @@ describe('CostCenter Actions', () => { it('should create the action', () => { const action = new CostCenterActions.LoadCostCentersFail({ params, - error: { error }, + error, }); expect({ ...action }).toEqual({ + error, type: CostCenterActions.LOAD_COST_CENTERS_FAIL, - payload: { params, error: { error } }, - meta: StateUtils.entityFailMeta(COST_CENTER_LIST, query, { - error, - }), + payload: { params, error }, + meta: StateUtils.entityFailMeta(COST_CENTER_LIST, query, error), }); }); }); @@ -180,6 +180,7 @@ describe('CostCenter Actions', () => { }); expect({ ...action }).toEqual({ + error, type: CostCenterActions.CREATE_COST_CENTER_FAIL, payload: { costCenterCode, @@ -237,6 +238,7 @@ describe('CostCenter Actions', () => { }); expect({ ...action }).toEqual({ + error, type: CostCenterActions.UPDATE_COST_CENTER_FAIL, payload: { costCenterCode, @@ -298,6 +300,7 @@ describe('CostCenter Actions', () => { }); expect({ ...action }).toEqual({ + error, type: CostCenterActions.LOAD_ASSIGNED_BUDGETS_FAIL, payload: { costCenterCode, @@ -362,6 +365,7 @@ describe('CostCenter Actions', () => { }); expect({ ...action }).toEqual({ + error, type: CostCenterActions.ASSIGN_BUDGET_FAIL, payload: { budgetCode, @@ -413,6 +417,7 @@ describe('CostCenter Actions', () => { }); expect({ ...action }).toEqual({ + error, type: CostCenterActions.UNASSIGN_BUDGET_FAIL, payload: { budgetCode, diff --git a/feature-libs/organization/administration/core/store/actions/cost-center.action.ts b/feature-libs/organization/administration/core/store/actions/cost-center.action.ts index 9de8f004a0c..b669a9e95b6 100644 --- a/feature-libs/organization/administration/core/store/actions/cost-center.action.ts +++ b/feature-libs/organization/administration/core/store/actions/cost-center.action.ts @@ -6,6 +6,7 @@ import { CostCenter, + ErrorActionType, ListModel, SearchConfig, StateUtils, @@ -52,6 +53,7 @@ export const UNASSIGN_BUDGET_FAIL = '[CostCenter] Unassign Budget fail'; export class LoadCostCenter extends StateUtils.EntityLoadAction { readonly type = LOAD_COST_CENTER; + constructor(public payload: { userId: string; costCenterCode: string }) { super(COST_CENTER_ENTITIES, payload.costCenterCode); } @@ -59,13 +61,17 @@ export class LoadCostCenter extends StateUtils.EntityLoadAction { export class LoadCostCenterFail extends StateUtils.EntityFailAction { readonly type = LOAD_COST_CENTER_FAIL; - constructor(public payload: { costCenterCode: string; error: any }) { + + constructor( + public payload: { costCenterCode: string; error: ErrorActionType } + ) { super(COST_CENTER_ENTITIES, payload.costCenterCode, payload.error); } } export class LoadCostCenterSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_COST_CENTER_SUCCESS; + constructor(public payload: CostCenter | CostCenter[]) { super( COST_CENTER_ENTITIES, @@ -78,6 +84,7 @@ export class LoadCostCenterSuccess extends StateUtils.EntitySuccessAction { export class LoadCostCenters extends StateUtils.EntityLoadAction { readonly type = LOAD_COST_CENTERS; + constructor( public payload: { userId: string; @@ -90,7 +97,10 @@ export class LoadCostCenters extends StateUtils.EntityLoadAction { export class LoadCostCentersFail extends StateUtils.EntityFailAction { readonly type = LOAD_COST_CENTERS_FAIL; - constructor(public payload: { params: SearchConfig; error: any }) { + + constructor( + public payload: { params: SearchConfig; error: ErrorActionType } + ) { super( COST_CENTER_LIST, StateUtils.serializeSearchConfig(payload.params), @@ -101,6 +111,7 @@ export class LoadCostCentersFail extends StateUtils.EntityFailAction { export class LoadCostCentersSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_COST_CENTERS_SUCCESS; + constructor( public payload: { page: ListModel; @@ -113,6 +124,7 @@ export class LoadCostCentersSuccess extends StateUtils.EntitySuccessAction { export class CreateCostCenter extends StateUtils.EntityLoadAction { readonly type = CREATE_COST_CENTER; + constructor(public payload: { userId: string; costCenter: CostCenter }) { super(COST_CENTER_ENTITIES, payload.costCenter.code ?? null); } @@ -120,13 +132,17 @@ export class CreateCostCenter extends StateUtils.EntityLoadAction { export class CreateCostCenterFail extends StateUtils.EntityFailAction { readonly type = CREATE_COST_CENTER_FAIL; - constructor(public payload: { costCenterCode: string; error: any }) { + + constructor( + public payload: { costCenterCode: string; error: ErrorActionType } + ) { super(COST_CENTER_ENTITIES, payload.costCenterCode, payload.error); } } export class CreateCostCenterSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_COST_CENTER_SUCCESS; + constructor(public payload: CostCenter) { super(COST_CENTER_ENTITIES, payload.code ?? null, payload); } @@ -134,6 +150,7 @@ export class CreateCostCenterSuccess extends StateUtils.EntitySuccessAction { export class UpdateCostCenter extends StateUtils.EntityLoadAction { readonly type = UPDATE_COST_CENTER; + constructor( public payload: { userId: string; @@ -147,13 +164,17 @@ export class UpdateCostCenter extends StateUtils.EntityLoadAction { export class UpdateCostCenterFail extends StateUtils.EntityFailAction { readonly type = UPDATE_COST_CENTER_FAIL; - constructor(public payload: { costCenterCode: string; error: any }) { + + constructor( + public payload: { costCenterCode: string; error: ErrorActionType } + ) { super(COST_CENTER_ENTITIES, payload.costCenterCode, payload.error); } } export class UpdateCostCenterSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_COST_CENTER_SUCCESS; + constructor(public payload: CostCenter) { super(COST_CENTER_ENTITIES, payload.code ?? '', payload); } @@ -161,6 +182,7 @@ export class UpdateCostCenterSuccess extends StateUtils.EntitySuccessAction { export class LoadAssignedBudgets extends StateUtils.EntityLoadAction { readonly type = LOAD_ASSIGNED_BUDGETS; + constructor( public payload: { userId: string; @@ -177,11 +199,12 @@ export class LoadAssignedBudgets extends StateUtils.EntityLoadAction { export class LoadAssignedBudgetsFail extends StateUtils.EntityFailAction { readonly type = LOAD_ASSIGNED_BUDGETS_FAIL; + constructor( public payload: { costCenterCode: string; params: SearchConfig; - error: any; + error: ErrorActionType; } ) { super( @@ -194,6 +217,7 @@ export class LoadAssignedBudgetsFail extends StateUtils.EntityFailAction { export class LoadAssignedBudgetsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_ASSIGNED_BUDGETS_SUCCESS; + constructor( public payload: { costCenterCode: string; @@ -210,6 +234,7 @@ export class LoadAssignedBudgetsSuccess extends StateUtils.EntitySuccessAction { export class AssignBudget extends StateUtils.EntityLoadAction { readonly type = ASSIGN_BUDGET; + constructor( public payload: { userId: string; @@ -223,13 +248,15 @@ export class AssignBudget extends StateUtils.EntityLoadAction { export class AssignBudgetFail extends StateUtils.EntityFailAction { readonly type = ASSIGN_BUDGET_FAIL; - constructor(public payload: { budgetCode: string; error: any }) { + + constructor(public payload: { budgetCode: string; error: ErrorActionType }) { super(BUDGET_ENTITIES, payload.budgetCode, payload.error); } } export class AssignBudgetSuccess extends StateUtils.EntitySuccessAction { readonly type = ASSIGN_BUDGET_SUCCESS; + constructor(public payload: { code: string; selected: boolean }) { super(BUDGET_ENTITIES, payload.code, payload); } @@ -237,6 +264,7 @@ export class AssignBudgetSuccess extends StateUtils.EntitySuccessAction { export class UnassignBudget extends StateUtils.EntityLoadAction { readonly type = UNASSIGN_BUDGET; + constructor( public payload: { userId: string; @@ -250,13 +278,15 @@ export class UnassignBudget extends StateUtils.EntityLoadAction { export class UnassignBudgetFail extends StateUtils.EntityFailAction { readonly type = UNASSIGN_BUDGET_FAIL; - constructor(public payload: { budgetCode: string; error: any }) { + + constructor(public payload: { budgetCode: string; error: ErrorActionType }) { super(BUDGET_ENTITIES, payload.budgetCode, payload.error); } } export class UnassignBudgetSuccess extends StateUtils.EntitySuccessAction { readonly type = UNASSIGN_BUDGET_SUCCESS; + constructor(public payload: { code: string; selected: boolean }) { super(BUDGET_ENTITIES, payload.code, payload); } diff --git a/feature-libs/organization/administration/core/store/actions/org-unit.action.spec.ts b/feature-libs/organization/administration/core/store/actions/org-unit.action.spec.ts index e84816507ff..f5465e8253c 100644 --- a/feature-libs/organization/administration/core/store/actions/org-unit.action.spec.ts +++ b/feature-libs/organization/administration/core/store/actions/org-unit.action.spec.ts @@ -50,7 +50,7 @@ const unit: B2BUnit = { uid: 'testUid' }; const unitCode: string = unit.uid; const userId = 'xxx@xxx.xxx'; -const error = 'anError'; +const error = { message: 'anError' }; describe('OrgUnit Actions', () => { describe('LoadOrgUnit Actions', () => { @@ -74,6 +74,7 @@ describe('OrgUnit Actions', () => { const action = new OrgUnitActions.LoadOrgUnitFail({ orgUnitId, error }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.LOAD_ORG_UNIT_FAIL, payload: { orgUnitId, error }, meta: StateUtils.entityFailMeta(ORG_UNIT_ENTITIES, orgUnitId, error), @@ -142,15 +143,18 @@ describe('OrgUnit Actions', () => { describe('LoadOrgUnitNodesFail', () => { it('should create the action', () => { const action = new OrgUnitActions.LoadOrgUnitNodesFail({ - error: { error }, + error, }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.LOAD_UNIT_NODES_FAIL, - payload: { error: { error } }, - meta: StateUtils.entityFailMeta(ORG_UNIT_NODE_LIST, ORG_UNIT_NODES, { - error, - }), + payload: { error }, + meta: StateUtils.entityFailMeta( + ORG_UNIT_NODE_LIST, + ORG_UNIT_NODES, + error + ), }); }); }); @@ -191,6 +195,7 @@ describe('OrgUnit Actions', () => { const action = new OrgUnitActions.LoadApprovalProcessesFail({ error }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.LOAD_APPROVAL_PROCESSES_FAIL, payload: { error }, meta: StateUtils.entityFailMeta( @@ -238,6 +243,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.CREATE_ORG_UNIT_FAIL, payload: { unitCode, error }, meta: StateUtils.entityFailMeta(ORG_UNIT_ENTITIES, unitCode, error), @@ -277,6 +283,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.UPDATE_ORG_UNIT_FAIL, payload: { unitCode, error }, meta: StateUtils.entityFailMeta(ORG_UNIT_ENTITIES, unitCode, error), @@ -316,6 +323,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.CREATE_ADDRESS_FAIL, payload: { addressId, error }, meta: StateUtils.entityFailMeta(ADDRESS_ENTITIES, addressId, error), @@ -356,6 +364,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.UPDATE_ADDRESS_FAIL, payload: { addressId, error }, meta: StateUtils.entityFailMeta(ADDRESS_ENTITIES, addressId, error), @@ -395,6 +404,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.DELETE_ADDRESS_FAIL, payload: { addressId, error }, meta: StateUtils.entityFailMeta(ADDRESS_ENTITIES, addressId, error), @@ -433,6 +443,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.LOAD_ADDRESSES_FAIL, payload: { orgUnitId, error }, meta: StateUtils.entityFailMeta(ADDRESS_LIST, orgUnitId, error), @@ -485,6 +496,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.ASSIGN_ROLE_FAIL, payload: { orgCustomerId, error }, meta: StateUtils.entityFailMeta( @@ -532,6 +544,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.UNASSIGN_ROLE_FAIL, payload: { orgCustomerId, error }, meta: StateUtils.entityFailMeta( @@ -576,6 +589,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.LOAD_UNIT_TREE_FAIL, payload: { error }, meta: StateUtils.entityFailMeta( @@ -621,6 +635,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.ASSIGN_APPROVER_FAIL, payload: { orgCustomerId, error }, meta: StateUtils.entityFailMeta( @@ -669,6 +684,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.UNASSIGN_APPROVER_FAIL, payload: { orgCustomerId, error }, meta: StateUtils.entityFailMeta( @@ -722,6 +738,7 @@ describe('OrgUnit Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrgUnitActions.LOAD_ASSIGNED_USERS_FAIL, payload: { orgUnitId, roleId, params, error }, meta: StateUtils.entityFailMeta( diff --git a/feature-libs/organization/administration/core/store/actions/org-unit.action.ts b/feature-libs/organization/administration/core/store/actions/org-unit.action.ts index 6853108f86b..18a31ebed55 100644 --- a/feature-libs/organization/administration/core/store/actions/org-unit.action.ts +++ b/feature-libs/organization/administration/core/store/actions/org-unit.action.ts @@ -8,6 +8,7 @@ import { Address, B2BApprovalProcess, B2BUnit, + ErrorActionType, ListModel, SearchConfig, StateUtils, @@ -21,8 +22,8 @@ import { ORG_UNIT_APPROVAL_PROCESSES_ENTITIES, ORG_UNIT_ASSIGNED_USERS, ORG_UNIT_ENTITIES, - ORG_UNIT_NODES, ORG_UNIT_NODE_LIST, + ORG_UNIT_NODES, ORG_UNIT_TREE, ORG_UNIT_TREE_ENTITY, } from '../organization-state'; @@ -102,6 +103,7 @@ export const CLEAR_ASSIGNED_USERS = '[B2BUnit] Clear Assigned Users'; export class LoadOrgUnit extends StateUtils.EntityLoadAction { readonly type = LOAD_ORG_UNIT; + constructor(public payload: { userId: string; orgUnitId: string }) { super(ORG_UNIT_ENTITIES, payload.orgUnitId); } @@ -109,7 +111,8 @@ export class LoadOrgUnit extends StateUtils.EntityLoadAction { export class LoadOrgUnitFail extends StateUtils.EntityFailAction { readonly type = LOAD_ORG_UNIT_FAIL; - constructor(public payload: { orgUnitId: string; error: any }) { + + constructor(public payload: { orgUnitId: string; error: ErrorActionType }) { super(ORG_UNIT_ENTITIES, payload.orgUnitId, payload.error); } } @@ -129,6 +132,7 @@ export class LoadOrgUnitSuccess extends StateUtils.EntitySuccessAction { export class LoadOrgUnitNodes extends StateUtils.EntityLoadAction { readonly type = LOAD_UNIT_NODES; + constructor( public payload: { userId: string; @@ -140,6 +144,7 @@ export class LoadOrgUnitNodes extends StateUtils.EntityLoadAction { export class LoadOrgUnitNodesFail extends StateUtils.EntityFailAction { readonly type = LOAD_UNIT_NODES_FAIL; + constructor(public payload: any) { super(ORG_UNIT_NODE_LIST, ORG_UNIT_NODES, payload.error); } @@ -147,6 +152,7 @@ export class LoadOrgUnitNodesFail extends StateUtils.EntityFailAction { export class LoadOrgUnitNodesSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_UNIT_NODES_SUCCESS; + constructor(public payload: B2BUnitNode[]) { super(ORG_UNIT_NODE_LIST, ORG_UNIT_NODES); } @@ -154,6 +160,7 @@ export class LoadOrgUnitNodesSuccess extends StateUtils.EntitySuccessAction { export class CreateUnit extends StateUtils.EntityLoadAction { readonly type = CREATE_ORG_UNIT; + constructor(public payload: { userId: string; unit: B2BUnit }) { super(ORG_UNIT_ENTITIES, payload.unit.uid ?? null); } @@ -161,13 +168,15 @@ export class CreateUnit extends StateUtils.EntityLoadAction { export class CreateUnitFail extends StateUtils.EntityFailAction { readonly type = CREATE_ORG_UNIT_FAIL; - constructor(public payload: { unitCode: string; error: any }) { + + constructor(public payload: { unitCode: string; error: ErrorActionType }) { super(ORG_UNIT_ENTITIES, payload.unitCode, payload.error); } } export class CreateUnitSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_ORG_UNIT_SUCCESS; + constructor(public payload: B2BUnit) { super(ORG_UNIT_ENTITIES, payload.uid ?? null, payload); } @@ -175,6 +184,7 @@ export class CreateUnitSuccess extends StateUtils.EntitySuccessAction { export class UpdateUnit extends StateUtils.EntityLoadAction { readonly type = UPDATE_ORG_UNIT; + constructor( public payload: { userId: string; unitCode: string; unit: B2BUnit } ) { @@ -184,13 +194,15 @@ export class UpdateUnit extends StateUtils.EntityLoadAction { export class UpdateUnitFail extends StateUtils.EntityFailAction { readonly type = UPDATE_ORG_UNIT_FAIL; - constructor(public payload: { unitCode: string; error: any }) { + + constructor(public payload: { unitCode: string; error: ErrorActionType }) { super(ORG_UNIT_ENTITIES, payload.unitCode, payload.error); } } export class UpdateUnitSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_ORG_UNIT_SUCCESS; + constructor(public payload: B2BUnit) { super(ORG_UNIT_ENTITIES, payload.uid ?? '', payload); } @@ -198,6 +210,7 @@ export class UpdateUnitSuccess extends StateUtils.EntitySuccessAction { export class LoadTree extends StateUtils.EntityLoadAction { readonly type = LOAD_UNIT_TREE; + constructor(public payload: { userId: string }) { super(ORG_UNIT_TREE_ENTITY, ORG_UNIT_TREE); } @@ -205,7 +218,8 @@ export class LoadTree extends StateUtils.EntityLoadAction { export class LoadTreeFail extends StateUtils.EntityFailAction { readonly type = LOAD_UNIT_TREE_FAIL; - constructor(public payload: { error: any }) { + + constructor(public payload: { error: ErrorActionType }) { super(ORG_UNIT_TREE_ENTITY, ORG_UNIT_TREE, payload.error); } } @@ -220,6 +234,7 @@ export class LoadTreeSuccess extends StateUtils.EntitySuccessAction { export class LoadApprovalProcesses extends StateUtils.EntityLoadAction { readonly type = LOAD_APPROVAL_PROCESSES; + constructor(public payload: { userId: string }) { super(ORG_UNIT_APPROVAL_PROCESSES_ENTITIES, ORG_UNIT_APPROVAL_PROCESSES); } @@ -227,7 +242,8 @@ export class LoadApprovalProcesses extends StateUtils.EntityLoadAction { export class LoadApprovalProcessesFail extends StateUtils.EntityFailAction { readonly type = LOAD_APPROVAL_PROCESSES_FAIL; - constructor(public payload: { error: any }) { + + constructor(public payload: { error: ErrorActionType }) { super( ORG_UNIT_APPROVAL_PROCESSES_ENTITIES, ORG_UNIT_APPROVAL_PROCESSES, @@ -246,6 +262,7 @@ export class LoadApprovalProcessesSuccess extends StateUtils.EntitySuccessAction export class LoadAssignedUsers extends StateUtils.EntityLoadAction { readonly type = LOAD_ASSIGNED_USERS; + constructor( public payload: { userId: string; @@ -266,6 +283,7 @@ export class LoadAssignedUsers extends StateUtils.EntityLoadAction { export class ClearAssignedUsers extends StateUtils.EntityRemoveAction { readonly type = CLEAR_ASSIGNED_USERS; + constructor( public payload: { orgUnitId: string; @@ -285,12 +303,13 @@ export class ClearAssignedUsers extends StateUtils.EntityRemoveAction { export class LoadAssignedUsersFail extends StateUtils.EntityFailAction { readonly type = LOAD_ASSIGNED_USERS_FAIL; + constructor( public payload: { orgUnitId: string; roleId: string; params: SearchConfig; - error: any; + error: ErrorActionType; } ) { super( @@ -306,6 +325,7 @@ export class LoadAssignedUsersFail extends StateUtils.EntityFailAction { export class LoadAssignedUsersSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_ASSIGNED_USERS_SUCCESS; + constructor( public payload: { orgUnitId: string; @@ -326,6 +346,7 @@ export class LoadAssignedUsersSuccess extends StateUtils.EntitySuccessAction { export class AssignRole extends StateUtils.EntityLoadAction { readonly type = ASSIGN_ROLE; + constructor( public payload: { userId: string; @@ -339,10 +360,11 @@ export class AssignRole extends StateUtils.EntityLoadAction { export class AssignRoleFail extends StateUtils.EntityFailAction { readonly type = ASSIGN_ROLE_FAIL; + constructor( public payload: { orgCustomerId: string; - error: any; + error: ErrorActionType; } ) { super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error); @@ -351,6 +373,7 @@ export class AssignRoleFail extends StateUtils.EntityFailAction { export class AssignRoleSuccess extends StateUtils.EntitySuccessAction { readonly type = ASSIGN_ROLE_SUCCESS; + constructor( public payload: { uid: string; roleId: string; selected: boolean } ) { @@ -360,6 +383,7 @@ export class AssignRoleSuccess extends StateUtils.EntitySuccessAction { export class UnassignRole extends StateUtils.EntityLoadAction { readonly type = UNASSIGN_ROLE; + constructor( public payload: { userId: string; @@ -373,10 +397,11 @@ export class UnassignRole extends StateUtils.EntityLoadAction { export class UnassignRoleFail extends StateUtils.EntityFailAction { readonly type = UNASSIGN_ROLE_FAIL; + constructor( public payload: { orgCustomerId: string; - error: any; + error: ErrorActionType; } ) { super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error); @@ -385,6 +410,7 @@ export class UnassignRoleFail extends StateUtils.EntityFailAction { export class UnassignRoleSuccess extends StateUtils.EntitySuccessAction { readonly type = UNASSIGN_ROLE_SUCCESS; + constructor( public payload: { uid: string; roleId: string; selected: boolean } ) { @@ -394,6 +420,7 @@ export class UnassignRoleSuccess extends StateUtils.EntitySuccessAction { export class AssignApprover extends StateUtils.EntityLoadAction { readonly type = ASSIGN_APPROVER; + constructor( public payload: { userId: string; @@ -408,10 +435,11 @@ export class AssignApprover extends StateUtils.EntityLoadAction { export class AssignApproverFail extends StateUtils.EntityFailAction { readonly type = ASSIGN_APPROVER_FAIL; + constructor( public payload: { orgCustomerId: string; - error: any; + error: ErrorActionType; } ) { super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error); @@ -420,6 +448,7 @@ export class AssignApproverFail extends StateUtils.EntityFailAction { export class AssignApproverSuccess extends StateUtils.EntitySuccessAction { readonly type = ASSIGN_APPROVER_SUCCESS; + constructor( public payload: { uid: string; roleId: string; selected: boolean } ) { @@ -429,6 +458,7 @@ export class AssignApproverSuccess extends StateUtils.EntitySuccessAction { export class UnassignApprover extends StateUtils.EntityLoadAction { readonly type = UNASSIGN_APPROVER; + constructor( public payload: { userId: string; @@ -443,10 +473,11 @@ export class UnassignApprover extends StateUtils.EntityLoadAction { export class UnassignApproverFail extends StateUtils.EntityFailAction { readonly type = UNASSIGN_APPROVER_FAIL; + constructor( public payload: { orgCustomerId: string; - error: any; + error: ErrorActionType; } ) { super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error); @@ -455,6 +486,7 @@ export class UnassignApproverFail extends StateUtils.EntityFailAction { export class UnassignApproverSuccess extends StateUtils.EntitySuccessAction { readonly type = UNASSIGN_APPROVER_SUCCESS; + constructor( public payload: { uid: string; roleId: string; selected: boolean } ) { @@ -464,6 +496,7 @@ export class UnassignApproverSuccess extends StateUtils.EntitySuccessAction { export class CreateAddress extends StateUtils.EntityLoadAction { readonly type = CREATE_ADDRESS; + constructor( public payload: { userId: string; orgUnitId: string; address: Address } ) { @@ -473,13 +506,15 @@ export class CreateAddress extends StateUtils.EntityLoadAction { export class CreateAddressFail extends StateUtils.EntityFailAction { readonly type = CREATE_ADDRESS_FAIL; - constructor(public payload: { addressId: string; error: any }) { + + constructor(public payload: { addressId: string; error: ErrorActionType }) { super(ADDRESS_ENTITIES, payload.addressId, payload.error); } } export class CreateAddressSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_ADDRESS_SUCCESS; + constructor(public payload: Address) { super(ADDRESS_ENTITIES, payload.id ?? null, payload); } @@ -487,6 +522,7 @@ export class CreateAddressSuccess extends StateUtils.EntitySuccessAction { export class UpdateAddress extends StateUtils.EntityLoadAction { readonly type = UPDATE_ADDRESS; + constructor( public payload: { userId: string; @@ -501,13 +537,15 @@ export class UpdateAddress extends StateUtils.EntityLoadAction { export class UpdateAddressFail extends StateUtils.EntityFailAction { readonly type = UPDATE_ADDRESS_FAIL; - constructor(public payload: { addressId: string; error: any }) { + + constructor(public payload: { addressId: string; error: ErrorActionType }) { super(ADDRESS_ENTITIES, payload.addressId, payload.error); } } export class UpdateAddressSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_ADDRESS_SUCCESS; + constructor(public payload: Address) { super(ADDRESS_ENTITIES, payload.id ?? '', payload); } @@ -515,6 +553,7 @@ export class UpdateAddressSuccess extends StateUtils.EntitySuccessAction { export class DeleteAddress extends StateUtils.EntityLoadAction { readonly type = DELETE_ADDRESS; + constructor( public payload: { userId: string; @@ -528,13 +567,15 @@ export class DeleteAddress extends StateUtils.EntityLoadAction { export class DeleteAddressFail extends StateUtils.EntityFailAction { readonly type = DELETE_ADDRESS_FAIL; - constructor(public payload: { addressId: string; error: any }) { + + constructor(public payload: { addressId: string; error: ErrorActionType }) { super(ADDRESS_ENTITIES, payload.addressId, payload.error); } } export class DeleteAddressSuccess extends StateUtils.EntityRemoveAction { readonly type = DELETE_ADDRESS_SUCCESS; + constructor(public payload: Address) { super(ADDRESS_ENTITIES, payload.id ?? ''); } @@ -542,6 +583,7 @@ export class DeleteAddressSuccess extends StateUtils.EntityRemoveAction { export class LoadAddressSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_ADDRESS_SUCCESS; + constructor(public payload: Address | Address[]) { super( ADDRESS_ENTITIES, @@ -554,6 +596,7 @@ export class LoadAddressSuccess extends StateUtils.EntitySuccessAction { export class LoadAddresses extends StateUtils.EntityLoadAction { readonly type = LOAD_ADDRESSES; + constructor(public payload: { userId: string; orgUnitId: string }) { super(ADDRESS_LIST, payload.orgUnitId); } @@ -561,13 +604,15 @@ export class LoadAddresses extends StateUtils.EntityLoadAction { export class LoadAddressesFail extends StateUtils.EntityFailAction { readonly type = LOAD_ADDRESSES_FAIL; - constructor(public payload: { orgUnitId: string; error: any }) { + + constructor(public payload: { orgUnitId: string; error: ErrorActionType }) { super(ADDRESS_LIST, payload.orgUnitId, payload.error); } } export class LoadAddressesSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_ADDRESSES_SUCCESS; + constructor( public payload: { page: ListModel; diff --git a/feature-libs/organization/administration/core/store/actions/permission.action.spec.ts b/feature-libs/organization/administration/core/store/actions/permission.action.spec.ts index 399948d7ce9..9c8e7913acc 100644 --- a/feature-libs/organization/administration/core/store/actions/permission.action.spec.ts +++ b/feature-libs/organization/administration/core/store/actions/permission.action.spec.ts @@ -20,7 +20,7 @@ const permissionType: OrderApprovalPermissionType = { const permissionTypes: OrderApprovalPermissionType[] = [permissionType]; const userId = 'xxx@xxx.xxx'; -const error = 'anError'; +const error = { message: 'anError' }; const params = { currentPage: 2 }; const query = '?pageSize=¤tPage=2&sort='; @@ -53,6 +53,7 @@ describe('Permission Actions', () => { }); expect({ ...action }).toEqual({ + error, type: PermissionActions.LOAD_PERMISSION_FAIL, payload: { permissionCode, @@ -137,15 +138,14 @@ describe('Permission Actions', () => { it('should create the action', () => { const action = new PermissionActions.LoadPermissionsFail({ params, - error: { error }, + error, }); expect({ ...action }).toEqual({ + error, type: PermissionActions.LOAD_PERMISSIONS_FAIL, - payload: { params, error: { error } }, - meta: StateUtils.entityFailMeta(PERMISSION_LIST, query, { - error, - }), + payload: { params, error }, + meta: StateUtils.entityFailMeta(PERMISSION_LIST, query, error), }); }); }); @@ -190,6 +190,7 @@ describe('Permission Actions', () => { }); expect({ ...action }).toEqual({ + error, type: PermissionActions.CREATE_PERMISSION_FAIL, payload: { permissionCode, @@ -247,6 +248,7 @@ describe('Permission Actions', () => { }); expect({ ...action }).toEqual({ + error, type: PermissionActions.UPDATE_PERMISSION_FAIL, payload: { permissionCode, @@ -302,6 +304,7 @@ describe('Permission Actions', () => { }); expect({ ...action }).toEqual({ + error, type: PermissionActions.LOAD_PERMISSION_TYPES_FAIL, payload: { permissionCode, diff --git a/feature-libs/organization/administration/core/store/actions/permission.action.ts b/feature-libs/organization/administration/core/store/actions/permission.action.ts index ada1f3366e2..336d9bf35db 100644 --- a/feature-libs/organization/administration/core/store/actions/permission.action.ts +++ b/feature-libs/organization/administration/core/store/actions/permission.action.ts @@ -5,6 +5,7 @@ */ import { + ErrorActionType, ListModel, OrderApprovalPermissionType, SearchConfig, @@ -45,6 +46,7 @@ export const LOAD_PERMISSION_TYPES_SUCCESS = export class LoadPermission extends StateUtils.EntityLoadAction { readonly type = LOAD_PERMISSION; + constructor(public payload: { userId: string; permissionCode: string }) { super(PERMISSION_ENTITIES, payload.permissionCode); } @@ -52,13 +54,17 @@ export class LoadPermission extends StateUtils.EntityLoadAction { export class LoadPermissionFail extends StateUtils.EntityFailAction { readonly type = LOAD_PERMISSION_FAIL; - constructor(public payload: { permissionCode: string; error: any }) { + + constructor( + public payload: { permissionCode: string; error: ErrorActionType } + ) { super(PERMISSION_ENTITIES, payload.permissionCode, payload.error); } } export class LoadPermissionSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_PERMISSION_SUCCESS; + constructor(public payload: Permission | Permission[]) { super( PERMISSION_ENTITIES, @@ -71,6 +77,7 @@ export class LoadPermissionSuccess extends StateUtils.EntitySuccessAction { export class LoadPermissions extends StateUtils.EntityLoadAction { readonly type = LOAD_PERMISSIONS; + constructor( public payload: { userId: string; @@ -83,7 +90,10 @@ export class LoadPermissions extends StateUtils.EntityLoadAction { export class LoadPermissionsFail extends StateUtils.EntityFailAction { readonly type = LOAD_PERMISSIONS_FAIL; - constructor(public payload: { params: SearchConfig; error: any }) { + + constructor( + public payload: { params: SearchConfig; error: ErrorActionType } + ) { super( PERMISSION_LIST, StateUtils.serializeSearchConfig(payload.params), @@ -94,6 +104,7 @@ export class LoadPermissionsFail extends StateUtils.EntityFailAction { export class LoadPermissionsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_PERMISSIONS_SUCCESS; + constructor( public payload: { page: ListModel; @@ -106,6 +117,7 @@ export class LoadPermissionsSuccess extends StateUtils.EntitySuccessAction { export class CreatePermission extends StateUtils.EntityLoadAction { readonly type = CREATE_PERMISSION; + constructor(public payload: { userId: string; permission: Permission }) { super(PERMISSION_ENTITIES, payload.permission.code ?? null); } @@ -113,13 +125,17 @@ export class CreatePermission extends StateUtils.EntityLoadAction { export class CreatePermissionFail extends StateUtils.EntityFailAction { readonly type = CREATE_PERMISSION_FAIL; - constructor(public payload: { permissionCode: string; error: any }) { + + constructor( + public payload: { permissionCode: string; error: ErrorActionType } + ) { super(PERMISSION_ENTITIES, payload.permissionCode, payload.error); } } export class CreatePermissionSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_PERMISSION_SUCCESS; + constructor(public payload: Permission) { super(PERMISSION_ENTITIES, payload.code ?? null, payload); } @@ -127,6 +143,7 @@ export class CreatePermissionSuccess extends StateUtils.EntitySuccessAction { export class UpdatePermission extends StateUtils.EntityLoadAction { readonly type = UPDATE_PERMISSION; + constructor( public payload: { userId: string; @@ -140,13 +157,17 @@ export class UpdatePermission extends StateUtils.EntityLoadAction { export class UpdatePermissionFail extends StateUtils.EntityFailAction { readonly type = UPDATE_PERMISSION_FAIL; - constructor(public payload: { permissionCode: string; error: any }) { + + constructor( + public payload: { permissionCode: string; error: ErrorActionType } + ) { super(PERMISSION_ENTITIES, payload.permissionCode, payload.error); } } export class UpdatePermissionSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_PERMISSION_SUCCESS; + constructor(public payload: Permission) { super(PERMISSION_ENTITIES, payload.code ?? '', payload); } @@ -154,6 +175,7 @@ export class UpdatePermissionSuccess extends StateUtils.EntitySuccessAction { export class LoadPermissionTypes extends StateUtils.EntityLoadAction { readonly type = LOAD_PERMISSION_TYPES; + constructor() { super(PERMISSION_TYPES_LIST, PERMISSION_TYPES); } @@ -161,6 +183,7 @@ export class LoadPermissionTypes extends StateUtils.EntityLoadAction { export class LoadPermissionTypesFail extends StateUtils.EntityFailAction { readonly type = LOAD_PERMISSION_TYPES_FAIL; + constructor(public payload: any) { super(PERMISSION_TYPES_LIST, PERMISSION_TYPES, payload.error); } @@ -168,6 +191,7 @@ export class LoadPermissionTypesFail extends StateUtils.EntityFailAction { export class LoadPermissionTypesSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_PERMISSION_TYPES_SUCCESS; + constructor(public payload: OrderApprovalPermissionType[]) { super(PERMISSION_TYPES_LIST, PERMISSION_TYPES); } diff --git a/feature-libs/organization/administration/core/store/actions/user-group.action.spec.ts b/feature-libs/organization/administration/core/store/actions/user-group.action.spec.ts index 23ec79dbb55..ad79fbac77f 100644 --- a/feature-libs/organization/administration/core/store/actions/user-group.action.spec.ts +++ b/feature-libs/organization/administration/core/store/actions/user-group.action.spec.ts @@ -17,7 +17,7 @@ const userGroup: UserGroup = { uid: userGroupId, }; const userId = 'xxx@xxx.xxx'; -const error = 'anError'; +const error = { message: 'anError' }; const params = { currentPage: 2 }; const query = '?pageSize=¤tPage=2&sort='; @@ -50,6 +50,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.LOAD_USER_GROUP_FAIL, payload: { userGroupId, error }, meta: StateUtils.entityFailMeta( @@ -126,15 +127,14 @@ describe('UserGroup Actions', () => { it('should create the action', () => { const action = new UserGroupActions.LoadUserGroupsFail({ params, - error: { error }, + error, }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.LOAD_USER_GROUPS_FAIL, - payload: { params, error: { error } }, - meta: StateUtils.entityFailMeta(USER_GROUP_LIST, query, { - error, - }), + payload: { params, error }, + meta: StateUtils.entityFailMeta(USER_GROUP_LIST, query, error), }); }); }); @@ -179,6 +179,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.CREATE_USER_GROUP_FAIL, payload: { userGroupId, @@ -231,6 +232,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.UPDATE_USER_GROUP_FAIL, payload: { userGroupId, @@ -282,6 +284,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.DELETE_USER_GROUP_FAIL, payload: { userGroupId, @@ -338,6 +341,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.LOAD_USER_GROUP_PERMISSIONS_FAIL, payload: { userGroupId, @@ -403,6 +407,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.USER_GROUP_ASSIGN_PERMISSION_FAIL, payload: { userGroupId, @@ -463,6 +468,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.USER_GROUP_UNASSIGN_PERMISSION_FAIL, payload: { userGroupId, @@ -526,6 +532,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.LOAD_USER_GROUP_AVAILABLE_CUSTOMERS_FAIL, payload: { userGroupId, @@ -591,6 +598,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.USER_GROUP_ASSIGN_MEMBER_FAIL, payload: { userGroupId, @@ -644,6 +652,7 @@ describe('UserGroup Actions', () => { }); expect({ ...action }).toEqual({ + error, type: UserGroupActions.USER_GROUP_UNASSIGN_MEMBER_FAIL, payload: { userGroupId, diff --git a/feature-libs/organization/administration/core/store/actions/user-group.action.ts b/feature-libs/organization/administration/core/store/actions/user-group.action.ts index e59758581d3..dbe689a517f 100644 --- a/feature-libs/organization/administration/core/store/actions/user-group.action.ts +++ b/feature-libs/organization/administration/core/store/actions/user-group.action.ts @@ -4,7 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ListModel, SearchConfig, StateUtils } from '@spartacus/core'; +import { + ErrorActionType, + ListModel, + SearchConfig, + StateUtils, +} from '@spartacus/core'; import { UserGroup } from '../../model/user-group.model'; import { B2B_USER_ENTITIES, @@ -80,6 +85,7 @@ export const USER_GROUP_UNASSIGN_PERMISSION_SUCCESS = export class LoadUserGroup extends StateUtils.EntityLoadAction { readonly type = LOAD_USER_GROUP; + constructor(public payload: { userId: string; userGroupId: string }) { super(USER_GROUP_ENTITIES, payload.userGroupId); } @@ -87,13 +93,15 @@ export class LoadUserGroup extends StateUtils.EntityLoadAction { export class LoadUserGroupFail extends StateUtils.EntityFailAction { readonly type = LOAD_USER_GROUP_FAIL; - constructor(public payload: { userGroupId: string; error: any }) { + + constructor(public payload: { userGroupId: string; error: ErrorActionType }) { super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error); } } export class LoadUserGroupSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_USER_GROUP_SUCCESS; + constructor(public payload: UserGroup | UserGroup[]) { super( USER_GROUP_ENTITIES, @@ -106,6 +114,7 @@ export class LoadUserGroupSuccess extends StateUtils.EntitySuccessAction { export class LoadUserGroups extends StateUtils.EntityLoadAction { readonly type = LOAD_USER_GROUPS; + constructor( public payload: { userId: string; @@ -118,7 +127,10 @@ export class LoadUserGroups extends StateUtils.EntityLoadAction { export class LoadUserGroupsFail extends StateUtils.EntityFailAction { readonly type = LOAD_USER_GROUPS_FAIL; - constructor(public payload: { params: SearchConfig; error: any }) { + + constructor( + public payload: { params: SearchConfig; error: ErrorActionType } + ) { super( USER_GROUP_LIST, StateUtils.serializeSearchConfig(payload.params), @@ -129,6 +141,7 @@ export class LoadUserGroupsFail extends StateUtils.EntityFailAction { export class LoadUserGroupsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_USER_GROUPS_SUCCESS; + constructor( public payload: { page: ListModel; @@ -141,6 +154,7 @@ export class LoadUserGroupsSuccess extends StateUtils.EntitySuccessAction { export class LoadPermissions extends StateUtils.EntityLoadAction { readonly type = LOAD_USER_GROUP_PERMISSIONS; + constructor( public payload: { userId: string; @@ -157,11 +171,12 @@ export class LoadPermissions extends StateUtils.EntityLoadAction { export class LoadPermissionsFail extends StateUtils.EntityFailAction { readonly type = LOAD_USER_GROUP_PERMISSIONS_FAIL; + constructor( public payload: { userGroupId: string; params: SearchConfig; - error: any; + error: ErrorActionType; } ) { super( @@ -174,6 +189,7 @@ export class LoadPermissionsFail extends StateUtils.EntityFailAction { export class LoadPermissionsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_USER_GROUP_PERMISSIONS_SUCCESS; + constructor( public payload: { userGroupId: string; @@ -190,6 +206,7 @@ export class LoadPermissionsSuccess extends StateUtils.EntitySuccessAction { export class LoadAvailableOrgCustomers extends StateUtils.EntityLoadAction { readonly type = LOAD_USER_GROUP_AVAILABLE_CUSTOMERS; + constructor( public payload: { userId: string; @@ -206,11 +223,12 @@ export class LoadAvailableOrgCustomers extends StateUtils.EntityLoadAction { export class LoadAvailableOrgCustomersFail extends StateUtils.EntityFailAction { readonly type = LOAD_USER_GROUP_AVAILABLE_CUSTOMERS_FAIL; + constructor( public payload: { userGroupId: string; params: SearchConfig; - error: any; + error: ErrorActionType; } ) { super( @@ -223,6 +241,7 @@ export class LoadAvailableOrgCustomersFail extends StateUtils.EntityFailAction { export class LoadAvailableOrgCustomersSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_USER_GROUP_AVAILABLE_CUSTOMERS_SUCCESS; + constructor( public payload: { userGroupId: string; @@ -239,6 +258,7 @@ export class LoadAvailableOrgCustomersSuccess extends StateUtils.EntitySuccessAc export class CreateUserGroup extends StateUtils.EntityLoadAction { readonly type = CREATE_USER_GROUP; + constructor(public payload: { userId: string; userGroup: UserGroup }) { super(USER_GROUP_ENTITIES, payload.userGroup.uid ?? null); } @@ -246,13 +266,15 @@ export class CreateUserGroup extends StateUtils.EntityLoadAction { export class CreateUserGroupFail extends StateUtils.EntityFailAction { readonly type = CREATE_USER_GROUP_FAIL; - constructor(public payload: { userGroupId: string; error: any }) { + + constructor(public payload: { userGroupId: string; error: ErrorActionType }) { super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error); } } export class CreateUserGroupSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_USER_GROUP_SUCCESS; + constructor(public payload: UserGroup) { super(USER_GROUP_ENTITIES, payload.uid ?? null, payload); } @@ -260,6 +282,7 @@ export class CreateUserGroupSuccess extends StateUtils.EntitySuccessAction { export class AssignMember extends StateUtils.EntityLoadAction { readonly type = USER_GROUP_ASSIGN_MEMBER; + constructor( public payload: { userId: string; @@ -273,11 +296,12 @@ export class AssignMember extends StateUtils.EntityLoadAction { export class AssignMemberFail extends StateUtils.EntityFailAction { readonly type = USER_GROUP_ASSIGN_MEMBER_FAIL; + constructor( public payload: { userGroupId: string; customerId: string; - error: any; + error: ErrorActionType; } ) { super(B2B_USER_ENTITIES, payload.customerId, payload.error); @@ -286,6 +310,7 @@ export class AssignMemberFail extends StateUtils.EntityFailAction { export class AssignMemberSuccess extends StateUtils.EntitySuccessAction { readonly type = USER_GROUP_ASSIGN_MEMBER_SUCCESS; + constructor(public payload: { customerId: string; selected: boolean }) { super(B2B_USER_ENTITIES, payload.customerId, payload); } @@ -293,6 +318,7 @@ export class AssignMemberSuccess extends StateUtils.EntitySuccessAction { export class AssignPermission extends StateUtils.EntityLoadAction { readonly type = USER_GROUP_ASSIGN_PERMISSION; + constructor( public payload: { userId: string; @@ -306,11 +332,12 @@ export class AssignPermission extends StateUtils.EntityLoadAction { export class AssignPermissionFail extends StateUtils.EntityFailAction { readonly type = USER_GROUP_ASSIGN_PERMISSION_FAIL; + constructor( public payload: { userGroupId: string; permissionUid: string; - error: any; + error: ErrorActionType; } ) { super(PERMISSION_ENTITIES, payload.permissionUid, payload.error); @@ -319,6 +346,7 @@ export class AssignPermissionFail extends StateUtils.EntityFailAction { export class AssignPermissionSuccess extends StateUtils.EntitySuccessAction { readonly type = USER_GROUP_ASSIGN_PERMISSION_SUCCESS; + constructor(public payload: { permissionUid: string; selected: boolean }) { super(PERMISSION_ENTITIES, payload.permissionUid, payload); } @@ -326,6 +354,7 @@ export class AssignPermissionSuccess extends StateUtils.EntitySuccessAction { export class UpdateUserGroup extends StateUtils.EntityLoadAction { readonly type = UPDATE_USER_GROUP; + constructor( public payload: { userId: string; @@ -339,13 +368,15 @@ export class UpdateUserGroup extends StateUtils.EntityLoadAction { export class UpdateUserGroupFail extends StateUtils.EntityFailAction { readonly type = UPDATE_USER_GROUP_FAIL; - constructor(public payload: { userGroupId: string; error: any }) { + + constructor(public payload: { userGroupId: string; error: ErrorActionType }) { super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error); } } export class UpdateUserGroupSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_USER_GROUP_SUCCESS; + constructor(public payload: UserGroup) { super(USER_GROUP_ENTITIES, payload.uid ?? '', payload); } @@ -353,6 +384,7 @@ export class UpdateUserGroupSuccess extends StateUtils.EntitySuccessAction { export class DeleteUserGroup extends StateUtils.EntityLoadAction { readonly type = DELETE_USER_GROUP; + constructor( public payload: { userId: string; @@ -365,13 +397,15 @@ export class DeleteUserGroup extends StateUtils.EntityLoadAction { export class DeleteUserGroupFail extends StateUtils.EntityFailAction { readonly type = DELETE_USER_GROUP_FAIL; - constructor(public payload: { userGroupId: string; error: any }) { + + constructor(public payload: { userGroupId: string; error: ErrorActionType }) { super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error); } } export class DeleteUserGroupSuccess extends StateUtils.EntitySuccessAction { readonly type = DELETE_USER_GROUP_SUCCESS; + constructor(public payload: UserGroup) { super(USER_GROUP_ENTITIES, payload.uid ?? '', payload); } @@ -379,6 +413,7 @@ export class DeleteUserGroupSuccess extends StateUtils.EntitySuccessAction { export class UnassignMember extends StateUtils.EntityLoadAction { readonly type = USER_GROUP_UNASSIGN_MEMBER; + constructor( public payload: { userId: string; @@ -392,11 +427,12 @@ export class UnassignMember extends StateUtils.EntityLoadAction { export class UnassignMemberFail extends StateUtils.EntityFailAction { readonly type = USER_GROUP_UNASSIGN_MEMBER_FAIL; + constructor( public payload: { userGroupId: string; customerId: string; - error: any; + error: ErrorActionType; } ) { super(B2B_USER_ENTITIES, payload.customerId, payload.error); @@ -405,6 +441,7 @@ export class UnassignMemberFail extends StateUtils.EntityFailAction { export class UnassignMemberSuccess extends StateUtils.EntitySuccessAction { readonly type = USER_GROUP_UNASSIGN_MEMBER_SUCCESS; + constructor(public payload: { customerId: string; selected: boolean }) { super(B2B_USER_ENTITIES, payload.customerId, payload); } @@ -412,6 +449,7 @@ export class UnassignMemberSuccess extends StateUtils.EntitySuccessAction { export class UnassignAllMembers extends StateUtils.EntityLoadAction { readonly type = USER_GROUP_UNASSIGN_ALL_MEMBERS; + constructor( public payload: { userId: string; @@ -424,13 +462,15 @@ export class UnassignAllMembers extends StateUtils.EntityLoadAction { export class UnassignAllMembersFail extends StateUtils.EntityFailAction { readonly type = USER_GROUP_UNASSIGN_ALL_MEMBERS_FAIL; - constructor(public payload: { userGroupId: string; error: any }) { + + constructor(public payload: { userGroupId: string; error: ErrorActionType }) { super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error); } } export class UnassignAllMembersSuccess extends StateUtils.EntitySuccessAction { readonly type = USER_GROUP_UNASSIGN_ALL_MEMBERS_SUCCESS; + constructor(public payload: UserGroup) { super(USER_GROUP_ENTITIES, payload.uid ?? '', payload); } @@ -438,6 +478,7 @@ export class UnassignAllMembersSuccess extends StateUtils.EntitySuccessAction { export class UnassignPermission extends StateUtils.EntityLoadAction { readonly type = USER_GROUP_UNASSIGN_PERMISSION; + constructor( public payload: { userId: string; @@ -451,11 +492,12 @@ export class UnassignPermission extends StateUtils.EntityLoadAction { export class UnassignPermissionFail extends StateUtils.EntityFailAction { readonly type = USER_GROUP_UNASSIGN_PERMISSION_FAIL; + constructor( public payload: { userGroupId: string; permissionUid: string; - error: any; + error: ErrorActionType; } ) { super(PERMISSION_ENTITIES, payload.permissionUid, payload.error); @@ -464,6 +506,7 @@ export class UnassignPermissionFail extends StateUtils.EntityFailAction { export class UnassignPermissionSuccess extends StateUtils.EntitySuccessAction { readonly type = USER_GROUP_UNASSIGN_PERMISSION_SUCCESS; + constructor(public payload: { permissionUid: string; selected: boolean }) { super(PERMISSION_ENTITIES, payload.permissionUid, payload); } diff --git a/feature-libs/organization/administration/core/store/effects/b2b-user.effect.ts b/feature-libs/organization/administration/core/store/effects/b2b-user.effect.ts index 4e4b2223f79..63670b5c3e8 100644 --- a/feature-libs/organization/administration/core/store/effects/b2b-user.effect.ts +++ b/feature-libs/organization/administration/core/store/effects/b2b-user.effect.ts @@ -18,7 +18,7 @@ import { StateUtils, User, UserIdService, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { UserAccountFacade } from '@spartacus/user/account/root'; import { Observable, from, of } from 'rxjs'; @@ -62,7 +62,7 @@ export class B2BUserEffects { of( new B2BUserActions.LoadB2BUserFail({ orgCustomerId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -116,7 +116,7 @@ export class B2BUserEffects { from([ new B2BUserActions.CreateB2BUserFail({ orgCustomerId: orgCustomer.customerId ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -161,7 +161,7 @@ export class B2BUserEffects { from([ new B2BUserActions.UpdateB2BUserFail({ orgCustomerId: orgCustomer.customerId ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -227,7 +227,7 @@ export class B2BUserEffects { of( new B2BUserActions.LoadB2BUsersFail({ params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -276,7 +276,7 @@ export class B2BUserEffects { new B2BUserActions.LoadB2BUserApproversFail({ orgCustomerId: payload.orgCustomerId, params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -327,7 +327,7 @@ export class B2BUserEffects { new B2BUserActions.LoadB2BUserPermissionsFail({ orgCustomerId: payload.orgCustomerId, params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -378,7 +378,7 @@ export class B2BUserEffects { new B2BUserActions.LoadB2BUserUserGroupsFail({ orgCustomerId: payload.orgCustomerId, params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -419,7 +419,7 @@ export class B2BUserEffects { new B2BUserActions.AssignB2BUserApproverFail({ orgCustomerId: payload.orgCustomerId, approverId: payload.approverId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -459,7 +459,7 @@ export class B2BUserEffects { new B2BUserActions.UnassignB2BUserApproverFail({ orgCustomerId: payload.orgCustomerId, approverId: payload.approverId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -497,7 +497,7 @@ export class B2BUserEffects { new B2BUserActions.AssignB2BUserPermissionFail({ orgCustomerId: payload.orgCustomerId, permissionId: payload.permissionId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -535,7 +535,7 @@ export class B2BUserEffects { new B2BUserActions.UnassignB2BUserPermissionFail({ orgCustomerId: payload.orgCustomerId, permissionId: payload.permissionId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -573,7 +573,7 @@ export class B2BUserEffects { new B2BUserActions.AssignB2BUserUserGroupFail({ orgCustomerId: payload.orgCustomerId, userGroupId: payload.userGroupId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -619,7 +619,7 @@ export class B2BUserEffects { new B2BUserActions.UnassignB2BUserUserGroupFail({ orgCustomerId: payload.orgCustomerId, userGroupId: payload.userGroupId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) diff --git a/feature-libs/organization/administration/core/store/effects/budget.effect.ts b/feature-libs/organization/administration/core/store/effects/budget.effect.ts index 05993fa3d24..7993cef7739 100644 --- a/feature-libs/organization/administration/core/store/effects/budget.effect.ts +++ b/feature-libs/organization/administration/core/store/effects/budget.effect.ts @@ -11,7 +11,7 @@ import { EntitiesModel, LoggerService, StateUtils, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, from, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; @@ -38,7 +38,7 @@ export class BudgetEffects { of( new BudgetActions.LoadBudgetFail({ budgetCode, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -74,7 +74,7 @@ export class BudgetEffects { of( new BudgetActions.LoadBudgetsFail({ params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -101,7 +101,7 @@ export class BudgetEffects { from([ new BudgetActions.CreateBudgetFail({ budgetCode: payload.budget.code ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -131,7 +131,7 @@ export class BudgetEffects { from([ new BudgetActions.UpdateBudgetFail({ budgetCode: payload.budget.code ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) diff --git a/feature-libs/organization/administration/core/store/effects/cost-center.effect.ts b/feature-libs/organization/administration/core/store/effects/cost-center.effect.ts index 92e816f53f0..1cbe8d98ada 100644 --- a/feature-libs/organization/administration/core/store/effects/cost-center.effect.ts +++ b/feature-libs/organization/administration/core/store/effects/cost-center.effect.ts @@ -12,7 +12,7 @@ import { EntitiesModel, LoggerService, StateUtils, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, from, of } from 'rxjs'; import { catchError, groupBy, map, mergeMap, switchMap } from 'rxjs/operators'; @@ -44,7 +44,7 @@ export class CostCenterEffects { of( new CostCenterActions.LoadCostCenterFail({ costCenterCode, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -80,7 +80,7 @@ export class CostCenterEffects { of( new CostCenterActions.LoadCostCentersFail({ params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -109,7 +109,7 @@ export class CostCenterEffects { from([ new CostCenterActions.CreateCostCenterFail({ costCenterCode: payload.costCenter.code ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -139,7 +139,7 @@ export class CostCenterEffects { from([ new CostCenterActions.UpdateCostCenterFail({ costCenterCode: payload.costCenter.code ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -185,7 +185,7 @@ export class CostCenterEffects { new CostCenterActions.LoadAssignedBudgetsFail({ costCenterCode, params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -219,7 +219,7 @@ export class CostCenterEffects { from([ new CostCenterActions.AssignBudgetFail({ budgetCode, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -252,7 +252,7 @@ export class CostCenterEffects { from([ new CostCenterActions.UnassignBudgetFail({ budgetCode, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) diff --git a/feature-libs/organization/administration/core/store/effects/org-unit.effect.ts b/feature-libs/organization/administration/core/store/effects/org-unit.effect.ts index 67786e5e6a7..997423e4893 100644 --- a/feature-libs/organization/administration/core/store/effects/org-unit.effect.ts +++ b/feature-libs/organization/administration/core/store/effects/org-unit.effect.ts @@ -5,7 +5,7 @@ */ import { HttpErrorResponse } from '@angular/common/http'; -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { B2BApprovalProcess, @@ -14,16 +14,16 @@ import { EntitiesModel, LoggerService, StateUtils, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; -import { Observable, from, of } from 'rxjs'; +import { from, Observable, of } from 'rxjs'; import { catchError, groupBy, map, mergeMap, switchMap } from 'rxjs/operators'; import { OrgUnitConnector } from '../../connectors/org-unit/org-unit.connector'; import { B2BUnitNode } from '../../model/unit-node.model'; import { B2BUserActions, - OrgUnitActions, OrganizationActions, + OrgUnitActions, } from '../actions/index'; @Injectable() @@ -56,7 +56,7 @@ export class OrgUnitEffects { of( new OrgUnitActions.LoadOrgUnitFail({ orgUnitId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -80,7 +80,7 @@ export class OrgUnitEffects { catchError((error: HttpErrorResponse) => of( new OrgUnitActions.LoadOrgUnitNodesFail({ - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -107,7 +107,7 @@ export class OrgUnitEffects { from([ new OrgUnitActions.CreateUnitFail({ unitCode: payload.unit.uid ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -138,7 +138,7 @@ export class OrgUnitEffects { from([ new OrgUnitActions.UpdateUnitFail({ unitCode: payload.unit.uid ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -163,7 +163,7 @@ export class OrgUnitEffects { catchError((error: HttpErrorResponse) => of( new OrgUnitActions.LoadTreeFail({ - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -188,7 +188,7 @@ export class OrgUnitEffects { catchError((error: HttpErrorResponse) => of( new OrgUnitActions.LoadApprovalProcessesFail({ - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -235,7 +235,7 @@ export class OrgUnitEffects { orgUnitId, roleId, params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -266,7 +266,7 @@ export class OrgUnitEffects { of( new OrgUnitActions.AssignRoleFail({ orgCustomerId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -295,7 +295,7 @@ export class OrgUnitEffects { of( new OrgUnitActions.UnassignRoleFail({ orgCustomerId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -328,7 +328,7 @@ export class OrgUnitEffects { from([ new OrgUnitActions.AssignApproverFail({ orgCustomerId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -362,7 +362,7 @@ export class OrgUnitEffects { from([ new OrgUnitActions.UnassignApproverFail({ orgCustomerId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -393,7 +393,7 @@ export class OrgUnitEffects { from([ new OrgUnitActions.CreateAddressFail({ addressId: payload.address.id ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -425,7 +425,7 @@ export class OrgUnitEffects { from([ new OrgUnitActions.UpdateAddressFail({ addressId: address.id ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -457,7 +457,7 @@ export class OrgUnitEffects { from([ new OrgUnitActions.DeleteAddressFail({ addressId: payload.addressId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) diff --git a/feature-libs/organization/administration/core/store/effects/permission.effect.ts b/feature-libs/organization/administration/core/store/effects/permission.effect.ts index 6f9be997c81..7844f97852b 100644 --- a/feature-libs/organization/administration/core/store/effects/permission.effect.ts +++ b/feature-libs/organization/administration/core/store/effects/permission.effect.ts @@ -12,7 +12,7 @@ import { LoggerService, OrderApprovalPermissionType, StateUtils, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, from, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; @@ -40,7 +40,7 @@ export class PermissionEffects { of( new PermissionActions.LoadPermissionFail({ permissionCode, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -76,7 +76,7 @@ export class PermissionEffects { of( new PermissionActions.LoadPermissionsFail({ params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -105,7 +105,7 @@ export class PermissionEffects { from([ new PermissionActions.CreatePermissionFail({ permissionCode: payload.permission.code ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -135,7 +135,7 @@ export class PermissionEffects { from([ new PermissionActions.UpdatePermissionFail({ permissionCode: payload.permission.code ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -162,7 +162,7 @@ export class PermissionEffects { catchError((error: HttpErrorResponse) => of( new PermissionActions.LoadPermissionTypesFail({ - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) diff --git a/feature-libs/organization/administration/core/store/effects/user-group.effect.ts b/feature-libs/organization/administration/core/store/effects/user-group.effect.ts index 8ca9c8257ce..f1d4d16b0a2 100644 --- a/feature-libs/organization/administration/core/store/effects/user-group.effect.ts +++ b/feature-libs/organization/administration/core/store/effects/user-group.effect.ts @@ -12,7 +12,7 @@ import { EntitiesModel, LoggerService, StateUtils, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, from, of } from 'rxjs'; import { catchError, groupBy, map, mergeMap, switchMap } from 'rxjs/operators'; @@ -45,7 +45,7 @@ export class UserGroupEffects { of( new UserGroupActions.LoadUserGroupFail({ userGroupId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -81,7 +81,7 @@ export class UserGroupEffects { of( new UserGroupActions.LoadUserGroupsFail({ params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -130,7 +130,7 @@ export class UserGroupEffects { new UserGroupActions.LoadPermissionsFail({ userGroupId: payload.userGroupId, params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -183,7 +183,7 @@ export class UserGroupEffects { new UserGroupActions.LoadAvailableOrgCustomersFail({ userGroupId: payload.userGroupId, params: payload.params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -212,7 +212,7 @@ export class UserGroupEffects { from([ new UserGroupActions.CreateUserGroupFail({ userGroupId: payload.userGroup.uid ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -243,7 +243,7 @@ export class UserGroupEffects { from([ new UserGroupActions.UpdateUserGroupFail({ userGroupId: payload.userGroup.uid ?? '', - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -273,7 +273,7 @@ export class UserGroupEffects { from([ new UserGroupActions.DeleteUserGroupFail({ userGroupId: payload.userGroupId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -311,7 +311,7 @@ export class UserGroupEffects { new UserGroupActions.AssignPermissionFail({ userGroupId: payload.userGroupId, permissionUid: payload.permissionUid, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -345,7 +345,7 @@ export class UserGroupEffects { new UserGroupActions.AssignMemberFail({ userGroupId: payload.userGroupId, customerId: payload.customerId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -383,7 +383,7 @@ export class UserGroupEffects { new UserGroupActions.UnassignMemberFail({ userGroupId: payload.userGroupId, customerId: payload.customerId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -421,7 +421,7 @@ export class UserGroupEffects { new UserGroupActions.UnassignPermissionFail({ userGroupId: payload.userGroupId, permissionUid: payload.permissionUid, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) @@ -453,7 +453,7 @@ export class UserGroupEffects { from([ new UserGroupActions.UnassignAllMembersFail({ userGroupId: payload.userGroupId, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), new OrganizationActions.OrganizationClearData(), ]) diff --git a/feature-libs/organization/order-approval/core/services/order-approval.service.spec.ts b/feature-libs/organization/order-approval/core/services/order-approval.service.spec.ts index 4d98b67882a..25cf8e5d769 100644 --- a/feature-libs/organization/order-approval/core/services/order-approval.service.spec.ts +++ b/feature-libs/organization/order-approval/core/services/order-approval.service.spec.ts @@ -238,7 +238,7 @@ describe('OrderApprovalService', () => { store.dispatch( new OrderApprovalActions.MakeDecisionFail({ orderApprovalCode, - error: 'error', + error: new Error('error'), }) ); diff --git a/feature-libs/organization/order-approval/core/store/actions/order-approval.action.spec.ts b/feature-libs/organization/order-approval/core/store/actions/order-approval.action.spec.ts index e1282a2544a..bd49f560885 100644 --- a/feature-libs/organization/order-approval/core/store/actions/order-approval.action.spec.ts +++ b/feature-libs/organization/order-approval/core/store/actions/order-approval.action.spec.ts @@ -22,7 +22,7 @@ const orderApprovalDecision: OrderApprovalDecision = { }; const userId = 'xxx@xxx.xxx'; -const error = 'anError'; +const error = { message: 'anError' }; const params = { currentPage: 2 }; const query = '?pageSize=¤tPage=2&sort='; @@ -58,6 +58,7 @@ describe('OrderApproval Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrderApprovalActions.LOAD_ORDER_APPROVAL_FAIL, payload: { orderApprovalCode, @@ -109,15 +110,14 @@ describe('OrderApproval Actions', () => { it('should create the action', () => { const action = new OrderApprovalActions.LoadOrderApprovalsFail({ params, - error: { error }, + error, }); expect({ ...action }).toEqual({ + error, type: OrderApprovalActions.LOAD_ORDER_APPROVALS_FAIL, - payload: { params, error: { error } }, - meta: StateUtils.entityFailMeta(ORDER_APPROVAL_LIST, query, { - error, - }), + payload: { params, error }, + meta: StateUtils.entityFailMeta(ORDER_APPROVAL_LIST, query, error), }); }); }); @@ -166,6 +166,7 @@ describe('OrderApproval Actions', () => { }); expect({ ...action }).toEqual({ + error, type: OrderApprovalActions.MAKE_DECISION_FAIL, payload: { orderApprovalCode, @@ -174,10 +175,7 @@ describe('OrderApproval Actions', () => { meta: StateUtils.entityFailMeta( PROCESS_FEATURE, ORDER_APPROVAL_MAKE_DECISION_PROCESS_ID, - { - orderApprovalCode, - error, - } + error ), }); }); diff --git a/feature-libs/organization/order-approval/core/store/actions/order-approval.action.ts b/feature-libs/organization/order-approval/core/store/actions/order-approval.action.ts index b0512dec1b3..b4ba3ba4de7 100644 --- a/feature-libs/organization/order-approval/core/store/actions/order-approval.action.ts +++ b/feature-libs/organization/order-approval/core/store/actions/order-approval.action.ts @@ -5,6 +5,7 @@ */ import { + ErrorActionType, ListModel, PROCESS_FEATURE, SearchConfig, @@ -42,6 +43,7 @@ export const MAKE_DECISION_RESET = export class LoadOrderApproval extends StateUtils.EntityLoadAction { readonly type = LOAD_ORDER_APPROVAL; + constructor(public payload: { userId: string; orderApprovalCode: string }) { super(ORDER_APPROVAL_ENTITIES, payload.orderApprovalCode); } @@ -49,13 +51,17 @@ export class LoadOrderApproval extends StateUtils.EntityLoadAction { export class LoadOrderApprovalFail extends StateUtils.EntityFailAction { readonly type = LOAD_ORDER_APPROVAL_FAIL; - constructor(public payload: { orderApprovalCode: string; error: any }) { + + constructor( + public payload: { orderApprovalCode: string; error: ErrorActionType } + ) { super(ORDER_APPROVAL_ENTITIES, payload.orderApprovalCode, payload.error); } } export class LoadOrderApprovalSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_ORDER_APPROVAL_SUCCESS; + constructor(public payload: OrderApproval | OrderApproval[]) { super( ORDER_APPROVAL_ENTITIES, @@ -68,6 +74,7 @@ export class LoadOrderApprovalSuccess extends StateUtils.EntitySuccessAction { export class LoadOrderApprovals extends StateUtils.EntityLoadAction { readonly type = LOAD_ORDER_APPROVALS; + constructor( public payload: { userId: string; @@ -83,7 +90,10 @@ export class LoadOrderApprovals extends StateUtils.EntityLoadAction { export class LoadOrderApprovalsFail extends StateUtils.EntityFailAction { readonly type = LOAD_ORDER_APPROVALS_FAIL; - constructor(public payload: { params: SearchConfig; error: any }) { + + constructor( + public payload: { params: SearchConfig; error: ErrorActionType } + ) { super( ORDER_APPROVAL_LIST, StateUtils.serializeSearchConfig(payload.params), @@ -94,6 +104,7 @@ export class LoadOrderApprovalsFail extends StateUtils.EntityFailAction { export class LoadOrderApprovalsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_ORDER_APPROVALS_SUCCESS; + constructor( public payload: { page: ListModel; @@ -109,6 +120,7 @@ export class LoadOrderApprovalsSuccess extends StateUtils.EntitySuccessAction { export class MakeDecision extends StateUtils.EntityLoadAction { readonly type = MAKE_DECISION; + constructor( public payload: { userId: string; @@ -122,13 +134,21 @@ export class MakeDecision extends StateUtils.EntityLoadAction { export class MakeDecisionFail extends StateUtils.EntityFailAction { readonly type = MAKE_DECISION_FAIL; - constructor(public payload: { orderApprovalCode: string; error: any }) { - super(PROCESS_FEATURE, ORDER_APPROVAL_MAKE_DECISION_PROCESS_ID, payload); + + constructor( + public payload: { orderApprovalCode: string; error: ErrorActionType } + ) { + super( + PROCESS_FEATURE, + ORDER_APPROVAL_MAKE_DECISION_PROCESS_ID, + payload.error + ); } } export class MakeDecisionSuccess extends StateUtils.EntitySuccessAction { readonly type = MAKE_DECISION_SUCCESS; + constructor( public payload: { orderApprovalCode: string; @@ -141,6 +161,7 @@ export class MakeDecisionSuccess extends StateUtils.EntitySuccessAction { export class MakeDecisionReset extends StateUtils.EntityLoaderResetAction { readonly type = MAKE_DECISION_RESET; + constructor() { super(PROCESS_FEATURE, ORDER_APPROVAL_MAKE_DECISION_PROCESS_ID); } diff --git a/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.ts b/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.ts index 89f2b4bf210..dc18d271474 100644 --- a/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.ts +++ b/feature-libs/organization/order-approval/core/store/effects/order-approval.effect.ts @@ -12,7 +12,7 @@ import { LoggerService, OCC_USER_ID_ANONYMOUS, StateUtils, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, of } from 'rxjs'; import { catchError, filter, map, switchMap } from 'rxjs/operators'; @@ -43,7 +43,7 @@ export class OrderApprovalEffects { of( new OrderApprovalActions.LoadOrderApprovalFail({ orderApprovalCode, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -80,7 +80,7 @@ export class OrderApprovalEffects { of( new OrderApprovalActions.LoadOrderApprovalsFail({ params: params, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -116,7 +116,7 @@ export class OrderApprovalEffects { of( new OrderApprovalActions.MakeDecisionFail({ orderApprovalCode: orderApprovalCode, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) diff --git a/feature-libs/organization/unit-order/core/store/actions/unit-order.action.spec.ts b/feature-libs/organization/unit-order/core/store/actions/unit-order.action.spec.ts index b6789f1adbe..917a96fdd25 100644 --- a/feature-libs/organization/unit-order/core/store/actions/unit-order.action.spec.ts +++ b/feature-libs/organization/unit-order/core/store/actions/unit-order.action.spec.ts @@ -30,12 +30,12 @@ describe('OrdersActions', () => { describe('LoadUnitOrdersFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = { message: 'mockError' }; const action = new UnitOrderActions.LoadUnitOrdersFail(error); expect({ ...action }).toEqual({ type: UnitOrderActions.LOAD_UNIT_ORDERS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(UNIT_ORDERS, error), }); }); @@ -97,12 +97,12 @@ describe('Order Details Actions', () => { describe('LoadOrderDetailsFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = { message: 'mockError' }; const action = new UnitOrderActions.LoadOrderDetailsFail(error); expect({ ...action }).toEqual({ type: UnitOrderActions.LOAD_ORDER_DETAILS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(UNIT_ORDER_DETAILS, error), }); }); diff --git a/feature-libs/organization/unit-order/core/store/actions/unit-order.action.ts b/feature-libs/organization/unit-order/core/store/actions/unit-order.action.ts index c10c13ba705..94a8173e120 100644 --- a/feature-libs/organization/unit-order/core/store/actions/unit-order.action.ts +++ b/feature-libs/organization/unit-order/core/store/actions/unit-order.action.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { StateUtils } from '@spartacus/core'; +import { ErrorActionType, StateUtils } from '@spartacus/core'; import { Order, OrderHistoryList } from '@spartacus/order/root'; -import { UNIT_ORDERS, UNIT_ORDER_DETAILS } from '../unit-order-state'; +import { UNIT_ORDER_DETAILS, UNIT_ORDERS } from '../unit-order-state'; export const LOAD_UNIT_ORDERS = '[Unit Order] Load Unit Orders'; export const LOAD_UNIT_ORDERS_FAIL = '[Unit Order] Load Unit Orders Fail'; @@ -22,6 +22,7 @@ export const CLEAR_ORDER_DETAILS = '[Unit Order] Clear Unit Order Details'; export class LoadUnitOrders extends StateUtils.LoaderLoadAction { readonly type = LOAD_UNIT_ORDERS; + constructor( public payload: { userId: string; @@ -37,13 +38,15 @@ export class LoadUnitOrders extends StateUtils.LoaderLoadAction { export class LoadUnitOrdersFail extends StateUtils.LoaderFailAction { readonly type = LOAD_UNIT_ORDERS_FAIL; - constructor(public payload: any) { - super(UNIT_ORDERS, payload); + + constructor(public error: ErrorActionType) { + super(UNIT_ORDERS, error); } } export class LoadUnitOrdersSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_UNIT_ORDERS_SUCCESS; + constructor(public payload?: OrderHistoryList) { super(UNIT_ORDERS); } @@ -51,6 +54,7 @@ export class LoadUnitOrdersSuccess extends StateUtils.LoaderSuccessAction { export class ClearUnitOrders extends StateUtils.LoaderResetAction { readonly type = CLEAR_UNIT_ORDERS; + constructor() { super(UNIT_ORDERS); } @@ -58,6 +62,7 @@ export class ClearUnitOrders extends StateUtils.LoaderResetAction { export class LoadOrderDetails extends StateUtils.LoaderLoadAction { readonly type = LOAD_ORDER_DETAILS; + constructor( public payload: { userId: string; @@ -70,13 +75,15 @@ export class LoadOrderDetails extends StateUtils.LoaderLoadAction { export class LoadOrderDetailsFail extends StateUtils.LoaderFailAction { readonly type = LOAD_ORDER_DETAILS_FAIL; - constructor(public payload: any) { - super(UNIT_ORDER_DETAILS, payload); + + constructor(public error: ErrorActionType) { + super(UNIT_ORDER_DETAILS, error); } } export class LoadOrderDetailsSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_ORDER_DETAILS_SUCCESS; + constructor(public payload: Order) { super(UNIT_ORDER_DETAILS); } @@ -84,6 +91,7 @@ export class LoadOrderDetailsSuccess extends StateUtils.LoaderSuccessAction { export class ClearOrderDetails extends StateUtils.LoaderResetAction { readonly type = CLEAR_ORDER_DETAILS; + constructor() { super(UNIT_ORDER_DETAILS); } diff --git a/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts b/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts index 60b614ef974..94ffef0240f 100644 --- a/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts +++ b/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.spec.ts @@ -3,7 +3,7 @@ import { TestBed } from '@angular/core/testing'; import { Actions } from '@ngrx/effects'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; -import { normalizeHttpError, SiteContextActions } from '@spartacus/core'; +import { SiteContextActions, tryNormalizeHttpError } from '@spartacus/core'; import { Order, OrderHistoryList } from '@spartacus/order/root'; import { cold, hot } from 'jasmine-marbles'; import { Observable, of, throwError } from 'rxjs'; @@ -24,7 +24,7 @@ const mockUserOrders: OrderHistoryList = { sorts: [], }; -const mockError = 'test-error'; +const mockError = new Error('test-error'); describe('Orders effect', () => { let ordersEffect: UnitOrderEffect; @@ -80,7 +80,7 @@ describe('Orders effect', () => { }); const completion = new UnitOrderActions.LoadUnitOrdersFail( - normalizeHttpError(mockError) + tryNormalizeHttpError(mockError) ); actions$ = hot('-a', { a: action }); @@ -127,14 +127,14 @@ describe('Orders effect', () => { it('should handle failures for load order details', () => { spyOn(orderHistoryConnector, 'getUnitOrderDetail').and.returnValue( - throwError('Error') + throwError(mockError) ); const action = new UnitOrderActions.LoadOrderDetails( mockOrderDetailsParams ); - const completion = new UnitOrderActions.LoadOrderDetailsFail(undefined); + const completion = new UnitOrderActions.LoadOrderDetailsFail(mockError); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); diff --git a/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.ts b/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.ts index aebcb163fd1..75d35da47db 100644 --- a/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.ts +++ b/feature-libs/organization/unit-order/core/store/effects/unit-order.effect.ts @@ -9,7 +9,7 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { LoggerService, SiteContextActions, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Order, OrderHistoryList } from '@spartacus/order/root'; import { Observable, of } from 'rxjs'; @@ -47,7 +47,7 @@ export class UnitOrderEffect { catchError((error) => of( new UnitOrderActions.LoadUnitOrdersFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -81,7 +81,7 @@ export class UnitOrderEffect { catchError((error) => of( new UnitOrderActions.LoadOrderDetailsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/organization/unit-order/core/store/reducers/unit-order.reducer.spec.ts b/feature-libs/organization/unit-order/core/store/reducers/unit-order.reducer.spec.ts index 24d404e0811..1cfc7f99d8d 100644 --- a/feature-libs/organization/unit-order/core/store/reducers/unit-order.reducer.spec.ts +++ b/feature-libs/organization/unit-order/core/store/reducers/unit-order.reducer.spec.ts @@ -45,7 +45,9 @@ describe('Unit Order Reducer', () => { describe('LOAD_UNIT_ORDERS_FAIL action', () => { it('should return the initial state', () => { - const action = new UnitOrderActions.LoadUnitOrdersFail('error'); + const action = new UnitOrderActions.LoadUnitOrdersFail( + new Error('error') + ); const state = unitOrdersReducer(initialState, action); expect(state).toEqual(initialState); }); diff --git a/feature-libs/pickup-in-store/core/store/actions/pickup-location.action.ts b/feature-libs/pickup-in-store/core/store/actions/pickup-location.action.ts index 20513b8f170..6f4cb644477 100644 --- a/feature-libs/pickup-in-store/core/store/actions/pickup-location.action.ts +++ b/feature-libs/pickup-in-store/core/store/actions/pickup-location.action.ts @@ -5,7 +5,7 @@ */ import { createAction, props } from '@ngrx/store'; -import { PointOfService } from '@spartacus/core'; +import { ErrorActionType, PointOfService } from '@spartacus/core'; import { AugmentedPointOfService, PickupOption, @@ -83,5 +83,5 @@ export const SetStoreDetailsSuccess = createAction( export const SetStoreDetailsFailure = createAction( STORE_DETAILS_FAIL, - props<{ payload: any }>() + props<{ error: ErrorActionType }>() ); diff --git a/feature-libs/pickup-in-store/core/store/actions/stock.action.spec.ts b/feature-libs/pickup-in-store/core/store/actions/stock.action.spec.ts index 8f5f473a705..a7bb330f218 100644 --- a/feature-libs/pickup-in-store/core/store/actions/stock.action.spec.ts +++ b/feature-libs/pickup-in-store/core/store/actions/stock.action.spec.ts @@ -59,12 +59,12 @@ describe('[Stock] Actions', () => { error: {}, }, }, - payload: {}, + error: {}, }; expect(RESULT.type).toEqual(EXPECTED.type); expect(RESULT.meta).toEqual(EXPECTED.meta); - expect(RESULT.payload).toEqual(EXPECTED.payload); + expect(RESULT.error).toEqual(EXPECTED.error); }); it('StockLevelSuccess', () => { diff --git a/feature-libs/pickup-in-store/core/store/actions/stock.action.ts b/feature-libs/pickup-in-store/core/store/actions/stock.action.ts index 377b00ba50c..68a150e70d1 100644 --- a/feature-libs/pickup-in-store/core/store/actions/stock.action.ts +++ b/feature-libs/pickup-in-store/core/store/actions/stock.action.ts @@ -5,7 +5,12 @@ */ import { createAction, props } from '@ngrx/store'; -import { StateUtils, Stock, StoreFinderStockSearchPage } from '@spartacus/core'; +import { + ErrorActionType, + StateUtils, + Stock, + StoreFinderStockSearchPage, +} from '@spartacus/core'; import { StockLocationSearchParams } from '@spartacus/pickup-in-store/root'; import { STOCK_DATA } from '../stock-state'; @@ -21,6 +26,7 @@ export const STOCK_LEVEL_AT_STORE_SUCCESS = export class StockLevel extends StateUtils.LoaderLoadAction { readonly type = STOCK_LEVEL; + constructor(public payload: StockLocationSearchParams) { super(STOCK_DATA); } @@ -28,6 +34,7 @@ export class StockLevel extends StateUtils.LoaderLoadAction { export class StockLevelOnHold extends StateUtils.LoaderLoadAction { readonly type = STOCK_LEVEL_ON_HOLD; + constructor() { super(STOCK_DATA); } @@ -35,8 +42,9 @@ export class StockLevelOnHold extends StateUtils.LoaderLoadAction { export class StockLevelFail extends StateUtils.LoaderFailAction { readonly type = STOCK_LEVEL_FAIL; - constructor(public payload: any) { - super(STOCK_DATA, payload); + + constructor(public error: ErrorActionType) { + super(STOCK_DATA, error); } } @@ -47,6 +55,7 @@ export type StockLevelSuccessPayload = { export class StockLevelSuccess extends StateUtils.LoaderSuccessAction { readonly type = STOCK_LEVEL_SUCCESS; + constructor(public payload: StockLevelSuccessPayload) { super(STOCK_DATA); } @@ -54,6 +63,7 @@ export class StockLevelSuccess extends StateUtils.LoaderSuccessAction { export class ClearStockData extends StateUtils.LoaderResetAction { readonly type = CLEAR_STOCK_DATA; + constructor() { super(STOCK_DATA); } diff --git a/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.spec.ts b/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.spec.ts index 09fcd550dd8..8bee2289583 100644 --- a/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.spec.ts +++ b/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.spec.ts @@ -83,7 +83,7 @@ describe('PickupLocationEffect with Error', () => { const error = new HttpErrorResponse({ error: 'error' }); const actionFailure = SetStoreDetailsFailure({ - payload: normalizeHttpError(error), + error: normalizeHttpError(error), }); actions$ = hot('-a', { a: action }); const expected = cold('-(b)', { b: actionFailure }); diff --git a/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.ts b/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.ts index 6f073bfbd0c..8b7e4c05afc 100644 --- a/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.ts +++ b/feature-libs/pickup-in-store/core/store/effects/pickup-location.effect.ts @@ -4,9 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { PickupLocationConnector } from '../../connectors'; @@ -41,7 +41,7 @@ export class PickupLocationEffect { catchError((error) => of( PickupLocationActions.SetStoreDetailsFailure({ - payload: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) diff --git a/feature-libs/pickup-in-store/core/store/effects/stock.effect.ts b/feature-libs/pickup-in-store/core/store/effects/stock.effect.ts index 7d78d26f436..2eec79a114d 100644 --- a/feature-libs/pickup-in-store/core/store/effects/stock.effect.ts +++ b/feature-libs/pickup-in-store/core/store/effects/stock.effect.ts @@ -6,7 +6,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { of } from 'rxjs'; import { catchError, concatMap, map, switchMap } from 'rxjs/operators'; import { StockConnector } from '../../connectors/index'; @@ -39,7 +39,7 @@ export class StockEffect { catchError((error) => of( new StockLevelActions.StockLevelFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/product-configurator/rulebased/core/state/actions/configurator-cart.action.ts b/feature-libs/product-configurator/rulebased/core/state/actions/configurator-cart.action.ts index acbd904d629..dfe26b99047 100644 --- a/feature-libs/product-configurator/rulebased/core/state/actions/configurator-cart.action.ts +++ b/feature-libs/product-configurator/rulebased/core/state/actions/configurator-cart.action.ts @@ -6,7 +6,7 @@ import { Action } from '@ngrx/store'; import { MULTI_CART_DATA } from '@spartacus/cart/base/core'; -import { StateUtils } from '@spartacus/core'; +import { StateUtils, ErrorActionType } from '@spartacus/core'; import { CommonConfigurator } from '@spartacus/product-configurator/common'; import { Configurator } from '../../model/configurator.model'; import { CONFIGURATOR_DATA } from '../configurator-state'; @@ -54,7 +54,7 @@ export class ReadCartEntryConfigurationSuccess extends StateUtils.EntitySuccessA export class ReadCartEntryConfigurationFail extends StateUtils.EntityFailAction { readonly type = READ_CART_ENTRY_CONFIGURATION_FAIL; - constructor(public payload: { ownerKey: string; error: any }) { + constructor(public payload: { ownerKey: string; error: ErrorActionType }) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); } } @@ -77,7 +77,7 @@ export class ReadOrderEntryConfigurationSuccess extends StateUtils.EntitySuccess export class ReadOrderEntryConfigurationFail extends StateUtils.EntityFailAction { readonly type = READ_ORDER_ENTRY_CONFIGURATION_FAIL; - constructor(public payload: { ownerKey: string; error: any }) { + constructor(public payload: { ownerKey: string; error: ErrorActionType }) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); } } diff --git a/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.spec.ts b/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.spec.ts index 0069ce16b76..6ea20263c6f 100644 --- a/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.spec.ts +++ b/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.spec.ts @@ -43,7 +43,7 @@ describe('ConfiguratorVariantActions', () => { it('should provide variant search fail action with proper type', () => { const action = new ConfiguratorVariantActions.SearchVariantsFail({ ownerKey: CONFIGURATION.owner.key, - error: 'Error', + error: new Error('Error'), }); expect(action.type).toBe(ConfiguratorVariantActions.SEARCH_VARIANTS_FAIL); }); diff --git a/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.ts b/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.ts index 9fa98a66a58..54caf206199 100644 --- a/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.ts +++ b/feature-libs/product-configurator/rulebased/core/state/actions/configurator-variant.action.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { StateUtils } from '@spartacus/core'; +import { StateUtils, ErrorActionType } from '@spartacus/core'; import { Configurator } from '../../model/configurator.model'; import { CONFIGURATOR_DATA } from '../configurator-state'; @@ -21,7 +21,7 @@ export class SearchVariants extends StateUtils.EntityLoadAction { export class SearchVariantsFail extends StateUtils.EntityFailAction { readonly type = SEARCH_VARIANTS_FAIL; - constructor(public payload: { ownerKey: string; error: any }) { + constructor(public payload: { ownerKey: string; error: ErrorActionType }) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); } } diff --git a/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.spec.ts b/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.spec.ts index d3ef1ed13a6..794a0de9764 100644 --- a/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.spec.ts +++ b/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.spec.ts @@ -79,12 +79,13 @@ describe('ConfiguratorActions', () => { describe('ReadConfigurationFail', () => { it('Should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const action = new ConfiguratorActions.ReadConfigurationFail({ ownerKey: PRODUCT_CODE, error: error, }); expect({ ...action }).toEqual({ + error, type: ConfiguratorActions.READ_CONFIGURATION_FAIL, payload: { ownerKey: PRODUCT_CODE, @@ -135,13 +136,14 @@ describe('ConfiguratorActions', () => { describe('UpdateConfigurationFail', () => { it('Should create the action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const action = new ConfiguratorActions.UpdateConfigurationFail({ configuration: CONFIGURATION, error: error, }); expect({ ...action }).toEqual({ + error, type: ConfiguratorActions.UPDATE_CONFIGURATION_FAIL, payload: { configuration: CONFIGURATION, @@ -201,13 +203,14 @@ describe('ConfiguratorActions', () => { }); it('should allow to create the fail action', () => { - const error = 'anError'; + const error = { message: 'anError' }; const action = new ConfiguratorActions.UpdateConfigurationOverviewFail({ ownerKey: CONFIGURATION.owner.key, error: error, }); expect({ ...action }).toEqual({ + error, type: ConfiguratorActions.UPDATE_CONFIGURATION_OVERVIEW_FAIL, payload: { ownerKey: CONFIGURATION.owner.key, diff --git a/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.ts b/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.ts index b39ee9f92cf..f0687235230 100644 --- a/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.ts +++ b/feature-libs/product-configurator/rulebased/core/state/actions/configurator.action.ts @@ -5,7 +5,7 @@ */ import { Action } from '@ngrx/store'; -import { StateUtils } from '@spartacus/core'; +import { ErrorAction, ErrorActionType, StateUtils } from '@spartacus/core'; import { CommonConfigurator } from '@spartacus/product-configurator/common'; import { Configurator } from '../../model/configurator.model'; import { CONFIGURATOR_DATA } from '../configurator-state'; @@ -72,6 +72,7 @@ export const CHECK_CONFLICT_DIALOG = '[Configurator] Check conflict dialog'; export class CreateConfiguration extends StateUtils.EntityLoadAction { readonly type = CREATE_CONFIGURATION; + constructor( public payload: { owner: CommonConfigurator.Owner; @@ -85,10 +86,11 @@ export class CreateConfiguration extends StateUtils.EntityLoadAction { export class CreateConfigurationFail extends StateUtils.EntityFailAction { readonly type = CREATE_CONFIGURATION_FAIL; + constructor( public payload: { ownerKey: string; - error: any; + error: ErrorActionType; } ) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); @@ -97,6 +99,7 @@ export class CreateConfigurationFail extends StateUtils.EntityFailAction { export class CreateConfigurationSuccess extends StateUtils.EntitySuccessAction { readonly type = CREATE_CONFIGURATION_SUCCESS; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -104,6 +107,7 @@ export class CreateConfigurationSuccess extends StateUtils.EntitySuccessAction { export class ReadConfiguration extends StateUtils.EntityLoadAction { readonly type = READ_CONFIGURATION; + constructor( public payload: { configuration: Configurator.Configuration; @@ -116,13 +120,15 @@ export class ReadConfiguration extends StateUtils.EntityLoadAction { export class ReadConfigurationFail extends StateUtils.EntityFailAction { readonly type = READ_CONFIGURATION_FAIL; - constructor(public payload: { ownerKey: string; error: any }) { + + constructor(public payload: { ownerKey: string; error: ErrorActionType }) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); } } export class ReadConfigurationSuccess extends StateUtils.EntitySuccessAction { readonly type = READ_CONFIGURATION_SUCCESS; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -130,6 +136,7 @@ export class ReadConfigurationSuccess extends StateUtils.EntitySuccessAction { export class UpdateConfiguration extends StateUtils.EntityProcessesIncrementAction { readonly type = UPDATE_CONFIGURATION; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); this.meta.loader = { @@ -138,8 +145,13 @@ export class UpdateConfiguration extends StateUtils.EntityProcessesIncrementActi } } -export class UpdateConfigurationFail extends StateUtils.EntityProcessesDecrementAction { +export class UpdateConfigurationFail + extends StateUtils.EntityProcessesDecrementAction + implements ErrorAction +{ + error: ErrorActionType = this.payload.error; readonly type = UPDATE_CONFIGURATION_FAIL; + constructor( public payload: { configuration: Configurator.Configuration; error: any } ) { @@ -152,6 +164,7 @@ export class UpdateConfigurationFail extends StateUtils.EntityProcessesDecrement export class UpdateConfigurationSuccess extends StateUtils.EntityProcessesDecrementAction { readonly type = UPDATE_CONFIGURATION_SUCCESS; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -159,6 +172,7 @@ export class UpdateConfigurationSuccess extends StateUtils.EntityProcessesDecrem export class UpdateConfigurationFinalizeSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_CONFIGURATION_FINALIZE_SUCCESS; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -166,6 +180,7 @@ export class UpdateConfigurationFinalizeSuccess extends StateUtils.EntitySuccess export class UpdateConfigurationFinalizeFail extends StateUtils.EntitySuccessAction { readonly type = UPDATE_CONFIGURATION_FINALIZE_FAIL; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -173,19 +188,23 @@ export class UpdateConfigurationFinalizeFail extends StateUtils.EntitySuccessAct export class UpdatePriceSummary extends StateUtils.EntityLoadAction { readonly type = UPDATE_PRICE_SUMMARY; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } } + export class UpdatePriceSummaryFail extends StateUtils.EntityFailAction { readonly type = UPDATE_PRICE_SUMMARY_FAIL; - constructor(public payload: { ownerKey: string; error: any }) { + + constructor(public payload: { ownerKey: string; error: ErrorActionType }) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); } } export class UpdatePriceSummarySuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_PRICE_SUMMARY_SUCCESS; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -193,6 +212,7 @@ export class UpdatePriceSummarySuccess extends StateUtils.EntitySuccessAction { export class ChangeGroup extends StateUtils.EntityLoadAction { readonly type = CHANGE_GROUP; + constructor( public payload: { configuration: Configurator.Configuration; @@ -210,6 +230,7 @@ export class ChangeGroup extends StateUtils.EntityLoadAction { export class ChangeGroupFinalize extends StateUtils.EntityLoadAction { readonly type = CHANGE_GROUP_FINALIZE; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -217,6 +238,7 @@ export class ChangeGroupFinalize extends StateUtils.EntityLoadAction { export class RemoveConfiguration extends StateUtils.EntityLoaderResetAction { readonly type = REMOVE_CONFIGURATION; + constructor(public payload: { ownerKey: string | string[] }) { super(CONFIGURATOR_DATA, payload.ownerKey); } @@ -224,6 +246,7 @@ export class RemoveConfiguration extends StateUtils.EntityLoaderResetAction { export class GetConfigurationOverview extends StateUtils.EntityLoadAction { readonly type = GET_CONFIGURATION_OVERVIEW; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -231,13 +254,15 @@ export class GetConfigurationOverview extends StateUtils.EntityLoadAction { export class GetConfigurationOverviewFail extends StateUtils.EntityFailAction { readonly type = GET_CONFIGURATION_OVERVIEW_FAIL; - constructor(public payload: { ownerKey: string; error: any }) { + + constructor(public payload: { ownerKey: string; error: ErrorActionType }) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); } } export class GetConfigurationOverviewSuccess extends StateUtils.EntitySuccessAction { readonly type = GET_CONFIGURATION_OVERVIEW_SUCCESS; + constructor( public payload: { ownerKey: string; overview: Configurator.Overview } ) { @@ -247,6 +272,7 @@ export class GetConfigurationOverviewSuccess extends StateUtils.EntitySuccessAct export class UpdateConfigurationOverview extends StateUtils.EntityLoadAction { readonly type = UPDATE_CONFIGURATION_OVERVIEW; + constructor(public payload: Configurator.Configuration) { super(CONFIGURATOR_DATA, payload.owner.key); } @@ -254,13 +280,15 @@ export class UpdateConfigurationOverview extends StateUtils.EntityLoadAction { export class UpdateConfigurationOverviewFail extends StateUtils.EntityFailAction { readonly type = UPDATE_CONFIGURATION_OVERVIEW_FAIL; - constructor(public payload: { ownerKey: string; error: any }) { + + constructor(public payload: { ownerKey: string; error: ErrorActionType }) { super(CONFIGURATOR_DATA, payload.ownerKey, payload.error); } } export class UpdateConfigurationOverviewSuccess extends StateUtils.EntitySuccessAction { readonly type = UPDATE_CONFIGURATION_OVERVIEW_SUCCESS; + constructor( public payload: { ownerKey: string; overview: Configurator.Overview } ) { @@ -309,6 +337,7 @@ export class SetMenuParentGroup extends StateUtils.EntitySuccessAction { export class SetGroupsVisited extends StateUtils.EntitySuccessAction { readonly type = SET_GROUPS_VISITED; + constructor(public payload: { entityKey: string; visitedGroups: string[] }) { super(CONFIGURATOR_DATA, payload.entityKey, payload.visitedGroups); } @@ -316,6 +345,7 @@ export class SetGroupsVisited extends StateUtils.EntitySuccessAction { export class RemoveProductBoundConfigurations implements Action { readonly type = REMOVE_PRODUCT_BOUND_CONFIGURATIONS; + constructor() { // Intentional Empty Constructor } @@ -323,6 +353,7 @@ export class RemoveProductBoundConfigurations implements Action { export class DissmissConflictDialoge extends StateUtils.EntitySuccessAction { readonly type = DISMISS_CONFLICT_DIALOG; + constructor(public ownerKey: string) { super(CONFIGURATOR_DATA, ownerKey); } @@ -330,6 +361,7 @@ export class DissmissConflictDialoge extends StateUtils.EntitySuccessAction { export class CheckConflictDialoge extends StateUtils.EntitySuccessAction { readonly type = CHECK_CONFLICT_DIALOG; + constructor(public ownerKey: string) { super(CONFIGURATOR_DATA, ownerKey); } diff --git a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.ts b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.ts index e7ff83b353d..0092acbb336 100644 --- a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.ts +++ b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-basic.effect.ts @@ -7,7 +7,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store, select } from '@ngrx/store'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { CommonConfigurator, CommonConfiguratorUtilsService, @@ -81,7 +81,7 @@ export class ConfiguratorBasicEffects { catchError((error) => [ new ConfiguratorActions.CreateConfigurationFail({ ownerKey: action.payload.owner.key, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ); @@ -110,7 +110,7 @@ export class ConfiguratorBasicEffects { catchError((error) => [ new ConfiguratorActions.ReadConfigurationFail({ ownerKey: action.payload.configuration.owner.key, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ); @@ -142,7 +142,7 @@ export class ConfiguratorBasicEffects { }); }), catchError((error) => { - const errorPayload = normalizeHttpError(error, this.logger); + const errorPayload = tryNormalizeHttpError(error, this.logger); return [ new ConfiguratorActions.UpdateConfigurationFail({ configuration: payload, @@ -174,7 +174,7 @@ export class ConfiguratorBasicEffects { ); }), catchError((error) => { - const errorPayload = normalizeHttpError(error, this.logger); + const errorPayload = tryNormalizeHttpError(error, this.logger); return [ new ConfiguratorActions.UpdatePriceSummaryFail({ ownerKey: payload.owner.key, @@ -207,7 +207,7 @@ export class ConfiguratorBasicEffects { }); }), catchError((error) => { - const errorPayload = normalizeHttpError(error, this.logger); + const errorPayload = tryNormalizeHttpError(error, this.logger); return [ new ConfiguratorActions.GetConfigurationOverviewFail({ ownerKey: payload.owner.key, @@ -243,7 +243,7 @@ export class ConfiguratorBasicEffects { ); }), catchError((error) => { - const errorPayload = normalizeHttpError(error, this.logger); + const errorPayload = tryNormalizeHttpError(error, this.logger); return [ new ConfiguratorActions.UpdateConfigurationOverviewFail({ ownerKey: payload.owner.key, @@ -437,7 +437,7 @@ export class ConfiguratorBasicEffects { catchError((error) => [ new ConfiguratorActions.ReadConfigurationFail({ ownerKey: action.payload.configuration.owner.key, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ); diff --git a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.ts b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.ts index 4068aeea9ef..2849a06ac5d 100644 --- a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.ts +++ b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-cart.effect.ts @@ -4,13 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { HttpErrorResponse } from '@angular/common/http'; -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { Store, select } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import { CartActions } from '@spartacus/cart/base/core'; import { CartModification } from '@spartacus/cart/base/root'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { CommonConfigurator, CommonConfiguratorUtilsService, @@ -34,6 +33,7 @@ type readConfigurationForCartEntryResultType = export const ERROR_MESSAGE_NO_ENTRY_NUMBER_FOUND = 'Entry number is required in addToCart response'; + @Injectable() /** * Common configurator effects related to cart handling @@ -84,10 +84,7 @@ export class ConfiguratorCartEffects { cartId: payload.cartId, productCode: payload.productCode, quantity: payload.quantity, - error: - error instanceof HttpErrorResponse - ? normalizeHttpError(error, this.logger) - : error, + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -123,7 +120,7 @@ export class ConfiguratorCartEffects { userId: payload.userId, cartId: payload.cartId, entryNumber: payload.cartEntryNumber, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }) ) ) @@ -166,7 +163,7 @@ export class ConfiguratorCartEffects { catchError((error) => [ new ConfiguratorActions.ReadCartEntryConfigurationFail({ ownerKey: action.payload.owner.key, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ); @@ -194,7 +191,7 @@ export class ConfiguratorCartEffects { catchError((error) => [ new ConfiguratorActions.ReadOrderEntryConfigurationFail({ ownerKey: action.payload.owner.key, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ); diff --git a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.ts b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.ts index 708cfacf484..d6b35a43037 100644 --- a/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.ts +++ b/feature-libs/product-configurator/rulebased/core/state/effects/configurator-variant.effect.ts @@ -6,7 +6,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { ConfiguratorType } from '@spartacus/product-configurator/common'; import { Observable } from 'rxjs'; import { catchError, filter, switchMap } from 'rxjs/operators'; @@ -50,7 +50,7 @@ export class ConfiguratorVariantEffects { catchError((error) => [ new ConfiguratorActions.SearchVariantsFail({ ownerKey: action.payload.owner.key, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), }), ]) ); 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 36a095c94d0..978a6017b62 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 @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { StateUtils } from '@spartacus/core'; +import { ErrorActionType, StateUtils } from '@spartacus/core'; import { CommonConfigurator } from '@spartacus/product-configurator/common'; import { ConfiguratorTextfield } from '../../model/configurator-textfield.model'; import { CONFIGURATION_TEXTFIELD_DATA } from '../configuration-textfield-state'; @@ -41,6 +41,7 @@ export const REMOVE_CONFIGURATION = export class CreateConfiguration extends StateUtils.LoaderLoadAction { readonly type = CREATE_CONFIGURATION; + constructor( public payload: { productCode: string; owner: CommonConfigurator.Owner } ) { @@ -50,13 +51,15 @@ export class CreateConfiguration extends StateUtils.LoaderLoadAction { export class CreateConfigurationFail extends StateUtils.LoaderFailAction { readonly type = CREATE_CONFIGURATION_FAIL; - constructor(public payload: any) { - super(CONFIGURATION_TEXTFIELD_DATA, payload); + + constructor(public error: ErrorActionType) { + super(CONFIGURATION_TEXTFIELD_DATA, error); } } export class CreateConfigurationSuccess extends StateUtils.LoaderSuccessAction { readonly type = CREATE_CONFIGURATION_SUCCESS; + constructor(public payload: ConfiguratorTextfield.Configuration) { super(CONFIGURATION_TEXTFIELD_DATA); } @@ -64,6 +67,7 @@ export class CreateConfigurationSuccess extends StateUtils.LoaderSuccessAction { export class UpdateConfiguration extends StateUtils.LoaderLoadAction { readonly type = UPDATE_CONFIGURATION; + constructor(public payload: ConfiguratorTextfield.Configuration) { super(CONFIGURATION_TEXTFIELD_DATA); } @@ -71,6 +75,7 @@ export class UpdateConfiguration extends StateUtils.LoaderLoadAction { export class AddToCart extends StateUtils.LoaderLoadAction { readonly type = ADD_TO_CART; + constructor(public payload: ConfiguratorTextfield.AddToCartParameters) { super(CONFIGURATION_TEXTFIELD_DATA); } @@ -78,13 +83,15 @@ export class AddToCart extends StateUtils.LoaderLoadAction { export class AddToCartFail extends StateUtils.LoaderFailAction { readonly type = ADD_TO_CART_FAIL; - constructor(public payload: any) { - super(CONFIGURATION_TEXTFIELD_DATA, payload); + + constructor(public error: ErrorActionType) { + super(CONFIGURATION_TEXTFIELD_DATA, error); } } export class UpdateCartEntryConfiguration extends StateUtils.LoaderLoadAction { readonly type = UPDATE_CART_ENTRY_CONFIGURATION; + constructor(public payload: ConfiguratorTextfield.UpdateCartEntryParameters) { super(CONFIGURATION_TEXTFIELD_DATA); } @@ -92,13 +99,15 @@ export class UpdateCartEntryConfiguration extends StateUtils.LoaderLoadAction { export class UpdateCartEntryConfigurationFail extends StateUtils.LoaderFailAction { readonly type = UPDATE_CART_ENTRY_CONFIGURATION_FAIL; - constructor(public payload: any) { - super(CONFIGURATION_TEXTFIELD_DATA, payload); + + constructor(public error: ErrorActionType) { + super(CONFIGURATION_TEXTFIELD_DATA, error); } } export class ReadCartEntryConfiguration extends StateUtils.LoaderLoadAction { readonly type = READ_CART_ENTRY_CONFIGURATION; + constructor( public payload: CommonConfigurator.ReadConfigurationFromCartEntryParameters ) { @@ -108,6 +117,7 @@ export class ReadCartEntryConfiguration extends StateUtils.LoaderLoadAction { export class ReadCartEntryConfigurationSuccess extends StateUtils.LoaderSuccessAction { readonly type = READ_CART_ENTRY_CONFIGURATION_SUCCESS; + constructor(public payload: ConfiguratorTextfield.Configuration) { super(CONFIGURATION_TEXTFIELD_DATA); } @@ -115,13 +125,15 @@ export class ReadCartEntryConfigurationSuccess extends StateUtils.LoaderSuccessA export class ReadCartEntryConfigurationFail extends StateUtils.LoaderFailAction { readonly type = READ_CART_ENTRY_CONFIGURATION_FAIL; - constructor(public payload: any) { - super(CONFIGURATION_TEXTFIELD_DATA, payload); + + constructor(public error: ErrorActionType) { + super(CONFIGURATION_TEXTFIELD_DATA, error); } } export class ReadOrderEntryConfiguration extends StateUtils.LoaderLoadAction { readonly type = READ_ORDER_ENTRY_CONFIGURATION; + constructor( public payload: CommonConfigurator.ReadConfigurationFromOrderEntryParameters ) { @@ -131,6 +143,7 @@ export class ReadOrderEntryConfiguration extends StateUtils.LoaderLoadAction { export class ReadOrderEntryConfigurationSuccess extends StateUtils.LoaderSuccessAction { readonly type = READ_ORDER_ENTRY_CONFIGURATION_SUCCESS; + constructor(public payload: ConfiguratorTextfield.Configuration) { super(CONFIGURATION_TEXTFIELD_DATA); } @@ -138,13 +151,15 @@ export class ReadOrderEntryConfigurationSuccess extends StateUtils.LoaderSuccess export class ReadOrderEntryConfigurationFail extends StateUtils.LoaderFailAction { readonly type = READ_ORDER_ENTRY_CONFIGURATION_FAIL; - constructor(public payload: any) { - super(CONFIGURATION_TEXTFIELD_DATA, payload); + + constructor(public error: ErrorActionType) { + super(CONFIGURATION_TEXTFIELD_DATA, error); } } export class RemoveConfiguration extends StateUtils.LoaderResetAction { readonly type = REMOVE_CONFIGURATION; + constructor() { super(CONFIGURATION_TEXTFIELD_DATA); } 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 57f63144a1b..5c3e1bb3eb4 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 @@ -7,7 +7,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { CartActions } from '@spartacus/cart/base/core'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { CommonConfigurator } from '@spartacus/product-configurator/common'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; @@ -43,7 +43,7 @@ export class ConfiguratorTextfieldEffects { catchError((error) => of( new ConfiguratorTextfieldActions.CreateConfigurationFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -74,7 +74,7 @@ export class ConfiguratorTextfieldEffects { catchError((error) => of( new ConfiguratorTextfieldActions.AddToCartFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -110,7 +110,7 @@ export class ConfiguratorTextfieldEffects { catchError((error) => of( new ConfiguratorTextfieldActions.UpdateCartEntryConfigurationFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -140,7 +140,7 @@ export class ConfiguratorTextfieldEffects { ]), catchError((error) => [ new ConfiguratorTextfieldActions.ReadCartEntryConfigurationFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ), ]) ); @@ -170,7 +170,7 @@ export class ConfiguratorTextfieldEffects { ]), catchError((error) => [ new ConfiguratorTextfieldActions.ReadOrderEntryConfigurationFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ), ]) ); diff --git a/feature-libs/storefinder/core/store/actions/find-stores.action.spec.ts b/feature-libs/storefinder/core/store/actions/find-stores.action.spec.ts index 5344676a1ce..1cda96488a4 100644 --- a/feature-libs/storefinder/core/store/actions/find-stores.action.spec.ts +++ b/feature-libs/storefinder/core/store/actions/find-stores.action.spec.ts @@ -50,13 +50,13 @@ describe('Find Stores Actions', () => { describe('FindStoresFail', () => { it('should create FindStoresFail action', () => { - const payload = { errorMessage: 'Error' }; - const action = new StoreFinderActions.FindStoresFail(payload); + const error = { message: 'Error' }; + const action = new StoreFinderActions.FindStoresFail(error); expect({ ...action }).toEqual({ type: StoreFinderActions.FIND_STORES_FAIL, - payload, - meta: StateUtils.failMeta(STORE_FINDER_DATA, payload), + error, + meta: StateUtils.failMeta(STORE_FINDER_DATA, error), }); }); }); @@ -92,15 +92,15 @@ describe('Find Stores Actions', () => { describe('FindStoreByIdFail', () => { it('should create FindStoreByIdFail action', () => { - const payload = { errorMessage: 'Error' }; - const action = new StoreFinderActions.FindStoreByIdFail(payload); + const error = { message: 'Error' }; + const action = new StoreFinderActions.FindStoreByIdFail(error); expect({ ...action, }).toEqual({ type: StoreFinderActions.FIND_STORE_BY_ID_FAIL, - payload, - meta: StateUtils.failMeta(STORE_FINDER_DATA, payload), + error, + meta: StateUtils.failMeta(STORE_FINDER_DATA, error), }); }); }); diff --git a/feature-libs/storefinder/core/store/actions/find-stores.action.ts b/feature-libs/storefinder/core/store/actions/find-stores.action.ts index dfbc1ab0aa2..b2ede1c5d16 100644 --- a/feature-libs/storefinder/core/store/actions/find-stores.action.ts +++ b/feature-libs/storefinder/core/store/actions/find-stores.action.ts @@ -5,7 +5,12 @@ */ import { STORE_FINDER_DATA } from '../store-finder-state'; -import { GeoPoint, SearchConfig, StateUtils } from '@spartacus/core'; +import { + ErrorActionType, + GeoPoint, + SearchConfig, + StateUtils, +} from '@spartacus/core'; export const FIND_STORES_ON_HOLD = '[StoreFinder] On Hold'; export const FIND_STORES = '[StoreFinder] Find Stores'; @@ -19,6 +24,7 @@ export const FIND_STORE_BY_ID_SUCCESS = export class FindStoresOnHold extends StateUtils.LoaderLoadAction { readonly type = FIND_STORES_ON_HOLD; + constructor() { super(STORE_FINDER_DATA); } @@ -26,6 +32,7 @@ export class FindStoresOnHold extends StateUtils.LoaderLoadAction { export class FindStores extends StateUtils.LoaderLoadAction { readonly type = FIND_STORES; + constructor( public payload: { queryText: string; @@ -42,13 +49,15 @@ export class FindStores extends StateUtils.LoaderLoadAction { export class FindStoresFail extends StateUtils.LoaderFailAction { readonly type = FIND_STORES_FAIL; - constructor(public payload: any) { - super(STORE_FINDER_DATA, payload); + + constructor(public error: ErrorActionType) { + super(STORE_FINDER_DATA, error); } } export class FindStoresSuccess extends StateUtils.LoaderSuccessAction { readonly type = FIND_STORES_SUCCESS; + constructor(public payload: any) { super(STORE_FINDER_DATA); } @@ -56,6 +65,7 @@ export class FindStoresSuccess extends StateUtils.LoaderSuccessAction { export class FindStoreById extends StateUtils.LoaderLoadAction { readonly type = FIND_STORE_BY_ID; + constructor(public payload: { storeId: string }) { super(STORE_FINDER_DATA); } @@ -63,13 +73,15 @@ export class FindStoreById extends StateUtils.LoaderLoadAction { export class FindStoreByIdFail extends StateUtils.LoaderFailAction { readonly type = FIND_STORE_BY_ID_FAIL; - constructor(public payload: any) { - super(STORE_FINDER_DATA, payload); + + constructor(public error: ErrorActionType) { + super(STORE_FINDER_DATA, error); } } export class FindStoreByIdSuccess extends StateUtils.LoaderSuccessAction { readonly type = FIND_STORE_BY_ID_SUCCESS; + constructor(public payload: any) { super(STORE_FINDER_DATA); } diff --git a/feature-libs/storefinder/core/store/actions/view-all-stores.action.spec.ts b/feature-libs/storefinder/core/store/actions/view-all-stores.action.spec.ts index b5b01e749d5..bb42d787214 100644 --- a/feature-libs/storefinder/core/store/actions/view-all-stores.action.spec.ts +++ b/feature-libs/storefinder/core/store/actions/view-all-stores.action.spec.ts @@ -16,13 +16,13 @@ describe('View All Stores Actions', () => { describe('ViewAllStoresFail', () => { it('should create ViewAllStoresFail action', () => { - const payload = { errorMessage: 'Error' }; - const action = new StoreFinderActions.ViewAllStoresFail(payload); + const error = { message: 'Error' }; + const action = new StoreFinderActions.ViewAllStoresFail(error); expect({ ...action }).toEqual({ type: StoreFinderActions.VIEW_ALL_STORES_FAIL, - payload, - meta: StateUtils.failMeta(STORE_FINDER_DATA, payload), + error, + meta: StateUtils.failMeta(STORE_FINDER_DATA, error), }); }); }); diff --git a/feature-libs/storefinder/core/store/actions/view-all-stores.action.ts b/feature-libs/storefinder/core/store/actions/view-all-stores.action.ts index 19a3eb3f021..3be628d1246 100644 --- a/feature-libs/storefinder/core/store/actions/view-all-stores.action.ts +++ b/feature-libs/storefinder/core/store/actions/view-all-stores.action.ts @@ -5,7 +5,7 @@ */ import { STORE_FINDER_DATA } from '../store-finder-state'; -import { StateUtils } from '@spartacus/core'; +import { ErrorActionType, StateUtils } from '@spartacus/core'; import { Action } from '@ngrx/store'; export const VIEW_ALL_STORES = '[StoreFinder] View All Stores'; @@ -15,6 +15,7 @@ export const CLEAR_STORE_FINDER_DATA = '[StoreFinder] Clear Data'; export class ViewAllStores extends StateUtils.LoaderLoadAction { readonly type = VIEW_ALL_STORES; + constructor() { super(STORE_FINDER_DATA); } @@ -22,13 +23,15 @@ export class ViewAllStores extends StateUtils.LoaderLoadAction { export class ViewAllStoresFail extends StateUtils.LoaderFailAction { readonly type = VIEW_ALL_STORES_FAIL; - constructor(public payload: any) { - super(STORE_FINDER_DATA, payload); + + constructor(public error: ErrorActionType) { + super(STORE_FINDER_DATA, error); } } export class ViewAllStoresSuccess extends StateUtils.LoaderSuccessAction { readonly type = VIEW_ALL_STORES_SUCCESS; + constructor(public payload: any) { super(STORE_FINDER_DATA); } diff --git a/feature-libs/storefinder/core/store/effects/find-stores.effect.ts b/feature-libs/storefinder/core/store/effects/find-stores.effect.ts index b1388a73adc..8271b9367d2 100644 --- a/feature-libs/storefinder/core/store/effects/find-stores.effect.ts +++ b/feature-libs/storefinder/core/store/effects/find-stores.effect.ts @@ -6,7 +6,7 @@ import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { LoggerService, normalizeHttpError } from '@spartacus/core'; +import { LoggerService, tryNormalizeHttpError } from '@spartacus/core'; import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; import { StoreFinderConnector } from '../../connectors/store-finder.connector'; @@ -52,7 +52,7 @@ export class FindStoresEffect { catchError((error) => of( new StoreFinderActions.FindStoresFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -74,7 +74,7 @@ export class FindStoresEffect { catchError((error) => of( new StoreFinderActions.FindStoreByIdFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/feature-libs/storefinder/core/store/effects/view-all-stores.effect.ts b/feature-libs/storefinder/core/store/effects/view-all-stores.effect.ts index b25384e846c..a475c2c991b 100644 --- a/feature-libs/storefinder/core/store/effects/view-all-stores.effect.ts +++ b/feature-libs/storefinder/core/store/effects/view-all-stores.effect.ts @@ -9,7 +9,7 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { LoggerService, SiteContextActions, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; @@ -45,7 +45,7 @@ export class ViewAllStoresEffect { catchError((error) => of( new StoreFinderActions.ViewAllStoresFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/integration-libs/cdc/core/store/actions/cdc-user-token.action.spec.ts b/integration-libs/cdc/core/store/actions/cdc-user-token.action.spec.ts index 63b5cce0ac2..cb092138139 100644 --- a/integration-libs/cdc/core/store/actions/cdc-user-token.action.spec.ts +++ b/integration-libs/cdc/core/store/actions/cdc-user-token.action.spec.ts @@ -22,13 +22,14 @@ describe('CDC User Token Actions', () => { describe('LoadUserTokenFail Action', () => { it('should create the action', () => { const data = { - error: 'anError', + error: new Error('anError'), initActionPayload: 'payload', } as any; const action = new CdcAuthActions.LoadCdcUserTokenFail(data); expect({ ...action }).toEqual({ + error: data.error, type: CdcAuthActions.LOAD_CDC_USER_TOKEN_FAIL, payload: data, }); diff --git a/integration-libs/cdc/core/store/actions/cdc-user-token.action.ts b/integration-libs/cdc/core/store/actions/cdc-user-token.action.ts index febc20b9f52..02d40f10087 100644 --- a/integration-libs/cdc/core/store/actions/cdc-user-token.action.ts +++ b/integration-libs/cdc/core/store/actions/cdc-user-token.action.ts @@ -5,7 +5,12 @@ */ import { Action } from '@ngrx/store'; -import { ErrorModel, HttpErrorModel } from '@spartacus/core'; +import { + ErrorAction, + ErrorActionType, + ErrorModel, + HttpErrorModel, +} from '@spartacus/core'; export const LOAD_CDC_USER_TOKEN = '[Auth] Load CDC User Token'; export const LOAD_CDC_USER_TOKEN_FAIL = '[Auth] Load CDC User Token Fail'; @@ -19,17 +24,20 @@ interface LoadUserTokenPayload { } interface LoadUserTokenFailurePayload { - error: ErrorModel | HttpErrorModel | any; + error: ErrorActionType; initialActionPayload: LoadUserTokenPayload; } -export class LoadCdcUserTokenFail implements Action { +export class LoadCdcUserTokenFail implements ErrorAction { + error: ErrorModel | HttpErrorModel | Error = this.payload.error; readonly type = LOAD_CDC_USER_TOKEN_FAIL; + constructor(public payload: LoadUserTokenFailurePayload) {} } export class LoadCdcUserToken implements Action { readonly type = LOAD_CDC_USER_TOKEN; + constructor(public payload: LoadUserTokenPayload) {} } diff --git a/integration-libs/cdc/core/store/effects/cdc-user-token.effect.ts b/integration-libs/cdc/core/store/effects/cdc-user-token.effect.ts index 903e76df69a..f4079820f5c 100644 --- a/integration-libs/cdc/core/store/effects/cdc-user-token.effect.ts +++ b/integration-libs/cdc/core/store/effects/cdc-user-token.effect.ts @@ -10,7 +10,7 @@ import { GlobalMessageService, GlobalMessageType, LoggerService, - normalizeHttpError, + tryNormalizeHttpError, } from '@spartacus/core'; import { EMPTY, Observable, of } from 'rxjs'; import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; @@ -48,7 +48,7 @@ export class CdcUserTokenEffects { ); return of( new CdcAuthActions.LoadCdcUserTokenFail({ - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), initialActionPayload: payload, }) ); diff --git a/projects/core/src/anonymous-consents/facade/anonymous-consents.service.spec.ts b/projects/core/src/anonymous-consents/facade/anonymous-consents.service.spec.ts index 16b8f740676..1a2a02a54f6 100644 --- a/projects/core/src/anonymous-consents/facade/anonymous-consents.service.spec.ts +++ b/projects/core/src/anonymous-consents/facade/anonymous-consents.service.spec.ts @@ -176,7 +176,9 @@ describe('AnonymousConsentsService', () => { it('getLoadTemplatesError should call getAnonymousConsentTemplatesError selector', () => { store.dispatch( - new AnonymousConsentsActions.LoadAnonymousConsentTemplatesFail('an error') + new AnonymousConsentsActions.LoadAnonymousConsentTemplatesFail( + new Error('an error') + ) ); let result = false; diff --git a/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.spec.ts b/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.spec.ts index 4281bd1b784..eeb2969b4b6 100644 --- a/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.spec.ts +++ b/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.spec.ts @@ -45,7 +45,7 @@ describe('anonymous consent actions', () => { }); describe('LoadAnonymousConsentTemplatesFail', () => { it('should create the action', () => { - const mockError = 'anError'; + const mockError = new Error('anError'); const action = new AnonymousConsentsActions.LoadAnonymousConsentTemplatesFail( mockError @@ -53,6 +53,7 @@ describe('anonymous consent actions', () => { expect({ ...action }).toEqual({ type: AnonymousConsentsActions.LOAD_ANONYMOUS_CONSENT_TEMPLATES_FAIL, meta: StateUtils.failMeta(ANONYMOUS_CONSENTS, mockError), + error: mockError, }); }); }); diff --git a/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts b/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts index daf3a74dd83..4d849a94ffe 100644 --- a/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts +++ b/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts @@ -10,6 +10,7 @@ import { } from '../../../model/consent.model'; import { StateUtils } from '../../../state/utils/index'; import { ANONYMOUS_CONSENTS } from '../anonymous-consents-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_ANONYMOUS_CONSENT_TEMPLATES = '[Anonymous Consents] Load Anonymous Consent Templates'; @@ -40,6 +41,7 @@ export const ANONYMOUS_CONSENT_CHECK_UPDATED_VERSIONS = export class LoadAnonymousConsentTemplates extends StateUtils.LoaderLoadAction { readonly type = LOAD_ANONYMOUS_CONSENT_TEMPLATES; + constructor() { super(ANONYMOUS_CONSENTS); } @@ -47,19 +49,23 @@ export class LoadAnonymousConsentTemplates extends StateUtils.LoaderLoadAction { export class LoadAnonymousConsentTemplatesSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_ANONYMOUS_CONSENT_TEMPLATES_SUCCESS; + constructor(public payload: ConsentTemplate[]) { super(ANONYMOUS_CONSENTS); } } + export class LoadAnonymousConsentTemplatesFail extends StateUtils.LoaderFailAction { readonly type = LOAD_ANONYMOUS_CONSENT_TEMPLATES_FAIL; - constructor(payload: any) { - super(ANONYMOUS_CONSENTS, payload); + + constructor(public error: ErrorActionType) { + super(ANONYMOUS_CONSENTS, error); } } export class ResetLoadAnonymousConsentTemplates extends StateUtils.LoaderResetAction { readonly type = RESET_LOAD_ANONYMOUS_CONSENT_TEMPLATES; + constructor() { super(ANONYMOUS_CONSENTS); } @@ -67,6 +73,7 @@ export class ResetLoadAnonymousConsentTemplates extends StateUtils.LoaderResetAc export class GetAllAnonymousConsents { readonly type = GET_ALL_ANONYMOUS_CONSENTS; + constructor() { // Intentional empty constructor } @@ -74,36 +81,43 @@ export class GetAllAnonymousConsents { export class GetAnonymousConsent { readonly type = GET_ANONYMOUS_CONSENT; + constructor(public templateCode: string) {} } export class SetAnonymousConsents { readonly type = SET_ANONYMOUS_CONSENTS; + constructor(public payload: AnonymousConsent[]) {} } export class GiveAnonymousConsent { readonly type = GIVE_ANONYMOUS_CONSENT; + constructor(public templateCode: string) {} } export class WithdrawAnonymousConsent { readonly type = WITHDRAW_ANONYMOUS_CONSENT; + constructor(public templateCode: string) {} } export class ToggleAnonymousConsentsBannerDissmissed { readonly type = TOGGLE_ANONYMOUS_CONSENTS_BANNER_DISMISSED; + constructor(public dismissed: boolean) {} } export class ToggleAnonymousConsentTemplatesUpdated { readonly type = TOGGLE_ANONYMOUS_CONSENT_TEMPLATES_UPDATED; + constructor(public updated: boolean) {} } export class AnonymousConsentCheckUpdatedVersions { readonly type = ANONYMOUS_CONSENT_CHECK_UPDATED_VERSIONS; + constructor() { // Intentional empty constructor } diff --git a/projects/core/src/anonymous-consents/store/effects/anonymous-consents.effect.ts b/projects/core/src/anonymous-consents/store/effects/anonymous-consents.effect.ts index 0222788febb..6215077dd4c 100644 --- a/projects/core/src/anonymous-consents/store/effects/anonymous-consents.effect.ts +++ b/projects/core/src/anonymous-consents/store/effects/anonymous-consents.effect.ts @@ -22,7 +22,7 @@ import { AuthActions, AuthService, UserIdService } from '../../../auth/index'; import { LoggerService } from '../../../logger'; import { UserConsentService } from '../../../user/facade/user-consent.service'; import { UserActions } from '../../../user/store/actions/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { AnonymousConsentsConfig } from '../../config/anonymous-consents-config'; import { AnonymousConsentTemplatesConnector } from '../../connectors/anonymous-consent-templates.connector'; import { AnonymousConsentsService } from '../../facade/index'; @@ -76,7 +76,7 @@ export class AnonymousConsentsEffects { catchError((error) => of( new AnonymousConsentsActions.LoadAnonymousConsentTemplatesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -118,7 +118,7 @@ export class AnonymousConsentsEffects { catchError((error) => of( new AnonymousConsentsActions.LoadAnonymousConsentTemplatesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/anonymous-consents/store/selectors/anonymous-consent-templates.selectors.spec.ts b/projects/core/src/anonymous-consents/store/selectors/anonymous-consent-templates.selectors.spec.ts index 0c21c2ab185..0891511d621 100644 --- a/projects/core/src/anonymous-consents/store/selectors/anonymous-consent-templates.selectors.spec.ts +++ b/projects/core/src/anonymous-consents/store/selectors/anonymous-consent-templates.selectors.spec.ts @@ -120,7 +120,7 @@ describe('anonymous consent templates selectors', () => { it('should return the error flag', () => { store.dispatch( new AnonymousConsentsActions.LoadAnonymousConsentTemplatesFail( - 'anError' + new Error('anError') ) ); diff --git a/projects/core/src/auth/client-auth/store/actions/client-token.action.spec.ts b/projects/core/src/auth/client-auth/store/actions/client-token.action.spec.ts index 79956451eea..1e0488b676e 100644 --- a/projects/core/src/auth/client-auth/store/actions/client-token.action.spec.ts +++ b/projects/core/src/auth/client-auth/store/actions/client-token.action.spec.ts @@ -23,11 +23,11 @@ describe('Client Token Actions', () => { describe('LoadClientTokenFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = new Error('anError'); const action = new ClientAuthActions.LoadClientTokenFail(error); expect({ ...action }).toEqual({ type: ClientAuthActions.LOAD_CLIENT_TOKEN_FAIL, - payload: error, + error, meta: StateUtils.failMeta(CLIENT_TOKEN_DATA, error), }); }); diff --git a/projects/core/src/auth/client-auth/store/actions/client-token.action.ts b/projects/core/src/auth/client-auth/store/actions/client-token.action.ts index c585932fa29..175c5274fb6 100644 --- a/projects/core/src/auth/client-auth/store/actions/client-token.action.ts +++ b/projects/core/src/auth/client-auth/store/actions/client-token.action.ts @@ -7,6 +7,7 @@ import { StateUtils } from '../../../../state/utils/index'; import { ClientToken } from '../../models/client-token.model'; import { CLIENT_TOKEN_DATA } from '../client-auth-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_CLIENT_TOKEN = '[Token] Load Client Token'; export const LOAD_CLIENT_TOKEN_FAIL = '[Token] Load Client Token Fail'; @@ -14,6 +15,7 @@ export const LOAD_CLIENT_TOKEN_SUCCESS = '[Token] Load Client Token Success'; export class LoadClientToken extends StateUtils.LoaderLoadAction { readonly type = LOAD_CLIENT_TOKEN; + constructor() { super(CLIENT_TOKEN_DATA); } @@ -21,13 +23,15 @@ export class LoadClientToken extends StateUtils.LoaderLoadAction { export class LoadClientTokenFail extends StateUtils.LoaderFailAction { readonly type = LOAD_CLIENT_TOKEN_FAIL; - constructor(public payload: any) { - super(CLIENT_TOKEN_DATA, payload); + + constructor(public error: ErrorActionType) { + super(CLIENT_TOKEN_DATA, error); } } export class LoadClientTokenSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_CLIENT_TOKEN_SUCCESS; + constructor(public payload: ClientToken) { super(CLIENT_TOKEN_DATA); } diff --git a/projects/core/src/auth/client-auth/store/effects/client-token.effect.ts b/projects/core/src/auth/client-auth/store/effects/client-token.effect.ts index a938c101c42..39faf9e27ee 100644 --- a/projects/core/src/auth/client-auth/store/effects/client-token.effect.ts +++ b/projects/core/src/auth/client-auth/store/effects/client-token.effect.ts @@ -9,7 +9,7 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, exhaustMap, map } from 'rxjs/operators'; import { LoggerService } from '../../../../logger'; -import { normalizeHttpError } from '../../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../../util/try-normalize-http-error'; import { ClientToken } from '../../../client-auth/models/client-token.model'; import { ClientAuthenticationTokenService } from '../../services/client-authentication-token.service'; import { ClientAuthActions } from '../actions/index'; @@ -32,7 +32,7 @@ export class ClientTokenEffect { catchError((error) => of( new ClientAuthActions.LoadClientTokenFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/base-core.module.ts b/projects/core/src/base-core.module.ts index d427123b4e6..a1a3b7698a1 100644 --- a/projects/core/src/base-core.module.ts +++ b/projects/core/src/base-core.module.ts @@ -20,6 +20,7 @@ import { ProcessModule } from './process/process.module'; import { SiteContextModule } from './site-context/site-context.module'; import { StateModule } from './state/state.module'; import { HttpErrorHandlerModule } from './error-handling'; +import { EffectsErrorHandlerModule } from './error-handling/effects-error-handler/effects-error-handler.module'; @NgModule({ imports: [ @@ -41,6 +42,7 @@ import { HttpErrorHandlerModule } from './error-handling'; /* This module should be imported by default starting from version 7.0 (CXSPA-3680)*/ //ErrorHandlingModule.forRoot(), + EffectsErrorHandlerModule.forRoot(), ], }) export class BaseCoreModule { diff --git a/projects/core/src/cms/store/actions/components.action.spec.ts b/projects/core/src/cms/store/actions/components.action.spec.ts index a314dc02275..1d94c9cd6fb 100755 --- a/projects/core/src/cms/store/actions/components.action.spec.ts +++ b/projects/core/src/cms/store/actions/components.action.spec.ts @@ -34,6 +34,7 @@ describe('Cms Component Actions', () => { }); expect({ ...action }).toEqual({ + error, payload: { uid: test_uid, error, diff --git a/projects/core/src/cms/store/actions/components.action.ts b/projects/core/src/cms/store/actions/components.action.ts index 2823140c2cc..ec759042512 100755 --- a/projects/core/src/cms/store/actions/components.action.ts +++ b/projects/core/src/cms/store/actions/components.action.ts @@ -8,6 +8,7 @@ import { CmsComponent } from '../../../model/cms.model'; import { PageContext } from '../../../routing/index'; import { StateUtils } from '../../../state/utils/index'; import { COMPONENT_ENTITY } from '../cms-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_CMS_COMPONENT = '[Cms] Load Component'; export const LOAD_CMS_COMPONENT_FAIL = '[Cms] Load Component Fail'; @@ -16,6 +17,7 @@ export const CMS_GET_COMPONENT_FROM_PAGE = '[Cms] Get Component from Page'; export class LoadCmsComponent extends StateUtils.EntityLoadAction { readonly type = LOAD_CMS_COMPONENT; + constructor( public payload: { uid: string; @@ -28,8 +30,13 @@ export class LoadCmsComponent extends StateUtils.EntityLoadAction { export class LoadCmsComponentFail extends StateUtils.EntityFailAction { readonly type = LOAD_CMS_COMPONENT_FAIL; + constructor( - public payload: { uid: string; error?: any; pageContext: PageContext } + public payload: { + uid: string; + error: ErrorActionType; + pageContext: PageContext; + } ) { super(COMPONENT_ENTITY, payload.uid, payload.error); } @@ -39,6 +46,7 @@ export class LoadCmsComponentSuccess< T extends CmsComponent > extends StateUtils.EntitySuccessAction { readonly type = LOAD_CMS_COMPONENT_SUCCESS; + constructor( public payload: { component: T; @@ -54,6 +62,7 @@ export class CmsGetComponentFromPage< T extends CmsComponent > extends StateUtils.EntitySuccessAction { readonly type = CMS_GET_COMPONENT_FROM_PAGE; + constructor( public payload: | { component: T; pageContext: PageContext } diff --git a/projects/core/src/cms/store/actions/navigation-entry-item.action.spec.ts b/projects/core/src/cms/store/actions/navigation-entry-item.action.spec.ts index a1683014728..b6c226f061c 100755 --- a/projects/core/src/cms/store/actions/navigation-entry-item.action.spec.ts +++ b/projects/core/src/cms/store/actions/navigation-entry-item.action.spec.ts @@ -26,20 +26,17 @@ describe('Navigation Entry Item Actions', () => { describe('LoadCmsNavigationItemsFail', () => { it('should create an action', () => { - const payload = { message: 'Load Error' }; + const error = { message: 'Load Error' }; const nodeId = 'test_uid'; - const action = new CmsActions.LoadCmsNavigationItemsFail( - nodeId, - payload - ); + const action = new CmsActions.LoadCmsNavigationItemsFail(nodeId, error); expect({ ...action }).toEqual({ type: CmsActions.LOAD_CMS_NAVIGATION_ITEMS_FAIL, - payload, + error, meta: StateUtils.entityFailMeta( NAVIGATION_DETAIL_ENTITY, nodeId, - payload + error ), }); }); diff --git a/projects/core/src/cms/store/actions/navigation-entry-item.action.ts b/projects/core/src/cms/store/actions/navigation-entry-item.action.ts index 958329c5014..b9fd205bcbd 100755 --- a/projects/core/src/cms/store/actions/navigation-entry-item.action.ts +++ b/projects/core/src/cms/store/actions/navigation-entry-item.action.ts @@ -6,6 +6,7 @@ import { StateUtils } from '../../../state/utils/index'; import { NAVIGATION_DETAIL_ENTITY } from '../cms-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_CMS_NAVIGATION_ITEMS = '[Cms] Load NavigationEntry items'; export const LOAD_CMS_NAVIGATION_ITEMS_FAIL = @@ -15,6 +16,7 @@ export const LOAD_CMS_NAVIGATION_ITEMS_SUCCESS = export class LoadCmsNavigationItems extends StateUtils.EntityLoadAction { readonly type = LOAD_CMS_NAVIGATION_ITEMS; + constructor(public payload: { nodeId: string; items: any[] }) { super(NAVIGATION_DETAIL_ENTITY, payload.nodeId); } @@ -22,13 +24,15 @@ export class LoadCmsNavigationItems extends StateUtils.EntityLoadAction { export class LoadCmsNavigationItemsFail extends StateUtils.EntityFailAction { readonly type = LOAD_CMS_NAVIGATION_ITEMS_FAIL; - constructor(nodeId: string, public payload: any) { - super(NAVIGATION_DETAIL_ENTITY, nodeId, payload); + + constructor(nodeId: string, public error: ErrorActionType) { + super(NAVIGATION_DETAIL_ENTITY, nodeId, error); } } export class LoadCmsNavigationItemsSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_CMS_NAVIGATION_ITEMS_SUCCESS; + constructor(public payload: { nodeId: string; components: any[] }) { super(NAVIGATION_DETAIL_ENTITY, payload.nodeId); } diff --git a/projects/core/src/cms/store/actions/page.action.spec.ts b/projects/core/src/cms/store/actions/page.action.spec.ts index a7a0478dfb4..7b3043539ec 100755 --- a/projects/core/src/cms/store/actions/page.action.spec.ts +++ b/projects/core/src/cms/store/actions/page.action.spec.ts @@ -25,15 +25,16 @@ describe('Cms Page Actions', () => { describe('LoadCmsPageDataFail', () => { it('should create the action', () => { - const payload = 'error'; - const action = new CmsActions.LoadCmsPageDataFail(pageContext, payload); + const error = new Error('error'); + const action = new CmsActions.LoadCmsPageDataFail(pageContext, error); expect({ ...action }).toEqual({ + error, type: CmsActions.LOAD_CMS_PAGE_DATA_FAIL, meta: StateUtils.entityFailMeta( pageContext.type, pageContext.id, - payload + error ), }); }); @@ -41,6 +42,7 @@ describe('Cms Page Actions', () => { describe('CmsSetPageFailIndex', () => { it('should create the action', () => { + const error = new Error('Failed to set cms page index'); const newIndex = 'index'; const action = new CmsActions.CmsSetPageFailIndex( pageContext, @@ -50,7 +52,12 @@ describe('Cms Page Actions', () => { expect({ ...action }).toEqual({ payload: newIndex, type: CmsActions.CMS_SET_PAGE_FAIL_INDEX, - meta: StateUtils.entityFailMeta(pageContext.type, pageContext.id), + meta: StateUtils.entityFailMeta( + pageContext.type, + pageContext.id, + error + ), + error, }); }); }); diff --git a/projects/core/src/cms/store/actions/page.action.ts b/projects/core/src/cms/store/actions/page.action.ts index af0dc85537c..a0cb91d24a5 100755 --- a/projects/core/src/cms/store/actions/page.action.ts +++ b/projects/core/src/cms/store/actions/page.action.ts @@ -7,6 +7,7 @@ import { PageContext } from '../../../routing/index'; import { StateUtils } from '../../../state/utils/index'; import { Page } from '../../model/page.model'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_CMS_PAGE_DATA = '[Cms] Load Page Data'; export const LOAD_CMS_PAGE_DATA_FAIL = '[Cms] Load Page Data Fail'; @@ -16,6 +17,7 @@ export const CMS_SET_PAGE_FAIL_INDEX = '[Cms] Set Page Fail Index'; export class LoadCmsPageData extends StateUtils.EntityLoadAction { readonly type = LOAD_CMS_PAGE_DATA; + constructor(public payload: PageContext) { super(payload.type ?? '', payload.id); } @@ -23,13 +25,15 @@ export class LoadCmsPageData extends StateUtils.EntityLoadAction { export class LoadCmsPageDataFail extends StateUtils.EntityFailAction { readonly type = LOAD_CMS_PAGE_DATA_FAIL; - constructor(pageContext: PageContext, error: any) { + + constructor(pageContext: PageContext, public error: ErrorActionType) { super(pageContext.type ?? '', pageContext.id, error); } } export class LoadCmsPageDataSuccess extends StateUtils.EntitySuccessAction { readonly type = LOAD_CMS_PAGE_DATA_SUCCESS; + constructor(pageContext: PageContext, payload: Page) { super(pageContext.type ?? '', pageContext.id, payload); } @@ -37,6 +41,7 @@ export class LoadCmsPageDataSuccess extends StateUtils.EntitySuccessAction { export class CmsSetPageSuccessIndex extends StateUtils.EntitySuccessAction { readonly type = CMS_SET_PAGE_SUCCESS_INDEX; + constructor(pageContext: PageContext, payload: Page) { super(pageContext.type ?? '', pageContext.id, payload); } @@ -44,8 +49,13 @@ export class CmsSetPageSuccessIndex extends StateUtils.EntitySuccessAction { export class CmsSetPageFailIndex extends StateUtils.EntityFailAction { readonly type = CMS_SET_PAGE_FAIL_INDEX; + constructor(pageContext: PageContext, public payload: string) { - super(pageContext.type ?? '', pageContext.id); + super( + pageContext.type ?? '', + pageContext.id, + new Error('Failed to set cms page index') + ); } } diff --git a/projects/core/src/cms/store/effects/components.effect.spec.ts b/projects/core/src/cms/store/effects/components.effect.spec.ts index 8ddf0696e9c..c2fe2c23bd5 100755 --- a/projects/core/src/cms/store/effects/components.effect.spec.ts +++ b/projects/core/src/cms/store/effects/components.effect.spec.ts @@ -98,6 +98,9 @@ describe('Component Effects', () => { pageContext, }); const completion = new CmsActions.LoadCmsComponentFail({ + error: { + message: `Failed to load CmsComponent ${pageContext.type} uid: comp1`, + }, uid: action.payload.uid, pageContext, }); diff --git a/projects/core/src/cms/store/effects/components.effect.ts b/projects/core/src/cms/store/effects/components.effect.ts index 55808175eb3..db3b2d25707 100755 --- a/projects/core/src/cms/store/effects/components.effect.ts +++ b/projects/core/src/cms/store/effects/components.effect.ts @@ -4,17 +4,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Action } from '@ngrx/store'; -import { Observable, from } from 'rxjs'; +import { from, Observable } from 'rxjs'; import { catchError, groupBy, mergeMap, switchMap } from 'rxjs/operators'; import { AuthActions } from '../../../auth/user-auth/store/actions/index'; import { LoggerService } from '../../../logger'; import { CmsComponent } from '../../../model/cms.model'; import { PageContext } from '../../../routing/index'; import { SiteContextActions } from '../../../site-context/store/actions/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { bufferDebounceTime } from '../../../util/rxjs/buffer-debounce-time'; import { withdrawOn } from '../../../util/rxjs/withdraw-on'; import { CmsComponentConnector } from '../../connectors/component/cms-component.connector'; @@ -94,6 +94,9 @@ export class ComponentsEffects { actions.push( new CmsActions.LoadCmsComponentFail({ uid, + error: { + message: `Failed to load CmsComponent ${pageContext.type} uid: ${uid}`, + }, pageContext, }) ); @@ -106,7 +109,7 @@ export class ComponentsEffects { (uid) => new CmsActions.LoadCmsComponentFail({ uid, - error: normalizeHttpError(error, this.logger), + error: tryNormalizeHttpError(error, this.logger), pageContext, }) ) diff --git a/projects/core/src/cms/store/effects/navigation-entry-item.effect.ts b/projects/core/src/cms/store/effects/navigation-entry-item.effect.ts index 8f514e8d44b..2a55b61927e 100755 --- a/projects/core/src/cms/store/effects/navigation-entry-item.effect.ts +++ b/projects/core/src/cms/store/effects/navigation-entry-item.effect.ts @@ -4,13 +4,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, filter, map, mergeMap, take } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; import { RoutingService } from '../../../routing/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { isNotUndefined } from '../../../util/type-guards'; import { CmsComponentConnector } from '../../connectors/component/cms-component.connector'; import { CmsActions } from '../actions/index'; @@ -54,7 +54,7 @@ export class NavigationEntryItemEffects { of( new CmsActions.LoadCmsNavigationItemsFail( data.nodeId, - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -71,7 +71,7 @@ export class NavigationEntryItemEffects { return of( new CmsActions.LoadCmsNavigationItemsFail( data.nodeId, - 'navigation nodes are empty' + new Error('navigation nodes are empty') ) ); } diff --git a/projects/core/src/cms/store/effects/page.effect.spec.ts b/projects/core/src/cms/store/effects/page.effect.spec.ts index 92a4446bb22..e7bd02e579c 100644 --- a/projects/core/src/cms/store/effects/page.effect.spec.ts +++ b/projects/core/src/cms/store/effects/page.effect.spec.ts @@ -15,7 +15,7 @@ import { CmsActions } from '../actions/index'; import { CMS_FEATURE } from '../cms-state'; import * as fromEffects from './page.effect'; import { HttpErrorResponse } from '@angular/common/http'; -import { normalizeHttpError } from '@spartacus/core'; +import { tryNormalizeHttpError } from '@spartacus/core'; function mockDateNow(): number { return 1000000000000; @@ -142,7 +142,7 @@ describe('Page Effects', () => { const completion = new CmsActions.LoadCmsPageDataFail( pageContext, - normalizeHttpError(error) + tryNormalizeHttpError(error) ); actions$ = hot('-a', { a: action }); diff --git a/projects/core/src/cms/store/effects/page.effect.ts b/projects/core/src/cms/store/effects/page.effect.ts index 689d0231d8c..0461fb97ed9 100755 --- a/projects/core/src/cms/store/effects/page.effect.ts +++ b/projects/core/src/cms/store/effects/page.effect.ts @@ -21,7 +21,7 @@ import { AuthActions } from '../../../auth/user-auth/store/actions/index'; import { LoggerService } from '../../../logger'; import { RoutingService } from '../../../routing/index'; import { SiteContextActions } from '../../../site-context/store/actions/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { CmsPageConnector } from '../../connectors/page/cms-page.connector'; import { CmsStructureModel } from '../../model/page.model'; import { serializePageContext } from '../../utils/cms-utils'; @@ -96,7 +96,7 @@ export class PageEffects { of( new CmsActions.LoadCmsPageDataFail( pageContext, - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/cms/store/reducers/page-index.reducer.spec.ts b/projects/core/src/cms/store/reducers/page-index.reducer.spec.ts index 8d03217345b..5a3250d8411 100644 --- a/projects/core/src/cms/store/reducers/page-index.reducer.spec.ts +++ b/projects/core/src/cms/store/reducers/page-index.reducer.spec.ts @@ -41,7 +41,7 @@ describe('Cms Page Index Reducer', () => { describe('LOAD_PAGE_DATA_FAIL action', () => { it('should return the initial state', () => { - const error = 'error'; + const error = new Error('error'); const { initialState } = fromPage; const action = new CmsActions.LoadCmsPageDataFail(pageContext, error); const state = fromPage.reducer(PageType.CONTENT_PAGE)( diff --git a/projects/core/src/error-handling/effects-error-handler/cx-error-hadnler.effect.spec.ts b/projects/core/src/error-handling/effects-error-handler/cx-error-hadnler.effect.spec.ts new file mode 100644 index 00000000000..f6a0dbd037d --- /dev/null +++ b/projects/core/src/error-handling/effects-error-handler/cx-error-hadnler.effect.spec.ts @@ -0,0 +1,72 @@ +import { TestBed } from '@angular/core/testing'; +import { Actions } from '@ngrx/effects'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { Observable, of } from 'rxjs'; +import { CxErrorHandlerEffect } from './cx-error-handler.effect'; +import { ErrorAction } from '@spartacus/core'; +import { EffectsErrorHandlerService } from './effects-error-handler.service'; +import { Action } from '@ngrx/store'; + +describe('CxErrorHandlerEffect', () => { + let effect: CxErrorHandlerEffect; + let actions$: Observable; + let effectErrorHandler: jasmine.SpyObj; + + beforeEach(() => { + const errorHandlerSpy = jasmine.createSpyObj('EffectsErrorHandlerService', [ + 'handleError', + 'filterActions', + ]); + + TestBed.configureTestingModule({ + providers: [ + CxErrorHandlerEffect, + provideMockActions(() => actions$), + { provide: EffectsErrorHandlerService, useValue: errorHandlerSpy }, + ], + }); + + effect = TestBed.inject(CxErrorHandlerEffect); + actions$ = TestBed.inject(Actions); + effectErrorHandler = TestBed.inject( + EffectsErrorHandlerService + ) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(effect).toBeTruthy(); + }); + + describe('error$', () => { + it('should handle error action', () => { + const mockErrorAction: ErrorAction = { + type: 'ERROR_ACTION_TYPE', + error: new Error(), + }; + + effectErrorHandler.filterActions.and.returnValue(true); + + actions$ = of(mockErrorAction); + + effect.error$.subscribe(); + + expect(effectErrorHandler.handleError).toHaveBeenCalledWith( + mockErrorAction + ); + }); + + it('should not handle non-error action', () => { + const mockNonErrorAction = { + type: 'SOME_ACTION', + }; + + effectErrorHandler.filterActions.and.returnValue(false); + + actions$ = of(mockNonErrorAction); + + effect.error$.subscribe(); + + expect(effectErrorHandler.handleError).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts b/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts new file mode 100644 index 00000000000..c22fef19170 --- /dev/null +++ b/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Injectable } from '@angular/core'; +import { Actions, createEffect } from '@ngrx/effects'; +import { filter, tap } from 'rxjs/operators'; +import { EffectsErrorHandlerService } from './effects-error-handler.service'; +import { ErrorAction } from '@spartacus/core'; +import { Observable } from 'rxjs'; + +@Injectable() +export class CxErrorHandlerEffect { + error$: Observable = createEffect( + () => + this.actions$.pipe( + filter(this.effectErrorHandler.filterActions), + tap((errorAction: ErrorAction) => + this.effectErrorHandler.handleError(errorAction) + ) + ), + { dispatch: false } + ); + + constructor( + protected actions$: Actions, + protected effectErrorHandler: EffectsErrorHandlerService + ) {} +} diff --git a/projects/core/src/error-handling/effects-error-handler/effects-error-handler.module.ts b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.module.ts new file mode 100644 index 00000000000..b6ac1ce6498 --- /dev/null +++ b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.module.ts @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { EffectsModule } from '@ngrx/effects'; +import { CxErrorHandlerEffect } from './cx-error-handler.effect'; +import { EffectsErrorHandlerService } from './effects-error-handler.service'; + +@NgModule({ + imports: [EffectsModule.forFeature([CxErrorHandlerEffect])], +}) +export class EffectsErrorHandlerModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: EffectsErrorHandlerModule, + providers: [EffectsErrorHandlerService], + }; + } +} diff --git a/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.spec.ts b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.spec.ts new file mode 100644 index 00000000000..45db001fcaa --- /dev/null +++ b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.spec.ts @@ -0,0 +1,91 @@ +import { TestBed } from '@angular/core/testing'; +import { ErrorHandler } from '@angular/core'; +import { EffectsErrorHandlerService } from './effects-error-handler.service'; +import { ErrorAction, HttpErrorModel } from '@spartacus/core'; +import { HttpErrorResponse } from '@angular/common/http'; +import { Action } from '@ngrx/store'; + +describe('EffectsErrorHandlerService', () => { + let service: EffectsErrorHandlerService; + let errorHandlerSpy: jasmine.SpyObj; + + beforeEach(() => { + const errorHandlerSpyObj = jasmine.createSpyObj('ErrorHandler', [ + 'handleError', + ]); + + TestBed.configureTestingModule({ + providers: [ + EffectsErrorHandlerService, + { provide: ErrorHandler, useValue: errorHandlerSpyObj }, + ], + }); + + service = TestBed.inject(EffectsErrorHandlerService); + errorHandlerSpy = TestBed.inject( + ErrorHandler + ) as jasmine.SpyObj; + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('handleError', () => { + it('should call ErrorHandler.handleError if error is not HttpErrorModel or HttpErrorResponse', () => { + const mockErrorAction: ErrorAction = { + type: 'ERROR_ACTION_TYPE', + error: new Error('Test error'), + }; + + service.handleError(mockErrorAction); + + expect(errorHandlerSpy.handleError).toHaveBeenCalledWith( + mockErrorAction.error + ); + }); + + it('should not call ErrorHandler.handleError if error is HttpErrorModel', () => { + const mockErrorAction: ErrorAction = { + type: 'ERROR_ACTION_TYPE', + error: new HttpErrorModel(), + }; + + service.handleError(mockErrorAction); + + expect(errorHandlerSpy.handleError).not.toHaveBeenCalled(); + }); + + it('should not call ErrorHandler.handleError if error is HttpErrorResponse', () => { + const mockErrorAction: ErrorAction = { + type: 'ERROR_ACTION_TYPE', + error: new HttpErrorResponse({}), + }; + + service.handleError(mockErrorAction); + + expect(errorHandlerSpy.handleError).not.toHaveBeenCalled(); + }); + }); + + describe('filterActions', () => { + it('should return true for action implementing ErrorAction interface', () => { + const mockErrorAction: ErrorAction = { + type: 'ERROR_ACTION_TYPE', + error: new Error(), + }; + + const result = service.filterActions(mockErrorAction); + + expect(result).toBeTrue(); + }); + + it('should return false for action not implementing ErrorAction interface', () => { + const mockNonErrorAction = { type: 'SOME_ACTION' }; + + const result = service.filterActions(mockNonErrorAction as Action); + + expect(result).toBeFalse(); + }); + }); +}); diff --git a/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts new file mode 100644 index 00000000000..b8ce182e244 --- /dev/null +++ b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ErrorHandler, Injectable } from '@angular/core'; +import { Action } from '@ngrx/store'; +import { ErrorAction, ErrorActionType, HttpErrorModel } from '@spartacus/core'; +import { HttpErrorResponse } from '@angular/common/http'; + +@Injectable() +export class EffectsErrorHandlerService { + constructor(protected errorHandler: ErrorHandler) {} + + handleError(action: ErrorAction): void { + const error: ErrorActionType = action.error; + + // Http errors are already handled in HttpErrorHandlerInterceptor. + // To avoid duplicate errors we want to check if the error is not of type + // HttpErrorModel or HttpErrorResponse. + const isNotHttpError = + !(error instanceof HttpErrorModel) && + !(error instanceof HttpErrorResponse); + + if (isNotHttpError) { + this.errorHandler.handleError(error); + } + } + + /** Here we want to filter which error actions should be handled. + * By default, we check if action implements interface ErrorAction */ + filterActions(action: Action): action is ErrorAction { + return 'error' in action; + } +} diff --git a/projects/core/src/error-handling/effects-error-handler/index.ts b/projects/core/src/error-handling/effects-error-handler/index.ts new file mode 100644 index 00000000000..dda1f07b450 --- /dev/null +++ b/projects/core/src/error-handling/effects-error-handler/index.ts @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './effects-error-handler.module'; +export * from './effects-error-handler.service'; +export * from '../../model/error-action'; diff --git a/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts b/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts index 7a0d11ca3ce..0233f49b53a 100644 --- a/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts +++ b/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts @@ -13,7 +13,6 @@ export class HttpErrorHandlerModule { static forRoot(): ModuleWithProviders { return { ngModule: HttpErrorHandlerModule, - providers: [ { provide: HTTP_INTERCEPTORS, diff --git a/projects/core/src/error-handling/index.ts b/projects/core/src/error-handling/index.ts index 210387276fa..67b2466aa3a 100644 --- a/projects/core/src/error-handling/index.ts +++ b/projects/core/src/error-handling/index.ts @@ -7,3 +7,4 @@ export * from './cx-error-handler'; export * from './error-handling.module'; export * from './http-error-handler'; +export * from './effects-error-handler'; diff --git a/projects/core/src/model/error-action.ts b/projects/core/src/model/error-action.ts new file mode 100644 index 00000000000..4f1963d5c45 --- /dev/null +++ b/projects/core/src/model/error-action.ts @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { HttpErrorResponse } from '@angular/common/http'; +import { Action } from '@ngrx/store'; +import { HttpErrorModel } from './index'; + +export type ErrorActionType = HttpErrorResponse | HttpErrorModel | Error; + +export interface ErrorAction extends Action { + error: ErrorActionType; +} diff --git a/projects/core/src/model/index.ts b/projects/core/src/model/index.ts index 5c0d1d0c5ea..bbd95c862cf 100644 --- a/projects/core/src/model/index.ts +++ b/projects/core/src/model/index.ts @@ -18,3 +18,4 @@ export * from './product-interest.model'; export * from './product-search.model'; export * from './product.model'; export * from './scoped-data'; +export * from './error-action'; diff --git a/projects/core/src/process/store/selectors/process.selectors.spec.ts b/projects/core/src/process/store/selectors/process.selectors.spec.ts index 934141fdd36..2c117c7362d 100644 --- a/projects/core/src/process/store/selectors/process.selectors.spec.ts +++ b/projects/core/src/process/store/selectors/process.selectors.spec.ts @@ -81,7 +81,11 @@ describe('Process selectors', () => { describe('getProcessErrorFactory', () => { it('should return success flag', () => { store.dispatch( - new StateUtils.EntityFailAction(PROCESS_FEATURE, MOCK_PROCESS_ID) + new StateUtils.EntityFailAction( + PROCESS_FEATURE, + MOCK_PROCESS_ID, + new Error('error') + ) ); let result = false; diff --git a/projects/core/src/product/store/actions/product-references.action.spec.ts b/projects/core/src/product/store/actions/product-references.action.spec.ts index 578ae9fe9b1..8c8ad9937f5 100644 --- a/projects/core/src/product/store/actions/product-references.action.spec.ts +++ b/projects/core/src/product/store/actions/product-references.action.spec.ts @@ -24,11 +24,11 @@ describe('Product References Actions', () => { describe('LOAD_PRODUCT_REFERENCES_FAIL', () => { it('should create the action', () => { - const payload: ErrorModel = { message: 'Load Error' }; - const action = new ProductActions.LoadProductReferencesFail(payload); + const error: ErrorModel = { message: 'Load Error' }; + const action = new ProductActions.LoadProductReferencesFail(error); expect({ ...action }).toEqual({ type: ProductActions.LOAD_PRODUCT_REFERENCES_FAIL, - payload, + error, }); }); }); diff --git a/projects/core/src/product/store/actions/product-references.action.ts b/projects/core/src/product/store/actions/product-references.action.ts index a823d671b1f..274486ccf6e 100644 --- a/projects/core/src/product/store/actions/product-references.action.ts +++ b/projects/core/src/product/store/actions/product-references.action.ts @@ -7,6 +7,7 @@ import { Action } from '@ngrx/store'; import { ErrorModel } from '../../../model/misc.model'; import { ProductReference } from '../../../model/product.model'; +import { ErrorAction } from '@spartacus/core'; export const LOAD_PRODUCT_REFERENCES = '[Product] Load Product References Data'; export const LOAD_PRODUCT_REFERENCES_FAIL = @@ -26,9 +27,9 @@ export class LoadProductReferences implements Action { ) {} } -export class LoadProductReferencesFail implements Action { +export class LoadProductReferencesFail implements ErrorAction { readonly type = LOAD_PRODUCT_REFERENCES_FAIL; - constructor(public payload: ErrorModel) {} + constructor(public error: ErrorModel) {} } export class LoadProductReferencesSuccess implements Action { diff --git a/projects/core/src/product/store/actions/product-reviews.action.spec.ts b/projects/core/src/product/store/actions/product-reviews.action.spec.ts index 370f5e7cb05..f0f94c54cd4 100644 --- a/projects/core/src/product/store/actions/product-reviews.action.spec.ts +++ b/projects/core/src/product/store/actions/product-reviews.action.spec.ts @@ -17,11 +17,11 @@ describe('Product Review Actions', () => { describe('LOAD_PRODUCT_REVIEWS_FAIL', () => { it('should create the action', () => { - const payload: ErrorModel = { message: 'Load Error' }; - const action = new ProductActions.LoadProductReviewsFail(payload); + const error: ErrorModel = { message: 'Load Error' }; + const action = new ProductActions.LoadProductReviewsFail(error); expect({ ...action }).toEqual({ type: ProductActions.LOAD_PRODUCT_REVIEWS_FAIL, - payload, + error, }); }); }); diff --git a/projects/core/src/product/store/actions/product-reviews.action.ts b/projects/core/src/product/store/actions/product-reviews.action.ts index a40d3851578..7c99a80d074 100644 --- a/projects/core/src/product/store/actions/product-reviews.action.ts +++ b/projects/core/src/product/store/actions/product-reviews.action.ts @@ -7,6 +7,7 @@ import { Action } from '@ngrx/store'; import { ErrorModel } from '../../../model/misc.model'; import { Review } from '../../../model/product.model'; +import { ErrorAction } from '@spartacus/core'; export const LOAD_PRODUCT_REVIEWS = '[Product] Load Product Reviews Data'; export const LOAD_PRODUCT_REVIEWS_FAIL = @@ -23,9 +24,9 @@ export class LoadProductReviews implements Action { constructor(public payload: string) {} } -export class LoadProductReviewsFail implements Action { +export class LoadProductReviewsFail implements ErrorAction { readonly type = LOAD_PRODUCT_REVIEWS_FAIL; - constructor(public payload: ErrorModel) {} + constructor(public error: ErrorModel) {} } export class LoadProductReviewsSuccess implements Action { @@ -38,9 +39,9 @@ export class PostProductReview implements Action { constructor(public payload: { productCode: string; review: Review }) {} } -export class PostProductReviewFail implements Action { +export class PostProductReviewFail implements ErrorAction { readonly type = POST_PRODUCT_REVIEW_FAIL; - constructor(public payload: string) {} + constructor(public error: ErrorModel) {} } export class PostProductReviewSuccess implements Action { diff --git a/projects/core/src/product/store/actions/product-search.action.spec.ts b/projects/core/src/product/store/actions/product-search.action.spec.ts index 5e0424ba973..5fb101e2d7c 100644 --- a/projects/core/src/product/store/actions/product-search.action.spec.ts +++ b/projects/core/src/product/store/actions/product-search.action.spec.ts @@ -39,12 +39,12 @@ describe('Product Search Actions', () => { describe('SearchProductsFail', () => { it('should create an action', () => { - const payload: ErrorModel = { message: 'Load Error' }; - const action = new fromProductSearch.SearchProductsFail(payload); + const error: ErrorModel = { message: 'Load Error' }; + const action = new fromProductSearch.SearchProductsFail(error); expect({ ...action }).toEqual({ type: fromProductSearch.SEARCH_PRODUCTS_FAIL, - payload, + error, auxiliary: undefined, }); }); @@ -94,12 +94,12 @@ describe('Product Search Actions', () => { describe('SearchProductSuggestionsFail', () => { it('should create an action', () => { - const payload: ErrorModel = { message: 'Load Error' }; - const action = new fromProductSearch.GetProductSuggestionsFail(payload); + const error: ErrorModel = { message: 'Load Error' }; + const action = new fromProductSearch.GetProductSuggestionsFail(error); expect({ ...action }).toEqual({ type: fromProductSearch.GET_PRODUCT_SUGGESTIONS_FAIL, - payload, + error, }); }); }); diff --git a/projects/core/src/product/store/actions/product-search.action.ts b/projects/core/src/product/store/actions/product-search.action.ts index 401ee54e9b3..f294d911c5f 100644 --- a/projects/core/src/product/store/actions/product-search.action.ts +++ b/projects/core/src/product/store/actions/product-search.action.ts @@ -12,6 +12,7 @@ import { Suggestion, } from '../../../model/product-search.model'; import { SearchConfig } from '../../model/search-config'; +import { ErrorAction } from '@spartacus/core'; export const SEARCH_PRODUCTS = '[Product] Search Products'; export const SEARCH_PRODUCTS_FAIL = '[Product] Search Products Fail'; @@ -32,12 +33,9 @@ export class SearchProducts implements Action { ) {} } -export class SearchProductsFail implements Action { +export class SearchProductsFail implements ErrorAction { readonly type = SEARCH_PRODUCTS_FAIL; - constructor( - public payload: ErrorModel | undefined, - public auxiliary?: boolean - ) {} + constructor(public error: ErrorModel, public auxiliary?: boolean) {} } export class SearchProductsSuccess implements Action { @@ -55,9 +53,9 @@ export class GetProductSuggestionsSuccess implements Action { constructor(public payload: Suggestion[]) {} } -export class GetProductSuggestionsFail implements Action { +export class GetProductSuggestionsFail implements ErrorAction { readonly type = GET_PRODUCT_SUGGESTIONS_FAIL; - constructor(public payload: ErrorModel | undefined) {} + constructor(public error: ErrorModel) {} } export class ClearProductSearchResult implements Action { diff --git a/projects/core/src/product/store/actions/product.action.spec.ts b/projects/core/src/product/store/actions/product.action.spec.ts index 2b2d2326e4f..66a0c72fa20 100644 --- a/projects/core/src/product/store/actions/product.action.spec.ts +++ b/projects/core/src/product/store/actions/product.action.spec.ts @@ -29,6 +29,7 @@ describe('Product Actions', () => { const action = new fromProduct.LoadProductFail(productCode, payload); expect({ ...action }).toEqual({ + error: payload, type: fromProduct.LOAD_PRODUCT_FAIL, payload, meta: EntityScopedLoaderActions.entityScopedFailMeta( diff --git a/projects/core/src/product/store/actions/product.action.ts b/projects/core/src/product/store/actions/product.action.ts index 01339af46d3..161250a03f4 100644 --- a/projects/core/src/product/store/actions/product.action.ts +++ b/projects/core/src/product/store/actions/product.action.ts @@ -10,6 +10,7 @@ import { EntityLoaderMeta } from '../../../state/utils/entity-loader/entity-load import { EntityScopedLoaderActions } from '../../../state/utils/scoped-loader/entity-scoped-loader.actions'; import { ProductScope } from '../../model/product-scope'; import { PRODUCT_DETAIL_ENTITY } from '../product-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_PRODUCT = '[Product] Load Product Data'; export const LOAD_PRODUCT_FAIL = '[Product] Load Product Data Fail'; @@ -27,6 +28,7 @@ export interface EntityScopedLoaderAction extends Action { export class LoadProduct extends EntityScopedLoaderActions.EntityScopedLoadAction { readonly type = LOAD_PRODUCT; + constructor(public payload: string, scope = '') { super(PRODUCT_DETAIL_ENTITY, payload, scope); } @@ -34,13 +36,19 @@ export class LoadProduct extends EntityScopedLoaderActions.EntityScopedLoadActio export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction { readonly type = LOAD_PRODUCT_FAIL; - constructor(productCode: string, public payload: any, scope = '') { - super(PRODUCT_DETAIL_ENTITY, productCode, scope, payload); + + constructor( + productCode: string, + public payload: ErrorActionType, + scope = '' + ) { + super(PRODUCT_DETAIL_ENTITY, productCode, payload, scope); } } export class LoadProductSuccess extends EntityScopedLoaderActions.EntityScopedSuccessAction { readonly type = LOAD_PRODUCT_SUCCESS; + constructor(public payload: Product, scope = '') { super(PRODUCT_DETAIL_ENTITY, payload.code ?? '', scope); } @@ -48,6 +56,7 @@ export class LoadProductSuccess extends EntityScopedLoaderActions.EntityScopedSu export class ClearProductPrice extends EntityScopedLoaderActions.EntityScopedResetAction { readonly type = CLEAR_PRODUCT_PRICE; + constructor() { super(PRODUCT_DETAIL_ENTITY, undefined, ProductScope.PRICE); } diff --git a/projects/core/src/product/store/effects/product-reviews.effect.ts b/projects/core/src/product/store/effects/product-reviews.effect.ts index c9aad4da941..27e22ae8278 100644 --- a/projects/core/src/product/store/effects/product-reviews.effect.ts +++ b/projects/core/src/product/store/effects/product-reviews.effect.ts @@ -62,7 +62,11 @@ export class ProductReviewsEffects { ); }), catchError((_error) => - of(new ProductActions.PostProductReviewFail(payload.productCode)) + of( + new ProductActions.PostProductReviewFail({ + message: payload.productCode, + } as ErrorModel) + ) ) ); }) diff --git a/projects/core/src/product/store/effects/product-search.effect.ts b/projects/core/src/product/store/effects/product-search.effect.ts index ca6d6e55d38..f84bda8107e 100644 --- a/projects/core/src/product/store/effects/product-search.effect.ts +++ b/projects/core/src/product/store/effects/product-search.effect.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, groupBy, map, mergeMap, switchMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { ProductSearchConnector } from '../../connectors/search/product-search.connector'; import { ProductActions } from '../actions/index'; @@ -38,7 +38,7 @@ export class ProductsSearchEffects { catchError((error) => of( new ProductActions.SearchProductsFail( - normalizeHttpError(error, this.logger), + tryNormalizeHttpError(error, this.logger), action.auxiliary ) ) @@ -72,7 +72,7 @@ export class ProductsSearchEffects { catchError((error) => of( new ProductActions.GetProductSuggestionsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/product/store/effects/product.effect.ts b/projects/core/src/product/store/effects/product.effect.ts index 3192d8fe193..0499cf4e80c 100644 --- a/projects/core/src/product/store/effects/product.effect.ts +++ b/projects/core/src/product/store/effects/product.effect.ts @@ -12,7 +12,7 @@ import { catchError, map, mergeMap } from 'rxjs/operators'; import { AuthActions } from '../../../auth/user-auth/store/actions'; import { LoggerService } from '../../../logger'; import { SiteContextActions } from '../../../site-context/store/actions/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { bufferDebounceTime } from '../../../util/rxjs/buffer-debounce-time'; import { withdrawOn } from '../../../util/rxjs/withdraw-on'; import { ProductConnector } from '../../connectors/product/product.connector'; @@ -74,7 +74,7 @@ export class ProductEffects { return of( new ProductActions.LoadProductFail( productLoad.code, - normalizeHttpError(error, this.logger), + tryNormalizeHttpError(error, this.logger), productLoad.scope ) ); @@ -83,7 +83,7 @@ export class ProductEffects { of( new ProductActions.LoadProductFail( productLoad.code, - 'Scoped product data does not exist', + new Error('Scoped product data does not exist'), productLoad.scope ) ) diff --git a/projects/core/src/site-context/store/actions/base-site.action.spec.ts b/projects/core/src/site-context/store/actions/base-site.action.spec.ts index 89077af6e33..93701be3ee8 100644 --- a/projects/core/src/site-context/store/actions/base-site.action.spec.ts +++ b/projects/core/src/site-context/store/actions/base-site.action.spec.ts @@ -14,12 +14,12 @@ describe('BaseSite Actions', () => { describe('LoadBaseSiteFail', () => { it('should create an action', () => { - const payload = { message: 'Load Error' }; - const action = new SiteContextActions.LoadBaseSiteFail(payload); + const error = { message: 'Load Error' }; + const action = new SiteContextActions.LoadBaseSiteFail(error); expect({ ...action }).toEqual({ type: SiteContextActions.LOAD_BASE_SITE_FAIL, - payload, + error, }); }); }); @@ -53,12 +53,12 @@ describe('BaseSite Actions', () => { describe('LoadBaseSitesFail', () => { it('should create an action', () => { - const payload = { message: 'Load Error' }; - const action = new SiteContextActions.LoadBaseSitesFail(payload); + const error = { message: 'Load Error' }; + const action = new SiteContextActions.LoadBaseSitesFail(error); expect({ ...action }).toEqual({ type: SiteContextActions.LOAD_BASE_SITES_FAIL, - payload, + error, }); }); }); diff --git a/projects/core/src/site-context/store/actions/base-site.action.ts b/projects/core/src/site-context/store/actions/base-site.action.ts index 4c6e820204e..5ee037e28d8 100644 --- a/projects/core/src/site-context/store/actions/base-site.action.ts +++ b/projects/core/src/site-context/store/actions/base-site.action.ts @@ -6,6 +6,7 @@ import { Action } from '@ngrx/store'; import { BaseSite } from '../../../model/misc.model'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const LOAD_BASE_SITE = '[Site-context] Load BaseSite'; export const LOAD_BASE_SITE_FAIL = '[Site-context] Load BaseSite Fail'; @@ -22,13 +23,15 @@ export class LoadBaseSite implements Action { readonly type = LOAD_BASE_SITE; } -export class LoadBaseSiteFail implements Action { +export class LoadBaseSiteFail implements ErrorAction { readonly type = LOAD_BASE_SITE_FAIL; - constructor(public payload: any) {} + + constructor(public error: ErrorActionType) {} } export class LoadBaseSiteSuccess implements Action { readonly type = LOAD_BASE_SITE_SUCCESS; + constructor(public payload: BaseSite) {} } @@ -36,18 +39,21 @@ export class LoadBaseSites implements Action { readonly type = LOAD_BASE_SITES; } -export class LoadBaseSitesFail implements Action { +export class LoadBaseSitesFail implements ErrorAction { readonly type = LOAD_BASE_SITES_FAIL; - constructor(public payload: any) {} + + constructor(public error: ErrorActionType) {} } export class LoadBaseSitesSuccess implements Action { readonly type = LOAD_BASE_SITES_SUCCESS; + constructor(public payload: BaseSite[]) {} } export class SetActiveBaseSite implements Action { readonly type = SET_ACTIVE_BASE_SITE; + constructor(public payload: string) {} } diff --git a/projects/core/src/site-context/store/actions/currencies.action.spec.ts b/projects/core/src/site-context/store/actions/currencies.action.spec.ts index 133b05631f7..10659ee242c 100644 --- a/projects/core/src/site-context/store/actions/currencies.action.spec.ts +++ b/projects/core/src/site-context/store/actions/currencies.action.spec.ts @@ -14,12 +14,12 @@ describe('Currencies Actions', () => { describe('LoadCurrenciesFail', () => { it('should create an action', () => { - const payload = { message: 'Load Error' }; - const action = new SiteContextActions.LoadCurrenciesFail(payload); + const error = { message: 'Load Error' }; + const action = new SiteContextActions.LoadCurrenciesFail(error); expect({ ...action }).toEqual({ type: SiteContextActions.LOAD_CURRENCIES_FAIL, - payload, + error, }); }); }); diff --git a/projects/core/src/site-context/store/actions/currencies.action.ts b/projects/core/src/site-context/store/actions/currencies.action.ts index bf04bffbd56..1089b834f55 100644 --- a/projects/core/src/site-context/store/actions/currencies.action.ts +++ b/projects/core/src/site-context/store/actions/currencies.action.ts @@ -6,6 +6,7 @@ import { Action } from '@ngrx/store'; import { Currency } from '../../../model/misc.model'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const LOAD_CURRENCIES = '[Site-context] Load Currencies'; export const LOAD_CURRENCIES_FAIL = '[Site-context] Load Currencies Fail'; @@ -17,9 +18,9 @@ export class LoadCurrencies implements Action { readonly type = LOAD_CURRENCIES; } -export class LoadCurrenciesFail implements Action { +export class LoadCurrenciesFail implements ErrorAction { readonly type = LOAD_CURRENCIES_FAIL; - constructor(public payload: any) {} + constructor(public error: ErrorActionType) {} } export class LoadCurrenciesSuccess implements Action { diff --git a/projects/core/src/site-context/store/actions/languages.action.spec.ts b/projects/core/src/site-context/store/actions/languages.action.spec.ts index e86a0cac248..1f58aa1b35d 100644 --- a/projects/core/src/site-context/store/actions/languages.action.spec.ts +++ b/projects/core/src/site-context/store/actions/languages.action.spec.ts @@ -14,12 +14,12 @@ describe('Languages Actions', () => { describe('LoadLanguagesFail', () => { it('should create an action', () => { - const payload = { message: 'Load Error' }; - const action = new SiteContextActions.LoadLanguagesFail(payload); + const error = { message: 'Load Error' }; + const action = new SiteContextActions.LoadLanguagesFail(error); expect({ ...action }).toEqual({ type: SiteContextActions.LOAD_LANGUAGES_FAIL, - payload, + error, }); }); }); diff --git a/projects/core/src/site-context/store/actions/languages.action.ts b/projects/core/src/site-context/store/actions/languages.action.ts index dddf77c5d02..95cefbfa7e8 100644 --- a/projects/core/src/site-context/store/actions/languages.action.ts +++ b/projects/core/src/site-context/store/actions/languages.action.ts @@ -6,6 +6,7 @@ import { Action } from '@ngrx/store'; import { Language } from '../../../model/misc.model'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const LOAD_LANGUAGES = '[Site-context] Load Languages'; export const LOAD_LANGUAGES_FAIL = '[Site-context] Load Languages Fail'; @@ -17,23 +18,27 @@ export class LoadLanguages implements Action { readonly type = LOAD_LANGUAGES; } -export class LoadLanguagesFail implements Action { +export class LoadLanguagesFail implements ErrorAction { readonly type = LOAD_LANGUAGES_FAIL; - constructor(public payload: any) {} + + constructor(public error: ErrorActionType) {} } export class LoadLanguagesSuccess implements Action { readonly type = LOAD_LANGUAGES_SUCCESS; + constructor(public payload: Language[]) {} } export class SetActiveLanguage implements Action { readonly type = SET_ACTIVE_LANGUAGE; + constructor(public payload: string) {} } export class LanguageChange implements Action { readonly type = LANGUAGE_CHANGE; + constructor( public payload: { previous: string | null; current: string | null } ) {} diff --git a/projects/core/src/site-context/store/effects/base-site.effect.ts b/projects/core/src/site-context/store/effects/base-site.effect.ts index 29f07851c3a..8efc10dfe8d 100644 --- a/projects/core/src/site-context/store/effects/base-site.effect.ts +++ b/projects/core/src/site-context/store/effects/base-site.effect.ts @@ -9,7 +9,7 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, exhaustMap, map } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { SiteConnector } from '../../connectors/site.connector'; import { SiteContextActions } from '../actions/index'; @@ -34,7 +34,7 @@ export class BaseSiteEffects { catchError((error) => of( new SiteContextActions.LoadBaseSiteFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -58,7 +58,7 @@ export class BaseSiteEffects { catchError((error) => of( new SiteContextActions.LoadBaseSitesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/site-context/store/effects/currencies.effect.ts b/projects/core/src/site-context/store/effects/currencies.effect.ts index dc5307c5aab..93ad845bc96 100644 --- a/projects/core/src/site-context/store/effects/currencies.effect.ts +++ b/projects/core/src/site-context/store/effects/currencies.effect.ts @@ -16,7 +16,7 @@ import { map, } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { SiteConnector } from '../../connectors/site.connector'; import { SiteContextActions } from '../actions/index'; import { getActiveCurrency } from '../selectors/currencies.selectors'; @@ -41,7 +41,7 @@ export class CurrenciesEffects { catchError((error) => of( new SiteContextActions.LoadCurrenciesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/site-context/store/effects/languages.effect.ts b/projects/core/src/site-context/store/effects/languages.effect.ts index 59c2bc3198c..5ab052ccfc7 100644 --- a/projects/core/src/site-context/store/effects/languages.effect.ts +++ b/projects/core/src/site-context/store/effects/languages.effect.ts @@ -16,7 +16,7 @@ import { map, } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { SiteConnector } from '../../connectors/site.connector'; import { SiteContextActions } from '../actions/index'; import { getActiveLanguage } from '../selectors/languages.selectors'; @@ -41,7 +41,7 @@ export class LanguagesEffects { catchError((error) => of( new SiteContextActions.LoadLanguagesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/state/utils/entity-loader/entity-loader.action.spec.ts b/projects/core/src/state/utils/entity-loader/entity-loader.action.spec.ts index f9015135036..7e6da9b51a2 100644 --- a/projects/core/src/state/utils/entity-loader/entity-loader.action.spec.ts +++ b/projects/core/src/state/utils/entity-loader/entity-loader.action.spec.ts @@ -30,14 +30,16 @@ describe('EntityLoader Actions', () => { describe('LoaderFailAction', () => { it('should create an action', () => { + const error = new Error('error'); const action = new EntityFailAction( TEST_ENTITY_TYPE, TEST_ENTITY_ID, - 'error' + error ); expect({ ...action }).toEqual({ type: ENTITY_FAIL_ACTION, - meta: entityFailMeta(TEST_ENTITY_TYPE, TEST_ENTITY_ID, 'error'), + meta: entityFailMeta(TEST_ENTITY_TYPE, TEST_ENTITY_ID, error), + error, }); }); }); diff --git a/projects/core/src/state/utils/entity-loader/entity-loader.action.ts b/projects/core/src/state/utils/entity-loader/entity-loader.action.ts index bf9347e25c1..ba48e554a9d 100644 --- a/projects/core/src/state/utils/entity-loader/entity-loader.action.ts +++ b/projects/core/src/state/utils/entity-loader/entity-loader.action.ts @@ -13,6 +13,7 @@ import { resetMeta, successMeta, } from '../loader/loader.action'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const ENTITY_LOAD_ACTION = '[ENTITY] LOAD'; export const ENTITY_FAIL_ACTION = '[ENTITY] LOAD FAIL'; @@ -70,22 +71,31 @@ export function entityResetMeta( export class EntityLoadAction implements EntityLoaderAction { type = ENTITY_LOAD_ACTION; readonly meta: EntityLoaderMeta; + constructor(entityType: string, id: string | string[] | null) { this.meta = entityLoadMeta(entityType, id); } } -export class EntityFailAction implements EntityLoaderAction { +export class EntityFailAction implements EntityLoaderAction, ErrorAction { type = ENTITY_FAIL_ACTION; + error: ErrorActionType; readonly meta: EntityLoaderMeta; - constructor(entityType: string, id: string | string[] | null, error?: any) { + + constructor( + entityType: string, + id: string | string[] | null, + error: ErrorActionType + ) { this.meta = entityFailMeta(entityType, id, error); + this.error = error; } } export class EntitySuccessAction implements EntityLoaderAction { type = ENTITY_SUCCESS_ACTION; readonly meta: EntityLoaderMeta; + constructor( entityType: string, id: string | string[] | null, @@ -98,6 +108,7 @@ export class EntitySuccessAction implements EntityLoaderAction { export class EntityLoaderResetAction implements EntityLoaderAction { type = ENTITY_RESET_ACTION; readonly meta: EntityLoaderMeta; + constructor(entityType: string, id: string | string[] | null) { this.meta = entityResetMeta(entityType, id); } diff --git a/projects/core/src/state/utils/entity-loader/entity-loader.reducer.spec.ts b/projects/core/src/state/utils/entity-loader/entity-loader.reducer.spec.ts index 2d7c7f3af34..6018b4dad43 100644 --- a/projects/core/src/state/utils/entity-loader/entity-loader.reducer.spec.ts +++ b/projects/core/src/state/utils/entity-loader/entity-loader.reducer.spec.ts @@ -44,7 +44,11 @@ describe('EntityLoader reducer', () => { describe('FAIL ACTION', () => { it('should set load state', () => { - const action = new EntityFailAction(TEST_ENTITY_TYPE, TEST_ENTITY_ID); + const action = new EntityFailAction( + TEST_ENTITY_TYPE, + TEST_ENTITY_ID, + new Error() + ); const state = entityLoaderReducer(TEST_ENTITY_TYPE)(undefined, action); const expectedState = { entities: { @@ -148,7 +152,11 @@ describe('EntityLoader reducer', () => { describe('FAIL ACTION', () => { it('should set load state', () => { - const action = new EntityFailAction(TEST_ENTITY_TYPE, TEST_ENTITIES_ID); + const action = new EntityFailAction( + TEST_ENTITY_TYPE, + TEST_ENTITIES_ID, + new Error() + ); const state = entityLoaderReducer(TEST_ENTITY_TYPE)(undefined, action); const expectedState = { entities: { diff --git a/projects/core/src/state/utils/loader/loader.action.spec.ts b/projects/core/src/state/utils/loader/loader.action.spec.ts index 17d9af5b185..d4bc73f68b9 100644 --- a/projects/core/src/state/utils/loader/loader.action.spec.ts +++ b/projects/core/src/state/utils/loader/loader.action.spec.ts @@ -29,10 +29,12 @@ describe('Loader Actions', () => { describe('LoaderFailAction', () => { it('should create an action', () => { - const action = new LoaderFailAction(TEST_ENTITY_TYPE, 'error'); + const error = new Error('error'); + const action = new LoaderFailAction(TEST_ENTITY_TYPE, error); expect({ ...action }).toEqual({ type: LOADER_FAIL_ACTION, - meta: failMeta(TEST_ENTITY_TYPE, 'error'), + meta: failMeta(TEST_ENTITY_TYPE, error), + error, }); }); }); diff --git a/projects/core/src/state/utils/loader/loader.action.ts b/projects/core/src/state/utils/loader/loader.action.ts index a6324fd088c..1fde5143470 100644 --- a/projects/core/src/state/utils/loader/loader.action.ts +++ b/projects/core/src/state/utils/loader/loader.action.ts @@ -5,6 +5,7 @@ */ import { Action } from '@ngrx/store'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const LOADER_LOAD_ACTION = '[LOADER] LOAD'; export const LOADER_FAIL_ACTION = '[LOADER] FAIL'; @@ -60,25 +61,31 @@ export function resetMeta(entityType: string): LoaderMeta { loader: {}, }; } + export class LoaderLoadAction implements LoaderAction { type = LOADER_LOAD_ACTION; readonly meta: LoaderMeta; + constructor(entityType: string) { this.meta = loadMeta(entityType); } } -export class LoaderFailAction implements LoaderAction { +export class LoaderFailAction implements LoaderAction, ErrorAction { type = LOADER_FAIL_ACTION; + error: ErrorActionType; readonly meta: LoaderMeta; - constructor(entityType: string, error?: any) { + + constructor(entityType: string, error: ErrorActionType) { this.meta = failMeta(entityType, error); + this.error = error; } } export class LoaderSuccessAction implements LoaderAction { type = LOADER_SUCCESS_ACTION; readonly meta: LoaderMeta; + constructor(entityType: string) { this.meta = successMeta(entityType); } @@ -87,6 +94,7 @@ export class LoaderSuccessAction implements LoaderAction { export class LoaderResetAction implements LoaderAction { type = LOADER_RESET_ACTION; readonly meta: LoaderMeta; + constructor(entityType: string) { this.meta = resetMeta(entityType); } diff --git a/projects/core/src/state/utils/loader/loader.reducer.spec.ts b/projects/core/src/state/utils/loader/loader.reducer.spec.ts index 1205053187c..ef4ccf84ce5 100644 --- a/projects/core/src/state/utils/loader/loader.reducer.spec.ts +++ b/projects/core/src/state/utils/loader/loader.reducer.spec.ts @@ -44,7 +44,8 @@ describe('Loader reducer', () => { describe('FAIL ACTION', () => { it('should set load state', () => { - const action = new LoaderFailAction(TEST_ENTITY_TYPE); + const error = new Error('error'); + const action = new LoaderFailAction(TEST_ENTITY_TYPE, error); const state = loaderReducer(TEST_ENTITY_TYPE)(undefined, action); const expectedState = { loading: false, diff --git a/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.spec.ts b/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.spec.ts index 1981e68e7e1..7274aff2ebf 100644 --- a/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.spec.ts +++ b/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.spec.ts @@ -32,11 +32,12 @@ describe('EntityScopedLoaderActions', () => { describe('EntityScopedFailAction', () => { it('should create an action', () => { + const error = new Error('error'); const action = new EntityScopedLoaderActions.EntityScopedFailAction( TEST_ENTITY_TYPE, TEST_ENTITY_ID, - SCOPE, - 'error' + error, + SCOPE ); expect({ ...action }).toEqual({ type: ENTITY_FAIL_ACTION, @@ -44,8 +45,9 @@ describe('EntityScopedLoaderActions', () => { TEST_ENTITY_TYPE, TEST_ENTITY_ID, SCOPE, - 'error' + error ), + error, }); }); }); diff --git a/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts b/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts index 2d8bcb9f8a8..e5b0c4408ee 100644 --- a/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts +++ b/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts @@ -6,16 +6,17 @@ import { Action } from '@ngrx/store'; import { + ENTITY_FAIL_ACTION, + ENTITY_LOAD_ACTION, + ENTITY_RESET_ACTION, + ENTITY_SUCCESS_ACTION, entityFailMeta, EntityLoaderMeta, entityLoadMeta, entityResetMeta, entitySuccessMeta, - ENTITY_FAIL_ACTION, - ENTITY_LOAD_ACTION, - ENTITY_RESET_ACTION, - ENTITY_SUCCESS_ACTION, } from '../entity-loader/entity-loader.action'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export namespace EntityScopedLoaderActions { export interface EntityScopedLoaderMeta extends EntityLoaderMeta { @@ -75,27 +76,34 @@ export namespace EntityScopedLoaderActions { export class EntityScopedLoadAction implements EntityScopedLoaderAction { type = ENTITY_LOAD_ACTION; readonly meta: EntityScopedLoaderMeta; + constructor(entityType: string, id: string | string[], scope?: string) { this.meta = entityScopedLoadMeta(entityType, id, scope); } } - export class EntityScopedFailAction implements EntityScopedLoaderAction { + export class EntityScopedFailAction + implements EntityScopedLoaderAction, ErrorAction + { type = ENTITY_FAIL_ACTION; + error: ErrorActionType; readonly meta: EntityScopedLoaderMeta; + constructor( entityType: string, id: string | string[], - scope?: string, - error?: any + error: ErrorActionType, + scope?: string ) { this.meta = entityScopedFailMeta(entityType, id, scope, error); + this.error = error; } } export class EntityScopedSuccessAction implements EntityScopedLoaderAction { type = ENTITY_SUCCESS_ACTION; readonly meta: EntityScopedLoaderMeta; + constructor( entityType: string, id: string | string[], @@ -109,6 +117,7 @@ export namespace EntityScopedLoaderActions { export class EntityScopedResetAction implements EntityScopedLoaderAction { type = ENTITY_RESET_ACTION; readonly meta: EntityScopedLoaderMeta; + constructor(entityType: string, id?: string | string[], scope?: string) { this.meta = entityScopedResetMeta(entityType, id, scope); } diff --git a/projects/core/src/user/facade/customer-coupon.service.spec.ts b/projects/core/src/user/facade/customer-coupon.service.spec.ts index e152fc103e7..065dc6318d6 100644 --- a/projects/core/src/user/facade/customer-coupon.service.spec.ts +++ b/projects/core/src/user/facade/customer-coupon.service.spec.ts @@ -163,7 +163,9 @@ describe('CustomerCouponService', () => { }); it('should getSubscribeCustomerCouponResultError() return the error flag', () => { - store.dispatch(new UserActions.SubscribeCustomerCouponFail('error')); + store.dispatch( + new UserActions.SubscribeCustomerCouponFail(new Error('error')) + ); let result = false; service @@ -214,7 +216,9 @@ describe('CustomerCouponService', () => { }); it('should getUnsubscribeCustomerCouponResultError() return the error flag', () => { - store.dispatch(new UserActions.UnsubscribeCustomerCouponFail('error')); + store.dispatch( + new UserActions.UnsubscribeCustomerCouponFail(new Error('error')) + ); let result = false; service diff --git a/projects/core/src/user/facade/user-consent.service.spec.ts b/projects/core/src/user/facade/user-consent.service.spec.ts index b41f0bd262f..90f4c2c87f7 100644 --- a/projects/core/src/user/facade/user-consent.service.spec.ts +++ b/projects/core/src/user/facade/user-consent.service.spec.ts @@ -188,7 +188,9 @@ describe('UserConsentService', () => { }); describe('getConsentsResultError', () => { it('should return the error flag', () => { - store.dispatch(new UserActions.LoadUserConsentsFail('an error')); + store.dispatch( + new UserActions.LoadUserConsentsFail(new Error('an error')) + ); let result = false; service @@ -391,7 +393,9 @@ describe('UserConsentService', () => { }); describe('getGiveConsentResultError', () => { it('should return the error flag', () => { - store.dispatch(new UserActions.GiveUserConsentFail('an error')); + store.dispatch( + new UserActions.GiveUserConsentFail(new Error('an error')) + ); let result = false; service @@ -457,7 +461,9 @@ describe('UserConsentService', () => { }); describe('getWithdrawConsentResultError', () => { it('should return the error flag', () => { - store.dispatch(new UserActions.WithdrawUserConsentFail('an error')); + store.dispatch( + new UserActions.WithdrawUserConsentFail(new Error('an error')) + ); let result = false; service diff --git a/projects/core/src/user/facade/user-interests.service.spec.ts b/projects/core/src/user/facade/user-interests.service.spec.ts index b08c2d069a2..39e8c19784f 100644 --- a/projects/core/src/user/facade/user-interests.service.spec.ts +++ b/projects/core/src/user/facade/user-interests.service.spec.ts @@ -154,7 +154,7 @@ describe('UserInterestsService', () => { }); it('should be able to get a product interest adding error flag', () => { - store.dispatch(new UserActions.AddProductInterestFail('error')); + store.dispatch(new UserActions.AddProductInterestFail(new Error('error'))); service .getAddProductInterestError() .subscribe((data) => expect(data).toEqual(true)) diff --git a/projects/core/src/user/store/actions/billing-countries.action.spec.ts b/projects/core/src/user/store/actions/billing-countries.action.spec.ts index ce3b574e2c8..be136ff4b98 100644 --- a/projects/core/src/user/store/actions/billing-countries.action.spec.ts +++ b/projects/core/src/user/store/actions/billing-countries.action.spec.ts @@ -14,12 +14,12 @@ describe('Billing Countries Actions', () => { describe('LoadBillingCountriesFail', () => { it('should create the action', () => { - const sampleError = 'sample error'; - const action = new UserActions.LoadBillingCountriesFail(sampleError); + const error = 'sample error'; + const action = new UserActions.LoadBillingCountriesFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_BILLING_COUNTRIES_FAIL, - payload: sampleError, + error, }); }); }); diff --git a/projects/core/src/user/store/actions/billing-countries.action.ts b/projects/core/src/user/store/actions/billing-countries.action.ts index 903e7acd58c..07c8c5ffc1e 100644 --- a/projects/core/src/user/store/actions/billing-countries.action.ts +++ b/projects/core/src/user/store/actions/billing-countries.action.ts @@ -5,6 +5,7 @@ */ import { Action } from '@ngrx/store'; +import { ErrorAction } from '@spartacus/core'; export const LOAD_BILLING_COUNTRIES = '[User] Load Billing Countries'; export const LOAD_BILLING_COUNTRIES_FAIL = '[User] Load Billing Countries Fail'; @@ -18,9 +19,9 @@ export class LoadBillingCountries implements Action { } } -export class LoadBillingCountriesFail implements Action { +export class LoadBillingCountriesFail implements ErrorAction { readonly type = LOAD_BILLING_COUNTRIES_FAIL; - constructor(public payload: any) {} + constructor(public error: any) {} } export class LoadBillingCountriesSuccess implements Action { diff --git a/projects/core/src/user/store/actions/customer-coupon.action.spec.ts b/projects/core/src/user/store/actions/customer-coupon.action.spec.ts index 50dbb083821..a8c2b95d474 100644 --- a/projects/core/src/user/store/actions/customer-coupon.action.spec.ts +++ b/projects/core/src/user/store/actions/customer-coupon.action.spec.ts @@ -67,6 +67,7 @@ const customerCoupon2Customer: CustomerCoupon2Customer = { coupon: coupon1, customer: {}, }; +const error = new Error('mockError'); describe('Customer Coupon Actions', () => { describe('LoadCustomerCoupons Action', () => { @@ -93,7 +94,7 @@ describe('Customer Coupon Actions', () => { expect({ ...action }).toEqual({ type: UserActions.LOAD_CUSTOMER_COUPONS_FAIL, - payload: error, + error, meta: failMeta(CUSTOMER_COUPONS, error), }); }); @@ -144,12 +145,11 @@ describe('Customer Coupon Actions', () => { describe('SubscribeCustomerCouponFail Action', () => { it('should create the action', () => { - const error = 'mockError'; const action = new UserActions.SubscribeCustomerCouponFail(error); expect({ ...action }).toEqual({ type: UserActions.SUBSCRIBE_CUSTOMER_COUPON_FAIL, - payload: error, + error, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, @@ -210,12 +210,11 @@ describe('Customer Coupon Actions', () => { describe('UnsubscribeCustomerCouponFail Action', () => { it('should create the action', () => { - const error = 'mockError'; const action = new UserActions.UnsubscribeCustomerCouponFail(error); expect({ ...action }).toEqual({ type: UserActions.UNSUBSCRIBE_CUSTOMER_COUPON_FAIL, - payload: error, + error, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, @@ -275,12 +274,11 @@ describe('Customer Coupon Actions', () => { describe('ClaimCustomerCouponFail Action', () => { it('should create the action', () => { - const error = 'mockError'; const action = new UserActions.ClaimCustomerCouponFail(error); expect({ ...action }).toEqual({ type: UserActions.CLAIM_CUSTOMER_COUPON_FAIL, - payload: error, + error, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, CLAIM_CUSTOMER_COUPON_PROCESS_ID, diff --git a/projects/core/src/user/store/actions/customer-coupon.action.ts b/projects/core/src/user/store/actions/customer-coupon.action.ts index 54a8377afaa..358763046a6 100644 --- a/projects/core/src/user/store/actions/customer-coupon.action.ts +++ b/projects/core/src/user/store/actions/customer-coupon.action.ts @@ -28,6 +28,7 @@ import { SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, } from '../user-state'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const LOAD_CUSTOMER_COUPONS = '[User] Load Customer Coupons'; export const LOAD_CUSTOMER_COUPONS_FAIL = '[User] Load Customer Coupons Fail'; @@ -71,10 +72,13 @@ export class LoadCustomerCoupons extends LoaderLoadAction { } } -export class LoadCustomerCouponsFail extends LoaderFailAction { +export class LoadCustomerCouponsFail + extends LoaderFailAction + implements ErrorAction +{ readonly type = LOAD_CUSTOMER_COUPONS_FAIL; - constructor(public payload: any) { - super(CUSTOMER_COUPONS, payload); + constructor(public error: any) { + super(CUSTOMER_COUPONS, error); } } @@ -107,8 +111,8 @@ export class SubscribeCustomerCoupon extends EntityLoadAction { export class SubscribeCustomerCouponFail extends EntityFailAction { readonly type = SUBSCRIBE_CUSTOMER_COUPON_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, payload); + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, error); } } @@ -140,8 +144,8 @@ export class UnsubscribeCustomerCoupon extends EntityLoadAction { export class UnsubscribeCustomerCouponFail extends EntityFailAction { readonly type = UNSUBSCRIBE_CUSTOMER_COUPON_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, payload); + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, error); } } @@ -173,8 +177,8 @@ export class ClaimCustomerCoupon extends EntityLoadAction { export class ClaimCustomerCouponFail extends EntityFailAction { readonly type = CLAIM_CUSTOMER_COUPON_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, CLAIM_CUSTOMER_COUPON_PROCESS_ID, payload); + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, CLAIM_CUSTOMER_COUPON_PROCESS_ID, error); } } diff --git a/projects/core/src/user/store/actions/delivery-countries.action.spec.ts b/projects/core/src/user/store/actions/delivery-countries.action.spec.ts index bf5b216d1a7..e73fca67d83 100644 --- a/projects/core/src/user/store/actions/delivery-countries.action.spec.ts +++ b/projects/core/src/user/store/actions/delivery-countries.action.spec.ts @@ -13,12 +13,12 @@ describe('Delivery Countries Actions', () => { describe('LoadDeliveryCountriesFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = new Error('anError'); const action = new UserActions.LoadDeliveryCountriesFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_DELIVERY_COUNTRIES_FAIL, - payload: error, + error, }); }); }); diff --git a/projects/core/src/user/store/actions/delivery-countries.action.ts b/projects/core/src/user/store/actions/delivery-countries.action.ts index a2a5ce9c791..96e339e28ad 100644 --- a/projects/core/src/user/store/actions/delivery-countries.action.ts +++ b/projects/core/src/user/store/actions/delivery-countries.action.ts @@ -6,6 +6,7 @@ import { Action } from '@ngrx/store'; import { Country } from '../../../model/address.model'; +import { ErrorAction, ErrorActionType } from '@spartacus/core'; export const LOAD_DELIVERY_COUNTRIES = '[User] Load Delivery Countries'; export const LOAD_DELIVERY_COUNTRIES_FAIL = @@ -15,18 +16,21 @@ export const LOAD_DELIVERY_COUNTRIES_SUCCESS = export class LoadDeliveryCountries implements Action { readonly type = LOAD_DELIVERY_COUNTRIES; + constructor() { // Intentional empty constructor } } -export class LoadDeliveryCountriesFail implements Action { +export class LoadDeliveryCountriesFail implements ErrorAction { readonly type = LOAD_DELIVERY_COUNTRIES_FAIL; - constructor(public payload: any) {} + + constructor(public error: ErrorActionType) {} } export class LoadDeliveryCountriesSuccess implements Action { readonly type = LOAD_DELIVERY_COUNTRIES_SUCCESS; + constructor(public payload: Country[]) {} } diff --git a/projects/core/src/user/store/actions/notification-preference.action.spec.ts b/projects/core/src/user/store/actions/notification-preference.action.spec.ts index dcf37f58a06..4d5685b7225 100644 --- a/projects/core/src/user/store/actions/notification-preference.action.spec.ts +++ b/projects/core/src/user/store/actions/notification-preference.action.spec.ts @@ -17,7 +17,7 @@ const mockNotificationPreference: NotificationPreference[] = [ visible: true, }, ]; -const error = 'anError'; +const error = new Error('anError'); describe('Notification Preference Actions', () => { describe('LoadNotificationPreferences', () => { @@ -36,7 +36,7 @@ describe('Notification Preference Actions', () => { const action = new UserActions.LoadNotificationPreferencesFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_NOTIFICATION_PREFERENCES_FAIL, - payload: error, + error, meta: StateUtils.failMeta(NOTIFICATION_PREFERENCES, error), }); }); @@ -80,7 +80,7 @@ describe('Notification Preference Actions', () => { const action = new UserActions.UpdateNotificationPreferencesFail(error); expect({ ...action }).toEqual({ type: UserActions.UPDATE_NOTIFICATION_PREFERENCES_FAIL, - payload: error, + error, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID, diff --git a/projects/core/src/user/store/actions/notification-preference.action.ts b/projects/core/src/user/store/actions/notification-preference.action.ts index eda262c22b8..ea00cc0579a 100644 --- a/projects/core/src/user/store/actions/notification-preference.action.ts +++ b/projects/core/src/user/store/actions/notification-preference.action.ts @@ -13,10 +13,11 @@ import { EntitySuccessAction, } from '../../../state/utils/entity-loader/entity-loader.action'; import { - UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID, NOTIFICATION_PREFERENCES, + UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID, } from '../user-state'; import { NotificationPreference } from '../../../model/notification-preference.model'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_NOTIFICATION_PREFERENCES = '[User] Load Notification Preferences'; @@ -37,6 +38,7 @@ export const CLEAR_NOTIFICATION_PREFERENCES = export class LoadNotificationPreferences extends StateUtils.LoaderLoadAction { readonly type = LOAD_NOTIFICATION_PREFERENCES; + constructor(public payload: string) { super(NOTIFICATION_PREFERENCES); } @@ -44,13 +46,15 @@ export class LoadNotificationPreferences extends StateUtils.LoaderLoadAction { export class LoadNotificationPreferencesFail extends StateUtils.LoaderFailAction { readonly type = LOAD_NOTIFICATION_PREFERENCES_FAIL; - constructor(public payload: any) { - super(NOTIFICATION_PREFERENCES, payload); + + constructor(public error: ErrorActionType) { + super(NOTIFICATION_PREFERENCES, error); } } export class LoadNotificationPreferencesSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_NOTIFICATION_PREFERENCES_SUCCESS; + constructor(public payload: NotificationPreference[]) { super(NOTIFICATION_PREFERENCES); } @@ -58,6 +62,7 @@ export class LoadNotificationPreferencesSuccess extends StateUtils.LoaderSuccess export class UpdateNotificationPreferences extends EntityLoadAction { readonly type = UPDATE_NOTIFICATION_PREFERENCES; + constructor( public payload: { userId: string; preferences: NotificationPreference[] } ) { @@ -67,13 +72,15 @@ export class UpdateNotificationPreferences extends EntityLoadAction { export class UpdateNotificationPreferencesFail extends EntityFailAction { readonly type = UPDATE_NOTIFICATION_PREFERENCES_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID, payload); + + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID, error); } } export class UpdateNotificationPreferencesSuccess extends EntitySuccessAction { readonly type = UPDATE_NOTIFICATION_PREFERENCES_SUCCESS; + constructor(public payload: NotificationPreference[]) { super(PROCESS_FEATURE, UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID); } @@ -81,6 +88,7 @@ export class UpdateNotificationPreferencesSuccess extends EntitySuccessAction { export class ResetNotificationPreferences extends EntityLoaderResetAction { readonly type = RESET_NOTIFICATION_PREFERENCES; + constructor() { super(PROCESS_FEATURE, UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID); } @@ -88,6 +96,7 @@ export class ResetNotificationPreferences extends EntityLoaderResetAction { export class ClearNotificationPreferences extends StateUtils.LoaderResetAction { readonly type = CLEAR_NOTIFICATION_PREFERENCES; + constructor() { super(NOTIFICATION_PREFERENCES); } diff --git a/projects/core/src/user/store/actions/payment-methods.action.spec.ts b/projects/core/src/user/store/actions/payment-methods.action.spec.ts index 0c56726926c..a0703748404 100644 --- a/projects/core/src/user/store/actions/payment-methods.action.spec.ts +++ b/projects/core/src/user/store/actions/payment-methods.action.spec.ts @@ -4,6 +4,7 @@ import { USER_PAYMENT_METHODS } from '../user-state'; import { UserActions } from './index'; const userId = '123'; +const error = { message: 'mockError' }; describe('User Payment Methods Actions', () => { describe('LoadUserPaymentMethods Actions', () => { @@ -20,12 +21,11 @@ describe('User Payment Methods Actions', () => { describe('LoadUserPaymentMethodsFail Action', () => { it('should create the action', () => { - const error = 'mockError'; const action = new UserActions.LoadUserPaymentMethodsFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_USER_PAYMENT_METHODS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(USER_PAYMENT_METHODS, error), }); }); @@ -62,11 +62,11 @@ describe('User Payment Methods Actions', () => { describe('SetDefaultUserPaymentMethodFail Action', () => { it('should create the action', () => { - const action = new UserActions.SetDefaultUserPaymentMethodFail(false); + const action = new UserActions.SetDefaultUserPaymentMethodFail(error); expect({ ...action }).toEqual({ + error, type: UserActions.SET_DEFAULT_USER_PAYMENT_METHOD_FAIL, - payload: false, - meta: StateUtils.failMeta(USER_PAYMENT_METHODS), + meta: StateUtils.failMeta(USER_PAYMENT_METHODS, error), }); }); }); @@ -95,11 +95,11 @@ describe('User Payment Methods Actions', () => { describe('DeleteUserPaymentMethodFail Action', () => { it('should create the action', () => { - const action = new UserActions.DeleteUserPaymentMethodFail(false); + const action = new UserActions.DeleteUserPaymentMethodFail(error); expect({ ...action }).toEqual({ type: UserActions.DELETE_USER_PAYMENT_METHOD_FAIL, - payload: false, - meta: StateUtils.failMeta(USER_PAYMENT_METHODS), + error, + meta: StateUtils.failMeta(USER_PAYMENT_METHODS, error), }); }); }); diff --git a/projects/core/src/user/store/actions/payment-methods.action.ts b/projects/core/src/user/store/actions/payment-methods.action.ts index 3e4fc0e525d..164b58df42b 100644 --- a/projects/core/src/user/store/actions/payment-methods.action.ts +++ b/projects/core/src/user/store/actions/payment-methods.action.ts @@ -7,6 +7,7 @@ import { PaymentDetails } from '../../../model/payment.model'; import { StateUtils } from '../../../state/utils/index'; import { USER_PAYMENT_METHODS } from '../user-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_USER_PAYMENT_METHODS = '[User] Load User Payment Methods'; export const LOAD_USER_PAYMENT_METHODS_FAIL = @@ -29,6 +30,7 @@ export const DELETE_USER_PAYMENT_METHOD_SUCCESS = export class LoadUserPaymentMethods extends StateUtils.LoaderLoadAction { readonly type = LOAD_USER_PAYMENT_METHODS; + constructor(public payload: string) { super(USER_PAYMENT_METHODS); } @@ -36,13 +38,15 @@ export class LoadUserPaymentMethods extends StateUtils.LoaderLoadAction { export class LoadUserPaymentMethodsFail extends StateUtils.LoaderFailAction { readonly type = LOAD_USER_PAYMENT_METHODS_FAIL; - constructor(public payload: any) { - super(USER_PAYMENT_METHODS, payload); + + constructor(public error: ErrorActionType) { + super(USER_PAYMENT_METHODS, error); } } export class LoadUserPaymentMethodsSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_USER_PAYMENT_METHODS_SUCCESS; + constructor(public payload: PaymentDetails[]) { super(USER_PAYMENT_METHODS); } @@ -50,6 +54,7 @@ export class LoadUserPaymentMethodsSuccess extends StateUtils.LoaderSuccessActio export class SetDefaultUserPaymentMethod extends StateUtils.LoaderLoadAction { readonly type = SET_DEFAULT_USER_PAYMENT_METHOD; + constructor(public payload: any) { super(USER_PAYMENT_METHODS); } @@ -57,13 +62,15 @@ export class SetDefaultUserPaymentMethod extends StateUtils.LoaderLoadAction { export class SetDefaultUserPaymentMethodFail extends StateUtils.LoaderFailAction { readonly type = SET_DEFAULT_USER_PAYMENT_METHOD_FAIL; - constructor(public payload: any) { - super(USER_PAYMENT_METHODS, payload); + + constructor(public error: ErrorActionType) { + super(USER_PAYMENT_METHODS, error); } } export class SetDefaultUserPaymentMethodSuccess extends StateUtils.LoaderSuccessAction { readonly type = SET_DEFAULT_USER_PAYMENT_METHOD_SUCCESS; + constructor(public payload: any) { super(USER_PAYMENT_METHODS); } @@ -71,6 +78,7 @@ export class SetDefaultUserPaymentMethodSuccess extends StateUtils.LoaderSuccess export class DeleteUserPaymentMethod extends StateUtils.LoaderLoadAction { readonly type = DELETE_USER_PAYMENT_METHOD; + constructor(public payload: any) { super(USER_PAYMENT_METHODS); } @@ -78,13 +86,15 @@ export class DeleteUserPaymentMethod extends StateUtils.LoaderLoadAction { export class DeleteUserPaymentMethodFail extends StateUtils.LoaderFailAction { readonly type = DELETE_USER_PAYMENT_METHOD_FAIL; - constructor(public payload: any) { - super(USER_PAYMENT_METHODS, payload); + + constructor(public error: ErrorActionType) { + super(USER_PAYMENT_METHODS, error); } } export class DeleteUserPaymentMethodSuccess extends StateUtils.LoaderSuccessAction { readonly type = DELETE_USER_PAYMENT_METHOD_SUCCESS; + constructor(public payload: any) { super(USER_PAYMENT_METHODS); } diff --git a/projects/core/src/user/store/actions/product-interests.actions.spec.ts b/projects/core/src/user/store/actions/product-interests.actions.spec.ts index 58b9f2e724c..e874269aafa 100644 --- a/projects/core/src/user/store/actions/product-interests.actions.spec.ts +++ b/projects/core/src/user/store/actions/product-interests.actions.spec.ts @@ -19,6 +19,7 @@ import { } from '../../../state/utils/entity-loader/entity-loader.action'; import { PROCESS_FEATURE } from '../../../process/store/process-state'; +const error = new Error('error'); const userId = 'qingyu@sap.com'; const productCode = '343898'; @@ -43,11 +44,10 @@ describe('Product Interests Actions', () => { }); describe('LoadProductInterestsFail Actions', () => { it('should be able to create the action', () => { - const error = 'error'; const action = new UserActions.LoadProductInterestsFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_PRODUCT_INTERESTS_FAIL, - payload: error, + error, meta: failMeta(PRODUCT_INTERESTS, error), }); }); @@ -105,11 +105,10 @@ describe('Product Interests Actions', () => { describe('RemoveProductInterestsFail Actions', () => { it('should be able to create the action', () => { - const error = 'remove fail'; const action = new UserActions.RemoveProductInterestFail(error); expect({ ...action }).toEqual({ type: UserActions.REMOVE_PRODUCT_INTEREST_FAIL, - payload: error, + error, meta: entityFailMeta( PROCESS_FEATURE, REMOVE_PRODUCT_INTERESTS_PROCESS_ID, @@ -152,11 +151,10 @@ describe('Product Interests Actions', () => { describe('AddProductInterestFail Action', () => { it('should be able to create the action', () => { - const error = 'add fail'; const action = new UserActions.AddProductInterestFail(error); expect({ ...action }).toEqual({ type: UserActions.ADD_PRODUCT_INTEREST_FAIL, - payload: error, + error, meta: entityFailMeta( PROCESS_FEATURE, ADD_PRODUCT_INTEREST_PROCESS_ID, diff --git a/projects/core/src/user/store/actions/product-interests.actions.ts b/projects/core/src/user/store/actions/product-interests.actions.ts index 17542ef0ff5..ab048bb375f 100644 --- a/projects/core/src/user/store/actions/product-interests.actions.ts +++ b/projects/core/src/user/store/actions/product-interests.actions.ts @@ -5,28 +5,29 @@ */ import { + ADD_PRODUCT_INTEREST_PROCESS_ID, PRODUCT_INTERESTS, REMOVE_PRODUCT_INTERESTS_PROCESS_ID, - ADD_PRODUCT_INTEREST_PROCESS_ID, } from '../user-state'; import { - ProductInterestSearchResult, - ProductInterestEntryRelation, NotificationType, + ProductInterestEntryRelation, + ProductInterestSearchResult, } from '../../../model/product-interest.model'; import { PROCESS_FEATURE } from '../../../process/store/process-state'; import { - LoaderLoadAction, LoaderFailAction, - LoaderSuccessAction, + LoaderLoadAction, LoaderResetAction, + LoaderSuccessAction, } from '../../../state/utils/loader/loader.action'; import { EntityFailAction, EntityLoadAction, - EntitySuccessAction, EntityLoaderResetAction, + EntitySuccessAction, } from '../../../state/utils/entity-loader/entity-loader.action'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_PRODUCT_INTERESTS = 'Load Product Interests'; export const LOAD_PRODUCT_INTERESTS_FAIL = 'Load Product Interests Fail'; @@ -48,6 +49,7 @@ export const CLEAR_PRODUCT_INTERESTS = 'Clear Product Interests'; export class LoadProductInterests extends LoaderLoadAction { readonly type = LOAD_PRODUCT_INTERESTS; + constructor( public payload: { userId: string; @@ -64,13 +66,15 @@ export class LoadProductInterests extends LoaderLoadAction { export class LoadProductInterestsFail extends LoaderFailAction { readonly type = LOAD_PRODUCT_INTERESTS_FAIL; - constructor(public payload: any) { - super(PRODUCT_INTERESTS, payload); + + constructor(public error: ErrorActionType) { + super(PRODUCT_INTERESTS, error); } } export class LoadProductInterestsSuccess extends LoaderSuccessAction { readonly type = LOAD_PRODUCT_INTERESTS_SUCCESS; + constructor(public payload: ProductInterestSearchResult) { super(PRODUCT_INTERESTS); } @@ -78,6 +82,7 @@ export class LoadProductInterestsSuccess extends LoaderSuccessAction { export class RemoveProductInterest extends EntityLoadAction { readonly type = REMOVE_PRODUCT_INTEREST; + constructor( public payload: { userId: string; @@ -91,6 +96,7 @@ export class RemoveProductInterest extends EntityLoadAction { export class RemoveProductInterestSuccess extends EntitySuccessAction { readonly type = REMOVE_PRODUCT_INTEREST_SUCCESS; + constructor(public payload: any) { super(PROCESS_FEATURE, REMOVE_PRODUCT_INTERESTS_PROCESS_ID); } @@ -98,13 +104,15 @@ export class RemoveProductInterestSuccess extends EntitySuccessAction { export class RemoveProductInterestFail extends EntityFailAction { readonly type = REMOVE_PRODUCT_INTEREST_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, REMOVE_PRODUCT_INTERESTS_PROCESS_ID, payload); + + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, REMOVE_PRODUCT_INTERESTS_PROCESS_ID, error); } } export class AddProductInterest extends EntityLoadAction { readonly type = ADD_PRODUCT_INTEREST; + constructor( public payload: { userId: string; @@ -118,6 +126,7 @@ export class AddProductInterest extends EntityLoadAction { export class AddProductInterestSuccess extends EntitySuccessAction { readonly type = ADD_PRODUCT_INTEREST_SUCCESS; + constructor(public payload: any) { super(PROCESS_FEATURE, ADD_PRODUCT_INTEREST_PROCESS_ID); } @@ -125,13 +134,15 @@ export class AddProductInterestSuccess extends EntitySuccessAction { export class AddProductInterestFail extends EntityFailAction { readonly type = ADD_PRODUCT_INTEREST_FAIL; - constructor(public payload: any) { - super(PROCESS_FEATURE, ADD_PRODUCT_INTEREST_PROCESS_ID, payload); + + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, ADD_PRODUCT_INTEREST_PROCESS_ID, error); } } export class ResetAddInterestState extends EntityLoaderResetAction { readonly type = ADD_PRODUCT_INTEREST_RESET; + constructor() { super(PROCESS_FEATURE, ADD_PRODUCT_INTEREST_PROCESS_ID); } @@ -139,6 +150,7 @@ export class ResetAddInterestState extends EntityLoaderResetAction { export class ResetRemoveInterestState extends EntityLoaderResetAction { readonly type = REMOVE_PRODUCT_INTEREST_RESET; + constructor() { super(PROCESS_FEATURE, REMOVE_PRODUCT_INTERESTS_PROCESS_ID); } @@ -146,6 +158,7 @@ export class ResetRemoveInterestState extends EntityLoaderResetAction { export class ClearProductInterests extends LoaderResetAction { readonly type = CLEAR_PRODUCT_INTERESTS; + constructor() { super(PRODUCT_INTERESTS); } diff --git a/projects/core/src/user/store/actions/regions.action.spec.ts b/projects/core/src/user/store/actions/regions.action.spec.ts index 523d4f25e67..490a8469f6b 100644 --- a/projects/core/src/user/store/actions/regions.action.spec.ts +++ b/projects/core/src/user/store/actions/regions.action.spec.ts @@ -18,12 +18,12 @@ describe('Regions Actions', () => { describe('LoadRegionsFail', () => { it('should create the action', () => { - const error = 'anError'; + const error = new Error('anError'); const action = new UserActions.LoadRegionsFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_REGIONS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(REGIONS, error), }); }); diff --git a/projects/core/src/user/store/actions/regions.action.ts b/projects/core/src/user/store/actions/regions.action.ts index 0da4f53c48c..2e4fc5f765f 100644 --- a/projects/core/src/user/store/actions/regions.action.ts +++ b/projects/core/src/user/store/actions/regions.action.ts @@ -8,6 +8,7 @@ import { Action } from '@ngrx/store'; import { Region } from '../../../model/address.model'; import { StateUtils } from '../../../state/utils/index'; import { REGIONS } from '../user-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_REGIONS = '[User] Load Regions'; export const LOAD_REGIONS_SUCCESS = '[User] Load Regions Success'; @@ -16,6 +17,7 @@ export const CLEAR_REGIONS = '[User] Clear Regions'; export class LoadRegions extends StateUtils.LoaderLoadAction { readonly type = LOAD_REGIONS; + constructor(public payload: string) { super(REGIONS); } @@ -23,13 +25,15 @@ export class LoadRegions extends StateUtils.LoaderLoadAction { export class LoadRegionsFail extends StateUtils.LoaderFailAction { readonly type = LOAD_REGIONS_FAIL; - constructor(public payload: any) { - super(REGIONS, payload); + + constructor(public error: ErrorActionType) { + super(REGIONS, error); } } export class LoadRegionsSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_REGIONS_SUCCESS; + constructor(public payload: { entities: Region[]; country: string }) { super(REGIONS); } @@ -37,6 +41,7 @@ export class LoadRegionsSuccess extends StateUtils.LoaderSuccessAction { export class ClearRegions implements Action { readonly type = CLEAR_REGIONS; + constructor() { // Intentional empty constructor } diff --git a/projects/core/src/user/store/actions/user-addresses.action.spec.ts b/projects/core/src/user/store/actions/user-addresses.action.spec.ts index 00279e01132..1b260efc578 100644 --- a/projects/core/src/user/store/actions/user-addresses.action.spec.ts +++ b/projects/core/src/user/store/actions/user-addresses.action.spec.ts @@ -3,6 +3,7 @@ import { StateUtils } from '../../../state/utils/index'; import { USER_ADDRESSES } from '../user-state'; import { UserActions } from './index'; +const error = new Error('mockError'); const userId = '123'; const address: Address = { companyName: 'sap', @@ -23,12 +24,11 @@ describe('User Addresses Actions', () => { describe('LoadUserAddressesFail Action', () => { it('should create the action', () => { - const error = 'mockError'; const action = new UserActions.LoadUserAddressesFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_USER_ADDRESSES_FAIL, - payload: error, + error, meta: StateUtils.failMeta(USER_ADDRESSES, error), }); }); @@ -70,12 +70,11 @@ describe('User Addresses Actions', () => { describe('AddUserAddressFail Action', () => { it('should create the action', () => { - const error = 'mockError'; const action = new UserActions.AddUserAddressFail(error); expect({ ...action }).toEqual({ type: UserActions.ADD_USER_ADDRESS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(USER_ADDRESSES, error), }); }); @@ -117,12 +116,11 @@ describe('User Addresses Actions', () => { describe('UpdateUserAddressFail Action', () => { it('should create the action', () => { - const error = 'mockError'; const action = new UserActions.UpdateUserAddressFail(error); expect({ ...action }).toEqual({ type: UserActions.UPDATE_USER_ADDRESS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(USER_ADDRESSES, error), }); }); diff --git a/projects/core/src/user/store/actions/user-addresses.action.ts b/projects/core/src/user/store/actions/user-addresses.action.ts index f2846733f68..a5b97e53319 100644 --- a/projects/core/src/user/store/actions/user-addresses.action.ts +++ b/projects/core/src/user/store/actions/user-addresses.action.ts @@ -7,6 +7,7 @@ import { Address } from '../../../model/address.model'; import { StateUtils } from '../../../state/utils/index'; import { USER_ADDRESSES } from '../user-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_USER_ADDRESSES = '[User] Load User Addresses'; export const LOAD_USER_ADDRESSES_FAIL = '[User] Load User Addresses Fail'; @@ -26,6 +27,7 @@ export const DELETE_USER_ADDRESS_SUCCESS = '[User] Delete User Address Success'; export class LoadUserAddresses extends StateUtils.LoaderLoadAction { readonly type = LOAD_USER_ADDRESSES; + constructor(public payload: string) { super(USER_ADDRESSES); } @@ -33,13 +35,15 @@ export class LoadUserAddresses extends StateUtils.LoaderLoadAction { export class LoadUserAddressesFail extends StateUtils.LoaderFailAction { readonly type = LOAD_USER_ADDRESSES_FAIL; - constructor(public payload: any) { - super(USER_ADDRESSES, payload); + + constructor(public error: ErrorActionType) { + super(USER_ADDRESSES, error); } } export class LoadUserAddressesSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_USER_ADDRESSES_SUCCESS; + constructor(public payload: Address[]) { super(USER_ADDRESSES); } @@ -48,6 +52,7 @@ export class LoadUserAddressesSuccess extends StateUtils.LoaderSuccessAction { // Adding address actions export class AddUserAddress extends StateUtils.LoaderLoadAction { readonly type = ADD_USER_ADDRESS; + constructor(public payload: { userId: string; address: Address }) { super(USER_ADDRESSES); } @@ -55,13 +60,15 @@ export class AddUserAddress extends StateUtils.LoaderLoadAction { export class AddUserAddressFail extends StateUtils.LoaderFailAction { readonly type = ADD_USER_ADDRESS_FAIL; - constructor(public payload: any) { - super(USER_ADDRESSES, payload); + + constructor(public error: ErrorActionType) { + super(USER_ADDRESSES, error); } } export class AddUserAddressSuccess extends StateUtils.LoaderSuccessAction { readonly type = ADD_USER_ADDRESS_SUCCESS; + constructor(public payload: any) { super(USER_ADDRESSES); } @@ -70,6 +77,7 @@ export class AddUserAddressSuccess extends StateUtils.LoaderSuccessAction { // Updating address actions export class UpdateUserAddress extends StateUtils.LoaderLoadAction { readonly type = UPDATE_USER_ADDRESS; + constructor( public payload: { userId: string; addressId: string; address: Address } ) { @@ -79,13 +87,15 @@ export class UpdateUserAddress extends StateUtils.LoaderLoadAction { export class UpdateUserAddressFail extends StateUtils.LoaderFailAction { readonly type = UPDATE_USER_ADDRESS_FAIL; - constructor(public payload: any) { - super(USER_ADDRESSES, payload); + + constructor(public error: ErrorActionType) { + super(USER_ADDRESSES, error); } } export class UpdateUserAddressSuccess extends StateUtils.LoaderSuccessAction { readonly type = UPDATE_USER_ADDRESS_SUCCESS; + constructor(public payload: any) { super(USER_ADDRESSES); } @@ -94,6 +104,7 @@ export class UpdateUserAddressSuccess extends StateUtils.LoaderSuccessAction { // Deleting address actions export class DeleteUserAddress extends StateUtils.LoaderLoadAction { readonly type = DELETE_USER_ADDRESS; + constructor(public payload: any) { super(USER_ADDRESSES); } @@ -101,13 +112,15 @@ export class DeleteUserAddress extends StateUtils.LoaderLoadAction { export class DeleteUserAddressFail extends StateUtils.LoaderFailAction { readonly type = DELETE_USER_ADDRESS_FAIL; - constructor(public payload: any) { - super(USER_ADDRESSES, payload); + + constructor(public error: ErrorActionType) { + super(USER_ADDRESSES, error); } } export class DeleteUserAddressSuccess extends StateUtils.LoaderSuccessAction { readonly type = DELETE_USER_ADDRESS_SUCCESS; + constructor(public payload: any) { super(USER_ADDRESSES); } diff --git a/projects/core/src/user/store/actions/user-consents.action.spec.ts b/projects/core/src/user/store/actions/user-consents.action.spec.ts index 18cce32bf09..b380b89da0c 100644 --- a/projects/core/src/user/store/actions/user-consents.action.spec.ts +++ b/projects/core/src/user/store/actions/user-consents.action.spec.ts @@ -20,12 +20,12 @@ describe('user consent actions', () => { }); describe('LoadUserConsentsFail', () => { it('should create the action', () => { - const payload = 'anError'; - const action = new UserActions.LoadUserConsentsFail(payload); + const error = new Error('anError'); + const action = new UserActions.LoadUserConsentsFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_USER_CONSENTS_FAIL, - payload, - meta: StateUtils.failMeta(USER_CONSENTS, payload), + error, + meta: StateUtils.failMeta(USER_CONSENTS, error), }); }); }); @@ -72,14 +72,15 @@ describe('user consent actions', () => { }); describe('GiveUserConsentFail', () => { it('should create the action', () => { - const payload = 'anError'; - const action = new UserActions.GiveUserConsentFail(payload); + const error = new Error('anError'); + const action = new UserActions.GiveUserConsentFail(error); expect({ ...action }).toEqual({ + error, type: UserActions.GIVE_USER_CONSENT_FAIL, meta: StateUtils.entityFailMeta( PROCESS_FEATURE, GIVE_CONSENT_PROCESS_ID, - payload + error ), }); }); diff --git a/projects/core/src/user/store/actions/user-consents.action.ts b/projects/core/src/user/store/actions/user-consents.action.ts index cab58b0c3c5..55129dcb5a7 100644 --- a/projects/core/src/user/store/actions/user-consents.action.ts +++ b/projects/core/src/user/store/actions/user-consents.action.ts @@ -12,6 +12,7 @@ import { USER_CONSENTS, WITHDRAW_CONSENT_PROCESS_ID, } from '../user-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_USER_CONSENTS = '[User] Load User Consents'; export const LOAD_USER_CONSENTS_SUCCESS = '[User] Load User Consents Success'; @@ -34,6 +35,7 @@ export const RESET_WITHDRAW_USER_CONSENT_PROCESS = export class LoadUserConsents extends StateUtils.LoaderLoadAction { readonly type = LOAD_USER_CONSENTS; + constructor(public payload: string) { super(USER_CONSENTS); } @@ -41,13 +43,15 @@ export class LoadUserConsents extends StateUtils.LoaderLoadAction { export class LoadUserConsentsFail extends StateUtils.LoaderFailAction { readonly type = LOAD_USER_CONSENTS_FAIL; - constructor(public payload: any) { - super(USER_CONSENTS, payload); + + constructor(public error: ErrorActionType) { + super(USER_CONSENTS, error); } } export class LoadUserConsentsSuccess extends StateUtils.LoaderSuccessAction { readonly type = LOAD_USER_CONSENTS_SUCCESS; + constructor(public payload: ConsentTemplate[]) { super(USER_CONSENTS); } @@ -55,6 +59,7 @@ export class LoadUserConsentsSuccess extends StateUtils.LoaderSuccessAction { export class ResetLoadUserConsents extends StateUtils.LoaderResetAction { readonly type = RESET_LOAD_USER_CONSENTS; + constructor() { super(USER_CONSENTS); } @@ -62,6 +67,7 @@ export class ResetLoadUserConsents extends StateUtils.LoaderResetAction { export class GiveUserConsent extends StateUtils.EntityLoadAction { readonly type = GIVE_USER_CONSENT; + constructor( public payload: { userId: string; @@ -75,13 +81,15 @@ export class GiveUserConsent extends StateUtils.EntityLoadAction { export class GiveUserConsentFail extends StateUtils.EntityFailAction { readonly type = GIVE_USER_CONSENT_FAIL; - constructor(payload: any) { - super(PROCESS_FEATURE, GIVE_CONSENT_PROCESS_ID, payload); + + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, GIVE_CONSENT_PROCESS_ID, error); } } export class GiveUserConsentSuccess extends StateUtils.EntitySuccessAction { readonly type = GIVE_USER_CONSENT_SUCCESS; + constructor(public consentTemplate: ConsentTemplate) { super(PROCESS_FEATURE, GIVE_CONSENT_PROCESS_ID); } @@ -89,6 +97,7 @@ export class GiveUserConsentSuccess extends StateUtils.EntitySuccessAction { export class ResetGiveUserConsentProcess extends StateUtils.EntityLoaderResetAction { readonly type = RESET_GIVE_USER_CONSENT_PROCESS; + constructor() { super(PROCESS_FEATURE, GIVE_CONSENT_PROCESS_ID); } @@ -96,6 +105,7 @@ export class ResetGiveUserConsentProcess extends StateUtils.EntityLoaderResetAct export class TransferAnonymousConsent { readonly type = TRANSFER_ANONYMOUS_CONSENT; + constructor( public payload: { userId: string; @@ -107,6 +117,7 @@ export class TransferAnonymousConsent { export class WithdrawUserConsent extends StateUtils.EntityLoadAction { readonly type = WITHDRAW_USER_CONSENT; + constructor( public payload: { userId: string; @@ -120,13 +131,15 @@ export class WithdrawUserConsent extends StateUtils.EntityLoadAction { export class WithdrawUserConsentFail extends StateUtils.EntityFailAction { readonly type = WITHDRAW_USER_CONSENT_FAIL; - constructor(payload: any) { - super(PROCESS_FEATURE, WITHDRAW_CONSENT_PROCESS_ID, payload); + + constructor(public error: ErrorActionType) { + super(PROCESS_FEATURE, WITHDRAW_CONSENT_PROCESS_ID, error); } } export class WithdrawUserConsentSuccess extends StateUtils.EntitySuccessAction { readonly type = WITHDRAW_USER_CONSENT_SUCCESS; + constructor() { super(PROCESS_FEATURE, WITHDRAW_CONSENT_PROCESS_ID); } @@ -134,6 +147,7 @@ export class WithdrawUserConsentSuccess extends StateUtils.EntitySuccessAction { export class ResetWithdrawUserConsentProcess extends StateUtils.EntityLoaderResetAction { readonly type = RESET_WITHDRAW_USER_CONSENT_PROCESS; + constructor() { super(PROCESS_FEATURE, WITHDRAW_CONSENT_PROCESS_ID); } diff --git a/projects/core/src/user/store/actions/user-cost-center.action.spec.ts b/projects/core/src/user/store/actions/user-cost-center.action.spec.ts index 317f7620c26..b93a8042b5b 100644 --- a/projects/core/src/user/store/actions/user-cost-center.action.spec.ts +++ b/projects/core/src/user/store/actions/user-cost-center.action.spec.ts @@ -21,12 +21,12 @@ describe('User Cost Centers Actions', () => { describe('LoadActiveCostCentersFail Action', () => { it('should create the action', () => { - const error = 'mockError'; + const error = new Error('mockError'); const action = new UserActions.LoadActiveCostCentersFail(error); expect({ ...action }).toEqual({ type: UserActions.LOAD_ACTIVE_COST_CENTERS_FAIL, - payload: error, + error, meta: StateUtils.failMeta(USER_COST_CENTERS, error), }); }); diff --git a/projects/core/src/user/store/actions/user-cost-center.action.ts b/projects/core/src/user/store/actions/user-cost-center.action.ts index 0d010f7868c..f0d49057c90 100644 --- a/projects/core/src/user/store/actions/user-cost-center.action.ts +++ b/projects/core/src/user/store/actions/user-cost-center.action.ts @@ -7,6 +7,7 @@ import { CostCenter } from '../../../model/org-unit.model'; import { StateUtils } from '../../../state/utils/index'; import { USER_COST_CENTERS } from '../user-state'; +import { ErrorActionType } from '@spartacus/core'; export const LOAD_ACTIVE_COST_CENTERS = '[User] Load Active CostCenters'; export const LOAD_ACTIVE_COST_CENTERS_FAIL = @@ -23,8 +24,8 @@ export class LoadActiveCostCenters extends StateUtils.LoaderLoadAction { export class LoadActiveCostCentersFail extends StateUtils.LoaderFailAction { readonly type = LOAD_ACTIVE_COST_CENTERS_FAIL; - constructor(public payload: any) { - super(USER_COST_CENTERS, payload); + constructor(public error: ErrorActionType) { + super(USER_COST_CENTERS, error); } } diff --git a/projects/core/src/user/store/effects/billing-countries.effect.ts b/projects/core/src/user/store/effects/billing-countries.effect.ts index 28dfca8bc82..04264b20a02 100644 --- a/projects/core/src/user/store/effects/billing-countries.effect.ts +++ b/projects/core/src/user/store/effects/billing-countries.effect.ts @@ -4,14 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; import { CountryType } from '../../../model/address.model'; import { SiteConnector } from '../../../site-context/connectors/site.connector'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserActions } from '../actions/index'; @Injectable() @@ -31,7 +31,7 @@ export class BillingCountriesEffect { catchError((error) => of( new UserActions.LoadBillingCountriesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/customer-coupon.effect.ts b/projects/core/src/user/store/effects/customer-coupon.effect.ts index f81902a5f1a..77dfc750127 100644 --- a/projects/core/src/user/store/effects/customer-coupon.effect.ts +++ b/projects/core/src/user/store/effects/customer-coupon.effect.ts @@ -10,7 +10,7 @@ import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; import { CustomerCouponSearchResult } from '../../../model/customer-coupon.model'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { CustomerCouponConnector } from '../../connectors/customer-coupon/customer-coupon.connector'; import * as fromCustomerCouponsAction from '../actions/customer-coupon.action'; @@ -43,7 +43,7 @@ export class CustomerCouponEffects { catchError((error) => of( new fromCustomerCouponsAction.LoadCustomerCouponsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -72,7 +72,7 @@ export class CustomerCouponEffects { catchError((error) => of( new fromCustomerCouponsAction.SubscribeCustomerCouponFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -101,7 +101,7 @@ export class CustomerCouponEffects { catchError((error) => of( new fromCustomerCouponsAction.UnsubscribeCustomerCouponFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -130,7 +130,7 @@ export class CustomerCouponEffects { catchError((error) => of( new fromCustomerCouponsAction.ClaimCustomerCouponFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/delivery-countries.effect.ts b/projects/core/src/user/store/effects/delivery-countries.effect.ts index 0293aef1f58..550398a1f88 100644 --- a/projects/core/src/user/store/effects/delivery-countries.effect.ts +++ b/projects/core/src/user/store/effects/delivery-countries.effect.ts @@ -11,7 +11,7 @@ import { catchError, map, switchMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; import { CountryType } from '../../../model/address.model'; import { SiteConnector } from '../../../site-context/connectors/site.connector'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserActions } from '../actions/index'; @Injectable() @@ -31,7 +31,7 @@ export class DeliveryCountriesEffects { catchError((error) => of( new UserActions.LoadDeliveryCountriesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/notification-preference.effect.spec.ts b/projects/core/src/user/store/effects/notification-preference.effect.spec.ts index 61971919d8f..606d1b1a320 100644 --- a/projects/core/src/user/store/effects/notification-preference.effect.spec.ts +++ b/projects/core/src/user/store/effects/notification-preference.effect.spec.ts @@ -18,7 +18,7 @@ const mockNotificationPreference: NotificationPreference[] = [ visible: true, }, ]; -const error = 'anError'; +const error = new Error('anError'); describe('Notification Preference Effect', () => { let notificationPreferenceEffects: fromEffect.NotificationPreferenceEffects; @@ -67,9 +67,7 @@ describe('Notification Preference Effect', () => { ); const action = new UserActions.LoadNotificationPreferences(userId); - const completion = new UserActions.LoadNotificationPreferencesFail( - undefined - ); + const completion = new UserActions.LoadNotificationPreferencesFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); @@ -112,7 +110,7 @@ describe('Notification Preference Effect', () => { preferences: mockNotificationPreference, }); const completion = new UserActions.UpdateNotificationPreferencesFail( - undefined + error ); actions$ = hot('-a', { a: action }); diff --git a/projects/core/src/user/store/effects/notification-preference.effect.ts b/projects/core/src/user/store/effects/notification-preference.effect.ts index f5176714fdf..d7c1c91dbac 100644 --- a/projects/core/src/user/store/effects/notification-preference.effect.ts +++ b/projects/core/src/user/store/effects/notification-preference.effect.ts @@ -9,7 +9,7 @@ import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserNotificationPreferenceConnector } from '../../connectors/notification-preference/user-notification-preference.connector'; import { UserActions } from '../actions/index'; @@ -33,7 +33,7 @@ export class NotificationPreferenceEffects { catchError((error) => of( new UserActions.LoadNotificationPreferencesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -60,7 +60,7 @@ export class NotificationPreferenceEffects { catchError((error) => of( new UserActions.UpdateNotificationPreferencesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/payment-methods.effect.ts b/projects/core/src/user/store/effects/payment-methods.effect.ts index a38bb301132..b40ffee7b1d 100644 --- a/projects/core/src/user/store/effects/payment-methods.effect.ts +++ b/projects/core/src/user/store/effects/payment-methods.effect.ts @@ -13,7 +13,7 @@ import { GlobalMessageService } from '../../../global-message/facade/global-mess import { GlobalMessageType } from '../../../global-message/models/global-message.model'; import { LoggerService } from '../../../logger'; import { PaymentDetails } from '../../../model/payment.model'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserPaymentConnector } from '../../connectors/payment/user-payment.connector'; import { UserActions } from '../actions/index'; @@ -33,7 +33,7 @@ export class UserPaymentMethodsEffects { catchError((error) => of( new UserActions.LoadUserPaymentMethodsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -57,7 +57,7 @@ export class UserPaymentMethodsEffects { catchError((error) => of( new UserActions.SetDefaultUserPaymentMethodFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -87,7 +87,7 @@ export class UserPaymentMethodsEffects { catchError((error) => of( new UserActions.DeleteUserPaymentMethodFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/product-interests.effect.spec.ts b/projects/core/src/user/store/effects/product-interests.effect.spec.ts index 0e78025aace..a36d6c7a7da 100644 --- a/projects/core/src/user/store/effects/product-interests.effect.spec.ts +++ b/projects/core/src/user/store/effects/product-interests.effect.spec.ts @@ -19,6 +19,7 @@ const loadParams = { currentPage: 1, sort: 'name:asc', }; +const error = new Error('error'); describe('Product Interests Effect', () => { let actions$: Actions; @@ -63,10 +64,10 @@ describe('Product Interests Effect', () => { }); it('should be able to handle failures for load product interests', () => { spyOn(userInterestConnector, 'getInterests').and.returnValue( - throwError('Error') + throwError(error) ); const action = new UserActions.LoadProductInterests(loadParams); - const completion = new UserActions.LoadProductInterestsFail(undefined); + const completion = new UserActions.LoadProductInterestsFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); @@ -141,10 +142,10 @@ describe('Product Interests Effect', () => { it('should be able to handle failures for remove product interest', () => { spyOn(userInterestConnector, 'removeInterest').and.returnValue( - throwError('Error') + throwError(error) ); const action = new UserActions.RemoveProductInterest(delParams); - const completion = new UserActions.RemoveProductInterestFail(undefined); + const completion = new UserActions.RemoveProductInterestFail(error); actions$ = hot('-a', { a: action }); const expected = cold('-b', { b: completion }); diff --git a/projects/core/src/user/store/effects/product-interests.effect.ts b/projects/core/src/user/store/effects/product-interests.effect.ts index d4d48b1f8c7..9a52ee2a54a 100644 --- a/projects/core/src/user/store/effects/product-interests.effect.ts +++ b/projects/core/src/user/store/effects/product-interests.effect.ts @@ -4,14 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Action } from '@ngrx/store'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; import { ProductInterestSearchResult } from '../../../model/product-interest.model'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserInterestsConnector } from '../../connectors/interests/user-interests.connector'; import { UserActions } from '../actions/index'; @@ -46,7 +46,7 @@ export class ProductInterestsEffect { catchError((error) => of( new UserActions.LoadProductInterestsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -79,7 +79,7 @@ export class ProductInterestsEffect { catchError((error) => of( new UserActions.RemoveProductInterestFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -111,7 +111,7 @@ export class ProductInterestsEffect { catchError((error) => of( new UserActions.AddProductInterestFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/regions.effect.ts b/projects/core/src/user/store/effects/regions.effect.ts index 8254281eaa1..c06495e0531 100644 --- a/projects/core/src/user/store/effects/regions.effect.ts +++ b/projects/core/src/user/store/effects/regions.effect.ts @@ -12,7 +12,7 @@ import { catchError, map, switchMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; import { SiteConnector } from '../../../site-context/connectors/site.connector'; import { StateUtils } from '../../../state/utils/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserActions } from '../actions/index'; import { REGIONS } from '../user-state'; @@ -38,7 +38,7 @@ export class RegionsEffects { catchError((error) => of( new UserActions.LoadRegionsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/user-addresses.effect.ts b/projects/core/src/user/store/effects/user-addresses.effect.ts index 265081c052f..7af49659489 100644 --- a/projects/core/src/user/store/effects/user-addresses.effect.ts +++ b/projects/core/src/user/store/effects/user-addresses.effect.ts @@ -14,7 +14,7 @@ import { } from '../../../global-message/index'; import { LoggerService } from '../../../logger'; import { Address } from '../../../model/address.model'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserAddressConnector } from '../../connectors/address/user-address.connector'; import { UserAddressService } from '../../facade/user-address.service'; import { UserActions } from '../actions/index'; @@ -36,7 +36,7 @@ export class UserAddressesEffects { catchError((error) => of( new UserActions.LoadUserAddressesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -60,7 +60,7 @@ export class UserAddressesEffects { catchError((error) => of( new UserActions.AddUserAddressFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -88,7 +88,7 @@ export class UserAddressesEffects { ); return of( new UserActions.UpdateUserAddressFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ); }) @@ -112,7 +112,7 @@ export class UserAddressesEffects { catchError((error) => of( new UserActions.DeleteUserAddressFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/user-consents.effect.spec.ts b/projects/core/src/user/store/effects/user-consents.effect.spec.ts index 9e3323923b8..1c41aafb115 100644 --- a/projects/core/src/user/store/effects/user-consents.effect.spec.ts +++ b/projects/core/src/user/store/effects/user-consents.effect.spec.ts @@ -7,7 +7,7 @@ import { GlobalMessageType } from '../../../global-message/models/global-message import { GlobalMessageActions } from '../../../global-message/store/actions'; import { ConsentTemplate } from '../../../model/consent.model'; import { SiteContextActions } from '../../../site-context/store/actions/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserConsentAdapter } from '../../connectors/index'; import { UserActions } from '../actions/index'; import * as fromEffect from './user-consents.effect'; @@ -111,7 +111,7 @@ describe('User Consents effect', () => { consentTemplateVersion, }); const completion = new UserActions.GiveUserConsentFail( - normalizeHttpError(mockError) + tryNormalizeHttpError(mockError) ); const closeMessage = new GlobalMessageActions.RemoveMessagesByType( GlobalMessageType.MSG_TYPE_ERROR @@ -138,7 +138,7 @@ describe('User Consents effect', () => { consentTemplateVersion, }); const completion = new UserActions.GiveUserConsentFail( - normalizeHttpError(mockError) + tryNormalizeHttpError(mockError) ); actions$ = hot('-a', { a: action }); diff --git a/projects/core/src/user/store/effects/user-consents.effect.ts b/projects/core/src/user/store/effects/user-consents.effect.ts index bc0a9380a7c..f973d06dd8a 100644 --- a/projects/core/src/user/store/effects/user-consents.effect.ts +++ b/projects/core/src/user/store/effects/user-consents.effect.ts @@ -12,7 +12,7 @@ import { GlobalMessageType } from '../../../global-message/models/global-message import { GlobalMessageActions } from '../../../global-message/store/actions'; import { LoggerService } from '../../../logger'; import { SiteContextActions } from '../../../site-context/store/actions/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserConsentConnector } from '../../connectors/consent/user-consent.connector'; import { UserActions } from '../actions/index'; @@ -38,7 +38,7 @@ export class UserConsentsEffect { catchError((error) => of( new UserActions.LoadUserConsentsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) @@ -69,7 +69,7 @@ export class UserConsentsEffect { | GlobalMessageActions.RemoveMessagesByType > = [ new UserActions.GiveUserConsentFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ), ]; if ( @@ -102,7 +102,7 @@ export class UserConsentsEffect { catchError((error) => of( new UserActions.WithdrawUserConsentFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/effects/user-cost-center.effect.ts b/projects/core/src/user/store/effects/user-cost-center.effect.ts index d2c1e53b30d..22442a9a299 100644 --- a/projects/core/src/user/store/effects/user-cost-center.effect.ts +++ b/projects/core/src/user/store/effects/user-cost-center.effect.ts @@ -4,14 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Injectable, inject } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, map, switchMap } from 'rxjs/operators'; import { LoggerService } from '../../../logger'; import { EntitiesModel } from '../../../model/misc.model'; import { CostCenter } from '../../../model/org-unit.model'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; import { UserCostCenterConnector } from '../../connectors/cost-center/user-cost-center.connector'; import { UserActions } from '../actions/index'; @@ -34,7 +34,7 @@ export class UserCostCenterEffects { catchError((error) => of( new UserActions.LoadActiveCostCentersFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/user/store/reducers/notification-preference.reducer.spec.ts b/projects/core/src/user/store/reducers/notification-preference.reducer.spec.ts index ca39fa60de5..6b4131efd4e 100644 --- a/projects/core/src/user/store/reducers/notification-preference.reducer.spec.ts +++ b/projects/core/src/user/store/reducers/notification-preference.reducer.spec.ts @@ -10,7 +10,7 @@ const mockNotificationPreference: NotificationPreference[] = [ visible: true, }, ]; -const error = 'anError'; +const error = new Error('anError'); describe('Notification Preference Reducer', () => { describe('undefined action', () => { diff --git a/projects/core/src/user/store/selectors/user-consents.selectors.spec.ts b/projects/core/src/user/store/selectors/user-consents.selectors.spec.ts index 45328955e06..19c16d75928 100644 --- a/projects/core/src/user/store/selectors/user-consents.selectors.spec.ts +++ b/projects/core/src/user/store/selectors/user-consents.selectors.spec.ts @@ -96,7 +96,7 @@ describe('User consents selectors', () => { }); describe('getConsentsError', () => { it('should return the error flag', () => { - store.dispatch(new UserActions.LoadUserConsentsFail('error')); + store.dispatch(new UserActions.LoadUserConsentsFail(new Error('error'))); let result = false; store diff --git a/projects/core/src/util/index.ts b/projects/core/src/util/index.ts index 6a4b012e02e..92afc3f28c2 100644 --- a/projects/core/src/util/index.ts +++ b/projects/core/src/util/index.ts @@ -12,6 +12,7 @@ export * from './glob.service'; export * from './http-params-uri.encoder'; export * from './java-reg-exp-converter/index'; export * from './normalize-http-error'; +export * from './try-normalize-http-error'; export * from './occ-http-error-constants'; export * from './occ-http-error-handlers'; export * from './regex-pattern'; diff --git a/projects/core/src/util/try-normalize-http-error.spec.ts b/projects/core/src/util/try-normalize-http-error.spec.ts new file mode 100644 index 00000000000..35186f1affc --- /dev/null +++ b/projects/core/src/util/try-normalize-http-error.spec.ts @@ -0,0 +1,33 @@ +import { + HttpErrorModel, + LoggerService, + tryNormalizeHttpError, +} from '@spartacus/core'; + +describe('tryNormalizeHttpError', () => { + let mockLogger: LoggerService; + + beforeEach(() => { + mockLogger = jasmine.createSpyObj('LoggerService', ['error']); + }); + + it('should return the normalized error when input is an HttpErrorModel', () => { + const inputError = new HttpErrorModel(); + const result = tryNormalizeHttpError(inputError, mockLogger); + + expect(result).toBe(inputError); + expect(mockLogger.error).not.toHaveBeenCalled(); + }); + + it('should return the original error when input is not HttpErrorModel or HttpErrorResponse', () => { + const inputError = new Error('An error occurred'); + + const result = tryNormalizeHttpError(inputError, mockLogger); + + expect(result).toBe(inputError); + expect(mockLogger.error).toHaveBeenCalledWith( + 'Error passed to normalizeHttpError is not HttpErrorResponse instance', + inputError + ); + }); +}); diff --git a/projects/core/src/util/try-normalize-http-error.ts b/projects/core/src/util/try-normalize-http-error.ts new file mode 100644 index 00000000000..eb2106f5f39 --- /dev/null +++ b/projects/core/src/util/try-normalize-http-error.ts @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { HttpErrorResponse } from '@angular/common/http'; +import { + HttpErrorModel, + LoggerService, + normalizeHttpError, +} from '@spartacus/core'; + +export function tryNormalizeHttpError( + error: HttpErrorResponse | HttpErrorModel | any, + logger?: LoggerService +): HttpErrorModel | Error { + return normalizeHttpError(error, logger) ?? error; +} From 4c7c66540684554c4ac7f3a93e975e193d0b8fa2 Mon Sep 17 00:00:00 2001 From: kpawelczak <42094017+kpawelczak@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:01:58 +0200 Subject: [PATCH 05/11] Unit test fixes (#17879) --- .../src/product/store/effects/product-references.effect.ts | 4 ++-- .../core/src/product/store/effects/product-reviews.effect.ts | 3 +-- .../src/state/utils/entity-loader/entity-loader.action.ts | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/projects/core/src/product/store/effects/product-references.effect.ts b/projects/core/src/product/store/effects/product-references.effect.ts index 01f026fa708..1fbad85e39f 100644 --- a/projects/core/src/product/store/effects/product-references.effect.ts +++ b/projects/core/src/product/store/effects/product-references.effect.ts @@ -10,8 +10,8 @@ import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { ProductReferencesConnector } from '../../connectors/references/product-references.connector'; import { ProductActions } from '../actions/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; import { LoggerService } from '../../../logger'; +import { tryNormalizeHttpError } from '@spartacus/core'; @Injectable() export class ProductReferencesEffects { @@ -36,7 +36,7 @@ export class ProductReferencesEffects { catchError((error) => of( new ProductActions.LoadProductReferencesFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/product/store/effects/product-reviews.effect.ts b/projects/core/src/product/store/effects/product-reviews.effect.ts index cb8eee2783e..a655102eb00 100644 --- a/projects/core/src/product/store/effects/product-reviews.effect.ts +++ b/projects/core/src/product/store/effects/product-reviews.effect.ts @@ -14,7 +14,6 @@ import { GlobalMessageService, GlobalMessageType, } from '../../../global-message/index'; -import { normalizeHttpError } from '../../../util/normalize-http-error'; import { LoggerService } from '../../../logger'; import { tryNormalizeHttpError } from '@spartacus/core'; @@ -39,7 +38,7 @@ export class ProductReviewsEffects { catchError((error) => of( new ProductActions.LoadProductReviewsFail( - normalizeHttpError(error, this.logger) + tryNormalizeHttpError(error, this.logger) ) ) ) diff --git a/projects/core/src/state/utils/entity-loader/entity-loader.action.ts b/projects/core/src/state/utils/entity-loader/entity-loader.action.ts index 4e222f5f7c9..6c21bbcc082 100644 --- a/projects/core/src/state/utils/entity-loader/entity-loader.action.ts +++ b/projects/core/src/state/utils/entity-loader/entity-loader.action.ts @@ -78,7 +78,7 @@ export class EntityLoadAction implements EntityLoaderAction { export class EntityFailAction implements EntityLoaderAction { type = ENTITY_FAIL_ACTION; readonly meta: EntityLoaderMeta; - constructor(entityType: string, id: EntityId, error?: any) { + constructor(entityType: string, id: EntityId, public error?: any) { this.meta = entityFailMeta(entityType, id, error); } } From db9157b0253188464f1be0148df6e647d7b9a210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Fra=C5=9B?= Date: Thu, 28 Sep 2023 10:38:02 +0200 Subject: [PATCH 06/11] feat: CXSPA-3781 SSR - Multi-provided error interceptors (#17865) This pull request introduces methodologies for integrating multiple error interceptors that manage errors within the Server-Side Rendering (SSR) framework. This architectural augmentation preserves backward compatibility, mitigating any potential disruptions for end-users upon the incorporation of new error interceptors into the system. With the introduction of this enhancement, it becomes easier for users to include new error interceptors, giving them the flexibility to determine the order in which these interceptors are applied within the system. This priority setting allows users to control how these interceptors operate and influence the workflow of the system. The order is: High priority Normal or no priority Low priority Preserves the original order within a group of interceptors with the same priority. --- .../error-handling/cx-error-handler.spec.ts | 53 ++++++ .../src/error-handling/cx-error-handler.ts | 26 ++- .../error-handling/error-handling.module.ts | 7 + .../error-interceptor.service.spec.ts | 154 ++++++++++++++++++ .../error-interceptor.service.ts | 121 ++++++++++++++ .../error-interceptors/error-interceptor.ts | 59 +++++++ .../error-interceptors/index.ts | 9 + .../logger-error.interceptor.spec.ts | 37 +++++ .../logger-error.interceptor.ts | 23 +++ projects/core/src/error-handling/index.ts | 1 + 10 files changed, 486 insertions(+), 4 deletions(-) create mode 100644 projects/core/src/error-handling/cx-error-handler.spec.ts create mode 100644 projects/core/src/error-handling/error-interceptors/error-interceptor.service.spec.ts create mode 100644 projects/core/src/error-handling/error-interceptors/error-interceptor.service.ts create mode 100644 projects/core/src/error-handling/error-interceptors/error-interceptor.ts create mode 100644 projects/core/src/error-handling/error-interceptors/index.ts create mode 100644 projects/core/src/error-handling/error-interceptors/logger-error.interceptor.spec.ts create mode 100644 projects/core/src/error-handling/error-interceptors/logger-error.interceptor.ts diff --git a/projects/core/src/error-handling/cx-error-handler.spec.ts b/projects/core/src/error-handling/cx-error-handler.spec.ts new file mode 100644 index 00000000000..5250ef4bb32 --- /dev/null +++ b/projects/core/src/error-handling/cx-error-handler.spec.ts @@ -0,0 +1,53 @@ +import { TestBed } from '@angular/core/testing'; +import { CxErrorHandler } from './cx-error-handler'; +import { + ChainedErrorInterceptorFn, + ERROR_INTERCEPTORS, + ErrorInterceptor, +} from './error-interceptors/error-interceptor'; +import { ErrorInterceptorService } from './error-interceptors/error-interceptor.service'; + +const mockChainFn = jasmine.createSpy(); + +class MockErrorInterceptor implements ErrorInterceptor { + intercept(error: unknown, _next: ChainedErrorInterceptorFn): void { + mockChainFn(error); + } +} + +describe('CxErrorHandler', () => { + let cxErrorHandler: CxErrorHandler; + let errorInterceptorService: ErrorInterceptorService; + + beforeEach(() => { + mockChainFn.calls.reset(); + + TestBed.configureTestingModule({ + providers: [ + CxErrorHandler, + ErrorInterceptorService, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptor, + multi: true, + }, + ], + }); + + cxErrorHandler = TestBed.inject(CxErrorHandler); + errorInterceptorService = TestBed.inject(ErrorInterceptorService); + }); + + it('should call interceptorsChain', () => { + const error = new Error('test error'); + const interceptorsChainSpy = spyOnProperty( + errorInterceptorService, + 'interceptorsChain', + 'get' + ).and.callThrough(); + + cxErrorHandler.handleError(error); + expect(mockChainFn).toHaveBeenCalledWith(error); + expect(interceptorsChainSpy).toHaveBeenCalled(); + }); +}); diff --git a/projects/core/src/error-handling/cx-error-handler.ts b/projects/core/src/error-handling/cx-error-handler.ts index 08f143c33cd..51785ba14f7 100644 --- a/projects/core/src/error-handling/cx-error-handler.ts +++ b/projects/core/src/error-handling/cx-error-handler.ts @@ -5,13 +5,31 @@ */ import { ErrorHandler, Injectable, inject } from '@angular/core'; -import { LoggerService } from '../logger/logger.service'; +import { LoggerService } from '../logger'; +import { ErrorInterceptorService } from './error-interceptors/error-interceptor.service'; +/** + * The CxErrorHandler is the default ErrorHandler for Spartacus. + * It is responsible for handling errors and passing them to the registered error interceptors. + * + * @method handleError - Handles the error by passing it to the registered error interceptors. + * + * @public + */ @Injectable() export class CxErrorHandler implements ErrorHandler { - logger = inject(LoggerService); + //TODO: Keep it updated with the latest version of Spartacus + /** + * @deprecated Since 6.6 - `LoggerService` is unused here. Instead it's now used in `LoggerErrorInterceptor` + */ + protected logger = inject(LoggerService); + protected errorInterceptorService = inject(ErrorInterceptorService); - handleError(error: any): void { - this.logger.error(error); + /** + * Error handler method. Handles the error by passing it to the registered error interceptors. + * @param error - The error to be handled. + */ + handleError(error: unknown): void { + this.errorInterceptorService.interceptorsChain(error); } } diff --git a/projects/core/src/error-handling/error-handling.module.ts b/projects/core/src/error-handling/error-handling.module.ts index 4f40b3f4aa6..838cf780426 100644 --- a/projects/core/src/error-handling/error-handling.module.ts +++ b/projects/core/src/error-handling/error-handling.module.ts @@ -6,6 +6,8 @@ import { ErrorHandler, ModuleWithProviders, NgModule } from '@angular/core'; import { CxErrorHandler } from './cx-error-handler'; +import { ERROR_INTERCEPTORS } from './error-interceptors/error-interceptor'; +import { LoggerErrorInterceptor } from './error-interceptors/logger-error.interceptor'; @NgModule() export class ErrorHandlingModule { @@ -13,6 +15,11 @@ export class ErrorHandlingModule { return { ngModule: ErrorHandlingModule, providers: [ + { + provide: ERROR_INTERCEPTORS, + useClass: LoggerErrorInterceptor, + multi: true, + }, { provide: ErrorHandler, useClass: CxErrorHandler, diff --git a/projects/core/src/error-handling/error-interceptors/error-interceptor.service.spec.ts b/projects/core/src/error-handling/error-interceptors/error-interceptor.service.spec.ts new file mode 100644 index 00000000000..6a0711f7133 --- /dev/null +++ b/projects/core/src/error-handling/error-interceptors/error-interceptor.service.spec.ts @@ -0,0 +1,154 @@ +import { TestBed } from '@angular/core/testing'; +import { + ChainedErrorInterceptorFn, + ERROR_INTERCEPTORS, + ErrorInterceptor, + ErrorInterceptorPriority, +} from './error-interceptor'; +import { ErrorInterceptorService } from './error-interceptor.service'; + +let interceptorsOrder: string[]; + +class MockErrorInterceptor implements ErrorInterceptor { + protected interceptorValue: string; + priority?: ErrorInterceptorPriority | undefined; + constructor(interceptorValue: string) { + this.interceptorValue = interceptorValue; + } + intercept(error: Error, next: ChainedErrorInterceptorFn): void { + interceptorsOrder.push(this.interceptorValue); + next(error); + } +} + +class MockErrorInterceptorA extends MockErrorInterceptor { + constructor() { + super('A - normal priority'); + } +} + +class MockErrorInterceptorB extends MockErrorInterceptor { + priority = ErrorInterceptorPriority.LOW; + constructor() { + super('B - low priority'); + } +} + +class MockErrorInterceptorC extends MockErrorInterceptor { + priority = ErrorInterceptorPriority.HIGH; + constructor() { + super('C - high priority'); + } +} + +class MockErrorInterceptorNoPass extends MockErrorInterceptor { + constructor() { + super('No Pass'); + } + intercept(_error: Error, _next: ChainedErrorInterceptorFn): void { + interceptorsOrder.push(this.interceptorValue); + // do not pass the error to the next interceptor + } +} + +describe('ErrorInterceptorService', () => { + let errorInterceptorService: ErrorInterceptorService; + + beforeEach(() => { + interceptorsOrder = []; + }); + + describe('when there are no error interceptors', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ErrorInterceptorService], + }); + + errorInterceptorService = TestBed.inject(ErrorInterceptorService); + }); + + it('should not call interceptors', () => { + const error = new Error('test error'); + errorInterceptorService.interceptorsChain(error); + expect(interceptorsOrder).toEqual([]); + }); + }); + + describe('when there are error interceptors', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + ErrorInterceptorService, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptorA, + multi: true, + }, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptorB, + multi: true, + }, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptorC, + multi: true, + }, + ], + }); + + errorInterceptorService = TestBed.inject(ErrorInterceptorService); + }); + + it('should call all error interceptors with the correct order', () => { + const error = new Error('test error'); + errorInterceptorService.interceptorsChain(error); + expect(interceptorsOrder).toEqual([ + 'C - high priority', + 'A - normal priority', + 'B - low priority', + ]); + }); + }); + + describe('when interceptor in the middle breaks the chain', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + ErrorInterceptorService, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptorA, + multi: true, + }, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptorB, + multi: true, + }, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptorC, + multi: true, + }, + { + provide: ERROR_INTERCEPTORS, + useClass: MockErrorInterceptorNoPass, + multi: true, + }, + ], + }); + + errorInterceptorService = TestBed.inject(ErrorInterceptorService); + }); + + it('should break the chain if any interceptor does not pass an error to the next interceptor', () => { + errorInterceptorService.interceptorsChain(new Error('test error')); + expect(interceptorsOrder).toEqual([ + 'C - high priority', + 'A - normal priority', + 'No Pass', + ]); + }); + }); +}); diff --git a/projects/core/src/error-handling/error-interceptors/error-interceptor.service.ts b/projects/core/src/error-handling/error-interceptors/error-interceptor.service.ts new file mode 100644 index 00000000000..32ad20b2e3e --- /dev/null +++ b/projects/core/src/error-handling/error-interceptors/error-interceptor.service.ts @@ -0,0 +1,121 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Injectable, inject } from '@angular/core'; +import { + ChainedErrorInterceptorFn, + ERROR_INTERCEPTORS, + ErrorInterceptor, + ErrorInterceptorPriority, +} from './error-interceptor'; + +/** + * Error interceptor service is responsible for sorting and handling error interceptors chain. + * + * @public + */ +@Injectable({ + providedIn: 'root', +}) +export class ErrorInterceptorService { + protected errorInterceptors: ErrorInterceptor[] = + inject(ERROR_INTERCEPTORS, { optional: true }) ?? []; + + protected sortedErrorInterceptors: ErrorInterceptor[] = + this.sortErrorInterceptors(this.errorInterceptors); + + get interceptorsChain(): ChainedErrorInterceptorFn { + // Similar to Angular's interceptors, error interceptors are organized from right to left, + // ensuring that the ultimate execution order is from left to right. + // In other words, if the interceptors array contains `[a, b, c]`, + // our goal is to create a chain that can be envisioned as c(b(a(end))), + // constructed by progressively adding elements from the innermost to the outermost. + return this.sortedErrorInterceptors.reduceRight( + (partialChain, interceptor) => + this.chainInterceptors(partialChain, interceptor), + () => {} + ); + } + + /** + * Sorts error interceptors based on priority. The higher the priority, the earlier the interceptor will be called. + * Preserves the original order within a group of interceptors with the same priority. + * + * @param errorInterceptors - error interceptors to sort + * @returns sorted error interceptors + * + */ + protected sortErrorInterceptors( + errorInterceptors: ErrorInterceptor[] + ): ErrorInterceptor[] { + const interceptorsGroupedByPriority = new Map< + ErrorInterceptorPriority, + ErrorInterceptor[] + >(); + errorInterceptors.forEach( + this.addToProperGroup(interceptorsGroupedByPriority) + ); + return this.mapToSortedArray(interceptorsGroupedByPriority); + } + + /** + * Handles error interceptors chain. + * @param partialChain - next chained interceptor function that handles the error and calls the next interceptor + * @param interceptor - current interceptor + * @returns chained interceptor function + * + */ + protected chainInterceptors( + partialChain: ChainedErrorInterceptorFn, + interceptor: ErrorInterceptor + ): ChainedErrorInterceptorFn { + return (error: unknown) => { + interceptor.intercept(error, partialChain); + }; + } + + /** + * Function that adds error interceptor to the proper group based on its priority. + * @param interceptor + * + */ + protected addToProperGroup( + interceptorsGroupedByPriority: Map< + ErrorInterceptorPriority, + ErrorInterceptor[] + > + ) { + return (interceptor: ErrorInterceptor) => { + const priority = interceptor.priority ?? ErrorInterceptorPriority.NORMAL; + const group = interceptorsGroupedByPriority.get(priority) ?? []; + group.push(interceptor); + interceptorsGroupedByPriority.set(priority, group); + }; + } + + /** + * Function that maps interceptors grouped by priority to a sorted array. + * The higher the priority, the earlier the interceptor will be called. + * + * @param interceptorsGroupedByPriority - interceptors grouped by priority + * @returns sorted array of interceptors + * + */ + protected mapToSortedArray( + interceptorsGroupedByPriority: Map< + ErrorInterceptorPriority, + ErrorInterceptor[] + > + ) { + return [...interceptorsGroupedByPriority.entries()] + .map(([priority, interceptors]) => ({ + priority, + interceptors, + })) + .sort((a, b) => b.priority - a.priority) //sort descending, from highest to lowest priority + .flatMap((item) => item.interceptors); + } +} diff --git a/projects/core/src/error-handling/error-interceptors/error-interceptor.ts b/projects/core/src/error-handling/error-interceptors/error-interceptor.ts new file mode 100644 index 00000000000..1dd625d27ff --- /dev/null +++ b/projects/core/src/error-handling/error-interceptors/error-interceptor.ts @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { InjectionToken } from '@angular/core'; + +/** + * ChainedErrorInterceptorFn is a function that is passed to the error interceptor. + * It is responsible for handling an error in some way. If the error is not resolved by this interceptor, + * it should pass the original error (or a new error) to the next interceptor in the chain. + * + * @public + */ +export type ChainedErrorInterceptorFn = (error: unknown) => void; + +/** + * Priority of the error interceptor. The higher the priority, the earlier the interceptor will be called. + * The order is: + * 1. High priority + * 2. Normal or no priority + * 3. Low priority + * + * Preserves the original order within a group of interceptors with the same priority. + * + * @public + */ +export enum ErrorInterceptorPriority { + HIGH = 10, + NORMAL = 0, + LOW = -10, +} + +/** + * Error interceptor is responsible for intercepting errors handled by the CxErrorHandler. + * Error interceptors can be provided with the proper priority. The higher the priority, the earlier the interceptor will be called. + * + * @member priority - Priority of the error interceptor. The higher the priority, the earlier the interceptor will be called. Parameter is optional. + * @method interceptError - Intercepts the error and passes it to the next interceptor in the chain if provided. + * + * @public + */ +export interface ErrorInterceptor { + priority?: ErrorInterceptorPriority; + intercept(error: unknown, next: ChainedErrorInterceptorFn): void; +} + +/** + * Injection token for error interceptors. + * Error interceptors can be provided with the proper priority. The higher the priority, the earlier the interceptor will be called. + * Spartacus is providing one error interceptor by default: + * - LoggerErrorInterceptor - responsible for logging errors to the LoggerService. + * + * @public + */ +export const ERROR_INTERCEPTORS = new InjectionToken( + 'ERROR_INTERCEPTORS' +); diff --git a/projects/core/src/error-handling/error-interceptors/index.ts b/projects/core/src/error-handling/error-interceptors/index.ts new file mode 100644 index 00000000000..804955d81bf --- /dev/null +++ b/projects/core/src/error-handling/error-interceptors/index.ts @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './error-interceptor'; +export * from './error-interceptor.service'; +export * from './logger-error.interceptor'; diff --git a/projects/core/src/error-handling/error-interceptors/logger-error.interceptor.spec.ts b/projects/core/src/error-handling/error-interceptors/logger-error.interceptor.spec.ts new file mode 100644 index 00000000000..fbfe708f5e9 --- /dev/null +++ b/projects/core/src/error-handling/error-interceptors/logger-error.interceptor.spec.ts @@ -0,0 +1,37 @@ +import { TestBed } from '@angular/core/testing'; +import { LoggerService } from '../../logger'; +import { ChainedErrorInterceptorFn } from './error-interceptor'; +import { LoggerErrorInterceptor } from './logger-error.interceptor'; + +class MockLogger implements Partial { + error = jasmine.createSpy('error'); +} + +const mockChainFn = jasmine.createSpy( + 'mockChainFn' +) as ChainedErrorInterceptorFn; + +describe('LoggerErrorInterceptor', () => { + let loggerErrorInterceptor: LoggerErrorInterceptor; + let logger: LoggerService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + LoggerErrorInterceptor, + { provide: LoggerService, useClass: MockLogger }, + ], + }); + + loggerErrorInterceptor = TestBed.inject(LoggerErrorInterceptor); + logger = TestBed.inject(LoggerService); + }); + + it('should log the error and call next interceptor', () => { + const error = new Error('test error'); + + loggerErrorInterceptor.intercept(error, mockChainFn); + expect(logger.error).toHaveBeenCalledWith(error); + expect(mockChainFn).toHaveBeenCalledWith(error); + }); +}); diff --git a/projects/core/src/error-handling/error-interceptors/logger-error.interceptor.ts b/projects/core/src/error-handling/error-interceptors/logger-error.interceptor.ts new file mode 100644 index 00000000000..5fa0980d8ba --- /dev/null +++ b/projects/core/src/error-handling/error-interceptors/logger-error.interceptor.ts @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Injectable, inject } from '@angular/core'; +import { LoggerService } from '../../logger'; +import { + ChainedErrorInterceptorFn, + ErrorInterceptor, +} from './error-interceptor'; + +@Injectable({ + providedIn: 'root', +}) +export class LoggerErrorInterceptor implements ErrorInterceptor { + logger = inject(LoggerService); + intercept(error: Error, next: ChainedErrorInterceptorFn): void { + this.logger.error(error); + next(error); + } +} diff --git a/projects/core/src/error-handling/index.ts b/projects/core/src/error-handling/index.ts index 67b2466aa3a..bb4863974a2 100644 --- a/projects/core/src/error-handling/index.ts +++ b/projects/core/src/error-handling/index.ts @@ -6,5 +6,6 @@ export * from './cx-error-handler'; export * from './error-handling.module'; +export * from './error-interceptors'; export * from './http-error-handler'; export * from './effects-error-handler'; From 37239d3b17069e5e4251e55060e20eeb58bde334 Mon Sep 17 00:00:00 2001 From: Caine Rotherham Date: Tue, 3 Oct 2023 10:13:06 +0200 Subject: [PATCH 07/11] test: SSR E2E Test Scenarios (#17666) Closes: https://jira.tools.sap/browse/CXSPA-3780 --- .env-cmdrc | 3 + .github/workflows/ci-merge-checks.yml | 27 +++++- .github/workflows/ci.yml | 27 +++++- ci-scripts/unit-tests.sh | 2 +- package-lock.json | 4 +- package.json | 4 + projects/schematics/src/dependencies.json | 3 + .../src/shared/utils/graph-utils.ts | 3 +- projects/ssr-tests/.eslintrc.json | 12 +++ projects/ssr-tests/.gitignore | 1 + projects/ssr-tests/jest.config.js | 35 ++++++++ projects/ssr-tests/package.json | 21 +++++ projects/ssr-tests/project.json | 22 +++++ projects/ssr-tests/setup-jest.ts | 8 ++ projects/ssr-tests/src/log.utils.ts | 85 +++++++++++++++++++ projects/ssr-tests/src/proxy.utils.ts | 85 +++++++++++++++++++ projects/ssr-tests/src/ssr-testing.spec.ts | 80 +++++++++++++++++ projects/ssr-tests/src/ssr.utils.ts | 36 ++++++++ projects/ssr-tests/tsconfig.json | 22 +++++ 19 files changed, 475 insertions(+), 5 deletions(-) create mode 100644 projects/ssr-tests/.eslintrc.json create mode 100644 projects/ssr-tests/.gitignore create mode 100644 projects/ssr-tests/jest.config.js create mode 100644 projects/ssr-tests/package.json create mode 100644 projects/ssr-tests/project.json create mode 100644 projects/ssr-tests/setup-jest.ts create mode 100644 projects/ssr-tests/src/log.utils.ts create mode 100644 projects/ssr-tests/src/proxy.utils.ts create mode 100644 projects/ssr-tests/src/ssr-testing.spec.ts create mode 100644 projects/ssr-tests/src/ssr.utils.ts create mode 100644 projects/ssr-tests/tsconfig.json diff --git a/.env-cmdrc b/.env-cmdrc index 21292fe4220..b4cb0edb83e 100644 --- a/.env-cmdrc +++ b/.env-cmdrc @@ -5,6 +5,9 @@ "local": { "CX_BASE_URL": "https://localhost:9002" }, + "local-http": { + "CX_BASE_URL": "http://localhost:9002" + }, "ci": { "CX_BASE_URL": "https://20.83.184.244:9002" }, diff --git a/.github/workflows/ci-merge-checks.yml b/.github/workflows/ci-merge-checks.yml index 773ebe1b4a8..17ff3bcbc25 100644 --- a/.github/workflows/ci-merge-checks.yml +++ b/.github/workflows/ci-merge-checks.yml @@ -150,8 +150,32 @@ jobs: BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} run: | ci-scripts/e2e-cypress.sh -s b2b + ssr_tests: + needs: [no_retries, validate_e2e_execution] + name: MC - SSR tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v2 + with: + path: | + node_modules + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Package installation + run: npm ci + - name: Build SSR Server + run: npm run build:libs && npm run build && npm run build:ssr:local-http + - name: Run SSR tests + run: npm run test:ssr:ci --verbose merge_checks_result: - needs: [b2c_e2e_tests, b2c_ssr_e2e_tests, b2b_e2e_tests] + needs: [b2c_e2e_tests, b2c_ssr_e2e_tests, b2b_e2e_tests, ssr_tests] name: MC - Result runs-on: ubuntu-latest if: ${{ always() }} @@ -163,4 +187,5 @@ jobs: if: | needs.b2c_e2e_tests.result == 'failure' || needs.b2c_e2e_tests.result == 'cancelled' || needs.b2c_ssr_e2e_tests.result == 'failure' || needs.b2c_ssr_e2e_tests.result == 'cancelled' || + needs.ssr_tests.result == 'failure' || needs.ssr_tests.result == 'cancelled' || needs.b2b_e2e_tests.result == 'failure' || needs.b2b_e2e_tests.result == 'cancelled' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de47f5387ed..4a23be758fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -216,8 +216,32 @@ jobs: BUILD_NUMBER: ci-build-number-${{ github.event.pull_request.head.sha || github.run_id }} run: | ci-scripts/e2e-cypress.sh -s b2b + ssr_tests: + needs: [no_retries, validate_e2e_execution] + name: SSR Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v2 + with: + path: | + node_modules + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Package installation + run: npm ci + - name: Build SSR Server + run: npm run build:libs && npm run build && npm run build:ssr:local-http + - name: Run SSR tests + run: npm run test:ssr:ci --verbose build_conclusion: - needs: [no_retries, unit_tests, linting, b2c_e2e_tests, b2c_ssr_e2e_tests, b2b_e2e_tests, sonarqube_scan] + needs: [no_retries, unit_tests, linting, b2c_e2e_tests, b2c_ssr_e2e_tests, b2b_e2e_tests, ssr_tests, sonarqube_scan] name: Build Conclusion runs-on: ubuntu-latest if: ${{ always() }} @@ -234,6 +258,7 @@ jobs: needs.linting.result == 'failure' || needs.linting.result == 'cancelled' || needs.b2c_e2e_tests.result == 'failure' || needs.b2c_e2e_tests.result == 'cancelled' || needs.b2c_ssr_e2e_tests.result == 'failure' || needs.b2c_ssr_e2e_tests.result == 'cancelled' || + needs.ssr_tests.result == 'failure' || needs.ssr_tests.result == 'cancelled' || needs.b2b_e2e_tests.result == 'failure' || needs.b2b_e2e_tests.result == 'cancelled' send_slack_message: needs: build_conclusion diff --git a/ci-scripts/unit-tests.sh b/ci-scripts/unit-tests.sh index 7f77ee450f0..5b18df3fd95 100755 --- a/ci-scripts/unit-tests.sh +++ b/ci-scripts/unit-tests.sh @@ -2,7 +2,7 @@ set -e set -o pipefail -EXCLUDE_APPLICATIONS=storefrontapp +EXCLUDE_APPLICATIONS=storefrontapp,ssr-tests EXCLUDE_JEST=storefrontstyles,schematics,setup echo "-----" diff --git a/package-lock.json b/package-lock.json index a15cbf50258..c56883d62a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -92,6 +92,7 @@ "eslint-plugin-prefer-arrow": "^1.2.3", "fs-extra": "^11.1.1", "glob": "^7.1.6", + "http-proxy": "^1.18.1", "http-server": "^14.1.1", "i18n-lint": "^1.1.0", "jasmine-core": "~4.6.0", @@ -12475,8 +12476,9 @@ }, "node_modules/http-proxy": { "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, - "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", diff --git a/package.json b/package.json index eca5dd81729..335f4b7e382 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "build:setup": "nx build setup --configuration production", "build:ssr": "env-cmd --no-override -e dev,b2c,$SPA_ENV nx run storefrontapp:server:production", "build:ssr:ci": "env-cmd -e ci,b2c,$SPA_ENV nx run storefrontapp:server:production", + "build:ssr:local-http": "env-cmd -e local-http,b2c,$SPA_ENV nx run storefrontapp:server:production", "build:storefinder": "npm --prefix feature-libs/storefinder run build:schematics && nx build storefinder --configuration production", "build:smartedit": "npm --prefix feature-libs/smartedit run build:schematics && nx build smartedit --configuration production", "build:tracking": "npm --prefix feature-libs/tracking run build:schematics && nx build tracking --configuration production", @@ -87,6 +88,8 @@ "test:storefront:lib": "nx test storefrontlib --source-map --code-coverage", "dev:ssr": "env-cmd --no-override -e dev,b2c,$SPA_ENV cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 nx run storefrontapp:serve-ssr", "serve:ssr": "node dist/storefrontapp-server/main.js", + "test:ssr": "env-cmd -e dev nx test ssr-tests", + "test:ssr:ci": "env-cmd -e ci nx test ssr-tests", "serve:ssr:ci": "NODE_TLS_REJECT_UNAUTHORIZED=0 SSR_TIMEOUT=0 node dist/storefrontapp-server/main.js", "serve:ssr:dev": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 node dist/storefrontapp-server/main.js", "prerender": "nx run storefrontapp:prerender --routes-file projects/storefrontapp/prerender.txt", @@ -183,6 +186,7 @@ "eslint-plugin-prefer-arrow": "^1.2.3", "fs-extra": "^11.1.1", "glob": "^7.1.6", + "http-proxy": "^1.18.1", "http-server": "^14.1.1", "i18n-lint": "^1.1.0", "jasmine-core": "~4.6.0", diff --git a/projects/schematics/src/dependencies.json b/projects/schematics/src/dependencies.json index f7390376483..0af4deb11ca 100644 --- a/projects/schematics/src/dependencies.json +++ b/projects/schematics/src/dependencies.json @@ -23,6 +23,9 @@ "parse5": "^6.0.1", "typescript": "^4.8.2" }, + "ssr-tests": { + "http-proxy": "^1.18.1" + }, "storefrontapp-e2e-cypress": {}, "@spartacus/storefront": { "@angular/common": "^15.2.9", diff --git a/projects/schematics/src/shared/utils/graph-utils.ts b/projects/schematics/src/shared/utils/graph-utils.ts index d773a265eb1..80061c89bdf 100644 --- a/projects/schematics/src/shared/utils/graph-utils.ts +++ b/projects/schematics/src/shared/utils/graph-utils.ts @@ -136,7 +136,8 @@ export function kahnsAlgorithm(graph: Graph): string[] { function createLibraryDependencyGraph(): Graph { const skip = CORE_SPARTACUS_SCOPES.concat( 'storefrontapp-e2e-cypress', - 'storefrontapp' + 'storefrontapp', + 'ssr-tests' ); const spartacusLibraries = Object.keys(collectedDependencies).filter( diff --git a/projects/ssr-tests/.eslintrc.json b/projects/ssr-tests/.eslintrc.json new file mode 100644 index 00000000000..04be8021092 --- /dev/null +++ b/projects/ssr-tests/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["**/*.d.ts"], + "overrides": [ + { + "files": ["*.ts"], + "rules": { + "no-console": "off" + } + } + ] +} diff --git a/projects/ssr-tests/.gitignore b/projects/ssr-tests/.gitignore new file mode 100644 index 00000000000..407f23ec56d --- /dev/null +++ b/projects/ssr-tests/.gitignore @@ -0,0 +1 @@ +ssr.log diff --git a/projects/ssr-tests/jest.config.js b/projects/ssr-tests/jest.config.js new file mode 100644 index 00000000000..8da788e525e --- /dev/null +++ b/projects/ssr-tests/jest.config.js @@ -0,0 +1,35 @@ +const { pathsToModuleNameMapper } = require('ts-jest'); +const { compilerOptions } = require('./tsconfig.json'); +const { defaultTransformerOptions } = require('jest-preset-angular/presets'); + +/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */ +module.exports = { + preset: 'jest-preset-angular', + globalSetup: 'jest-preset-angular/global-setup', + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, { + prefix: '/', + }), + setupFilesAfterEnv: ['/setup-jest.ts'], + testMatch: ['**/+(*.)+(spec).+(ts)'], + transform: { + '^.+\\.(ts|js|mjs|html|svg)$': [ + 'jest-preset-angular', + { + ...defaultTransformerOptions, + tsconfig: '/tsconfig.json', + }, + ], + }, + + collectCoverage: false, + coverageReporters: ['json', 'lcov', 'text', 'clover'], + coverageDirectory: '/../../coverage/ssr-tests', + coverageThreshold: { + global: { + statements: 90, + branches: 74, + functions: 90, + lines: 90, + }, + }, +}; diff --git a/projects/ssr-tests/package.json b/projects/ssr-tests/package.json new file mode 100644 index 00000000000..1b96a2b1714 --- /dev/null +++ b/projects/ssr-tests/package.json @@ -0,0 +1,21 @@ +{ + "name": "ssr-tests", + "description": "Spartacus SSR Tests", + "keywords": [ + "spartacus", + "ssr", + "tests" + ], + "author": "SAP, Spartacus team", + "license": "Apache-2.0", + "private": true, + "scripts": { + "test": "../../node_modules/.bin/jest --config ./jest.config.js" + }, + "dependencies": { + "tslib": "^2.6.2" + }, + "peerDependencies": { + "http-proxy": "^1.18.1" + } +} diff --git a/projects/ssr-tests/project.json b/projects/ssr-tests/project.json new file mode 100644 index 00000000000..63b9ea51fb0 --- /dev/null +++ b/projects/ssr-tests/project.json @@ -0,0 +1,22 @@ +{ + "name": "ssr-tests", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "projects/ssr-tests/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": ["projects/ssr-tests/**/*.ts"] + } + }, + "test-jest": { + "executor": "nx:run-commands", + "options": { + "command": "npm run test", + "cwd": "projects/ssr-tests" + } + } + }, + "tags": ["type:util"] +} diff --git a/projects/ssr-tests/setup-jest.ts b/projects/ssr-tests/setup-jest.ts new file mode 100644 index 00000000000..919fe8743ac --- /dev/null +++ b/projects/ssr-tests/setup-jest.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import 'jest-preset-angular/setup-jest'; +import 'zone.js'; diff --git a/projects/ssr-tests/src/log.utils.ts b/projects/ssr-tests/src/log.utils.ts new file mode 100644 index 00000000000..17d077da428 --- /dev/null +++ b/projects/ssr-tests/src/log.utils.ts @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Contains methods pertaining to reading, writing and asserting of the ssr log + * generated by a running ssr server for the sake of testing ssr. + */ + +import * as fs from 'fs'; + +/** + * Path where SSR log file from server will be generated and read from. + */ +const SSR_LOG_PATH = './ssr.log'; + +/** + * Writes no characters to log to clear log file. + */ +export function clearSsrLogFile(): void { + fs.writeFileSync(SSR_LOG_PATH, ''); +} + +/** + * Returns all text in the log as a single string. + */ +export function getLogText(): string { + return fs.readFileSync(SSR_LOG_PATH).toString(); +} + +/** + * Reads log and returns messages as string array. + */ +export function getLogMessages(): string[] { + const data = fs.readFileSync(SSR_LOG_PATH).toString(); + return ( + data + .toString() + .split('\n') + // We're interested only in JSON logs from Spartacus SSR app. + // We ignore plain text logs coming from other sources, like `Node Express server listening on http://localhost:4200` + .filter((text: string) => text.charAt(0) === '{') + .map((text: any) => JSON.parse(text).message) + ); +} + +/** + * Check that log contains expected messages in string array. + * Fail test if log does not contain expected messages. + */ +export function assertMessages(expected: string[]): void { + const messages = getLogMessages(); + for (const message of expected) { + expect(messages).toContain(message); + } +} + +/** + * Check log every interval to see if log contains text. + * Keeps waiting until log contains text or test times out. + */ +export async function waitUntilLogContainsText( + text: string, + checkInterval = 500 +): Promise { + return new Promise((resolve) => { + if (doesLogContainText(text)) { + return resolve(true); + } + return setTimeout( + () => resolve(waitUntilLogContainsText(text)), + checkInterval + ); + }); +} + +/** + * Returns true if log contains string. + */ +export function doesLogContainText(text: string): boolean { + const data = fs.readFileSync(SSR_LOG_PATH).toString(); + return data.includes(text); +} diff --git a/projects/ssr-tests/src/proxy.utils.ts b/projects/ssr-tests/src/proxy.utils.ts new file mode 100644 index 00000000000..990a7e8bb6f --- /dev/null +++ b/projects/ssr-tests/src/proxy.utils.ts @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as http from 'http'; +import * as httpProxy from 'http-proxy'; + +const proxy = (httpProxy).createProxyServer({ secure: false }); + +/** + * Default settings to send http requests. + */ +const REQUEST_OPTIONS = { + host: 'localhost', + port: 4000, +}; + +interface ProxyOptions { + /** + * The url to reroute requests to. + */ + target: string; + /** + * Number of seconds to delay requests before sending. + */ + delay?: number; + /** + * Number of status code to set response to. + */ + throwStatus?: number; +} + +/** + * Starts an http proxy server on port 9002 with the provided options. + */ +export async function startProxyServer(options: ProxyOptions) { + return new Promise((resolve) => { + const server = http.createServer((req: any, res: any) => { + const forwardRequest = () => + proxy.web(req, res, { target: options.target }); + + if (options.throwStatus) { + proxy.on('proxyRes', (proxyRes: any) => { + proxyRes.statusCode = options.throwStatus; + }); + } + + if (options.delay) { + setTimeout(forwardRequest, options.delay); + } else { + forwardRequest(); + } + }); + + server.listen(9002, () => { + resolve(server); + }); + }); +} + +/** + * Send an http GET request to a given url. + */ +export async function sendRequest(path: string) { + return new Promise((resolve, reject) => { + const req = http.get({ ...REQUEST_OPTIONS, path }, (res: any) => { + const bodyChunks: string[] = []; + + res + .on('data', (chunk: any) => { + bodyChunks.push(chunk); + }) + .on('end', () => { + res.bodyChunks = bodyChunks; + return resolve(res); + }); + }); + + req.on('error', (e: Error) => { + reject(e); + }); + }); +} diff --git a/projects/ssr-tests/src/ssr-testing.spec.ts b/projects/ssr-tests/src/ssr-testing.spec.ts new file mode 100644 index 00000000000..645938bb4ae --- /dev/null +++ b/projects/ssr-tests/src/ssr-testing.spec.ts @@ -0,0 +1,80 @@ +import * as Log from './log.utils'; +import * as ProxyServer from './proxy.utils'; +import * as Ssr from './ssr.utils'; + +const BACKEND_BASE_URL: string = process.env.CX_BASE_URL || ''; + +describe('SSR E2E', () => { + let proxy: any; + const REQUEST_PATH = '/electronics-spa/en/USD/'; + + beforeEach(async () => { + Log.clearSsrLogFile(); + await Ssr.startSsrServer(); + }); + + afterEach(async () => { + await proxy.close(); + await Ssr.killSsrServer(); + }); + + it('should receive success response with request', async () => { + proxy = await ProxyServer.startProxyServer({ + target: BACKEND_BASE_URL, + }); + const response: any = await ProxyServer.sendRequest(REQUEST_PATH); + expect(response.statusCode).toEqual(200); + + // Rendering should not complete in the first request. + // App should fall back to csr. + Log.assertMessages([ + `Rendering started (${REQUEST_PATH})`, + `Request is waiting for the SSR rendering to complete (${REQUEST_PATH})`, + ]); + }); + + // TODO: Test incomplete + xit('should receive cached response with next request', async () => { + proxy = await ProxyServer.startProxyServer({ + target: BACKEND_BASE_URL, + }); + const response: any = await ProxyServer.sendRequest(REQUEST_PATH); + expect(response.statusCode).toEqual(200); + + Log.assertMessages(['Render from cache (/)']); + }); + + // TODO: Test incomplete + xit('should receive 404 response when page is not existing', () => {}); + + // TODO: Test incomplete + xit('should receive 500 error response with request', async () => { + proxy = await ProxyServer.startProxyServer({ + target: BACKEND_BASE_URL, + throwStatus: 500, + }); + proxy.on('proxyRes', function (proxyRes: any) { + proxyRes.statusCode = 500; + }); + const response: any = await ProxyServer.sendRequest('/'); + expect(response.statusCode).toEqual(500); + }); + + // TODO: Currently, the ssr server still responds with 200 quickly despite the proxy delay + // TODO: Test incomplete + xit('should receive 500 error response with timed-out request', async () => { + proxy = await ProxyServer.startProxyServer({ + target: BACKEND_BASE_URL, + delay: 10000, + }); + const response: any = await ProxyServer.sendRequest('/timeout'); + + // Waits a time for server to timeout + await new Promise((res) => setTimeout(res, 15000)); + + // TODO: Assert ssr server log for timeout error + Log.assertMessages(['timeout']); + + expect(response.statusCode).toEqual(500); + }, 20000); +}); diff --git a/projects/ssr-tests/src/ssr.utils.ts b/projects/ssr-tests/src/ssr.utils.ts new file mode 100644 index 00000000000..f1d7848b4fd --- /dev/null +++ b/projects/ssr-tests/src/ssr.utils.ts @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 SAP Spartacus team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Contains methods pertaining to managing the ssr server process for testing purposes. + */ + +import * as childProcess from 'child_process'; +import * as Log from './log.utils'; + +/** + * Start an ssr server instance at the given port (default 4000). + * The server will output a log file at the test project root named "ssr.log". + * Funtion finishes once the server is initialized. + */ +export async function startSsrServer(port = 4000) { + childProcess.exec( + `NODE_TLS_REJECT_UNAUTHORIZED=0 PORT=${port} npm run serve:ssr --prefix ../../> ssr.log` + ); + await Log.waitUntilLogContainsText(`Node Express server listening on `); +} + +/** + * Kills the ssr server process at the given port (default 4000). + * Promise resolves once the kill command is executed. + */ +export function killSsrServer(port = 4000) { + return new Promise((resolve) => { + childProcess.exec(`kill $(lsof -t -i:${port})`, () => { + resolve(true); + }); + }); +} diff --git a/projects/ssr-tests/tsconfig.json b/projects/ssr-tests/tsconfig.json new file mode 100644 index 00000000000..74bac1c56e0 --- /dev/null +++ b/projects/ssr-tests/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "module": "CommonJs", + "types": ["jest"], + "declaration": true, + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noUnusedParameters": true, + "noUnusedLocals": true, + "skipDefaultLibCheck": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "strictNullChecks": true, + "resolveJsonModule": true, + "esModuleInterop": true + }, + "include": ["src/**.ts"] +} From 32d82d6fb8386ad2ea710b1dd4ee777637daf479 Mon Sep 17 00:00:00 2001 From: Caine Rotherham Date: Mon, 9 Oct 2023 12:03:45 +0200 Subject: [PATCH 08/11] chore: Enable cache build check on epic branches (#17938) --- .../workflows/ci-continuous-integration.yml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/ci-continuous-integration.yml b/.github/workflows/ci-continuous-integration.yml index 2044ac9fa45..2ece32ab5ad 100644 --- a/.github/workflows/ci-continuous-integration.yml +++ b/.github/workflows/ci-continuous-integration.yml @@ -46,6 +46,33 @@ jobs: - name: Run unit tests run: | ci-scripts/unit-tests.sh + build-libs: + # TODO: For Testing SSR Epic only, remove job before merge + name: CI - Build Libraries + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v2 + with: + path: | + node_modules + projects/storefrontapp-e2e-cypress/node_modules + ~/.cache/Cypress + key: nodemodules-${{ github.event.pull_request.base.sha }} + restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} + - name: Package installation + run: npm ci + - name: Build Libraries + run: | + npm run build:libs sonarqube_scan: name: CI - SonarQube Scan runs-on: ubuntu-latest From 77e8d8dd51761e71e1002e074751bd2c8b634914 Mon Sep 17 00:00:00 2001 From: kpawelczak <42094017+kpawelczak@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:24:13 +0200 Subject: [PATCH 09/11] fix build failure (#17944) --- .../store/actions/anonymous-consents.action.ts | 2 +- .../auth/client-auth/store/actions/client-token.action.ts | 2 +- projects/core/src/cms/store/actions/components.action.ts | 2 +- .../src/cms/store/actions/navigation-entry-item.action.ts | 2 +- projects/core/src/cms/store/actions/page.action.ts | 2 +- .../effects-error-handler/cx-error-handler.effect.ts | 2 +- .../effects-error-handler.service.ts | 6 +++++- .../product/store/actions/product-references.action.ts | 2 +- .../src/product/store/actions/product-reviews.action.ts | 2 +- .../src/product/store/actions/product-search.action.ts | 2 +- projects/core/src/product/store/actions/product.action.ts | 2 +- .../product/store/effects/product-references.effect.ts | 2 +- .../src/product/store/effects/product-reviews.effect.ts | 2 +- .../src/site-context/store/actions/base-site.action.ts | 2 +- .../src/site-context/store/actions/currencies.action.ts | 2 +- .../src/site-context/store/actions/languages.action.ts | 2 +- projects/core/src/state/utils/loader/loader.action.ts | 2 +- .../utils/scoped-loader/entity-scoped-loader.actions.ts | 2 +- .../src/user/store/actions/billing-countries.action.ts | 2 +- .../core/src/user/store/actions/customer-coupon.action.ts | 2 +- .../src/user/store/actions/delivery-countries.action.ts | 2 +- .../user/store/actions/notification-preference.action.ts | 2 +- .../core/src/user/store/actions/payment-methods.action.ts | 2 +- .../src/user/store/actions/product-interests.actions.ts | 2 +- projects/core/src/user/store/actions/regions.action.ts | 2 +- .../core/src/user/store/actions/user-addresses.action.ts | 2 +- .../core/src/user/store/actions/user-consents.action.ts | 2 +- .../src/user/store/actions/user-cost-center.action.ts | 2 +- projects/core/src/util/try-normalize-http-error.ts | 8 +++----- 29 files changed, 35 insertions(+), 33 deletions(-) diff --git a/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts b/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts index 4d849a94ffe..7ae988bd238 100644 --- a/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts +++ b/projects/core/src/anonymous-consents/store/actions/anonymous-consents.action.ts @@ -10,7 +10,7 @@ import { } from '../../../model/consent.model'; import { StateUtils } from '../../../state/utils/index'; import { ANONYMOUS_CONSENTS } from '../anonymous-consents-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_ANONYMOUS_CONSENT_TEMPLATES = '[Anonymous Consents] Load Anonymous Consent Templates'; diff --git a/projects/core/src/auth/client-auth/store/actions/client-token.action.ts b/projects/core/src/auth/client-auth/store/actions/client-token.action.ts index 175c5274fb6..c8c29ff6dba 100644 --- a/projects/core/src/auth/client-auth/store/actions/client-token.action.ts +++ b/projects/core/src/auth/client-auth/store/actions/client-token.action.ts @@ -7,7 +7,7 @@ import { StateUtils } from '../../../../state/utils/index'; import { ClientToken } from '../../models/client-token.model'; import { CLIENT_TOKEN_DATA } from '../client-auth-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../../model/index'; export const LOAD_CLIENT_TOKEN = '[Token] Load Client Token'; export const LOAD_CLIENT_TOKEN_FAIL = '[Token] Load Client Token Fail'; diff --git a/projects/core/src/cms/store/actions/components.action.ts b/projects/core/src/cms/store/actions/components.action.ts index ec759042512..6d900b83023 100755 --- a/projects/core/src/cms/store/actions/components.action.ts +++ b/projects/core/src/cms/store/actions/components.action.ts @@ -8,7 +8,7 @@ import { CmsComponent } from '../../../model/cms.model'; import { PageContext } from '../../../routing/index'; import { StateUtils } from '../../../state/utils/index'; import { COMPONENT_ENTITY } from '../cms-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_CMS_COMPONENT = '[Cms] Load Component'; export const LOAD_CMS_COMPONENT_FAIL = '[Cms] Load Component Fail'; diff --git a/projects/core/src/cms/store/actions/navigation-entry-item.action.ts b/projects/core/src/cms/store/actions/navigation-entry-item.action.ts index b9fd205bcbd..a09e5aebc78 100755 --- a/projects/core/src/cms/store/actions/navigation-entry-item.action.ts +++ b/projects/core/src/cms/store/actions/navigation-entry-item.action.ts @@ -6,7 +6,7 @@ import { StateUtils } from '../../../state/utils/index'; import { NAVIGATION_DETAIL_ENTITY } from '../cms-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_CMS_NAVIGATION_ITEMS = '[Cms] Load NavigationEntry items'; export const LOAD_CMS_NAVIGATION_ITEMS_FAIL = diff --git a/projects/core/src/cms/store/actions/page.action.ts b/projects/core/src/cms/store/actions/page.action.ts index a0cb91d24a5..6d11908ac65 100755 --- a/projects/core/src/cms/store/actions/page.action.ts +++ b/projects/core/src/cms/store/actions/page.action.ts @@ -7,7 +7,7 @@ import { PageContext } from '../../../routing/index'; import { StateUtils } from '../../../state/utils/index'; import { Page } from '../../model/page.model'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_CMS_PAGE_DATA = '[Cms] Load Page Data'; export const LOAD_CMS_PAGE_DATA_FAIL = '[Cms] Load Page Data Fail'; diff --git a/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts b/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts index c22fef19170..736c0d16f50 100644 --- a/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts +++ b/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts @@ -8,7 +8,7 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect } from '@ngrx/effects'; import { filter, tap } from 'rxjs/operators'; import { EffectsErrorHandlerService } from './effects-error-handler.service'; -import { ErrorAction } from '@spartacus/core'; +import { ErrorAction } from '../../model/index'; import { Observable } from 'rxjs'; @Injectable() diff --git a/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts index b8ce182e244..362ea8f60b3 100644 --- a/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts +++ b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.service.ts @@ -6,7 +6,11 @@ import { ErrorHandler, Injectable } from '@angular/core'; import { Action } from '@ngrx/store'; -import { ErrorAction, ErrorActionType, HttpErrorModel } from '@spartacus/core'; +import { + ErrorAction, + ErrorActionType, + HttpErrorModel, +} from '../../model/index'; import { HttpErrorResponse } from '@angular/common/http'; @Injectable() diff --git a/projects/core/src/product/store/actions/product-references.action.ts b/projects/core/src/product/store/actions/product-references.action.ts index f8865742554..d2c61bce318 100644 --- a/projects/core/src/product/store/actions/product-references.action.ts +++ b/projects/core/src/product/store/actions/product-references.action.ts @@ -7,7 +7,7 @@ import { Action } from '@ngrx/store'; import { ErrorModel } from '../../../model/misc.model'; import { ProductReference } from '../../../model/product.model'; -import { ErrorAction } from '@spartacus/core'; +import { ErrorAction } from '../../../model'; export const LOAD_PRODUCT_REFERENCES = '[Product] Load Product References Data'; export const LOAD_PRODUCT_REFERENCES_FAIL = diff --git a/projects/core/src/product/store/actions/product-reviews.action.ts b/projects/core/src/product/store/actions/product-reviews.action.ts index d0a85edafff..6a56f68e99b 100644 --- a/projects/core/src/product/store/actions/product-reviews.action.ts +++ b/projects/core/src/product/store/actions/product-reviews.action.ts @@ -7,7 +7,7 @@ import { Action } from '@ngrx/store'; import { ErrorModel } from '../../../model/misc.model'; import { Review } from '../../../model/product.model'; -import { ErrorAction } from '@spartacus/core'; +import { ErrorAction } from '../../../model/index'; export const LOAD_PRODUCT_REVIEWS = '[Product] Load Product Reviews Data'; export const LOAD_PRODUCT_REVIEWS_FAIL = diff --git a/projects/core/src/product/store/actions/product-search.action.ts b/projects/core/src/product/store/actions/product-search.action.ts index f294d911c5f..81be9bbe513 100644 --- a/projects/core/src/product/store/actions/product-search.action.ts +++ b/projects/core/src/product/store/actions/product-search.action.ts @@ -12,7 +12,7 @@ import { Suggestion, } from '../../../model/product-search.model'; import { SearchConfig } from '../../model/search-config'; -import { ErrorAction } from '@spartacus/core'; +import { ErrorAction } from '../../../model/index'; export const SEARCH_PRODUCTS = '[Product] Search Products'; export const SEARCH_PRODUCTS_FAIL = '[Product] Search Products Fail'; diff --git a/projects/core/src/product/store/actions/product.action.ts b/projects/core/src/product/store/actions/product.action.ts index 161250a03f4..5e5d1575556 100644 --- a/projects/core/src/product/store/actions/product.action.ts +++ b/projects/core/src/product/store/actions/product.action.ts @@ -10,7 +10,7 @@ import { EntityLoaderMeta } from '../../../state/utils/entity-loader/entity-load import { EntityScopedLoaderActions } from '../../../state/utils/scoped-loader/entity-scoped-loader.actions'; import { ProductScope } from '../../model/product-scope'; import { PRODUCT_DETAIL_ENTITY } from '../product-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_PRODUCT = '[Product] Load Product Data'; export const LOAD_PRODUCT_FAIL = '[Product] Load Product Data Fail'; diff --git a/projects/core/src/product/store/effects/product-references.effect.ts b/projects/core/src/product/store/effects/product-references.effect.ts index 1fbad85e39f..58e3e8b5fac 100644 --- a/projects/core/src/product/store/effects/product-references.effect.ts +++ b/projects/core/src/product/store/effects/product-references.effect.ts @@ -11,7 +11,7 @@ import { catchError, map, mergeMap } from 'rxjs/operators'; import { ProductReferencesConnector } from '../../connectors/references/product-references.connector'; import { ProductActions } from '../actions/index'; import { LoggerService } from '../../../logger'; -import { tryNormalizeHttpError } from '@spartacus/core'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; @Injectable() export class ProductReferencesEffects { diff --git a/projects/core/src/product/store/effects/product-reviews.effect.ts b/projects/core/src/product/store/effects/product-reviews.effect.ts index a655102eb00..1f0afbfaab4 100644 --- a/projects/core/src/product/store/effects/product-reviews.effect.ts +++ b/projects/core/src/product/store/effects/product-reviews.effect.ts @@ -15,7 +15,7 @@ import { GlobalMessageType, } from '../../../global-message/index'; import { LoggerService } from '../../../logger'; -import { tryNormalizeHttpError } from '@spartacus/core'; +import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error'; @Injectable() export class ProductReviewsEffects { diff --git a/projects/core/src/site-context/store/actions/base-site.action.ts b/projects/core/src/site-context/store/actions/base-site.action.ts index 5ee037e28d8..ce0c8aff1fe 100644 --- a/projects/core/src/site-context/store/actions/base-site.action.ts +++ b/projects/core/src/site-context/store/actions/base-site.action.ts @@ -6,7 +6,7 @@ import { Action } from '@ngrx/store'; import { BaseSite } from '../../../model/misc.model'; -import { ErrorAction, ErrorActionType } from '@spartacus/core'; +import { ErrorAction, ErrorActionType } from '../../../model/index'; export const LOAD_BASE_SITE = '[Site-context] Load BaseSite'; export const LOAD_BASE_SITE_FAIL = '[Site-context] Load BaseSite Fail'; diff --git a/projects/core/src/site-context/store/actions/currencies.action.ts b/projects/core/src/site-context/store/actions/currencies.action.ts index 1089b834f55..9b5119dd5ab 100644 --- a/projects/core/src/site-context/store/actions/currencies.action.ts +++ b/projects/core/src/site-context/store/actions/currencies.action.ts @@ -6,7 +6,7 @@ import { Action } from '@ngrx/store'; import { Currency } from '../../../model/misc.model'; -import { ErrorAction, ErrorActionType } from '@spartacus/core'; +import { ErrorAction, ErrorActionType } from '../../../model/index'; export const LOAD_CURRENCIES = '[Site-context] Load Currencies'; export const LOAD_CURRENCIES_FAIL = '[Site-context] Load Currencies Fail'; diff --git a/projects/core/src/site-context/store/actions/languages.action.ts b/projects/core/src/site-context/store/actions/languages.action.ts index 95cefbfa7e8..1919c89c380 100644 --- a/projects/core/src/site-context/store/actions/languages.action.ts +++ b/projects/core/src/site-context/store/actions/languages.action.ts @@ -6,7 +6,7 @@ import { Action } from '@ngrx/store'; import { Language } from '../../../model/misc.model'; -import { ErrorAction, ErrorActionType } from '@spartacus/core'; +import { ErrorAction, ErrorActionType } from '../../../model/index'; export const LOAD_LANGUAGES = '[Site-context] Load Languages'; export const LOAD_LANGUAGES_FAIL = '[Site-context] Load Languages Fail'; diff --git a/projects/core/src/state/utils/loader/loader.action.ts b/projects/core/src/state/utils/loader/loader.action.ts index 1fde5143470..0b5f3ffb27c 100644 --- a/projects/core/src/state/utils/loader/loader.action.ts +++ b/projects/core/src/state/utils/loader/loader.action.ts @@ -5,7 +5,7 @@ */ import { Action } from '@ngrx/store'; -import { ErrorAction, ErrorActionType } from '@spartacus/core'; +import { ErrorAction, ErrorActionType } from '../../../model/index'; export const LOADER_LOAD_ACTION = '[LOADER] LOAD'; export const LOADER_FAIL_ACTION = '[LOADER] FAIL'; diff --git a/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts b/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts index e5b0c4408ee..ac0f1cb8b03 100644 --- a/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts +++ b/projects/core/src/state/utils/scoped-loader/entity-scoped-loader.actions.ts @@ -16,7 +16,7 @@ import { entityResetMeta, entitySuccessMeta, } from '../entity-loader/entity-loader.action'; -import { ErrorAction, ErrorActionType } from '@spartacus/core'; +import { ErrorAction, ErrorActionType } from '../../../model/index'; export namespace EntityScopedLoaderActions { export interface EntityScopedLoaderMeta extends EntityLoaderMeta { diff --git a/projects/core/src/user/store/actions/billing-countries.action.ts b/projects/core/src/user/store/actions/billing-countries.action.ts index 07c8c5ffc1e..ff1afd128dc 100644 --- a/projects/core/src/user/store/actions/billing-countries.action.ts +++ b/projects/core/src/user/store/actions/billing-countries.action.ts @@ -5,7 +5,7 @@ */ import { Action } from '@ngrx/store'; -import { ErrorAction } from '@spartacus/core'; +import { ErrorAction } from '../../../model/index'; export const LOAD_BILLING_COUNTRIES = '[User] Load Billing Countries'; export const LOAD_BILLING_COUNTRIES_FAIL = '[User] Load Billing Countries Fail'; diff --git a/projects/core/src/user/store/actions/customer-coupon.action.ts b/projects/core/src/user/store/actions/customer-coupon.action.ts index 358763046a6..ee5458bcad1 100644 --- a/projects/core/src/user/store/actions/customer-coupon.action.ts +++ b/projects/core/src/user/store/actions/customer-coupon.action.ts @@ -28,7 +28,7 @@ import { SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, } from '../user-state'; -import { ErrorAction, ErrorActionType } from '@spartacus/core'; +import { ErrorAction, ErrorActionType } from '../../../model/index'; export const LOAD_CUSTOMER_COUPONS = '[User] Load Customer Coupons'; export const LOAD_CUSTOMER_COUPONS_FAIL = '[User] Load Customer Coupons Fail'; diff --git a/projects/core/src/user/store/actions/delivery-countries.action.ts b/projects/core/src/user/store/actions/delivery-countries.action.ts index 96e339e28ad..36bc17e6c14 100644 --- a/projects/core/src/user/store/actions/delivery-countries.action.ts +++ b/projects/core/src/user/store/actions/delivery-countries.action.ts @@ -6,7 +6,7 @@ import { Action } from '@ngrx/store'; import { Country } from '../../../model/address.model'; -import { ErrorAction, ErrorActionType } from '@spartacus/core'; +import { ErrorAction, ErrorActionType } from '../../../model/index'; export const LOAD_DELIVERY_COUNTRIES = '[User] Load Delivery Countries'; export const LOAD_DELIVERY_COUNTRIES_FAIL = diff --git a/projects/core/src/user/store/actions/notification-preference.action.ts b/projects/core/src/user/store/actions/notification-preference.action.ts index ea00cc0579a..91737b963df 100644 --- a/projects/core/src/user/store/actions/notification-preference.action.ts +++ b/projects/core/src/user/store/actions/notification-preference.action.ts @@ -17,7 +17,7 @@ import { UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID, } from '../user-state'; import { NotificationPreference } from '../../../model/notification-preference.model'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_NOTIFICATION_PREFERENCES = '[User] Load Notification Preferences'; diff --git a/projects/core/src/user/store/actions/payment-methods.action.ts b/projects/core/src/user/store/actions/payment-methods.action.ts index 164b58df42b..93b989b1b2f 100644 --- a/projects/core/src/user/store/actions/payment-methods.action.ts +++ b/projects/core/src/user/store/actions/payment-methods.action.ts @@ -7,7 +7,7 @@ import { PaymentDetails } from '../../../model/payment.model'; import { StateUtils } from '../../../state/utils/index'; import { USER_PAYMENT_METHODS } from '../user-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_USER_PAYMENT_METHODS = '[User] Load User Payment Methods'; export const LOAD_USER_PAYMENT_METHODS_FAIL = diff --git a/projects/core/src/user/store/actions/product-interests.actions.ts b/projects/core/src/user/store/actions/product-interests.actions.ts index ab048bb375f..7204d44181b 100644 --- a/projects/core/src/user/store/actions/product-interests.actions.ts +++ b/projects/core/src/user/store/actions/product-interests.actions.ts @@ -27,7 +27,7 @@ import { EntityLoaderResetAction, EntitySuccessAction, } from '../../../state/utils/entity-loader/entity-loader.action'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_PRODUCT_INTERESTS = 'Load Product Interests'; export const LOAD_PRODUCT_INTERESTS_FAIL = 'Load Product Interests Fail'; diff --git a/projects/core/src/user/store/actions/regions.action.ts b/projects/core/src/user/store/actions/regions.action.ts index 2e4fc5f765f..99567e2e403 100644 --- a/projects/core/src/user/store/actions/regions.action.ts +++ b/projects/core/src/user/store/actions/regions.action.ts @@ -8,7 +8,7 @@ import { Action } from '@ngrx/store'; import { Region } from '../../../model/address.model'; import { StateUtils } from '../../../state/utils/index'; import { REGIONS } from '../user-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_REGIONS = '[User] Load Regions'; export const LOAD_REGIONS_SUCCESS = '[User] Load Regions Success'; diff --git a/projects/core/src/user/store/actions/user-addresses.action.ts b/projects/core/src/user/store/actions/user-addresses.action.ts index a5b97e53319..4a092998eb0 100644 --- a/projects/core/src/user/store/actions/user-addresses.action.ts +++ b/projects/core/src/user/store/actions/user-addresses.action.ts @@ -7,7 +7,7 @@ import { Address } from '../../../model/address.model'; import { StateUtils } from '../../../state/utils/index'; import { USER_ADDRESSES } from '../user-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_USER_ADDRESSES = '[User] Load User Addresses'; export const LOAD_USER_ADDRESSES_FAIL = '[User] Load User Addresses Fail'; diff --git a/projects/core/src/user/store/actions/user-consents.action.ts b/projects/core/src/user/store/actions/user-consents.action.ts index 55129dcb5a7..2f6f4cb865b 100644 --- a/projects/core/src/user/store/actions/user-consents.action.ts +++ b/projects/core/src/user/store/actions/user-consents.action.ts @@ -12,7 +12,7 @@ import { USER_CONSENTS, WITHDRAW_CONSENT_PROCESS_ID, } from '../user-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_USER_CONSENTS = '[User] Load User Consents'; export const LOAD_USER_CONSENTS_SUCCESS = '[User] Load User Consents Success'; diff --git a/projects/core/src/user/store/actions/user-cost-center.action.ts b/projects/core/src/user/store/actions/user-cost-center.action.ts index f0d49057c90..fd827d41141 100644 --- a/projects/core/src/user/store/actions/user-cost-center.action.ts +++ b/projects/core/src/user/store/actions/user-cost-center.action.ts @@ -7,7 +7,7 @@ import { CostCenter } from '../../../model/org-unit.model'; import { StateUtils } from '../../../state/utils/index'; import { USER_COST_CENTERS } from '../user-state'; -import { ErrorActionType } from '@spartacus/core'; +import { ErrorActionType } from '../../../model/index'; export const LOAD_ACTIVE_COST_CENTERS = '[User] Load Active CostCenters'; export const LOAD_ACTIVE_COST_CENTERS_FAIL = diff --git a/projects/core/src/util/try-normalize-http-error.ts b/projects/core/src/util/try-normalize-http-error.ts index eb2106f5f39..d4a983a3c85 100644 --- a/projects/core/src/util/try-normalize-http-error.ts +++ b/projects/core/src/util/try-normalize-http-error.ts @@ -5,11 +5,9 @@ */ import { HttpErrorResponse } from '@angular/common/http'; -import { - HttpErrorModel, - LoggerService, - normalizeHttpError, -} from '@spartacus/core'; +import { HttpErrorModel } from '../model'; +import { LoggerService } from '../logger'; +import { normalizeHttpError } from './normalize-http-error'; export function tryNormalizeHttpError( error: HttpErrorResponse | HttpErrorModel | any, From a2ee3731856298282b14fc5b6730c0573d13e908 Mon Sep 17 00:00:00 2001 From: Caine Rotherham Date: Mon, 16 Oct 2023 22:06:13 +0200 Subject: [PATCH 10/11] test: Test ssr server returns 500 status code when api request returns 500 code (#17915) Closes: https://jira.tools.sap/browse/CXSPA-4744 --- .../workflows/ci-continuous-integration.yml | 43 +++++++------------ projects/ssr-tests/project.json | 2 +- projects/ssr-tests/src/ssr-testing.spec.ts | 6 +-- 3 files changed, 18 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci-continuous-integration.yml b/.github/workflows/ci-continuous-integration.yml index 2ece32ab5ad..6f3079055f9 100644 --- a/.github/workflows/ci-continuous-integration.yml +++ b/.github/workflows/ci-continuous-integration.yml @@ -46,33 +46,6 @@ jobs: - name: Run unit tests run: | ci-scripts/unit-tests.sh - build-libs: - # TODO: For Testing SSR Epic only, remove job before merge - name: CI - Build Libraries - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: ${{ env.NODE_VERSION }} - - name: Cache node_modules - id: cache-node-modules - uses: actions/cache@v2 - with: - path: | - node_modules - projects/storefrontapp-e2e-cypress/node_modules - ~/.cache/Cypress - key: nodemodules-${{ github.event.pull_request.base.sha }} - restore-keys: nodemodules-${{ github.event.pull_request.base.sha }} - - name: Package installation - run: npm ci - - name: Build Libraries - run: | - npm run build:libs sonarqube_scan: name: CI - SonarQube Scan runs-on: ubuntu-latest @@ -114,6 +87,22 @@ jobs: - name: Run linting validation run: | ci-scripts/validate-lint.sh + # TODO: For testing ssr-tests epic only. Remove before merging to ssr error handling epic. + ssr_tests: + name: SSR tests (for testing ssr-tests epic only) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Package installation + run: npm ci + - name: Build SSR Server + run: npm run build:libs && npm run build && npm run build:ssr:local-http + - name: Run SSR tests + run: npm run test:ssr:ci --verbose ci_result: needs: [unit_tests, sonarqube_scan, linting] name: CI - Result diff --git a/projects/ssr-tests/project.json b/projects/ssr-tests/project.json index 63b9ea51fb0..50a28e6a8e9 100644 --- a/projects/ssr-tests/project.json +++ b/projects/ssr-tests/project.json @@ -13,7 +13,7 @@ "test-jest": { "executor": "nx:run-commands", "options": { - "command": "npm run test", + "command": "npm run test --verbose", "cwd": "projects/ssr-tests" } } diff --git a/projects/ssr-tests/src/ssr-testing.spec.ts b/projects/ssr-tests/src/ssr-testing.spec.ts index 645938bb4ae..0e8e05d7ad1 100644 --- a/projects/ssr-tests/src/ssr-testing.spec.ts +++ b/projects/ssr-tests/src/ssr-testing.spec.ts @@ -47,15 +47,11 @@ describe('SSR E2E', () => { // TODO: Test incomplete xit('should receive 404 response when page is not existing', () => {}); - // TODO: Test incomplete - xit('should receive 500 error response with request', async () => { + it('should receive 500 error response when a backend API returned server error', async () => { proxy = await ProxyServer.startProxyServer({ target: BACKEND_BASE_URL, throwStatus: 500, }); - proxy.on('proxyRes', function (proxyRes: any) { - proxyRes.statusCode = 500; - }); const response: any = await ProxyServer.sendRequest('/'); expect(response.statusCode).toEqual(500); }); From 6a82c07742fd69624f418944d4eb1f50d2a4f2bf Mon Sep 17 00:00:00 2001 From: Caine Rotherham Date: Wed, 18 Oct 2023 13:35:17 +0200 Subject: [PATCH 11/11] test: Test for SSR 404 status code (#17966) Closes: https://jira.tools.sap/browse/CXSPA-4743 --- projects/ssr-tests/src/ssr-testing.spec.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/projects/ssr-tests/src/ssr-testing.spec.ts b/projects/ssr-tests/src/ssr-testing.spec.ts index 0e8e05d7ad1..4b41bd1245c 100644 --- a/projects/ssr-tests/src/ssr-testing.spec.ts +++ b/projects/ssr-tests/src/ssr-testing.spec.ts @@ -44,8 +44,15 @@ describe('SSR E2E', () => { Log.assertMessages(['Render from cache (/)']); }); - // TODO: Test incomplete - xit('should receive 404 response when page is not existing', () => {}); + it('should receive 404 response when page is not existing', async () => { + proxy = await ProxyServer.startProxyServer({ + target: BACKEND_BASE_URL, + }); + const response: any = await ProxyServer.sendRequest( + REQUEST_PATH + '/not-existing-page' + ); + expect(response.statusCode).toEqual(404); + }); it('should receive 500 error response when a backend API returned server error', async () => { proxy = await ProxyServer.startProxyServer({