diff --git a/.eslintrc.json b/.eslintrc.json
index de13d0dadbf..308ab98249a 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -114,6 +114,12 @@
"@nx/workspace/use-provide-default-feature-toggles": "error",
"@nx/workspace/use-provide-default-feature-toggles-factory": "error"
}
+ },
+ {
+ "files": ["*.action*.ts"],
+ "rules": {
+ "@nx/workspace/no-ngrx-fail-action-without-error-action-implementation": "error"
+ }
}
]
}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4910fb35ca4..86e8d8b95cd 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-backend
+ - 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,7 +258,8 @@ 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.b2b_e2e_tests.result == 'failure' || needs.b2b_e2e_tests.result == 'cancelled'
+ needs.b2b_e2e_tests.result == 'failure' || needs.b2b_e2e_tests.result == 'cancelled' ||
+ needs.ssr_tests.result == 'failure' || needs.ssr_tests.result == 'cancelled'
send_slack_message:
needs: build_conclusion
name: Slack message for failed CI build in Spartacus
diff --git a/core-libs/setup/ssr/engine/__snapshots__/cx-common-engine.spec.ts.snap b/core-libs/setup/ssr/engine/__snapshots__/cx-common-engine.spec.ts.snap
new file mode 100644
index 00000000000..36a99e199f2
--- /dev/null
+++ b/core-libs/setup/ssr/engine/__snapshots__/cx-common-engine.spec.ts.snap
@@ -0,0 +1,12 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CxCommonEngine should handle APP_INITIALIZER errors the standard Angular way and throw if any occurred 1`] = `
+"R3InjectorError(TokenServerModule)[InjectionToken SOME_TOKEN -> InjectionToken SOME_TOKEN]:
+ NullInjectorError: No provider for InjectionToken SOME_TOKEN!"
+`;
+
+exports[`CxCommonEngine should handle errors propagated from SSR 1`] = `"test error"`;
+
+exports[`CxCommonEngine should not override providers passed to options 1`] = `"
message:test"`;
+
+exports[`CxCommonEngine should return html if no errors 1`] = `"some template"`;
diff --git a/core-libs/setup/ssr/engine/cx-common-engine.spec.ts b/core-libs/setup/ssr/engine/cx-common-engine.spec.ts
new file mode 100644
index 00000000000..4a37596fe60
--- /dev/null
+++ b/core-libs/setup/ssr/engine/cx-common-engine.spec.ts
@@ -0,0 +1,130 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '@angular/compiler';
+
+import { Component, InjectionToken, NgModule, inject } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { ServerModule } from '@angular/platform-server';
+import { PROPAGATE_ERROR_TO_SERVER } from '../error-handling/error-response/propagate-error-to-server';
+import { CxCommonEngine } from './cx-common-engine';
+
+// Test how the CxCommonEngine handles successful server-side rendering
+@Component({ selector: 'cx-mock', template: 'some template' })
+export class SuccessComponent {}
+
+@NgModule({
+ imports: [BrowserModule, ServerModule],
+ declarations: [SuccessComponent],
+ bootstrap: [SuccessComponent],
+})
+export class SuccessServerModule {}
+
+// Test how the CxCommonEngine handles propagated error
+@Component({
+ selector: 'cx-response',
+ template: ``,
+})
+export class WithPropagatedErrorComponent {
+ constructor() {
+ inject(PROPAGATE_ERROR_TO_SERVER)(new Error('test error'));
+ }
+}
+
+@NgModule({
+ imports: [BrowserModule, ServerModule],
+ declarations: [WithPropagatedErrorComponent],
+ bootstrap: [WithPropagatedErrorComponent],
+})
+export class WithPropagatedErrorServerModule {}
+
+// Test that the CxCommonEngine doesn't override providers
+// If SOME_TOKEN not provided, test how the CxCommonEngine handles APP_INITIALIZER errors
+export const SOME_TOKEN = new InjectionToken('SOME_TOKEN');
+
+@Component({
+ selector: 'cx-token',
+ template: `message:{{ someToken }}`,
+})
+export class TokenComponent {
+ someToken = inject(SOME_TOKEN);
+}
+
+@NgModule({
+ imports: [BrowserModule, ServerModule],
+ declarations: [TokenComponent],
+ bootstrap: [TokenComponent],
+})
+export class TokenServerModule {}
+
+describe('CxCommonEngine', () => {
+ let engine: CxCommonEngine;
+
+ beforeAll(() => {
+ jest.spyOn(console, 'error').mockImplementation();
+ jest.spyOn(console, 'log').mockImplementation();
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should return html if no errors', async () => {
+ engine = new CxCommonEngine({
+ bootstrap: SuccessServerModule,
+ });
+
+ const html = await engine.render({
+ url: 'http://localhost:4200',
+ document: '',
+ });
+
+ // Cannot use `.toMatchInlineSnapshot()` due to bug in jest:
+ // see: https://github.com/thymikee/jest-preset-angular/issues/1084
+ expect(html).toMatchSnapshot();
+ });
+
+ it('should not override providers passed to options', async () => {
+ engine = new CxCommonEngine({
+ bootstrap: TokenServerModule,
+ });
+
+ const html = await engine.render({
+ url: 'http://localhost:4200',
+ document: '',
+ providers: [{ provide: SOME_TOKEN, useValue: 'test' }],
+ });
+
+ // Cannot use `.toMatchInlineSnapshot()` due to bug in jest:
+ // see: https://github.com/thymikee/jest-preset-angular/issues/1084
+ expect(html).toMatchSnapshot();
+ });
+
+ it('should handle APP_INITIALIZER errors the standard Angular way and throw if any occurred', async () => {
+ engine = new CxCommonEngine({
+ bootstrap: TokenServerModule,
+ });
+
+ // Cannot use `.toMatchInlineSnapshot()` due to bug in jest:
+ // see: https://github.com/thymikee/jest-preset-angular/issues/1084
+ await expect(
+ engine.render({
+ url: 'http://localhost:4200',
+ document: '',
+ })
+ ).rejects.toThrowErrorMatchingSnapshot();
+ });
+
+ it('should handle errors propagated from SSR', async () => {
+ engine = new CxCommonEngine({
+ bootstrap: WithPropagatedErrorServerModule,
+ });
+
+ // Cannot use `.toMatchInlineSnapshot()` due to bug in jest:
+ // see: https://github.com/thymikee/jest-preset-angular/issues/1084
+ await expect(
+ engine.render({
+ url: 'http://localhost:4200',
+ document: '',
+ })
+ ).rejects.toThrowErrorMatchingSnapshot();
+ });
+});
diff --git a/core-libs/setup/ssr/engine/cx-common-engine.ts b/core-libs/setup/ssr/engine/cx-common-engine.ts
new file mode 100644
index 00000000000..6abbcd08a41
--- /dev/null
+++ b/core-libs/setup/ssr/engine/cx-common-engine.ts
@@ -0,0 +1,67 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ CommonEngine,
+ CommonEngineOptions,
+ CommonEngineRenderOptions,
+} from '@angular/ssr';
+import { PROPAGATE_ERROR_TO_SERVER } from '../error-handling/error-response/propagate-error-to-server';
+
+/**
+ * The Spartacus extension of the Angular's `CommonEngine`. It is able to handle the propagated server responses caught during server-side rendering of a Spartacus app.
+ * For reference, see Angular's source code: https://github.com/angular/angular-cli/blob/6cf866225ab09f8b4b3803c000b632bed8448ce4/packages/angular/ssr/src/common-engine.ts#L56
+ *
+ * @extends {CommonEngine}
+ */
+export class CxCommonEngine extends CommonEngine {
+ constructor(options?: CommonEngineOptions) {
+ super(options);
+ }
+
+ /**
+ * @override
+ * Renders for the given options.
+ * If an error is populated from the rendered applications
+ * (via `PROPAGATE_ERROR_TO_SERVER` callback), then such an error
+ * will be thrown and the result promise rejected - but only AFTER the rendering is complete.
+ * In other words, at first an error occurs and it's captured, then we wait until the rendering completes
+ * and ONLY then we reject the promise with the payload being the encountered error.
+ *
+ * Note: if more errors are captured during the rendering, only the first one will be used
+ * as the payload of the rejected promise, others won't.
+ *
+ * @param {CommonEngineRenderOptions} options - The options to render.
+ * @returns {Promise} Promise which resolves with the rendered HTML as a string
+ * OR rejects with the error, if any is propagated from the rendered app.
+ */
+ override async render(options: CommonEngineRenderOptions): Promise {
+ let error: undefined | unknown;
+
+ return super
+ .render({
+ ...options,
+ providers: [
+ {
+ provide: PROPAGATE_ERROR_TO_SERVER,
+ useFactory: () => {
+ return (propagatedError: unknown) => {
+ // We're interested only the first propagated error, so we use `??=` instead of `=`:
+ error ??= propagatedError;
+ };
+ },
+ },
+ ...(options.providers ?? []),
+ ],
+ })
+ .then((html: string) => {
+ if (error) {
+ throw error;
+ }
+ return html;
+ });
+ }
+}
diff --git a/core-libs/setup/ssr/engine/ng-express-engine.spec.ts b/core-libs/setup/ssr/engine/ng-express-engine.spec.ts
index 9f2cb0ef33c..6871dd25eea 100644
--- a/core-libs/setup/ssr/engine/ng-express-engine.spec.ts
+++ b/core-libs/setup/ssr/engine/ng-express-engine.spec.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
*/
diff --git a/core-libs/setup/ssr/engine/ng-express-engine.ts b/core-libs/setup/ssr/engine/ng-express-engine.ts
index 826ea5c45ed..45b6c669dfd 100644
--- a/core-libs/setup/ssr/engine/ng-express-engine.ts
+++ b/core-libs/setup/ssr/engine/ng-express-engine.ts
@@ -5,13 +5,10 @@
*/
import { StaticProvider } from '@angular/core';
-import {
- CommonEngine,
- CommonEngineOptions,
- CommonEngineRenderOptions,
-} from '@angular/ssr';
+import { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr';
import { Request, Response } from 'express';
import { REQUEST, RESPONSE } from '../tokens/express.tokens';
+import { CxCommonEngine } from './cx-common-engine';
/**
* @license
@@ -80,7 +77,7 @@ export interface RenderOptions extends CommonEngineRenderOptions {
* - https://github.com/angular/universal/blob/e798d256de5e4377b704e63d993dc56ea35df97d/modules/express-engine/src/main.ts
*/
export function ngExpressEngine(setupOptions: NgSetupOptions) {
- const engine = new CommonEngine({
+ const engine = new CxCommonEngine({
bootstrap: setupOptions.bootstrap,
providers: setupOptions.providers,
enablePerformanceProfiler: setupOptions.enablePerformanceProfiler,
diff --git a/core-libs/setup/ssr/error-handling/error-response/index.ts b/core-libs/setup/ssr/error-handling/error-response/index.ts
new file mode 100644
index 00000000000..734d871cfb3
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/error-response/index.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './propagate-error-to-server';
diff --git a/core-libs/setup/ssr/error-handling/error-response/propagate-error-to-server.ts b/core-libs/setup/ssr/error-handling/error-response/propagate-error-to-server.ts
new file mode 100644
index 00000000000..391a651b4be
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/error-response/propagate-error-to-server.ts
@@ -0,0 +1,22 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { InjectionToken } from '@angular/core';
+
+/**
+ * Propagates the given error object to the higher layer in the server.
+ *
+ * It's meant to propagate errors for example to ExpressJS layer when using SSR
+ * or to a Prerendering Worker when using Server Prerendering.
+ * Currently, it's provided OOTB only in SSR (not prerendering), in the `CxCommonEngine` class.
+ *
+ * Note: We need it until Angular implements a proper propagation of async errors
+ * from an app to the the higher layer in the server.
+ * For more, see the Angular issue https://github.com/angular/angular/issues/33642
+ */
+export const PROPAGATE_ERROR_TO_SERVER = new InjectionToken<
+ (error: unknown) => void
+>('PROPAGATE_ERROR_RESPONSE');
diff --git a/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.spec.ts b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.spec.ts
new file mode 100644
index 00000000000..898dcc39fa9
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.spec.ts
@@ -0,0 +1,56 @@
+import { HttpErrorResponse } from '@angular/common/http';
+import { CmsPageNotFoundOutboundHttpError } from '@spartacus/core';
+import { defaultExpressErrorHandlers } from './express-error-handlers';
+
+describe('expressErrorHandlers', () => {
+ let documentContent: string;
+ let req: any;
+ let res: any;
+ let next: any;
+
+ beforeEach(() => {
+ documentContent = 'some document content';
+ req = {};
+ res = {
+ set: jest.fn(),
+ status: jest.fn().mockReturnThis(),
+ send: jest.fn(),
+ };
+ });
+
+ it('should do nothing if headers are already sent', () => {
+ const err = new HttpErrorResponse({
+ error: 'Page not found',
+ });
+ const errorRequestHandler = defaultExpressErrorHandlers(documentContent);
+ res.headersSent = true;
+
+ errorRequestHandler(err, req, res, next);
+
+ expect(res.set).not.toHaveBeenCalled();
+ expect(res.status).not.toHaveBeenCalled();
+ expect(res.send).not.toHaveBeenCalled();
+ });
+
+ it('should handle CmsPageNotFoundOutboundHttpError', () => {
+ const err = new CmsPageNotFoundOutboundHttpError('Page not found');
+ const errorRequestHandler = defaultExpressErrorHandlers(documentContent);
+
+ errorRequestHandler(err, req, res, next);
+
+ expect(res.set).toHaveBeenCalledWith('Cache-Control', 'no-store');
+ expect(res.status).toHaveBeenCalledWith(404);
+ expect(res.send).toHaveBeenCalledWith(documentContent);
+ });
+
+ it('should handle unknown error', () => {
+ const err = new Error('unknown error');
+ const errorRequestHandler = defaultExpressErrorHandlers(documentContent);
+
+ errorRequestHandler(err, req, res, next);
+
+ expect(res.set).toHaveBeenCalledWith('Cache-Control', 'no-store');
+ expect(res.status).toHaveBeenCalledWith(500);
+ expect(res.send).toHaveBeenCalledWith(documentContent);
+ });
+});
diff --git a/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.ts b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.ts
new file mode 100644
index 00000000000..42aba5c9d49
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/express-error-handlers/express-error-handlers.ts
@@ -0,0 +1,32 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ CmsPageNotFoundOutboundHttpError,
+ HttpResponseStatus,
+} from '@spartacus/core';
+import { ErrorRequestHandler } from 'express';
+
+/**
+ * Returns default handlers which results in a fallback to client side rendering.
+ * - If cms page not found, the document content is sent to the client with the appropriate 404 status code.
+ * - For rest of errors, the document content is sent to the client with the appropriate status 500 code.
+ *
+ * @param documentContent The document content to be sent to the client.
+ * @returns The error request handler.
+ */
+export const defaultExpressErrorHandlers =
+ (documentContent: string): ErrorRequestHandler =>
+ (err, _req, res, _next) => {
+ if (!res.headersSent) {
+ res.set('Cache-Control', 'no-store');
+ const statusCode =
+ err instanceof CmsPageNotFoundOutboundHttpError
+ ? HttpResponseStatus.NOT_FOUND
+ : HttpResponseStatus.INTERNAL_SERVER_ERROR;
+ res.status(statusCode).send(documentContent);
+ }
+ };
diff --git a/core-libs/setup/ssr/error-handling/express-error-handlers/index.ts b/core-libs/setup/ssr/error-handling/express-error-handlers/index.ts
new file mode 100644
index 00000000000..00fe9d6251f
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/express-error-handlers/index.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './express-error-handlers';
diff --git a/core-libs/setup/ssr/error-handling/index.ts b/core-libs/setup/ssr/error-handling/index.ts
new file mode 100644
index 00000000000..c96a4b4acde
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/index.ts
@@ -0,0 +1,9 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './error-response/index';
+export * from './express-error-handlers/index';
+export * from './multi-error-handlers/index';
diff --git a/core-libs/setup/ssr/error-handling/multi-error-handlers/index.ts b/core-libs/setup/ssr/error-handling/multi-error-handlers/index.ts
new file mode 100644
index 00000000000..d1c6353b9e5
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/multi-error-handlers/index.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './propagating-to-server-error-handler';
diff --git a/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.spec.ts b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.spec.ts
new file mode 100644
index 00000000000..fa7e7358767
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.spec.ts
@@ -0,0 +1,54 @@
+import { TestBed } from '@angular/core/testing';
+import { FeatureConfigService } from '@spartacus/core';
+import { PROPAGATE_ERROR_TO_SERVER } from '../error-response/propagate-error-to-server';
+import { PropagatingToServerErrorHandler } from './propagating-to-server-error-handler';
+
+describe('PropagatingToServerErrorHandler', () => {
+ let propagatingToServerErrorHandler: PropagatingToServerErrorHandler;
+ let featureConfigService: FeatureConfigService;
+ let propagateErrorResponse: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ PropagatingToServerErrorHandler,
+ FeatureConfigService,
+ {
+ provide: PROPAGATE_ERROR_TO_SERVER,
+ useValue: jest.fn(),
+ },
+ ],
+ });
+ propagatingToServerErrorHandler = TestBed.inject(
+ PropagatingToServerErrorHandler
+ );
+ propagateErrorResponse = TestBed.inject(PROPAGATE_ERROR_TO_SERVER);
+ featureConfigService = TestBed.inject(FeatureConfigService);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should propagate error when propagateErrorsToServer is enabled', () => {
+ jest
+ .spyOn(featureConfigService, 'isEnabled')
+ .mockImplementationOnce((val) => val === 'propagateErrorsToServer');
+ const error = new Error('test error');
+
+ propagatingToServerErrorHandler.handleError(error);
+
+ expect(propagateErrorResponse as jest.Mock).toHaveBeenCalledWith(error);
+ });
+
+ it('should not propagate error when propagateErrorsToServer is disabled', () => {
+ jest
+ .spyOn(featureConfigService, 'isEnabled')
+ .mockImplementationOnce((val) => !(val === 'propagateErrorsToServer'));
+ const error = new Error('test error');
+
+ propagatingToServerErrorHandler.handleError(error);
+
+ expect(propagateErrorResponse as jest.Mock).not.toHaveBeenCalled();
+ });
+});
diff --git a/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.ts b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.ts
new file mode 100644
index 00000000000..68a65aa0e96
--- /dev/null
+++ b/core-libs/setup/ssr/error-handling/multi-error-handlers/propagating-to-server-error-handler.ts
@@ -0,0 +1,40 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable, inject } from '@angular/core';
+import { FeatureConfigService, MultiErrorHandler } from '@spartacus/core';
+import { PROPAGATE_ERROR_TO_SERVER } from '../error-response/propagate-error-to-server';
+
+/**
+ * Propagates the given error object to the higher layer in the server.
+ *
+ * It's meant to propagate errors for example to ExpressJS layer when using SSR
+ * or to a Prerendering Worker when using Server Prerendering.
+ * Currently, it's provided OOTB only in SSR (not prerendering), in the `CxCommonEngine` class.
+ *
+ * Note: We need it until Angular implements a proper propagation of async errors
+ * from an app to the the higher layer in the server.
+ * For more, see the Angular issue https://github.com/angular/angular/issues/33642
+ *
+ * Intended to be used as part of a multi-error handler strategy.
+ *
+ * @see MultiErrorHandler
+ */
+@Injectable({
+ providedIn: 'root',
+})
+export class PropagatingToServerErrorHandler implements MultiErrorHandler {
+ protected propagateErrorToServer = inject(PROPAGATE_ERROR_TO_SERVER);
+ private featureConfigService: FeatureConfigService =
+ inject(FeatureConfigService);
+
+ handleError(error: unknown): void {
+ if (!this.featureConfigService.isEnabled('propagateErrorsToServer')) {
+ return;
+ }
+ this.propagateErrorToServer(error);
+ }
+}
diff --git a/core-libs/setup/ssr/optimized-engine/index.ts b/core-libs/setup/ssr/optimized-engine/index.ts
index 18afefbeb11..cfa41066a42 100644
--- a/core-libs/setup/ssr/optimized-engine/index.ts
+++ b/core-libs/setup/ssr/optimized-engine/index.ts
@@ -6,6 +6,7 @@
export * from './optimized-ssr-engine';
export * from './rendering-cache';
+export * from './rendering-cache.model';
export * from './rendering-strategy-resolver';
export * from './rendering-strategy-resolver-options';
export { RequestContext, getRequestContext } from './request-context';
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 ce3b478b3e1..1ea94319538 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
@@ -20,6 +20,7 @@ jest.mock('fs', () => ({
readFileSync: () => '',
}));
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
+const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
class MockExpressServerLogger implements Partial {
log(message: string, context: ExpressServerLoggerContext): void {
@@ -39,7 +40,7 @@ class MockExpressServerLogger implements Partial {
*/
class TestEngineRunner {
/** Accumulates html output for engine runs */
- renders: string[] = [];
+ renders: (string | Error)[] = [];
/** Accumulates response parameters for engine runs */
responseParams: object[] = [];
@@ -48,7 +49,11 @@ class TestEngineRunner {
optimizedSsrEngine: OptimizedSsrEngine;
engineInstance: NgExpressEngineInstance;
- constructor(options: SsrOptimizationOptions, renderTime?: number) {
+ constructor(
+ options: SsrOptimizationOptions,
+ renderTime?: number,
+ params?: { withError?: boolean }
+ ) {
// mocked engine instance that will render test output in 100 milliseconds
const engineInstanceMock = (
filePath: string,
@@ -56,17 +61,31 @@ class TestEngineRunner {
callback: SsrCallbackFn
) => {
setTimeout(() => {
- callback(undefined, `${filePath}-${this.renderCount++}`);
+ const result = `${filePath}-${this.renderCount++}`;
+
+ if (params?.withError) {
+ const err = new Error(result);
+ callback(err, undefined);
+ } else {
+ callback(undefined, result);
+ }
}, renderTime ?? defaultRenderTime);
};
- this.optimizedSsrEngine = new OptimizedSsrEngine(
- engineInstanceMock,
- options
- );
+ this.optimizedSsrEngine = new OptimizedSsrEngine(engineInstanceMock, {
+ ...options,
+ });
this.engineInstance = this.optimizedSsrEngine.engineInstance;
}
+ /** Create engine that results with error during render */
+ static withError(
+ options: SsrOptimizationOptions,
+ renderTime = defaultRenderTime
+ ): TestEngineRunner {
+ return new TestEngineRunner(options, renderTime, { withError: true });
+ }
+
/** Run request against the engine. The result will be stored in rendering property. */
request(
url: string,
@@ -102,8 +121,8 @@ class TestEngineRunner {
},
};
- this.engineInstance(url, optionsMock, (_, html): void => {
- this.renders.push(html ?? '');
+ this.engineInstance(url, optionsMock, (error, html): void => {
+ this.renders.push(html ?? error ?? '');
this.responseParams.push(response);
});
@@ -176,7 +195,11 @@ describe('OptimizedSsrEngine', () => {
"maxRenderTime": 300000,
"reuseCurrentRendering": true,
"renderingStrategyResolver": "() => ssr_optimization_options_1.RenderingStrategy.ALWAYS_SSR",
- "logger": "DefaultExpressServerLogger"
+ "logger": "DefaultExpressServerLogger",
+ "shouldCacheRenderingResult": "({ options, entry }) => !(options.ssrFeatureToggles?.avoidCachingErrors === true &&\\n Boolean(entry.err))",
+ "ssrFeatureToggles": {
+ "avoidCachingErrors": false
+ }
}
}
}",
@@ -185,6 +208,34 @@ describe('OptimizedSsrEngine', () => {
});
});
+ describe('rendering', () => {
+ it('should return rendered output if no errors', fakeAsync(() => {
+ const originalUrl = 'a';
+ const engineRunner = new TestEngineRunner({}).request('a');
+
+ tick(200);
+ expect(engineRunner.renders).toEqual(['a-0']);
+ expect(consoleLogSpy).toHaveBeenCalledWith(
+ expect.stringContaining(
+ `Request is resolved with the SSR rendering result (${originalUrl})`
+ )
+ );
+ }));
+
+ it('should return error if rendering fails', fakeAsync(() => {
+ const originalUrl = 'a';
+ const engineRunner = TestEngineRunner.withError({}).request('a');
+
+ tick(200);
+ expect(engineRunner.renders).toEqual([new Error('a-0')]);
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
+ expect.stringContaining(
+ `Request is resolved with the SSR rendering error (${originalUrl})`
+ )
+ );
+ }));
+ });
+
describe('rendering cache', () => {
it('should be initialized with default optimization options none of the custom options are provided', () => {
const engineRunner = new TestEngineRunner({});
@@ -304,7 +355,6 @@ describe('OptimizedSsrEngine', () => {
tick(200);
expect(engineRunner.renders).toEqual(['a-0', 'a-1', 'a-2']);
}));
-
it('should cache requests if enabled', fakeAsync(() => {
const engineRunner = new TestEngineRunner({
cache: true,
@@ -334,6 +384,148 @@ describe('OptimizedSsrEngine', () => {
}));
});
+ describe('avoidCachingErrors option', () => {
+ describe('when using default shouldCacheRenderingResult', () => {
+ it('should not cache errors if `avoidCachingErrors` is set to true', fakeAsync(() => {
+ const engineRunner = TestEngineRunner.withError({
+ cache: true,
+ ssrFeatureToggles: {
+ avoidCachingErrors: true,
+ },
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual([
+ new Error('a-0'),
+ new Error('a-1'),
+ new Error('a-2'),
+ ]);
+ }));
+
+ it('should cache errors if `avoidCachingErrors` is set to false', fakeAsync(() => {
+ const engineRunner = TestEngineRunner.withError({
+ cache: true,
+ ssrFeatureToggles: {
+ avoidCachingErrors: false,
+ },
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual([
+ new Error('a-0'),
+ new Error('a-0'),
+ new Error('a-0'),
+ ]);
+ }));
+
+ it('should cache HTML if `avoidCachingErrors` is set to true', fakeAsync(() => {
+ const engineRunner = new TestEngineRunner({
+ cache: true,
+ ssrFeatureToggles: {
+ avoidCachingErrors: true,
+ },
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']);
+ }));
+
+ it('should cache HTML if `avoidCachingErrors` is set to false', fakeAsync(() => {
+ const engineRunner = new TestEngineRunner({
+ cache: true,
+ ssrFeatureToggles: {
+ avoidCachingErrors: true,
+ },
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']);
+ }));
+ });
+ });
+
+ describe('shouldCacheRenderingResult option', () => {
+ it('should not cache errors if `shouldCacheRenderingResult` returns false', fakeAsync(() => {
+ const engineRunner = TestEngineRunner.withError({
+ cache: true,
+ shouldCacheRenderingResult: () => false,
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual([
+ new Error('a-0'),
+ new Error('a-1'),
+ new Error('a-2'),
+ ]);
+ }));
+
+ it('should cache errors if `shouldCacheRenderingResult` returns true', fakeAsync(() => {
+ const engineRunner = TestEngineRunner.withError({
+ cache: true,
+ shouldCacheRenderingResult: () => true,
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual([
+ new Error('a-0'),
+ new Error('a-0'),
+ new Error('a-0'),
+ ]);
+ }));
+
+ it('should not cache HTML if `shouldCacheRenderingResult` returns false', fakeAsync(() => {
+ const engineRunner = new TestEngineRunner({
+ cache: true,
+ shouldCacheRenderingResult: () => false,
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual(['a-0', 'a-1', 'a-2']);
+ }));
+
+ it('should cache HTML if `shouldCacheRenderingResult` returns true', fakeAsync(() => {
+ const engineRunner = new TestEngineRunner({
+ cache: true,
+ shouldCacheRenderingResult: () => true,
+ }).request('a');
+
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ engineRunner.request('a');
+ tick(200);
+ expect(engineRunner.renders).toEqual(['a-0', 'a-0', 'a-0']);
+ }));
+ });
+
describe('concurrency option', () => {
it('should limit concurrency and fallback to csr', fakeAsync(() => {
const engineRunner = new TestEngineRunner({
@@ -1261,56 +1453,41 @@ describe('OptimizedSsrEngine', () => {
it('should use the default server logger, if custom logger is not specified', () => {
new TestEngineRunner({});
+ expect(consoleLogSpy).toHaveBeenCalled();
+ });
+ it('should use the provided logger', () => {
+ new TestEngineRunner({
+ logger: new MockExpressServerLogger() as ExpressServerLogger,
+ });
expect(consoleLogSpy.mock.lastCall).toMatchInlineSnapshot(`
[
- "{
- "message": "[spartacus] SSR optimization engine initialized",
- "context": {
- "timestamp": "2023-01-01T00:00:00.000Z",
+ "[spartacus] SSR optimization engine initialized",
+ {
"options": {
"cacheSize": 3000,
"concurrency": 10,
- "timeout": 3000,
"forcedSsrTimeout": 60000,
+ "logger": "MockExpressServerLogger",
"maxRenderTime": 300000,
- "reuseCurrentRendering": true,
- "renderingStrategyResolver": "(request) => {\\n if (hasExcludedUrl(request, defaultAlwaysCsrOptions.excludedUrls)) {\\n return ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR;\\n }\\n return shouldFallbackToCsr(request, options)\\n ? ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR\\n : ssr_optimization_options_1.RenderingStrategy.DEFAULT;\\n}",
- "logger": "DefaultExpressServerLogger"
+ "renderingStrategyResolver": "(request) => {
+ if (hasExcludedUrl(request, defaultAlwaysCsrOptions.excludedUrls)) {
+ return ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR;
}
- }
+ return shouldFallbackToCsr(request, options)
+ ? ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR
+ : ssr_optimization_options_1.RenderingStrategy.DEFAULT;
}",
+ "reuseCurrentRendering": true,
+ "shouldCacheRenderingResult": "({ options, entry }) => !(options.ssrFeatureToggles?.avoidCachingErrors === true &&
+ Boolean(entry.err))",
+ "ssrFeatureToggles": {
+ "avoidCachingErrors": false,
+ },
+ "timeout": 3000,
+ },
+ },
]
`);
});
-
- it('should use the provided logger', () => {
- new TestEngineRunner({
- logger: new MockExpressServerLogger() as ExpressServerLogger,
- });
- expect(consoleLogSpy.mock.lastCall).toMatchInlineSnapshot(`
- [
- "[spartacus] SSR optimization engine initialized",
- {
- "options": {
- "cacheSize": 3000,
- "concurrency": 10,
- "forcedSsrTimeout": 60000,
- "logger": "MockExpressServerLogger",
- "maxRenderTime": 300000,
- "renderingStrategyResolver": "(request) => {
- if (hasExcludedUrl(request, defaultAlwaysCsrOptions.excludedUrls)) {
- return ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR;
- }
- return shouldFallbackToCsr(request, options)
- ? ssr_optimization_options_1.RenderingStrategy.ALWAYS_CSR
- : ssr_optimization_options_1.RenderingStrategy.DEFAULT;
- }",
- "reuseCurrentRendering": true,
- "timeout": 3000,
- },
- },
- ]
- `);
- });
});
});
diff --git a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts
index f5a61e7e08a..84894d223b2 100644
--- a/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts
+++ b/core-libs/setup/ssr/optimized-engine/optimized-ssr-engine.ts
@@ -74,6 +74,11 @@ export class OptimizedSsrEngine {
...defaultSsrOptimizationOptions,
// overrides the default options
...ssrOptions,
+ // merge feature toggles
+ ssrFeatureToggles: {
+ ...defaultSsrOptimizationOptions.ssrFeatureToggles,
+ ...ssrOptions.ssrFeatureToggles,
+ },
}
: undefined;
@@ -292,11 +297,7 @@ export class OptimizedSsrEngine {
clearTimeout(requestTimeout);
callback(err, html);
- this.log(
- `Request is resolved with the SSR rendering result (${request?.originalUrl})`,
- true,
- { request }
- );
+ this.logForRenderResult(err, html, request);
// store the render only if caching is enabled
if (this.ssrOptions?.cache) {
@@ -475,4 +476,27 @@ export class OptimizedSsrEngine {
renderCallback(err, html);
});
}
+
+ /**
+ * Logs the result of the render.
+ */
+ private logForRenderResult(
+ err: unknown | undefined,
+ html: string | undefined,
+ request: Request
+ ): void {
+ if (html) {
+ this.log(
+ `Request is resolved with the SSR rendering result (${request?.originalUrl})`,
+ true,
+ { request }
+ );
+ }
+ if (err) {
+ this.logger.error(
+ `Request is resolved with the SSR rendering error (${request?.originalUrl})`,
+ { request, error: err }
+ );
+ }
+ }
}
diff --git a/core-libs/setup/ssr/optimized-engine/rendering-cache.model.ts b/core-libs/setup/ssr/optimized-engine/rendering-cache.model.ts
new file mode 100644
index 00000000000..d95a53c8f0b
--- /dev/null
+++ b/core-libs/setup/ssr/optimized-engine/rendering-cache.model.ts
@@ -0,0 +1,15 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * Represents a rendering entry in the rendering cache.
+ */
+export interface RenderingEntry {
+ html?: any;
+ err?: any;
+ time?: number;
+ rendering?: boolean;
+}
diff --git a/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts b/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts
index 5cc6e8794e0..81592e76823 100644
--- a/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts
+++ b/core-libs/setup/ssr/optimized-engine/rendering-cache.spec.ts
@@ -1,12 +1,21 @@
///
import { RenderingCache } from './rendering-cache';
+import {
+ SsrOptimizationOptions,
+ defaultSsrOptimizationOptions,
+} from './ssr-optimization-options';
+
+const options: SsrOptimizationOptions = {
+ shouldCacheRenderingResult:
+ defaultSsrOptimizationOptions.shouldCacheRenderingResult,
+};
describe('RenderingCache', () => {
let renderingCache: RenderingCache;
beforeEach(() => {
- renderingCache = new RenderingCache({});
+ renderingCache = new RenderingCache(options);
});
it('should create rendering cache instance', () => {
@@ -77,13 +86,13 @@ describe('RenderingCache with ttl', () => {
let renderingCache: RenderingCache;
beforeEach(() => {
- renderingCache = new RenderingCache({ ttl: 100 });
+ renderingCache = new RenderingCache({ ...options, ttl: 100 });
});
describe('get', () => {
it('should return timestamp', () => {
renderingCache.store('test', null, 'testHtml');
- expect(renderingCache.get('test').time).toBeTruthy();
+ expect(renderingCache.get('test')?.time).toBeTruthy();
});
});
@@ -118,7 +127,7 @@ describe('RenderingCache with cacheSize', () => {
let renderingCache: RenderingCache;
beforeEach(() => {
- renderingCache = new RenderingCache({ cacheSize: 2 });
+ renderingCache = new RenderingCache({ ...options, cacheSize: 2 });
});
describe('get', () => {
@@ -151,4 +160,75 @@ describe('RenderingCache with cacheSize', () => {
expect(renderingCache.get('c')).toBeTruthy();
});
});
+
+ describe('RenderingCache and shouldCacheRenderingResult', () => {
+ let renderingCache: RenderingCache;
+
+ describe('if default shouldCacheRenderingResult', () => {
+ it('should cache HTML if avoidCachingErrors is false', () => {
+ renderingCache = new RenderingCache({
+ ...options,
+ ssrFeatureToggles: {
+ avoidCachingErrors: false,
+ },
+ });
+ renderingCache.store('a', undefined, 'a');
+ expect(renderingCache.get('a')).toEqual({ html: 'a', err: undefined });
+ });
+
+ it('should cache HTML if avoidCachingErrors is true', () => {
+ renderingCache = new RenderingCache({
+ ...options,
+ ssrFeatureToggles: {
+ avoidCachingErrors: false,
+ },
+ });
+ renderingCache.store('a', undefined, 'a');
+ expect(renderingCache.get('a')).toEqual({ html: 'a', err: undefined });
+ });
+
+ it('should cache errors if avoidCachingErrors is false', () => {
+ renderingCache = new RenderingCache({
+ ...options,
+ ssrFeatureToggles: {
+ avoidCachingErrors: false,
+ },
+ });
+ renderingCache.store('a', new Error('err'), 'a');
+ expect(renderingCache.get('a')).toEqual({
+ html: 'a',
+ err: new Error('err'),
+ });
+ });
+
+ it('should not cache errors if avoidCachingErrors is true', () => {
+ renderingCache = new RenderingCache({
+ ...options,
+ ssrFeatureToggles: {
+ avoidCachingErrors: true,
+ },
+ });
+ renderingCache.store('a', new Error('err'), 'a');
+ expect(renderingCache.get('a')).toBeFalsy();
+ });
+ });
+
+ describe('if shouldCacheRenderingResult is not defined', () => {
+ beforeEach(() => {
+ renderingCache = new RenderingCache({
+ ...options,
+ shouldCacheRenderingResult: undefined,
+ });
+ });
+ it('should not cache a html', () => {
+ renderingCache.store('a', undefined, 'a');
+ expect(renderingCache.get('a')).toBeFalsy();
+ });
+
+ it('should not cache an error', () => {
+ renderingCache.store('a', new Error('err'), 'a');
+ expect(renderingCache.get('a')).toBeFalsy();
+ });
+ });
+ });
});
diff --git a/core-libs/setup/ssr/optimized-engine/rendering-cache.ts b/core-libs/setup/ssr/optimized-engine/rendering-cache.ts
index 88833775be2..f7700687fb7 100644
--- a/core-libs/setup/ssr/optimized-engine/rendering-cache.ts
+++ b/core-libs/setup/ssr/optimized-engine/rendering-cache.ts
@@ -4,15 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { RenderingEntry } from './rendering-cache.model';
import { SsrOptimizationOptions } from './ssr-optimization-options';
-export interface RenderingEntry {
- html?: any;
- err?: any;
- time?: number;
- rendering?: boolean;
-}
-
export class RenderingCache {
protected renders = new Map();
@@ -37,7 +31,15 @@ export class RenderingCache {
this.renders.delete(this.renders.keys().next().value);
}
}
- this.renders.set(key, entry);
+ // cache only if shouldCacheRenderingResult return true
+ if (
+ this.options?.shouldCacheRenderingResult?.({
+ options: this.options,
+ entry,
+ })
+ ) {
+ this.renders.set(key, entry);
+ }
}
get(key: string): RenderingEntry | undefined {
diff --git a/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts b/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts
index d0fc77f6884..9f3610ef5da 100644
--- a/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts
+++ b/core-libs/setup/ssr/optimized-engine/ssr-optimization-options.ts
@@ -6,6 +6,7 @@
import { Request } from 'express';
import { DefaultExpressServerLogger, ExpressServerLogger } from '../logger';
+import { RenderingEntry } from './rendering-cache.model';
import { defaultRenderingStrategyResolver } from './rendering-strategy-resolver';
import { defaultRenderingStrategyResolverOptions } from './rendering-strategy-resolver-options';
@@ -136,6 +137,42 @@ export interface SsrOptimizationOptions {
* By default, the DefaultExpressServerLogger is used.
*/
logger?: ExpressServerLogger;
+
+ /**
+ * When caching is enabled, this function tells whether the given rendering result
+ * (html or error) should be cached.
+ *
+ * By default, all html rendering results are cached. By default, also all errors are cached
+ * unless the separate option `avoidCachingErrors` is enabled.
+ */
+ shouldCacheRenderingResult?: ({
+ options,
+ entry,
+ }: {
+ options: SsrOptimizationOptions;
+ entry: Pick;
+ }) => boolean;
+
+ /**
+ * Toggles providing granular adaptation to breaking changes in OptimizedSsrEngine.
+ * They are temporary and will be removed in the future.
+ * Each toggle has its own lifespan.
+ *
+ * Note: They are related only to the `OptimizedSsrEngine`. In particular, they
+ * are different from Spartacus's regular feature toggles provided in the Angular app.
+ */
+ ssrFeatureToggles?: {
+ /**
+ * Determines if rendering errors should be skipped from caching.
+ *
+ * It's recommended to set to `true` (i.e. errors are skipped from caching),
+ * which will become the default behavior, when this feature toggle is removed.
+ *
+ * It only affects the default `shouldCacheRenderingResult`.
+ * Custom implementations of `shouldCacheRenderingResult` may ignore this setting.
+ */
+ avoidCachingErrors?: boolean;
+ };
}
export enum RenderingStrategy {
@@ -144,7 +181,16 @@ export enum RenderingStrategy {
ALWAYS_SSR = 1,
}
-export const defaultSsrOptimizationOptions: SsrOptimizationOptions = {
+/**
+ * Deeply required type for `featureToggles` property.
+ */
+type DeepRequired = {
+ [P in keyof T]-?: DeepRequired;
+};
+
+export const defaultSsrOptimizationOptions: SsrOptimizationOptions &
+ // To not forget adding default values, when adding new feature toggles in the type in the future
+ DeepRequired> = {
cacheSize: 3000,
concurrency: 10,
timeout: 3_000,
@@ -155,4 +201,12 @@ export const defaultSsrOptimizationOptions: SsrOptimizationOptions = {
defaultRenderingStrategyResolverOptions
),
logger: new DefaultExpressServerLogger(),
+ shouldCacheRenderingResult: ({ options, entry }) =>
+ !(
+ options.ssrFeatureToggles?.avoidCachingErrors === true &&
+ Boolean(entry.err)
+ ),
+ ssrFeatureToggles: {
+ avoidCachingErrors: false,
+ },
};
diff --git a/core-libs/setup/ssr/providers/ssr-providers.ts b/core-libs/setup/ssr/providers/ssr-providers.ts
index 9293c835d19..b6af3698cef 100644
--- a/core-libs/setup/ssr/providers/ssr-providers.ts
+++ b/core-libs/setup/ssr/providers/ssr-providers.ts
@@ -4,12 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { StaticProvider } from '@angular/core';
+import { Provider, StaticProvider } from '@angular/core';
import {
LoggerService,
+ MULTI_ERROR_HANDLER,
SERVER_REQUEST_ORIGIN,
SERVER_REQUEST_URL,
} from '@spartacus/core';
+
+import { PropagatingToServerErrorHandler } from '../error-handling/multi-error-handlers';
import { getRequestOrigin } from '../express-utils/express-request-origin';
import { getRequestUrl } from '../express-utils/express-request-url';
import { serverLoggerServiceFactory } from '../logger';
@@ -21,7 +24,7 @@ import { serverRequestUrlFactory } from './server-request-url';
/**
* Returns the providers used for SSR and pre-rendering processes.
*/
-export function provideServer(options?: ServerOptions): StaticProvider[] {
+export function provideServer(options?: ServerOptions): Provider[] {
return [
{
provide: SERVER_REQUEST_ORIGIN,
@@ -35,6 +38,11 @@ export function provideServer(options?: ServerOptions): StaticProvider[] {
provide: LoggerService,
useFactory: serverLoggerServiceFactory,
},
+ {
+ provide: MULTI_ERROR_HANDLER,
+ useExisting: PropagatingToServerErrorHandler,
+ multi: true,
+ },
];
}
/**
diff --git a/core-libs/setup/ssr/public_api.ts b/core-libs/setup/ssr/public_api.ts
index 6ead7398692..b1a6d0e428e 100644
--- a/core-libs/setup/ssr/public_api.ts
+++ b/core-libs/setup/ssr/public_api.ts
@@ -5,7 +5,9 @@
*/
export * from './engine-decorator/index';
+export * from './engine/cx-common-engine';
export * from './engine/ng-express-engine';
+export * from './error-handling/index';
export * from './logger/index';
export * from './optimized-engine/index';
export * from './providers/index';
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..06486b7ceea 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,13 @@ 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),
+ meta: StateUtils.failMeta(CUSTOMER_SEARCH_DATA, error),
payload: error,
+ error,
});
});
@@ -76,13 +77,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 0f13450c7a8..bcadde1e86c 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 { ErrorAction, StateUtils } from '@spartacus/core';
import {
CUSTOMER_LIST_CUSTOMERS_SEARCH_DATA,
CUSTOMER_SEARCH_DATA,
@@ -32,10 +32,13 @@ export class CustomerSearch extends StateUtils.LoaderLoadAction {
}
}
-export class CustomerSearchFail extends StateUtils.LoaderFailAction {
+export class CustomerSearchFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = CUSTOMER_SEARCH_FAIL;
constructor(public payload: any) {
- super(CUSTOMER_SEARCH_DATA);
+ super(CUSTOMER_SEARCH_DATA, payload);
}
}
@@ -60,10 +63,13 @@ export class CustomerListCustomersSearch extends StateUtils.LoaderLoadAction {
}
}
-export class CustomerListCustomersSearchFail extends StateUtils.LoaderFailAction {
+export class CustomerListCustomersSearchFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = CUSTOMER_LIST_CUSTOMERS_SEARCH_FAIL;
constructor(public payload: any) {
- super(CUSTOMER_LIST_CUSTOMERS_SEARCH_DATA);
+ 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 4595c500e63..3db1d1487fd 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';
@@ -35,7 +35,7 @@ export class CustomerEffects {
}
return of(
new AsmActions.CustomerSearchFail(
- normalizeHttpError(error, this.logger)
+ tryNormalizeHttpError(error, this.logger)
)
);
})
@@ -59,7 +59,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 81d5117171f..ffe6e7ae199 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, 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
+{
+ public error: any;
readonly type = CART_ADD_ENTRY_FAIL;
+
constructor(
public payload: {
error: any;
@@ -67,11 +74,13 @@ export class CartAddEntryFail extends StateUtils.EntityProcessesDecrementAction
}
) {
super(MULTI_CART_DATA, payload.cartId);
+ this.error = payload.error;
}
}
export class CartRemoveEntry extends StateUtils.EntityProcessesIncrementAction {
readonly type = CART_REMOVE_ENTRY;
+
constructor(
public payload: { cartId: string; userId: string; entryNumber: string }
) {
@@ -81,6 +90,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,8 +98,13 @@ export class CartRemoveEntrySuccess extends StateUtils.EntityProcessesDecrementA
}
}
-export class CartRemoveEntryFail extends StateUtils.EntityProcessesDecrementAction {
+export class CartRemoveEntryFail
+ extends StateUtils.EntityProcessesDecrementAction
+ implements ErrorAction
+{
+ public error: any;
readonly type = CART_REMOVE_ENTRY_FAIL;
+
constructor(
public payload: {
error: any;
@@ -99,11 +114,13 @@ export class CartRemoveEntryFail extends StateUtils.EntityProcessesDecrementActi
}
) {
super(MULTI_CART_DATA, payload.cartId);
+ this.error = payload.error;
}
}
export class CartUpdateEntry extends StateUtils.EntityProcessesIncrementAction {
readonly type = CART_UPDATE_ENTRY;
+
constructor(
public payload: {
userId: string;
@@ -120,6 +137,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,8 +152,13 @@ export class CartUpdateEntrySuccess extends StateUtils.EntityProcessesDecrementA
}
}
-export class CartUpdateEntryFail extends StateUtils.EntityProcessesDecrementAction {
+export class CartUpdateEntryFail
+ extends StateUtils.EntityProcessesDecrementAction
+ implements ErrorAction
+{
+ public error: any;
readonly type = CART_UPDATE_ENTRY_FAIL;
+
constructor(
public payload: {
error: any;
@@ -148,6 +171,7 @@ export class CartUpdateEntryFail extends StateUtils.EntityProcessesDecrementActi
}
) {
super(MULTI_CART_DATA, payload.cartId);
+ this.error = payload.error;
}
}
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 2aaef5507ac..5fcd6c55c32 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,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { PROCESS_FEATURE, StateUtils } from '@spartacus/core';
+import { ErrorAction, 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 +22,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 }
) {
@@ -29,8 +30,12 @@ export class CartAddVoucher extends StateUtils.EntityLoadAction {
}
}
-export class CartAddVoucherFail extends StateUtils.EntityFailAction {
+export class CartAddVoucherFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CART_ADD_VOUCHER_FAIL;
+
constructor(
public payload: {
userId: string;
@@ -45,6 +50,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 +63,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 +72,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,8 +80,13 @@ export class CartRemoveVoucher extends StateUtils.EntityProcessesIncrementAction
}
}
-export class CartRemoveVoucherFail extends StateUtils.EntityProcessesDecrementAction {
+export class CartRemoveVoucherFail
+ extends StateUtils.EntityProcessesDecrementAction
+ implements ErrorAction
+{
+ public error: any;
readonly type = CART_REMOVE_VOUCHER_FAIL;
+
constructor(
public payload: {
error: any;
@@ -83,11 +96,13 @@ export class CartRemoveVoucherFail extends StateUtils.EntityProcessesDecrementAc
}
) {
super(MULTI_CART_DATA, payload.cartId);
+ this.error = payload.error;
}
}
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 87e5317038f..d71f4e41602 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 { ErrorAction, StateUtils } from '@spartacus/core';
import { MULTI_CART_DATA } from '../multi-cart-state';
export const CREATE_CART = '[Cart] Create Cart';
@@ -58,10 +58,13 @@ interface CreateCartFailPayload extends CreateCartPayload {
error: any;
}
-export class CreateCartFail extends StateUtils.EntityFailAction {
+export class CreateCartFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_CART_FAIL;
constructor(public payload: CreateCartFailPayload) {
- super(MULTI_CART_DATA, payload.tempCartId);
+ super(MULTI_CART_DATA, payload.tempCartId, payload.error);
}
}
@@ -86,7 +89,11 @@ export class AddEmailToCart extends StateUtils.EntityProcessesIncrementAction {
}
}
-export class AddEmailToCartFail extends StateUtils.EntityProcessesDecrementAction {
+export class AddEmailToCartFail
+ extends StateUtils.EntityProcessesDecrementAction
+ implements ErrorAction
+{
+ public error: any;
readonly type = ADD_EMAIL_TO_CART_FAIL;
constructor(
public payload: {
@@ -97,6 +104,7 @@ export class AddEmailToCartFail extends StateUtils.EntityProcessesDecrementActio
}
) {
super(MULTI_CART_DATA, payload.cartId);
+ this.error = payload.error;
}
}
@@ -128,7 +136,10 @@ interface LoadCartFailPayload extends LoadCartPayload {
error: any;
}
-export class LoadCartFail extends StateUtils.EntityFailAction {
+export class LoadCartFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_CART_FAIL;
constructor(public payload: LoadCartFailPayload) {
super(MULTI_CART_DATA, payload.cartId, payload.error);
@@ -220,9 +231,12 @@ export class DeleteCartSuccess extends StateUtils.EntityRemoveAction {
}
}
-export class DeleteCartFail implements Action {
+export class DeleteCartFail implements ErrorAction {
+ public error: any;
readonly type = DELETE_CART_FAIL;
- constructor(public payload: { userId: string; cartId: string; error: any }) {}
+ constructor(public payload: { userId: string; cartId: string; error: any }) {
+ this.error = payload.error;
+ }
}
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 326e67637fb..6d88190ca12 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 4b06a745dab..65e6c7b3185 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
@@ -5,8 +5,8 @@ import { provideMockActions } from '@ngrx/effects/testing';
import {
GlobalMessageService,
LoggerService,
- normalizeHttpError,
OccConfig,
+ tryNormalizeHttpError,
} from '@spartacus/core';
import { cold, hot } from 'jasmine-marbles';
import { Observable, of, throwError } from 'rxjs';
@@ -104,7 +104,7 @@ describe('Cart Voucher effect', () => {
userId,
cartId,
voucherId,
- error: normalizeHttpError(error, new MockLoggerService()),
+ error: tryNormalizeHttpError(error, new MockLoggerService()),
});
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 8e4b1ee254e..5c925c80711 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 622472f47e5..eff7ce32594 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
@@ -11,7 +11,7 @@ import {
OccConfig,
SiteContextActions,
USER_FEATURE,
- normalizeHttpError,
+ tryNormalizeHttpError,
} from '@spartacus/core';
import { cold, hot } from 'jasmine-marbles';
import * as fromClientAuthReducers from 'projects/core/src/auth/client-auth/store/reducers/index';
@@ -212,7 +212,7 @@ describe('Cart effect', () => {
loadMock.and.returnValue(throwError(() => httpError));
const removeCartCompletion = new CartActions.LoadCartFail({
...payload,
- error: normalizeHttpError(httpError, new MockLoggerService()),
+ error: tryNormalizeHttpError(httpError, new MockLoggerService()),
});
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 8ecffc3e6f0..cc795e3e973 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 a68935b36ab..8cfae6d1c2e 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 { ErrorAction, 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;
@@ -66,8 +68,12 @@ export class LoadSavedCartSuccess extends StateUtils.EntitySuccessAction {
}
}
-export class LoadSavedCartFail extends StateUtils.EntityFailAction {
+export class LoadSavedCartFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
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 +81,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 +93,7 @@ export class LoadSavedCarts extends StateUtils.EntityLoadAction {
export class LoadSavedCartsSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_SAVED_CARTS_SUCCESS;
+
constructor(
public payload: {
userId: string;
@@ -95,8 +103,12 @@ export class LoadSavedCartsSuccess extends StateUtils.EntitySuccessAction {
}
}
-export class LoadSavedCartsFail extends StateUtils.EntityFailAction {
+export class LoadSavedCartsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_SAVED_CARTS_FAIL;
+
constructor(
public payload: {
userId: string;
@@ -109,6 +121,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 +129,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 +142,7 @@ export class RestoreSavedCart extends StateUtils.EntityLoadAction {
export class RestoreSavedCartSuccess extends StateUtils.EntitySuccessAction {
readonly type = RESTORE_SAVED_CART_SUCCESS;
+
constructor(
public payload: {
userId: string;
@@ -138,8 +153,12 @@ export class RestoreSavedCartSuccess extends StateUtils.EntitySuccessAction {
}
}
-export class RestoreSavedCartFail extends StateUtils.EntityFailAction {
+export class RestoreSavedCartFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = RESTORE_SAVED_CART_FAIL;
+
constructor(
public payload: {
userId: string;
@@ -153,6 +172,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 +180,7 @@ export class ClearRestoreSavedCart extends StateUtils.EntityLoaderResetAction {
export class SaveCart extends StateUtils.EntityLoadAction {
readonly type = SAVE_CART;
+
constructor(
public payload: {
userId: string;
@@ -174,6 +195,7 @@ export class SaveCart extends StateUtils.EntityLoadAction {
export class SaveCartSuccess extends StateUtils.EntitySuccessAction {
readonly type = SAVE_CART_SUCCESS;
+
constructor(
public payload: {
userId: string;
@@ -186,8 +208,12 @@ export class SaveCartSuccess extends StateUtils.EntitySuccessAction {
}
}
-export class SaveCartFail extends StateUtils.EntityFailAction {
+export class SaveCartFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = SAVE_CART_FAIL;
+
constructor(
public payload: {
userId: string;
@@ -203,6 +229,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 +237,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 +252,7 @@ export class EditSavedCart extends StateUtils.EntityLoadAction {
export class EditSavedCartSuccess extends StateUtils.EntitySuccessAction {
readonly type = EDIT_SAVED_CART_SUCCESS;
+
constructor(
public payload: {
userId: string;
@@ -236,8 +265,12 @@ export class EditSavedCartSuccess extends StateUtils.EntitySuccessAction {
}
}
-export class EditSavedCartFail extends StateUtils.EntityFailAction {
+export class EditSavedCartFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = EDIT_SAVED_CART_FAIL;
+
constructor(
public payload: {
userId: string;
@@ -253,6 +286,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 +300,7 @@ export class CloneSavedCart extends StateUtils.EntityLoadAction {
export class CloneSavedCartSuccess extends StateUtils.EntitySuccessAction {
readonly type = CLONE_SAVED_CART_SUCCESS;
+
constructor(
public payload: {
userId: string;
@@ -277,8 +312,12 @@ export class CloneSavedCartSuccess extends StateUtils.EntitySuccessAction {
}
}
-export class CloneSavedCartFail extends StateUtils.EntityFailAction {
+export class CloneSavedCartFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CLONE_SAVED_CART_FAIL;
+
constructor(
public payload: {
userId: string;
@@ -293,6 +332,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 e6cef4315ab..65f9b795b77 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),
})
)
)
@@ -155,7 +155,7 @@ export class SavedCartEffects {
new SavedCartActions.RestoreSavedCartFail({
userId,
cartId,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -201,7 +201,7 @@ export class SavedCartEffects {
cartId,
saveCartName,
saveCartDescription,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -245,7 +245,7 @@ export class SavedCartEffects {
cartId,
saveCartName,
saveCartDescription,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -288,7 +288,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 8afeb14e0e9..8212d378db9 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 { ErrorAction, 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,13 +31,27 @@ 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);
}
}
-export class CreateWishListFail extends StateUtils.EntityFailAction {
+export class CreateWishListFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_WISH_LIST_FAIL;
+ constructor(payload: { cartId: string; error: any });
+ /**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor(
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ payload: { cartId: string }
+ );
constructor(public payload: { cartId: string; error?: any }) {
super(MULTI_CART_DATA, payload.cartId, payload.error);
}
@@ -56,12 +71,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);
}
@@ -76,8 +94,12 @@ interface LoadWishListFailPayload {
error: any;
}
-export class LoadWishListFail extends StateUtils.EntityFailAction {
+export class LoadWishListFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
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 25ca2f7091d..46ed6b4bc2d 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 ff791305072..8a5e090d7d0 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-by-id.action.spec.ts b/feature-libs/order/core/store/actions/consignment-tracking-by-id.action.spec.ts
index c6f8ddb641e..550cbfe15c1 100644
--- a/feature-libs/order/core/store/actions/consignment-tracking-by-id.action.spec.ts
+++ b/feature-libs/order/core/store/actions/consignment-tracking-by-id.action.spec.ts
@@ -52,6 +52,7 @@ describe('Consignment Tracking By Id Actions', () => {
'order1,cons1',
'error'
),
+ error: 'error',
});
});
});
diff --git a/feature-libs/order/core/store/actions/consignment-tracking-by-id.action.ts b/feature-libs/order/core/store/actions/consignment-tracking-by-id.action.ts
index f4c58c306c0..821c2078ffc 100644
--- a/feature-libs/order/core/store/actions/consignment-tracking-by-id.action.ts
+++ b/feature-libs/order/core/store/actions/consignment-tracking-by-id.action.ts
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { StateUtils } from '@spartacus/core';
+import { ErrorAction, StateUtils } from '@spartacus/core';
import { ConsignmentTracking } from '@spartacus/order/root';
import {
CONSIGNMENT_TRACKING_BY_ID_ENTITIES,
@@ -37,7 +37,10 @@ export class LoadConsignmentTrackingById extends StateUtils.EntityLoadAction {
}
}
-export class LoadConsignmentTrackingByIdFail extends StateUtils.EntityFailAction {
+export class LoadConsignmentTrackingByIdFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_CONSIGNMENT_TRACKING_BY_ID_FAIL;
constructor(
public payload: { orderCode: string; consignmentCode: string; error: any }
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..79c8169cb6f 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,13 @@ 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 26866946166..81cfd900edf 100644
--- a/feature-libs/order/core/store/actions/consignment-tracking.action.ts
+++ b/feature-libs/order/core/store/actions/consignment-tracking.action.ts
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '@spartacus/core';
import { ConsignmentTracking } from '@spartacus/order/root';
export const LOAD_CONSIGNMENT_TRACKING = '[Order] Load Consignment Tracking';
@@ -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,24 @@ 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) {}
+ public error: any;
+
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
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-by-id.action.spec.ts b/feature-libs/order/core/store/actions/order-by-id.action.spec.ts
index 3d4f4ac646d..ca6cb963205 100644
--- a/feature-libs/order/core/store/actions/order-by-id.action.spec.ts
+++ b/feature-libs/order/core/store/actions/order-by-id.action.spec.ts
@@ -43,6 +43,7 @@ describe('Order By Id Actions', () => {
'order1',
'error'
),
+ error: 'error',
});
});
});
diff --git a/feature-libs/order/core/store/actions/order-by-id.action.ts b/feature-libs/order/core/store/actions/order-by-id.action.ts
index 1ee032efdf5..ce60bac8198 100644
--- a/feature-libs/order/core/store/actions/order-by-id.action.ts
+++ b/feature-libs/order/core/store/actions/order-by-id.action.ts
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { StateUtils } from '@spartacus/core';
+import { ErrorAction, StateUtils } from '@spartacus/core';
import { Order } from '@spartacus/order/root';
import { ORDER_BY_ID_ENTITIES } from '../order-state';
@@ -18,7 +18,10 @@ export class LoadOrderById extends StateUtils.EntityLoadAction {
}
}
-export class LoadOrderByIdFail extends StateUtils.EntityFailAction {
+export class LoadOrderByIdFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORDER_BY_ID_FAIL;
constructor(public payload: { code: string; error: any }) {
super(ORDER_BY_ID_ENTITIES, payload.code, payload.error);
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..d6fa53c9687 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,13 @@ 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 +82,13 @@ 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 a17248e326f..87b6db9347e 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 { ErrorAction, PROCESS_FEATURE, StateUtils } from '@spartacus/core';
import {
CancellationRequestEntryInputList,
Order,
@@ -33,7 +33,10 @@ export class LoadOrderDetails extends StateUtils.LoaderLoadAction {
}
}
-export class LoadOrderDetailsFail extends StateUtils.LoaderFailAction {
+export class LoadOrderDetailsFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORDER_DETAILS_FAIL;
constructor(public payload: any) {
super(ORDER_DETAILS, payload);
@@ -67,7 +70,10 @@ export class CancelOrder extends StateUtils.EntityLoadAction {
}
}
-export class CancelOrderFail extends StateUtils.EntityFailAction {
+export class CancelOrderFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CANCEL_ORDER_FAIL;
constructor(public payload: any) {
super(PROCESS_FEATURE, CANCEL_ORDER_PROCESS_ID, payload);
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..850ead25b13 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,13 @@ 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 +102,13 @@ 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 +153,13 @@ 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 +200,13 @@ 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 127c9334adf..e8bcef25884 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 { ErrorAction, PROCESS_FEATURE, StateUtils } from '@spartacus/core';
import {
ReturnRequest,
ReturnRequestEntryInputList,
@@ -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;
@@ -64,8 +65,12 @@ export class CreateOrderReturnRequest extends StateUtils.LoaderLoadAction {
}
}
-export class CreateOrderReturnRequestFail extends StateUtils.LoaderFailAction {
+export class CreateOrderReturnRequestFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = CREATE_ORDER_RETURN_REQUEST_FAIL;
+
constructor(public payload: any) {
super(RETURN_REQUEST_DETAILS, payload);
}
@@ -73,6 +78,7 @@ export class CreateOrderReturnRequestFail extends StateUtils.LoaderFailAction {
export class CreateOrderReturnRequestSuccess extends StateUtils.LoaderSuccessAction {
readonly type = CREATE_ORDER_RETURN_REQUEST_SUCCESS;
+
constructor(public payload: ReturnRequest) {
super(RETURN_REQUEST_DETAILS);
}
@@ -80,6 +86,7 @@ export class CreateOrderReturnRequestSuccess extends StateUtils.LoaderSuccessAct
export class LoadOrderReturnRequest extends StateUtils.LoaderLoadAction {
readonly type = LOAD_ORDER_RETURN_REQUEST;
+
constructor(
public payload: {
userId: string;
@@ -90,8 +97,12 @@ export class LoadOrderReturnRequest extends StateUtils.LoaderLoadAction {
}
}
-export class LoadOrderReturnRequestFail extends StateUtils.LoaderFailAction {
+export class LoadOrderReturnRequestFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORDER_RETURN_REQUEST_FAIL;
+
constructor(public payload: any) {
super(RETURN_REQUEST_DETAILS, payload);
}
@@ -99,6 +110,7 @@ export class LoadOrderReturnRequestFail extends StateUtils.LoaderFailAction {
export class LoadOrderReturnRequestSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_ORDER_RETURN_REQUEST_SUCCESS;
+
constructor(public payload: ReturnRequest) {
super(RETURN_REQUEST_DETAILS);
}
@@ -106,6 +118,7 @@ export class LoadOrderReturnRequestSuccess extends StateUtils.LoaderSuccessActio
export class CancelOrderReturnRequest extends StateUtils.EntityLoadAction {
readonly type = CANCEL_ORDER_RETURN_REQUEST;
+
constructor(
public payload: {
userId: string;
@@ -117,8 +130,12 @@ export class CancelOrderReturnRequest extends StateUtils.EntityLoadAction {
}
}
-export class CancelOrderReturnRequestFail extends StateUtils.EntityFailAction {
+export class CancelOrderReturnRequestFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CANCEL_ORDER_RETURN_REQUEST_FAIL;
+
constructor(public payload: any) {
super(PROCESS_FEATURE, CANCEL_RETURN_PROCESS_ID, payload);
}
@@ -126,6 +143,7 @@ export class CancelOrderReturnRequestFail extends StateUtils.EntityFailAction {
export class CancelOrderReturnRequestSuccess extends StateUtils.EntitySuccessAction {
readonly type = CANCEL_ORDER_RETURN_REQUEST_SUCCESS;
+
constructor() {
super(PROCESS_FEATURE, CANCEL_RETURN_PROCESS_ID);
}
@@ -133,6 +151,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;
@@ -145,8 +164,12 @@ export class LoadOrderReturnRequestList extends StateUtils.LoaderLoadAction {
}
}
-export class LoadOrderReturnRequestListFail extends StateUtils.LoaderFailAction {
+export class LoadOrderReturnRequestListFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORDER_RETURN_REQUEST_LIST_FAIL;
+
constructor(public payload: any) {
super(RETURN_REQUESTS, payload);
}
@@ -154,6 +177,7 @@ export class LoadOrderReturnRequestListFail extends StateUtils.LoaderFailAction
export class LoadOrderReturnRequestListSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_ORDER_RETURN_REQUEST_LIST_SUCCESS;
+
constructor(public payload: ReturnRequestList) {
super(RETURN_REQUESTS);
}
@@ -161,6 +185,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 +193,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 +201,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..02c93b013cd 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,13 @@ 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 465393a5729..3a05a2b0401 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 { ErrorAction, 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;
@@ -28,8 +29,12 @@ export class LoadUserOrders extends StateUtils.LoaderLoadAction {
}
}
-export class LoadUserOrdersFail extends StateUtils.LoaderFailAction {
+export class LoadUserOrdersFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_ORDERS_FAIL;
+
constructor(public payload: any) {
super(ORDERS, payload);
}
@@ -37,6 +42,7 @@ export class LoadUserOrdersFail extends StateUtils.LoaderFailAction {
export class LoadUserOrdersSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_USER_ORDERS_SUCCESS;
+
constructor(public payload: OrderHistoryList) {
super(ORDERS);
}
@@ -44,6 +50,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..5ed4edb4989 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,15 @@ 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),
+ payload: error,
+ error,
+ meta: StateUtils.failMeta(REPLENISHMENT_ORDER_DETAILS, error),
});
});
});
@@ -123,16 +123,16 @@ 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,
+ payload: error,
+ 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 9fc45715438..5f3645fe3af 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 { ErrorAction, 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,13 +43,18 @@ 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);
}
}
-export class LoadReplenishmentOrderDetailsFail extends StateUtils.LoaderFailAction {
+export class LoadReplenishmentOrderDetailsFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_REPLENISHMENT_ORDER_DETAILS_FAIL;
+
constructor(public payload: any) {
super(REPLENISHMENT_ORDER_DETAILS, payload);
}
@@ -56,6 +62,7 @@ export class LoadReplenishmentOrderDetailsFail extends StateUtils.LoaderFailActi
export class ClearReplenishmentOrderDetails extends StateUtils.LoaderResetAction {
readonly type = ClEAR_REPLENISHMENT_ORDER_DETAILS;
+
constructor() {
super(REPLENISHMENT_ORDER_DETAILS);
}
@@ -63,6 +70,7 @@ export class ClearReplenishmentOrderDetails extends StateUtils.LoaderResetAction
export class CancelReplenishmentOrder extends StateUtils.EntityLoadAction {
readonly type = CANCEL_REPLENISHMENT_ORDER;
+
constructor(
public payload: {
userId: string;
@@ -75,13 +83,18 @@ 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);
}
}
-export class CancelReplenishmentOrderFail extends StateUtils.EntityFailAction {
+export class CancelReplenishmentOrderFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CANCEL_REPLENISHMENT_ORDER_FAIL;
+
constructor(public payload: any) {
super(PROCESS_FEATURE, CANCEL_REPLENISHMENT_ORDER_PROCESS_ID, payload);
}
@@ -89,6 +102,7 @@ export class CancelReplenishmentOrderFail extends StateUtils.EntityFailAction {
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..9182a61fb43 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,13 @@ 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 a011db32c87..9753e57bbdf 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 { ErrorAction, 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;
@@ -31,8 +32,12 @@ export class LoadUserReplenishmentOrders extends StateUtils.LoaderLoadAction {
}
}
-export class LoadUserReplenishmentOrdersFail extends StateUtils.LoaderFailAction {
+export class LoadUserReplenishmentOrdersFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_REPLENISHMENT_ORDERS_FAIL;
+
constructor(public payload: any) {
super(REPLENISHMENT_ORDERS, payload);
}
@@ -40,6 +45,7 @@ export class LoadUserReplenishmentOrdersFail extends StateUtils.LoaderFailAction
export class LoadUserReplenishmentOrdersSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_USER_REPLENISHMENT_ORDERS_SUCCESS;
+
constructor(public payload: ReplenishmentOrderList) {
super(REPLENISHMENT_ORDERS);
}
@@ -47,6 +53,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-by-id.effect.spec.ts b/feature-libs/order/core/store/effects/consignment-tracking-by-id.effect.spec.ts
index 0d58937ab8d..f6bd572a29b 100644
--- a/feature-libs/order/core/store/effects/consignment-tracking-by-id.effect.spec.ts
+++ b/feature-libs/order/core/store/effects/consignment-tracking-by-id.effect.spec.ts
@@ -2,7 +2,11 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { Actions } from '@ngrx/effects';
import { provideMockActions } from '@ngrx/effects/testing';
-import { LoggerService, normalizeHttpError, OccConfig } from '@spartacus/core';
+import {
+ LoggerService,
+ OccConfig,
+ tryNormalizeHttpError,
+} from '@spartacus/core';
import { ConsignmentTracking } from '@spartacus/order/root';
import { cold, hot } from 'jasmine-marbles';
import { Observable, of, throwError } from 'rxjs';
@@ -74,8 +78,9 @@ describe('Consignment Tracking By Id effect', () => {
});
it('should handle failures for load consignment tracking by id', () => {
+ const error = new Error('error');
spyOn(orderHistoryConnector, 'getConsignmentTracking').and.returnValue(
- throwError('Error')
+ throwError(() => error)
);
const action = new OrderActions.LoadConsignmentTrackingById(
@@ -85,7 +90,7 @@ describe('Consignment Tracking By Id effect', () => {
const completion = new OrderActions.LoadConsignmentTrackingByIdFail({
orderCode: mockTrackingParams.orderCode,
consignmentCode: mockTrackingParams.consignmentCode,
- error: normalizeHttpError('Error', new MockLoggerService()),
+ error: tryNormalizeHttpError(error, new MockLoggerService()),
});
actions$ = hot('-a', { a: action });
diff --git a/feature-libs/order/core/store/effects/consignment-tracking-by-id.effect.ts b/feature-libs/order/core/store/effects/consignment-tracking-by-id.effect.ts
index 9ad511ae733..67ba82444e3 100644
--- a/feature-libs/order/core/store/effects/consignment-tracking-by-id.effect.ts
+++ b/feature-libs/order/core/store/effects/consignment-tracking-by-id.effect.ts
@@ -6,7 +6,7 @@
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 { ConsignmentTracking } from '@spartacus/order/root';
import { Observable, of } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';
@@ -47,7 +47,7 @@ export class ConsignmentTrackingByIdEffects {
new OrderActions.LoadConsignmentTrackingByIdFail({
orderCode: payload.orderCode,
consignmentCode: payload.consignmentCode,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
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 2c1d2aa4681..704e19e8e3f 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
@@ -79,17 +79,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 603da8ae04b..fc47ba29160 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-by-id.effect.spec.ts b/feature-libs/order/core/store/effects/order-by-id.effect.spec.ts
index 1823a6119d9..93cf31a7a0d 100644
--- a/feature-libs/order/core/store/effects/order-by-id.effect.spec.ts
+++ b/feature-libs/order/core/store/effects/order-by-id.effect.spec.ts
@@ -2,7 +2,11 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { Actions } from '@ngrx/effects';
import { provideMockActions } from '@ngrx/effects/testing';
-import { LoggerService, normalizeHttpError, OccConfig } from '@spartacus/core';
+import {
+ LoggerService,
+ OccConfig,
+ tryNormalizeHttpError,
+} from '@spartacus/core';
import { Order } from '@spartacus/order/root';
import { cold, hot } from 'jasmine-marbles';
import { Observable, of, throwError } from 'rxjs';
@@ -67,13 +71,16 @@ describe('Order By Id effect', () => {
});
it('should handle failures for load order by id', () => {
- spyOn(orderHistoryConnector, 'get').and.returnValue(throwError('Error'));
+ const error = new Error('error');
+ spyOn(orderHistoryConnector, 'get').and.returnValue(
+ throwError(() => error)
+ );
const action = new OrderActions.LoadOrderById(mockOrderParams);
const completion = new OrderActions.LoadOrderByIdFail({
code: mockOrderParams.code,
- error: normalizeHttpError('Error', new MockLoggerService()),
+ error: tryNormalizeHttpError(error, new MockLoggerService()),
});
actions$ = hot('-a', { a: action });
diff --git a/feature-libs/order/core/store/effects/order-by-id.effect.ts b/feature-libs/order/core/store/effects/order-by-id.effect.ts
index 6bb6d9c681b..826508025fe 100644
--- a/feature-libs/order/core/store/effects/order-by-id.effect.ts
+++ b/feature-libs/order/core/store/effects/order-by-id.effect.ts
@@ -7,7 +7,7 @@
import { HttpErrorResponse } from '@angular/common/http';
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 { Order } from '@spartacus/order/root';
import { Observable, of } from 'rxjs';
import { catchError, concatMap, map } from 'rxjs/operators';
@@ -34,7 +34,7 @@ export class OrderByIdEffect {
return of(
new OrderActions.LoadOrderByIdFail({
code,
- error: normalizeHttpError(error, this.logger),
+ error: 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 604582eb2a0..d6a70c38153 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
@@ -49,6 +49,8 @@ class MockUserIdService implements Partial {
}
}
+const error = new Error('error');
+
describe('Order Details effect', () => {
let orderDetailsEffect: fromOrderDetailsEffect.OrderDetailsEffect;
let orderHistoryConnector: OrderHistoryConnector;
@@ -100,12 +102,12 @@ describe('Order Details effect', () => {
it('should handle failures for load order details', () => {
spyOn(orderHistoryConnector, 'get').and.returnValue(
- throwError(() => 'Error')
+ 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 });
@@ -130,12 +132,12 @@ describe('Order Details effect', () => {
it('should handle failures for cancel an order', () => {
spyOn(orderHistoryConnector, 'cancel').and.returnValue(
- throwError(() => 'Error')
+ 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 526c2430351..c858bf09d39 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 dc5db9648a6..8127b7b1bf4 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
@@ -36,6 +36,7 @@ const mockCancelReturnRequest = {
returnRequestModification: {},
};
+const error = new Error('error');
class MockLoggerService {
log(): void {}
warn(): void {}
@@ -92,7 +93,7 @@ describe('Order Return Request effect', () => {
it('should handle failures for create order return request', () => {
spyOn(orderHistoryConnector, 'return').and.returnValue(
- throwError(() => 'Error')
+ throwError(() => error)
);
const action = new OrderActions.CreateOrderReturnRequest({
@@ -100,9 +101,7 @@ describe('Order Return Request effect', () => {
returnRequestInput,
});
- const completion = new OrderActions.CreateOrderReturnRequestFail(
- undefined
- );
+ const completion = new OrderActions.CreateOrderReturnRequestFail(error);
actions$ = hot('-a', { a: action });
const expected = cold('-b', { b: completion });
@@ -137,16 +136,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 });
@@ -181,7 +178,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({
@@ -189,7 +186,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 });
@@ -222,16 +219,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 6543449f5d3..1cdc7e2d530 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 19c52372264..77dc0eacc9b 100644
--- a/feature-libs/order/core/store/effects/orders.effect.spec.ts
+++ b/feature-libs/order/core/store/effects/orders.effect.spec.ts
@@ -5,8 +5,8 @@ import { provideMockActions } from '@ngrx/effects/testing';
import { Action } from '@ngrx/store';
import {
LoggerService,
- normalizeHttpError,
SiteContextActions,
+ tryNormalizeHttpError,
} from '@spartacus/core';
import { OrderHistoryList } from '@spartacus/order/root';
import { cold, hot } from 'jasmine-marbles';
@@ -97,7 +97,7 @@ describe('Orders effect', () => {
});
const completion = new OrderActions.LoadUserOrdersFail(
- normalizeHttpError(mockError, new MockLoggerService())
+ tryNormalizeHttpError(mockError, new MockLoggerService())
);
actions$ = hot('-a', { a: action });
@@ -143,7 +143,7 @@ describe('Orders effect', () => {
});
const completion = new OrderActions.LoadUserOrdersFail(
- normalizeHttpError(mockError, new MockLoggerService())
+ tryNormalizeHttpError(mockError, new MockLoggerService())
);
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 08a63fd5c22..02b8f182acb 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 4bdcea2fefe..25df50c89c5 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
@@ -5,8 +5,8 @@ import {
GlobalMessageService,
GlobalMessageType,
LoggerService,
- normalizeHttpError,
Translatable,
+ tryNormalizeHttpError,
} from '@spartacus/core';
import { ReplenishmentOrder } from '@spartacus/order/root';
import { cold, hot } from 'jasmine-marbles';
@@ -112,7 +112,7 @@ describe('ReplenishmentOrderDetailsEffect', () => {
replenishmentOrderCode: mockReplenishmentCode,
});
const completion = new OrderActions.LoadReplenishmentOrderDetailsFail(
- normalizeHttpError(mockError, new MockLoggerService())
+ tryNormalizeHttpError(mockError, new MockLoggerService())
);
actions$ = hot('-a', { a: action });
@@ -156,7 +156,7 @@ describe('ReplenishmentOrderDetailsEffect', () => {
replenishmentOrderCode: mockReplenishmentCode,
});
const completion = new OrderActions.CancelReplenishmentOrderFail(
- normalizeHttpError(mockError, new MockLoggerService())
+ tryNormalizeHttpError(mockError, new MockLoggerService())
);
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 d60a67d28af..e13e6ae996c 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 bdc21be111f..bdb50799b3d 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 { LoggerService, normalizeHttpError } from '@spartacus/core';
+import { LoggerService, tryNormalizeHttpError } from '@spartacus/core';
import { ReplenishmentOrderList } from '@spartacus/order/root';
import { cold, hot } from 'jasmine-marbles';
import { Observable, of, throwError } from 'rxjs';
@@ -75,8 +75,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({
@@ -85,7 +86,7 @@ describe('Replenishment Orders effect', () => {
});
const completion = new OrderActions.LoadUserReplenishmentOrdersFail(
- normalizeHttpError('Error', new MockLoggerService())
+ tryNormalizeHttpError(error, new MockLoggerService())
);
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 0adecd6136b..5b12c6a092d 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/consignment-tracking-by-id.reducer.spec.ts b/feature-libs/order/core/store/reducers/consignment-tracking-by-id.reducer.spec.ts
index ce76b144125..9ea316f0860 100644
--- a/feature-libs/order/core/store/reducers/consignment-tracking-by-id.reducer.spec.ts
+++ b/feature-libs/order/core/store/reducers/consignment-tracking-by-id.reducer.spec.ts
@@ -32,10 +32,11 @@ describe('Consignment Tracking By Id Reducer', () => {
describe('for LOAD_CONSIGNMENT_TRACKING_BY_ID_FAIL action', () => {
it('should return the default state', () => {
+ const error = new Error('error');
const action = new OrderActions.LoadConsignmentTrackingByIdFail({
orderCode: 'order1',
consignmentCode: 'cons1',
- error: 'there is error',
+ error,
});
const state = fromTrackingReducer.reducer(
initialStateOfConsignmentTrackingById,
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..a1e0f89d11f 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,15 @@ 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 +202,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.CREATE_B2B_USER_FAIL,
payload: {
orgCustomerId,
@@ -267,6 +269,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.UPDATE_B2B_USER_FAIL,
payload: {
orgCustomerId,
@@ -323,6 +326,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.LOAD_B2B_USER_APPROVERS_FAIL,
payload: {
orgCustomerId,
@@ -386,13 +390,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 +442,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 +499,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.LOAD_B2B_USER_PERMISSIONS_FAIL,
payload: {
orgCustomerId,
@@ -556,6 +563,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.ASSIGN_B2B_USER_PERMISSION_FAIL,
payload: {
orgCustomerId,
@@ -611,6 +619,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.UNASSIGN_B2B_USER_PERMISSION_FAIL,
payload: {
orgCustomerId,
@@ -671,6 +680,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.LOAD_B2B_USER_USER_GROUPS_FAIL,
payload: {
orgCustomerId,
@@ -734,6 +744,7 @@ describe('B2BUser Actions', () => {
});
expect({ ...action }).toEqual({
+ error,
type: B2BUserActions.ASSIGN_B2B_USER_USER_GROUP_FAIL,
payload: {
orgCustomerId,
@@ -789,6 +800,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 a6bfd8ef0ee..bc1e8ab7a2e 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,
+ ErrorAction,
+ ListModel,
+ SearchConfig,
+ StateUtils,
+} from '@spartacus/core';
import {
B2B_USER_APPROVERS,
B2B_USER_ENTITIES,
@@ -89,13 +95,18 @@ 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);
}
}
-export class LoadB2BUserFail extends StateUtils.EntityFailAction {
+export class LoadB2BUserFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_B2B_USER_FAIL;
+
constructor(public payload: { orgCustomerId: string; error: any }) {
super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error);
}
@@ -103,6 +114,7 @@ export class LoadB2BUserFail extends StateUtils.EntityFailAction {
export class LoadB2BUserSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_B2B_USER_SUCCESS;
+
constructor(public payload: B2BUser | B2BUser[]) {
super(
B2B_USER_ENTITIES,
@@ -115,13 +127,18 @@ 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);
}
}
-export class CreateB2BUserFail extends StateUtils.EntityFailAction {
+export class CreateB2BUserFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_B2B_USER_FAIL;
+
constructor(public payload: { orgCustomerId: string; error: any }) {
super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error);
}
@@ -129,6 +146,7 @@ export class CreateB2BUserFail extends StateUtils.EntityFailAction {
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 +154,7 @@ export class CreateB2BUserSuccess extends StateUtils.EntitySuccessAction {
export class UpdateB2BUser extends StateUtils.EntityLoadAction {
readonly type = UPDATE_B2B_USER;
+
constructor(
public payload: {
userId: string;
@@ -147,8 +166,12 @@ export class UpdateB2BUser extends StateUtils.EntityLoadAction {
}
}
-export class UpdateB2BUserFail extends StateUtils.EntityFailAction {
+export class UpdateB2BUserFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_B2B_USER_FAIL;
+
constructor(public payload: { orgCustomerId: string; error: any }) {
super(B2B_USER_ENTITIES, payload.orgCustomerId, payload.error);
}
@@ -156,6 +179,7 @@ export class UpdateB2BUserFail extends StateUtils.EntityFailAction {
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 +187,7 @@ export class UpdateB2BUserSuccess extends StateUtils.EntitySuccessAction {
export class LoadB2BUsers extends StateUtils.EntityLoadAction {
readonly type = LOAD_B2B_USERS;
+
constructor(
public payload: {
userId: string;
@@ -173,8 +198,12 @@ export class LoadB2BUsers extends StateUtils.EntityLoadAction {
}
}
-export class LoadB2BUsersFail extends StateUtils.EntityFailAction {
+export class LoadB2BUsersFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_B2B_USERS_FAIL;
+
constructor(public payload: { params: SearchConfig; error: any }) {
super(
USER_LIST,
@@ -186,6 +215,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 +228,7 @@ export class LoadB2BUsersSuccess extends StateUtils.EntitySuccessAction {
export class LoadB2BUserApprovers extends StateUtils.EntityLoadAction {
readonly type = LOAD_B2B_USER_APPROVERS;
+
constructor(
public payload: {
userId: string;
@@ -212,8 +243,12 @@ export class LoadB2BUserApprovers extends StateUtils.EntityLoadAction {
}
}
-export class LoadB2BUserApproversFail extends StateUtils.EntityFailAction {
+export class LoadB2BUserApproversFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_B2B_USER_APPROVERS_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -231,6 +266,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 +283,7 @@ export class LoadB2BUserApproversSuccess extends StateUtils.EntitySuccessAction
export class AssignB2BUserApprover extends StateUtils.EntityLoadAction {
readonly type = ASSIGN_B2B_USER_APPROVER;
+
constructor(
public payload: {
userId: string;
@@ -258,8 +295,12 @@ export class AssignB2BUserApprover extends StateUtils.EntityLoadAction {
}
}
-export class AssignB2BUserApproverFail extends StateUtils.EntityFailAction {
+export class AssignB2BUserApproverFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = ASSIGN_B2B_USER_APPROVER_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -267,12 +308,13 @@ export class AssignB2BUserApproverFail extends StateUtils.EntityFailAction {
error: any;
}
) {
- 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 +327,7 @@ export class AssignB2BUserApproverSuccess extends StateUtils.EntitySuccessAction
export class UnassignB2BUserApprover extends StateUtils.EntityLoadAction {
readonly type = UNASSIGN_B2B_USER_APPROVER;
+
constructor(
public payload: {
userId: string;
@@ -296,8 +339,12 @@ export class UnassignB2BUserApprover extends StateUtils.EntityLoadAction {
}
}
-export class UnassignB2BUserApproverFail extends StateUtils.EntityFailAction {
+export class UnassignB2BUserApproverFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UNASSIGN_B2B_USER_APPROVER_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -305,12 +352,13 @@ export class UnassignB2BUserApproverFail extends StateUtils.EntityFailAction {
error: any;
}
) {
- 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 +371,7 @@ export class UnassignB2BUserApproverSuccess extends StateUtils.EntitySuccessActi
export class LoadB2BUserPermissions extends StateUtils.EntityLoadAction {
readonly type = LOAD_B2B_USER_PERMISSIONS;
+
constructor(
public payload: {
userId: string;
@@ -337,8 +386,12 @@ export class LoadB2BUserPermissions extends StateUtils.EntityLoadAction {
}
}
-export class LoadB2BUserPermissionsFail extends StateUtils.EntityFailAction {
+export class LoadB2BUserPermissionsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_B2B_USER_PERMISSIONS_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -352,6 +405,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 +422,7 @@ export class LoadB2BUserPermissionsSuccess extends StateUtils.EntitySuccessActio
export class AssignB2BUserPermission extends StateUtils.EntityLoadAction {
readonly type = ASSIGN_B2B_USER_PERMISSION;
+
constructor(
public payload: {
userId: string;
@@ -379,8 +434,12 @@ export class AssignB2BUserPermission extends StateUtils.EntityLoadAction {
}
}
-export class AssignB2BUserPermissionFail extends StateUtils.EntityFailAction {
+export class AssignB2BUserPermissionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = ASSIGN_B2B_USER_PERMISSION_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -394,6 +453,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 +466,7 @@ export class AssignB2BUserPermissionSuccess extends StateUtils.EntitySuccessActi
export class UnassignB2BUserPermission extends StateUtils.EntityLoadAction {
readonly type = UNASSIGN_B2B_USER_PERMISSION;
+
constructor(
public payload: {
userId: string;
@@ -417,8 +478,12 @@ export class UnassignB2BUserPermission extends StateUtils.EntityLoadAction {
}
}
-export class UnassignB2BUserPermissionFail extends StateUtils.EntityFailAction {
+export class UnassignB2BUserPermissionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UNASSIGN_B2B_USER_PERMISSION_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -432,6 +497,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 +510,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;
@@ -458,8 +525,12 @@ export class LoadB2BUserUserGroups extends StateUtils.EntityLoadAction {
}
}
-export class LoadB2BUserUserGroupsFail extends StateUtils.EntityFailAction {
+export class LoadB2BUserUserGroupsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_B2B_USER_USER_GROUPS_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -477,6 +548,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 +565,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;
@@ -504,8 +577,12 @@ export class AssignB2BUserUserGroup extends StateUtils.EntityLoadAction {
}
}
-export class AssignB2BUserUserGroupFail extends StateUtils.EntityFailAction {
+export class AssignB2BUserUserGroupFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = ASSIGN_B2B_USER_USER_GROUP_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -519,6 +596,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 +609,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;
@@ -542,8 +621,12 @@ export class UnassignB2BUserUserGroup extends StateUtils.EntityLoadAction {
}
}
-export class UnassignB2BUserUserGroupFail extends StateUtils.EntityFailAction {
+export class UnassignB2BUserUserGroupFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UNASSIGN_B2B_USER_USER_GROUP_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -557,6 +640,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 6a80d05d986..24581c5d301 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 {
+ ErrorAction,
+ ListModel,
+ SearchConfig,
+ StateUtils,
+} from '@spartacus/core';
import { Budget } from '../../model/budget.model';
import { BUDGET_ENTITIES, BUDGET_LIST } from '../organization-state';
@@ -26,13 +31,18 @@ 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);
}
}
-export class LoadBudgetFail extends StateUtils.EntityFailAction {
+export class LoadBudgetFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_BUDGET_FAIL;
+
constructor(public payload: { budgetCode: string; error: any }) {
super(BUDGET_ENTITIES, payload.budgetCode, payload.error);
}
@@ -40,6 +50,7 @@ export class LoadBudgetFail extends StateUtils.EntityFailAction {
export class LoadBudgetSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_BUDGET_SUCCESS;
+
constructor(public payload: Budget | Budget[]) {
super(
BUDGET_ENTITIES,
@@ -52,6 +63,7 @@ export class LoadBudgetSuccess extends StateUtils.EntitySuccessAction {
export class LoadBudgets extends StateUtils.EntityLoadAction {
readonly type = LOAD_BUDGETS;
+
constructor(
public payload: {
userId: string;
@@ -62,8 +74,12 @@ export class LoadBudgets extends StateUtils.EntityLoadAction {
}
}
-export class LoadBudgetsFail extends StateUtils.EntityFailAction {
+export class LoadBudgetsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_BUDGETS_FAIL;
+
constructor(public payload: { params: SearchConfig; error: any }) {
super(
BUDGET_LIST,
@@ -75,6 +91,7 @@ export class LoadBudgetsFail extends StateUtils.EntityFailAction {
export class LoadBudgetsSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_BUDGETS_SUCCESS;
+
constructor(
public payload: {
page: ListModel;
@@ -87,13 +104,18 @@ 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);
}
}
-export class CreateBudgetFail extends StateUtils.EntityFailAction {
+export class CreateBudgetFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_BUDGET_FAIL;
+
constructor(public payload: { budgetCode: string; error: any }) {
super(BUDGET_ENTITIES, payload.budgetCode, payload.error);
}
@@ -101,6 +123,7 @@ export class CreateBudgetFail extends StateUtils.EntityFailAction {
export class CreateBudgetSuccess extends StateUtils.EntitySuccessAction {
readonly type = CREATE_BUDGET_SUCCESS;
+
constructor(public payload: Budget) {
super(BUDGET_ENTITIES, payload.code ?? null, payload);
}
@@ -108,6 +131,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 }
) {
@@ -115,8 +139,12 @@ export class UpdateBudget extends StateUtils.EntityLoadAction {
}
}
-export class UpdateBudgetFail extends StateUtils.EntityFailAction {
+export class UpdateBudgetFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_BUDGET_FAIL;
+
constructor(public payload: { budgetCode: string; error: any }) {
super(BUDGET_ENTITIES, payload.budgetCode, payload.error);
}
@@ -124,6 +152,7 @@ export class UpdateBudgetFail extends StateUtils.EntityFailAction {
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 b6396808c91..81ea3c3a4f3 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,
+ ErrorAction,
ListModel,
SearchConfig,
StateUtils,
@@ -52,13 +53,18 @@ 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);
}
}
-export class LoadCostCenterFail extends StateUtils.EntityFailAction {
+export class LoadCostCenterFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_COST_CENTER_FAIL;
+
constructor(public payload: { costCenterCode: string; error: any }) {
super(COST_CENTER_ENTITIES, payload.costCenterCode, payload.error);
}
@@ -66,6 +72,7 @@ export class LoadCostCenterFail extends StateUtils.EntityFailAction {
export class LoadCostCenterSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_COST_CENTER_SUCCESS;
+
constructor(public payload: CostCenter | CostCenter[]) {
super(
COST_CENTER_ENTITIES,
@@ -78,6 +85,7 @@ export class LoadCostCenterSuccess extends StateUtils.EntitySuccessAction {
export class LoadCostCenters extends StateUtils.EntityLoadAction {
readonly type = LOAD_COST_CENTERS;
+
constructor(
public payload: {
userId: string;
@@ -88,8 +96,12 @@ export class LoadCostCenters extends StateUtils.EntityLoadAction {
}
}
-export class LoadCostCentersFail extends StateUtils.EntityFailAction {
+export class LoadCostCentersFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_COST_CENTERS_FAIL;
+
constructor(public payload: { params: SearchConfig; error: any }) {
super(
COST_CENTER_LIST,
@@ -101,6 +113,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,13 +126,18 @@ 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);
}
}
-export class CreateCostCenterFail extends StateUtils.EntityFailAction {
+export class CreateCostCenterFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_COST_CENTER_FAIL;
+
constructor(public payload: { costCenterCode: string; error: any }) {
super(COST_CENTER_ENTITIES, payload.costCenterCode, payload.error);
}
@@ -127,6 +145,7 @@ export class CreateCostCenterFail extends StateUtils.EntityFailAction {
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 +153,7 @@ export class CreateCostCenterSuccess extends StateUtils.EntitySuccessAction {
export class UpdateCostCenter extends StateUtils.EntityLoadAction {
readonly type = UPDATE_COST_CENTER;
+
constructor(
public payload: {
userId: string;
@@ -145,8 +165,12 @@ export class UpdateCostCenter extends StateUtils.EntityLoadAction {
}
}
-export class UpdateCostCenterFail extends StateUtils.EntityFailAction {
+export class UpdateCostCenterFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_COST_CENTER_FAIL;
+
constructor(public payload: { costCenterCode: string; error: any }) {
super(COST_CENTER_ENTITIES, payload.costCenterCode, payload.error);
}
@@ -154,6 +178,7 @@ export class UpdateCostCenterFail extends StateUtils.EntityFailAction {
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 +186,7 @@ export class UpdateCostCenterSuccess extends StateUtils.EntitySuccessAction {
export class LoadAssignedBudgets extends StateUtils.EntityLoadAction {
readonly type = LOAD_ASSIGNED_BUDGETS;
+
constructor(
public payload: {
userId: string;
@@ -175,8 +201,12 @@ export class LoadAssignedBudgets extends StateUtils.EntityLoadAction {
}
}
-export class LoadAssignedBudgetsFail extends StateUtils.EntityFailAction {
+export class LoadAssignedBudgetsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ASSIGNED_BUDGETS_FAIL;
+
constructor(
public payload: {
costCenterCode: string;
@@ -194,6 +224,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 +241,7 @@ export class LoadAssignedBudgetsSuccess extends StateUtils.EntitySuccessAction {
export class AssignBudget extends StateUtils.EntityLoadAction {
readonly type = ASSIGN_BUDGET;
+
constructor(
public payload: {
userId: string;
@@ -221,8 +253,12 @@ export class AssignBudget extends StateUtils.EntityLoadAction {
}
}
-export class AssignBudgetFail extends StateUtils.EntityFailAction {
+export class AssignBudgetFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = ASSIGN_BUDGET_FAIL;
+
constructor(public payload: { budgetCode: string; error: any }) {
super(BUDGET_ENTITIES, payload.budgetCode, payload.error);
}
@@ -230,6 +266,7 @@ export class AssignBudgetFail extends StateUtils.EntityFailAction {
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 +274,7 @@ export class AssignBudgetSuccess extends StateUtils.EntitySuccessAction {
export class UnassignBudget extends StateUtils.EntityLoadAction {
readonly type = UNASSIGN_BUDGET;
+
constructor(
public payload: {
userId: string;
@@ -248,8 +286,12 @@ export class UnassignBudget extends StateUtils.EntityLoadAction {
}
}
-export class UnassignBudgetFail extends StateUtils.EntityFailAction {
+export class UnassignBudgetFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UNASSIGN_BUDGET_FAIL;
+
constructor(public payload: { budgetCode: string; error: any }) {
super(BUDGET_ENTITIES, payload.budgetCode, payload.error);
}
@@ -257,6 +299,7 @@ export class UnassignBudgetFail extends StateUtils.EntityFailAction {
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 5a7633d7f0b..b414f9eeb40 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,
+ ErrorAction,
ListModel,
SearchConfig,
StateUtils,
@@ -102,13 +103,18 @@ 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);
}
}
-export class LoadOrgUnitFail extends StateUtils.EntityFailAction {
+export class LoadOrgUnitFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORG_UNIT_FAIL;
+
constructor(public payload: { orgUnitId: string; error: any }) {
super(ORG_UNIT_ENTITIES, payload.orgUnitId, payload.error);
}
@@ -129,6 +135,7 @@ export class LoadOrgUnitSuccess extends StateUtils.EntitySuccessAction {
export class LoadOrgUnitNodes extends StateUtils.EntityLoadAction {
readonly type = LOAD_UNIT_NODES;
+
constructor(
public payload: {
userId: string;
@@ -138,8 +145,12 @@ export class LoadOrgUnitNodes extends StateUtils.EntityLoadAction {
}
}
-export class LoadOrgUnitNodesFail extends StateUtils.EntityFailAction {
+export class LoadOrgUnitNodesFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_UNIT_NODES_FAIL;
+
constructor(public payload: any) {
super(ORG_UNIT_NODE_LIST, ORG_UNIT_NODES, payload.error);
}
@@ -147,6 +158,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,13 +166,18 @@ 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);
}
}
-export class CreateUnitFail extends StateUtils.EntityFailAction {
+export class CreateUnitFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_ORG_UNIT_FAIL;
+
constructor(public payload: { unitCode: string; error: any }) {
super(ORG_UNIT_ENTITIES, payload.unitCode, payload.error);
}
@@ -168,6 +185,7 @@ export class CreateUnitFail extends StateUtils.EntityFailAction {
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 +193,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 }
) {
@@ -182,8 +201,12 @@ export class UpdateUnit extends StateUtils.EntityLoadAction {
}
}
-export class UpdateUnitFail extends StateUtils.EntityFailAction {
+export class UpdateUnitFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_ORG_UNIT_FAIL;
+
constructor(public payload: { unitCode: string; error: any }) {
super(ORG_UNIT_ENTITIES, payload.unitCode, payload.error);
}
@@ -191,6 +214,7 @@ export class UpdateUnitFail extends StateUtils.EntityFailAction {
export class UpdateUnitSuccess extends StateUtils.EntitySuccessAction {
readonly type = UPDATE_ORG_UNIT_SUCCESS;
+
constructor(public payload: B2BUnit) {
super(ORG_UNIT_ENTITIES, payload.uid ?? '', payload);
}
@@ -198,13 +222,18 @@ 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);
}
}
-export class LoadTreeFail extends StateUtils.EntityFailAction {
+export class LoadTreeFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_UNIT_TREE_FAIL;
+
constructor(public payload: { error: any }) {
super(ORG_UNIT_TREE_ENTITY, ORG_UNIT_TREE, payload.error);
}
@@ -220,13 +249,18 @@ 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);
}
}
-export class LoadApprovalProcessesFail extends StateUtils.EntityFailAction {
+export class LoadApprovalProcessesFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_APPROVAL_PROCESSES_FAIL;
+
constructor(public payload: { error: any }) {
super(
ORG_UNIT_APPROVAL_PROCESSES_ENTITIES,
@@ -246,6 +280,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 +301,7 @@ export class LoadAssignedUsers extends StateUtils.EntityLoadAction {
export class ClearAssignedUsers extends StateUtils.EntityRemoveAction {
readonly type = CLEAR_ASSIGNED_USERS;
+
constructor(
public payload: {
orgUnitId: string;
@@ -283,8 +319,12 @@ export class ClearAssignedUsers extends StateUtils.EntityRemoveAction {
}
}
-export class LoadAssignedUsersFail extends StateUtils.EntityFailAction {
+export class LoadAssignedUsersFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ASSIGNED_USERS_FAIL;
+
constructor(
public payload: {
orgUnitId: string;
@@ -306,6 +346,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 +367,7 @@ export class LoadAssignedUsersSuccess extends StateUtils.EntitySuccessAction {
export class AssignRole extends StateUtils.EntityLoadAction {
readonly type = ASSIGN_ROLE;
+
constructor(
public payload: {
userId: string;
@@ -337,8 +379,12 @@ export class AssignRole extends StateUtils.EntityLoadAction {
}
}
-export class AssignRoleFail extends StateUtils.EntityFailAction {
+export class AssignRoleFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = ASSIGN_ROLE_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -351,6 +397,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 +407,7 @@ export class AssignRoleSuccess extends StateUtils.EntitySuccessAction {
export class UnassignRole extends StateUtils.EntityLoadAction {
readonly type = UNASSIGN_ROLE;
+
constructor(
public payload: {
userId: string;
@@ -371,8 +419,12 @@ export class UnassignRole extends StateUtils.EntityLoadAction {
}
}
-export class UnassignRoleFail extends StateUtils.EntityFailAction {
+export class UnassignRoleFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UNASSIGN_ROLE_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -385,6 +437,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 +447,7 @@ export class UnassignRoleSuccess extends StateUtils.EntitySuccessAction {
export class AssignApprover extends StateUtils.EntityLoadAction {
readonly type = ASSIGN_APPROVER;
+
constructor(
public payload: {
userId: string;
@@ -406,8 +460,12 @@ export class AssignApprover extends StateUtils.EntityLoadAction {
}
}
-export class AssignApproverFail extends StateUtils.EntityFailAction {
+export class AssignApproverFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = ASSIGN_APPROVER_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -420,6 +478,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 +488,7 @@ export class AssignApproverSuccess extends StateUtils.EntitySuccessAction {
export class UnassignApprover extends StateUtils.EntityLoadAction {
readonly type = UNASSIGN_APPROVER;
+
constructor(
public payload: {
userId: string;
@@ -441,8 +501,12 @@ export class UnassignApprover extends StateUtils.EntityLoadAction {
}
}
-export class UnassignApproverFail extends StateUtils.EntityFailAction {
+export class UnassignApproverFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UNASSIGN_APPROVER_FAIL;
+
constructor(
public payload: {
orgCustomerId: string;
@@ -455,6 +519,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 +529,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 }
) {
@@ -471,8 +537,12 @@ export class CreateAddress extends StateUtils.EntityLoadAction {
}
}
-export class CreateAddressFail extends StateUtils.EntityFailAction {
+export class CreateAddressFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_ADDRESS_FAIL;
+
constructor(public payload: { addressId: string; error: any }) {
super(ADDRESS_ENTITIES, payload.addressId, payload.error);
}
@@ -480,6 +550,7 @@ export class CreateAddressFail extends StateUtils.EntityFailAction {
export class CreateAddressSuccess extends StateUtils.EntitySuccessAction {
readonly type = CREATE_ADDRESS_SUCCESS;
+
constructor(public payload: Address) {
super(ADDRESS_ENTITIES, payload.id ?? null, payload);
}
@@ -487,6 +558,7 @@ export class CreateAddressSuccess extends StateUtils.EntitySuccessAction {
export class UpdateAddress extends StateUtils.EntityLoadAction {
readonly type = UPDATE_ADDRESS;
+
constructor(
public payload: {
userId: string;
@@ -499,8 +571,12 @@ export class UpdateAddress extends StateUtils.EntityLoadAction {
}
}
-export class UpdateAddressFail extends StateUtils.EntityFailAction {
+export class UpdateAddressFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_ADDRESS_FAIL;
+
constructor(public payload: { addressId: string; error: any }) {
super(ADDRESS_ENTITIES, payload.addressId, payload.error);
}
@@ -508,6 +584,7 @@ export class UpdateAddressFail extends StateUtils.EntityFailAction {
export class UpdateAddressSuccess extends StateUtils.EntitySuccessAction {
readonly type = UPDATE_ADDRESS_SUCCESS;
+
constructor(public payload: Address) {
super(ADDRESS_ENTITIES, payload.id ?? '', payload);
}
@@ -515,6 +592,7 @@ export class UpdateAddressSuccess extends StateUtils.EntitySuccessAction {
export class DeleteAddress extends StateUtils.EntityLoadAction {
readonly type = DELETE_ADDRESS;
+
constructor(
public payload: {
userId: string;
@@ -526,8 +604,12 @@ export class DeleteAddress extends StateUtils.EntityLoadAction {
}
}
-export class DeleteAddressFail extends StateUtils.EntityFailAction {
+export class DeleteAddressFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = DELETE_ADDRESS_FAIL;
+
constructor(public payload: { addressId: string; error: any }) {
super(ADDRESS_ENTITIES, payload.addressId, payload.error);
}
@@ -535,6 +617,7 @@ export class DeleteAddressFail extends StateUtils.EntityFailAction {
export class DeleteAddressSuccess extends StateUtils.EntityRemoveAction {
readonly type = DELETE_ADDRESS_SUCCESS;
+
constructor(public payload: Address) {
super(ADDRESS_ENTITIES, payload.id ?? '');
}
@@ -542,6 +625,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,13 +638,18 @@ 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);
}
}
-export class LoadAddressesFail extends StateUtils.EntityFailAction {
+export class LoadAddressesFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ADDRESSES_FAIL;
+
constructor(public payload: { orgUnitId: string; error: any }) {
super(ADDRESS_LIST, payload.orgUnitId, payload.error);
}
@@ -568,6 +657,7 @@ export class LoadAddressesFail extends StateUtils.EntityFailAction {
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 578a25af9be..3d18979e334 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 {
+ ErrorAction,
ListModel,
OrderApprovalPermissionType,
SearchConfig,
@@ -45,13 +46,18 @@ 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);
}
}
-export class LoadPermissionFail extends StateUtils.EntityFailAction {
+export class LoadPermissionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_PERMISSION_FAIL;
+
constructor(public payload: { permissionCode: string; error: any }) {
super(PERMISSION_ENTITIES, payload.permissionCode, payload.error);
}
@@ -59,6 +65,7 @@ export class LoadPermissionFail extends StateUtils.EntityFailAction {
export class LoadPermissionSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_PERMISSION_SUCCESS;
+
constructor(public payload: Permission | Permission[]) {
super(
PERMISSION_ENTITIES,
@@ -71,6 +78,7 @@ export class LoadPermissionSuccess extends StateUtils.EntitySuccessAction {
export class LoadPermissions extends StateUtils.EntityLoadAction {
readonly type = LOAD_PERMISSIONS;
+
constructor(
public payload: {
userId: string;
@@ -81,8 +89,12 @@ export class LoadPermissions extends StateUtils.EntityLoadAction {
}
}
-export class LoadPermissionsFail extends StateUtils.EntityFailAction {
+export class LoadPermissionsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_PERMISSIONS_FAIL;
+
constructor(public payload: { params: SearchConfig; error: any }) {
super(
PERMISSION_LIST,
@@ -94,6 +106,7 @@ export class LoadPermissionsFail extends StateUtils.EntityFailAction {
export class LoadPermissionsSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_PERMISSIONS_SUCCESS;
+
constructor(
public payload: {
page: ListModel;
@@ -106,13 +119,18 @@ 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);
}
}
-export class CreatePermissionFail extends StateUtils.EntityFailAction {
+export class CreatePermissionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_PERMISSION_FAIL;
+
constructor(public payload: { permissionCode: string; error: any }) {
super(PERMISSION_ENTITIES, payload.permissionCode, payload.error);
}
@@ -120,6 +138,7 @@ export class CreatePermissionFail extends StateUtils.EntityFailAction {
export class CreatePermissionSuccess extends StateUtils.EntitySuccessAction {
readonly type = CREATE_PERMISSION_SUCCESS;
+
constructor(public payload: Permission) {
super(PERMISSION_ENTITIES, payload.code ?? null, payload);
}
@@ -127,6 +146,7 @@ export class CreatePermissionSuccess extends StateUtils.EntitySuccessAction {
export class UpdatePermission extends StateUtils.EntityLoadAction {
readonly type = UPDATE_PERMISSION;
+
constructor(
public payload: {
userId: string;
@@ -138,8 +158,12 @@ export class UpdatePermission extends StateUtils.EntityLoadAction {
}
}
-export class UpdatePermissionFail extends StateUtils.EntityFailAction {
+export class UpdatePermissionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_PERMISSION_FAIL;
+
constructor(public payload: { permissionCode: string; error: any }) {
super(PERMISSION_ENTITIES, payload.permissionCode, payload.error);
}
@@ -147,6 +171,7 @@ export class UpdatePermissionFail extends StateUtils.EntityFailAction {
export class UpdatePermissionSuccess extends StateUtils.EntitySuccessAction {
readonly type = UPDATE_PERMISSION_SUCCESS;
+
constructor(public payload: Permission) {
super(PERMISSION_ENTITIES, payload.code ?? '', payload);
}
@@ -154,13 +179,18 @@ export class UpdatePermissionSuccess extends StateUtils.EntitySuccessAction {
export class LoadPermissionTypes extends StateUtils.EntityLoadAction {
readonly type = LOAD_PERMISSION_TYPES;
+
constructor() {
super(PERMISSION_TYPES_LIST, PERMISSION_TYPES);
}
}
-export class LoadPermissionTypesFail extends StateUtils.EntityFailAction {
+export class LoadPermissionTypesFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_PERMISSION_TYPES_FAIL;
+
constructor(public payload: any) {
super(PERMISSION_TYPES_LIST, PERMISSION_TYPES, payload.error);
}
@@ -168,6 +198,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 e7575e891c0..5de3ac85520 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 {
+ ErrorAction,
+ ListModel,
+ SearchConfig,
+ StateUtils,
+} from '@spartacus/core';
import { UserGroup } from '../../model/user-group.model';
import {
B2B_USER_ENTITIES,
@@ -80,13 +85,18 @@ 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);
}
}
-export class LoadUserGroupFail extends StateUtils.EntityFailAction {
+export class LoadUserGroupFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_GROUP_FAIL;
+
constructor(public payload: { userGroupId: string; error: any }) {
super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error);
}
@@ -94,6 +104,7 @@ export class LoadUserGroupFail extends StateUtils.EntityFailAction {
export class LoadUserGroupSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_USER_GROUP_SUCCESS;
+
constructor(public payload: UserGroup | UserGroup[]) {
super(
USER_GROUP_ENTITIES,
@@ -106,6 +117,7 @@ export class LoadUserGroupSuccess extends StateUtils.EntitySuccessAction {
export class LoadUserGroups extends StateUtils.EntityLoadAction {
readonly type = LOAD_USER_GROUPS;
+
constructor(
public payload: {
userId: string;
@@ -116,8 +128,12 @@ export class LoadUserGroups extends StateUtils.EntityLoadAction {
}
}
-export class LoadUserGroupsFail extends StateUtils.EntityFailAction {
+export class LoadUserGroupsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_GROUPS_FAIL;
+
constructor(public payload: { params: SearchConfig; error: any }) {
super(
USER_GROUP_LIST,
@@ -129,6 +145,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 +158,7 @@ export class LoadUserGroupsSuccess extends StateUtils.EntitySuccessAction {
export class LoadPermissions extends StateUtils.EntityLoadAction {
readonly type = LOAD_USER_GROUP_PERMISSIONS;
+
constructor(
public payload: {
userId: string;
@@ -155,8 +173,12 @@ export class LoadPermissions extends StateUtils.EntityLoadAction {
}
}
-export class LoadPermissionsFail extends StateUtils.EntityFailAction {
+export class LoadPermissionsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_GROUP_PERMISSIONS_FAIL;
+
constructor(
public payload: {
userGroupId: string;
@@ -174,6 +196,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 +213,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;
@@ -204,8 +228,12 @@ export class LoadAvailableOrgCustomers extends StateUtils.EntityLoadAction {
}
}
-export class LoadAvailableOrgCustomersFail extends StateUtils.EntityFailAction {
+export class LoadAvailableOrgCustomersFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_GROUP_AVAILABLE_CUSTOMERS_FAIL;
+
constructor(
public payload: {
userGroupId: string;
@@ -223,6 +251,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,13 +268,18 @@ 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);
}
}
-export class CreateUserGroupFail extends StateUtils.EntityFailAction {
+export class CreateUserGroupFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_USER_GROUP_FAIL;
+
constructor(public payload: { userGroupId: string; error: any }) {
super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error);
}
@@ -253,6 +287,7 @@ export class CreateUserGroupFail extends StateUtils.EntityFailAction {
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 +295,7 @@ export class CreateUserGroupSuccess extends StateUtils.EntitySuccessAction {
export class AssignMember extends StateUtils.EntityLoadAction {
readonly type = USER_GROUP_ASSIGN_MEMBER;
+
constructor(
public payload: {
userId: string;
@@ -271,8 +307,12 @@ export class AssignMember extends StateUtils.EntityLoadAction {
}
}
-export class AssignMemberFail extends StateUtils.EntityFailAction {
+export class AssignMemberFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = USER_GROUP_ASSIGN_MEMBER_FAIL;
+
constructor(
public payload: {
userGroupId: string;
@@ -286,6 +326,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 +334,7 @@ export class AssignMemberSuccess extends StateUtils.EntitySuccessAction {
export class AssignPermission extends StateUtils.EntityLoadAction {
readonly type = USER_GROUP_ASSIGN_PERMISSION;
+
constructor(
public payload: {
userId: string;
@@ -304,8 +346,12 @@ export class AssignPermission extends StateUtils.EntityLoadAction {
}
}
-export class AssignPermissionFail extends StateUtils.EntityFailAction {
+export class AssignPermissionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = USER_GROUP_ASSIGN_PERMISSION_FAIL;
+
constructor(
public payload: {
userGroupId: string;
@@ -319,6 +365,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 +373,7 @@ export class AssignPermissionSuccess extends StateUtils.EntitySuccessAction {
export class UpdateUserGroup extends StateUtils.EntityLoadAction {
readonly type = UPDATE_USER_GROUP;
+
constructor(
public payload: {
userId: string;
@@ -337,8 +385,12 @@ export class UpdateUserGroup extends StateUtils.EntityLoadAction {
}
}
-export class UpdateUserGroupFail extends StateUtils.EntityFailAction {
+export class UpdateUserGroupFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_USER_GROUP_FAIL;
+
constructor(public payload: { userGroupId: string; error: any }) {
super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error);
}
@@ -346,6 +398,7 @@ export class UpdateUserGroupFail extends StateUtils.EntityFailAction {
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 +406,7 @@ export class UpdateUserGroupSuccess extends StateUtils.EntitySuccessAction {
export class DeleteUserGroup extends StateUtils.EntityLoadAction {
readonly type = DELETE_USER_GROUP;
+
constructor(
public payload: {
userId: string;
@@ -363,8 +417,12 @@ export class DeleteUserGroup extends StateUtils.EntityLoadAction {
}
}
-export class DeleteUserGroupFail extends StateUtils.EntityFailAction {
+export class DeleteUserGroupFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = DELETE_USER_GROUP_FAIL;
+
constructor(public payload: { userGroupId: string; error: any }) {
super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error);
}
@@ -372,6 +430,7 @@ export class DeleteUserGroupFail extends StateUtils.EntityFailAction {
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 +438,7 @@ export class DeleteUserGroupSuccess extends StateUtils.EntitySuccessAction {
export class UnassignMember extends StateUtils.EntityLoadAction {
readonly type = USER_GROUP_UNASSIGN_MEMBER;
+
constructor(
public payload: {
userId: string;
@@ -390,8 +450,12 @@ export class UnassignMember extends StateUtils.EntityLoadAction {
}
}
-export class UnassignMemberFail extends StateUtils.EntityFailAction {
+export class UnassignMemberFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = USER_GROUP_UNASSIGN_MEMBER_FAIL;
+
constructor(
public payload: {
userGroupId: string;
@@ -405,6 +469,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 +477,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;
@@ -422,8 +488,12 @@ export class UnassignAllMembers extends StateUtils.EntityLoadAction {
}
}
-export class UnassignAllMembersFail extends StateUtils.EntityFailAction {
+export class UnassignAllMembersFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = USER_GROUP_UNASSIGN_ALL_MEMBERS_FAIL;
+
constructor(public payload: { userGroupId: string; error: any }) {
super(USER_GROUP_ENTITIES, payload.userGroupId, payload.error);
}
@@ -431,6 +501,7 @@ export class UnassignAllMembersFail extends StateUtils.EntityFailAction {
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 +509,7 @@ export class UnassignAllMembersSuccess extends StateUtils.EntitySuccessAction {
export class UnassignPermission extends StateUtils.EntityLoadAction {
readonly type = USER_GROUP_UNASSIGN_PERMISSION;
+
constructor(
public payload: {
userId: string;
@@ -449,8 +521,12 @@ export class UnassignPermission extends StateUtils.EntityLoadAction {
}
}
-export class UnassignPermissionFail extends StateUtils.EntityFailAction {
+export class UnassignPermissionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = USER_GROUP_UNASSIGN_PERMISSION_FAIL;
+
constructor(
public payload: {
userGroupId: string;
@@ -464,6 +540,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 3838aa94467..eee2149aaec 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
@@ -19,7 +19,7 @@ import {
StateUtils,
User,
UserIdService,
- normalizeHttpError,
+ tryNormalizeHttpError,
} from '@spartacus/core';
import { UserAccountFacade } from '@spartacus/user/account/root';
import { Observable, from, of } from 'rxjs';
@@ -68,7 +68,7 @@ export class B2BUserEffects {
of(
new B2BUserActions.LoadB2BUserFail({
orgCustomerId,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -130,7 +130,7 @@ export class B2BUserEffects {
from([
new B2BUserActions.CreateB2BUserFail({
orgCustomerId: orgCustomer.customerId ?? '',
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
}),
new OrganizationActions.OrganizationClearData(),
])
@@ -175,7 +175,7 @@ export class B2BUserEffects {
from([
new B2BUserActions.UpdateB2BUserFail({
orgCustomerId: orgCustomer.customerId ?? '',
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
}),
new OrganizationActions.OrganizationClearData(),
])
@@ -241,7 +241,7 @@ export class B2BUserEffects {
of(
new B2BUserActions.LoadB2BUsersFail({
params: payload.params,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -290,7 +290,7 @@ export class B2BUserEffects {
new B2BUserActions.LoadB2BUserApproversFail({
orgCustomerId: payload.orgCustomerId,
params: payload.params,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -341,7 +341,7 @@ export class B2BUserEffects {
new B2BUserActions.LoadB2BUserPermissionsFail({
orgCustomerId: payload.orgCustomerId,
params: payload.params,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -392,7 +392,7 @@ export class B2BUserEffects {
new B2BUserActions.LoadB2BUserUserGroupsFail({
orgCustomerId: payload.orgCustomerId,
params: payload.params,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -433,7 +433,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(),
])
@@ -473,7 +473,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(),
])
@@ -511,7 +511,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(),
])
@@ -549,7 +549,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(),
])
@@ -587,7 +587,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(),
])
@@ -633,7 +633,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 7fe42acea1b..3148e6fb872 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 fc41ba9919b..5ca9c333e95 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 f4b1b2865f8..654cfef2ac6 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,
@@ -15,16 +15,16 @@ import {
FeatureConfigService,
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()
@@ -62,7 +62,7 @@ export class OrgUnitEffects {
of(
new OrgUnitActions.LoadOrgUnitFail({
orgUnitId,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -86,7 +86,7 @@ export class OrgUnitEffects {
catchError((error: HttpErrorResponse) =>
of(
new OrgUnitActions.LoadOrgUnitNodesFail({
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -113,7 +113,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(),
])
@@ -144,7 +144,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(),
])
@@ -169,7 +169,7 @@ export class OrgUnitEffects {
catchError((error: HttpErrorResponse) =>
of(
new OrgUnitActions.LoadTreeFail({
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -194,7 +194,7 @@ export class OrgUnitEffects {
catchError((error: HttpErrorResponse) =>
of(
new OrgUnitActions.LoadApprovalProcessesFail({
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -241,7 +241,7 @@ export class OrgUnitEffects {
orgUnitId,
roleId,
params,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -272,7 +272,7 @@ export class OrgUnitEffects {
of(
new OrgUnitActions.AssignRoleFail({
orgCustomerId,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -301,7 +301,7 @@ export class OrgUnitEffects {
of(
new OrgUnitActions.UnassignRoleFail({
orgCustomerId,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
})
)
)
@@ -334,7 +334,7 @@ export class OrgUnitEffects {
from([
new OrgUnitActions.AssignApproverFail({
orgCustomerId,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
}),
new OrganizationActions.OrganizationClearData(),
])
@@ -368,7 +368,7 @@ export class OrgUnitEffects {
from([
new OrgUnitActions.UnassignApproverFail({
orgCustomerId,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
}),
new OrganizationActions.OrganizationClearData(),
])
@@ -406,7 +406,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(),
])
@@ -438,7 +438,7 @@ export class OrgUnitEffects {
from([
new OrgUnitActions.UpdateAddressFail({
addressId: address.id ?? '',
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
}),
new OrganizationActions.OrganizationClearData(),
])
@@ -470,7 +470,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 1eddc2a6291..3afc85849a4 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 a5c2f0dd3f2..1c7f01ef09e 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 425f05707f1..1e114bc14ac 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 {
+ ErrorAction,
ListModel,
PROCESS_FEATURE,
SearchConfig,
@@ -42,13 +43,18 @@ 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);
}
}
-export class LoadOrderApprovalFail extends StateUtils.EntityFailAction {
+export class LoadOrderApprovalFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORDER_APPROVAL_FAIL;
+
constructor(public payload: { orderApprovalCode: string; error: any }) {
super(ORDER_APPROVAL_ENTITIES, payload.orderApprovalCode, payload.error);
}
@@ -56,6 +62,7 @@ export class LoadOrderApprovalFail extends StateUtils.EntityFailAction {
export class LoadOrderApprovalSuccess extends StateUtils.EntitySuccessAction {
readonly type = LOAD_ORDER_APPROVAL_SUCCESS;
+
constructor(public payload: OrderApproval | OrderApproval[]) {
super(
ORDER_APPROVAL_ENTITIES,
@@ -68,6 +75,7 @@ export class LoadOrderApprovalSuccess extends StateUtils.EntitySuccessAction {
export class LoadOrderApprovals extends StateUtils.EntityLoadAction {
readonly type = LOAD_ORDER_APPROVALS;
+
constructor(
public payload: {
userId: string;
@@ -81,8 +89,12 @@ export class LoadOrderApprovals extends StateUtils.EntityLoadAction {
}
}
-export class LoadOrderApprovalsFail extends StateUtils.EntityFailAction {
+export class LoadOrderApprovalsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORDER_APPROVALS_FAIL;
+
constructor(public payload: { params: SearchConfig; error: any }) {
super(
ORDER_APPROVAL_LIST,
@@ -94,6 +106,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 +122,7 @@ export class LoadOrderApprovalsSuccess extends StateUtils.EntitySuccessAction {
export class MakeDecision extends StateUtils.EntityLoadAction {
readonly type = MAKE_DECISION;
+
constructor(
public payload: {
userId: string;
@@ -120,15 +134,24 @@ export class MakeDecision extends StateUtils.EntityLoadAction {
}
}
-export class MakeDecisionFail extends StateUtils.EntityFailAction {
+export class MakeDecisionFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = MAKE_DECISION_FAIL;
+
constructor(public payload: { orderApprovalCode: string; error: any }) {
- super(PROCESS_FEATURE, ORDER_APPROVAL_MAKE_DECISION_PROCESS_ID, payload);
+ 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 +164,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 d24a246894c..ffbb140c317 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..9b3b7a53afe 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,13 @@ 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 +98,13 @@ 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 276ba1bb4e8..b66988f6f04 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,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { StateUtils } from '@spartacus/core';
+import { ErrorAction, StateUtils } from '@spartacus/core';
import { Order, OrderHistoryList } from '@spartacus/order/root';
import { UNIT_ORDERS, UNIT_ORDER_DETAILS } from '../unit-order-state';
@@ -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;
@@ -35,8 +36,12 @@ export class LoadUnitOrders extends StateUtils.LoaderLoadAction {
}
}
-export class LoadUnitOrdersFail extends StateUtils.LoaderFailAction {
+export class LoadUnitOrdersFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_UNIT_ORDERS_FAIL;
+
constructor(public payload: any) {
super(UNIT_ORDERS, payload);
}
@@ -44,6 +49,7 @@ export class LoadUnitOrdersFail extends StateUtils.LoaderFailAction {
export class LoadUnitOrdersSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_UNIT_ORDERS_SUCCESS;
+
constructor(public payload?: OrderHistoryList) {
super(UNIT_ORDERS);
}
@@ -51,6 +57,7 @@ export class LoadUnitOrdersSuccess extends StateUtils.LoaderSuccessAction {
export class ClearUnitOrders extends StateUtils.LoaderResetAction {
readonly type = CLEAR_UNIT_ORDERS;
+
constructor() {
super(UNIT_ORDERS);
}
@@ -58,6 +65,7 @@ export class ClearUnitOrders extends StateUtils.LoaderResetAction {
export class LoadOrderDetails extends StateUtils.LoaderLoadAction {
readonly type = LOAD_ORDER_DETAILS;
+
constructor(
public payload: {
userId: string;
@@ -68,8 +76,12 @@ export class LoadOrderDetails extends StateUtils.LoaderLoadAction {
}
}
-export class LoadOrderDetailsFail extends StateUtils.LoaderFailAction {
+export class LoadOrderDetailsFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ORDER_DETAILS_FAIL;
+
constructor(public payload: any) {
super(UNIT_ORDER_DETAILS, payload);
}
@@ -77,6 +89,7 @@ export class LoadOrderDetailsFail extends StateUtils.LoaderFailAction {
export class LoadOrderDetailsSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_ORDER_DETAILS_SUCCESS;
+
constructor(public payload: Order) {
super(UNIT_ORDER_DETAILS);
}
@@ -84,6 +97,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 8c9ae636375..f38f80c58a2 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
@@ -4,7 +4,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';
@@ -89,7 +89,7 @@ describe('Orders effect', () => {
});
const completion = new UnitOrderActions.LoadUnitOrdersFail(
- normalizeHttpError(mockError, new MockLoggerService())
+ tryNormalizeHttpError(mockError, new MockLoggerService())
);
actions$ = hot('-a', { a: action });
@@ -135,7 +135,7 @@ describe('Orders effect', () => {
});
it('should handle failures for load order details', () => {
- const mockNormalizedError = normalizeHttpError(
+ const mockNormalizedError = tryNormalizeHttpError(
mockError,
new MockLoggerService()
);
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 970c5402628..498d8e3edd8 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 2c1d48056b3..b31c7fc0e55 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,6 +5,7 @@
*/
import { createAction, props } from '@ngrx/store';
+import { TypedAction } from '@ngrx/store/src/models';
import { PointOfService } from '@spartacus/core';
import {
AugmentedPointOfService,
@@ -80,8 +81,23 @@ export const SetStoreDetailsSuccess = createAction(
STORE_DETAILS_SUCCESS,
props<{ payload: PointOfService }>()
);
-
-export const SetStoreDetailsFailure = createAction(
+// The `error` property was added later to the return type,
+// to be recognizable by `CxErrorHandlerEffect` (which expects
+// `error` property in every fail action).
+// However, we keep the original property `payload` as the only
+// one required _input props_ parameter, to avoid introducing a breaking change.
+const _SetStoreDetailsFailure = createAction(
STORE_DETAILS_FAIL,
props<{ payload: any }>()
);
+export const SetStoreDetailsFailure = ($props: {
+ payload: any;
+}): {
+ payload: any;
+ error: any;
+} & TypedAction => {
+ return {
+ ..._SetStoreDetailsFailure($props),
+ error: $props.payload,
+ };
+};
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..1dceb0a06a1 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
@@ -1,15 +1,15 @@
import { STOCK_DATA } from '../stock-state';
import {
- ClearStockData,
CLEAR_STOCK_DATA,
- StockLevel,
- StockLevelFail,
- StockLevelOnHold,
- StockLevelSuccess,
+ ClearStockData,
STOCK_LEVEL,
STOCK_LEVEL_FAIL,
STOCK_LEVEL_ON_HOLD,
STOCK_LEVEL_SUCCESS,
+ StockLevel,
+ StockLevelFail,
+ StockLevelOnHold,
+ StockLevelSuccess,
} from './stock.action';
describe('[Stock] Actions', () => {
@@ -60,11 +60,13 @@ describe('[Stock] Actions', () => {
},
},
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 7c40ed7d7fd..09f62b1dcb1 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 {
+ ErrorAction,
+ 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,13 +34,18 @@ export class StockLevel extends StateUtils.LoaderLoadAction {
export class StockLevelOnHold extends StateUtils.LoaderLoadAction {
readonly type = STOCK_LEVEL_ON_HOLD;
+
constructor() {
super(STOCK_DATA);
}
}
-export class StockLevelFail extends StateUtils.LoaderFailAction {
+export class StockLevelFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = STOCK_LEVEL_FAIL;
+
constructor(public payload: any) {
super(STOCK_DATA, payload);
}
@@ -47,6 +58,7 @@ export type StockLevelSuccessPayload = {
export class StockLevelSuccess extends StateUtils.LoaderSuccessAction {
readonly type = STOCK_LEVEL_SUCCESS;
+
constructor(public payload: StockLevelSuccessPayload) {
super(STOCK_DATA);
}
@@ -54,6 +66,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 1f5668dacc9..b57082c9a7d 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
@@ -3,7 +3,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { StoreModule } from '@ngrx/store';
-import { normalizeHttpError } from '@spartacus/core';
+import { tryNormalizeHttpError } from '@spartacus/core';
import { cold, hot } from 'jasmine-marbles';
import { Observable } from 'rxjs';
@@ -91,7 +91,7 @@ describe('PickupLocationEffect with Error', () => {
const error = new HttpErrorResponse({ error: 'error' });
const actionFailure = SetStoreDetailsFailure({
- payload: normalizeHttpError(error, new MockLoggerService()),
+ payload: tryNormalizeHttpError(error, new MockLoggerService()),
});
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 c6662e42589..b252fc9876d 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),
+ payload: 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 f84c2eed5a4..ab807e64cda 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 400ad7d0f5e..47239dd1906 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 { ErrorAction, StateUtils } from '@spartacus/core';
import { CommonConfigurator } from '@spartacus/product-configurator/common';
import { Configurator } from '../../model/configurator.model';
import { CONFIGURATOR_DATA } from '../configurator-state';
@@ -52,7 +52,10 @@ export class ReadCartEntryConfigurationSuccess extends StateUtils.EntitySuccessA
}
}
-export class ReadCartEntryConfigurationFail extends StateUtils.EntityFailAction {
+export class ReadCartEntryConfigurationFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = READ_CART_ENTRY_CONFIGURATION_FAIL;
constructor(public payload: { ownerKey: string; error: any }) {
super(CONFIGURATOR_DATA, payload.ownerKey, payload.error);
@@ -75,7 +78,10 @@ export class ReadOrderEntryConfigurationSuccess extends StateUtils.EntitySuccess
}
}
-export class ReadOrderEntryConfigurationFail extends StateUtils.EntityFailAction {
+export class ReadOrderEntryConfigurationFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = READ_ORDER_ENTRY_CONFIGURATION_FAIL;
constructor(public payload: { ownerKey: string; error: any }) {
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 43cc958c133..8693978d3b4 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 { ErrorAction, StateUtils } from '@spartacus/core';
import { Configurator } from '../../model/configurator.model';
import { CONFIGURATOR_DATA } from '../configurator-state';
@@ -19,7 +19,10 @@ export class SearchVariants extends StateUtils.EntityLoadAction {
}
}
-export class SearchVariantsFail extends StateUtils.EntityFailAction {
+export class SearchVariantsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = SEARCH_VARIANTS_FAIL;
constructor(public payload: { ownerKey: string; error: any }) {
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 40e4d117165..579a9ed9e56 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, 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;
@@ -83,8 +84,12 @@ export class CreateConfiguration extends StateUtils.EntityLoadAction {
}
}
-export class CreateConfigurationFail extends StateUtils.EntityFailAction {
+export class CreateConfigurationFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = CREATE_CONFIGURATION_FAIL;
+
constructor(
public payload: {
ownerKey: string;
@@ -97,6 +102,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 +110,7 @@ export class CreateConfigurationSuccess extends StateUtils.EntitySuccessAction {
export class ReadConfiguration extends StateUtils.EntityLoadAction {
readonly type = READ_CONFIGURATION;
+
constructor(
public payload: {
configuration: Configurator.Configuration;
@@ -114,8 +121,12 @@ export class ReadConfiguration extends StateUtils.EntityLoadAction {
}
}
-export class ReadConfigurationFail extends StateUtils.EntityFailAction {
+export class ReadConfigurationFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = READ_CONFIGURATION_FAIL;
+
constructor(public payload: { ownerKey: string; error: any }) {
super(CONFIGURATOR_DATA, payload.ownerKey, payload.error);
}
@@ -123,6 +134,7 @@ export class ReadConfigurationFail extends StateUtils.EntityFailAction {
export class ReadConfigurationSuccess extends StateUtils.EntitySuccessAction {
readonly type = READ_CONFIGURATION_SUCCESS;
+
constructor(public payload: Configurator.Configuration) {
super(CONFIGURATOR_DATA, payload.owner.key);
}
@@ -130,6 +142,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 +151,13 @@ export class UpdateConfiguration extends StateUtils.EntityProcessesIncrementActi
}
}
-export class UpdateConfigurationFail extends StateUtils.EntityProcessesDecrementAction {
+export class UpdateConfigurationFail
+ extends StateUtils.EntityProcessesDecrementAction
+ implements ErrorAction
+{
+ public error: any;
readonly type = UPDATE_CONFIGURATION_FAIL;
+
constructor(
public payload: { configuration: Configurator.Configuration; error: any }
) {
@@ -147,11 +165,13 @@ export class UpdateConfigurationFail extends StateUtils.EntityProcessesDecrement
this.meta.loader = {
error: payload.error,
};
+ this.error = payload.error;
}
}
export class UpdateConfigurationSuccess extends StateUtils.EntityProcessesDecrementAction {
readonly type = UPDATE_CONFIGURATION_SUCCESS;
+
constructor(public payload: Configurator.Configuration) {
super(CONFIGURATOR_DATA, payload.owner.key);
}
@@ -159,13 +179,18 @@ 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);
}
}
-export class UpdateConfigurationFinalizeFail extends StateUtils.EntityFailAction {
+export class UpdateConfigurationFinalizeFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_CONFIGURATION_FINALIZE_FAIL;
+
constructor(public payload: Configurator.Configuration) {
super(CONFIGURATOR_DATA, payload.owner.key);
}
@@ -173,12 +198,18 @@ export class UpdateConfigurationFinalizeFail extends StateUtils.EntityFailAction
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 {
+
+export class UpdatePriceSummaryFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_PRICE_SUMMARY_FAIL;
+
constructor(public payload: { ownerKey: string; error: any }) {
super(CONFIGURATOR_DATA, payload.ownerKey, payload.error);
}
@@ -211,6 +242,7 @@ export class UpdatePriceSummarySuccess extends StateUtils.EntitySuccessAction {
export class ChangeGroup extends StateUtils.EntityLoadAction {
readonly type = CHANGE_GROUP;
+
constructor(
public payload: {
configuration: Configurator.Configuration;
@@ -228,6 +260,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);
}
@@ -235,6 +268,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);
}
@@ -242,13 +276,18 @@ 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);
}
}
-export class GetConfigurationOverviewFail extends StateUtils.EntityFailAction {
+export class GetConfigurationOverviewFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = GET_CONFIGURATION_OVERVIEW_FAIL;
+
constructor(public payload: { ownerKey: string; error: any }) {
super(CONFIGURATOR_DATA, payload.ownerKey, payload.error);
}
@@ -256,6 +295,7 @@ export class GetConfigurationOverviewFail extends StateUtils.EntityFailAction {
export class GetConfigurationOverviewSuccess extends StateUtils.EntitySuccessAction {
readonly type = GET_CONFIGURATION_OVERVIEW_SUCCESS;
+
constructor(
public payload: { ownerKey: string; overview: Configurator.Overview }
) {
@@ -265,13 +305,18 @@ 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);
}
}
-export class UpdateConfigurationOverviewFail extends StateUtils.EntityFailAction {
+export class UpdateConfigurationOverviewFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_CONFIGURATION_OVERVIEW_FAIL;
+
constructor(public payload: { ownerKey: string; error: any }) {
super(CONFIGURATOR_DATA, payload.ownerKey, payload.error);
}
@@ -279,6 +324,7 @@ export class UpdateConfigurationOverviewFail extends StateUtils.EntityFailAction
export class UpdateConfigurationOverviewSuccess extends StateUtils.EntitySuccessAction {
readonly type = UPDATE_CONFIGURATION_OVERVIEW_SUCCESS;
+
constructor(
public payload: { ownerKey: string; overview: Configurator.Overview }
) {
@@ -327,6 +373,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);
}
@@ -334,6 +381,7 @@ export class SetGroupsVisited extends StateUtils.EntitySuccessAction {
export class RemoveProductBoundConfigurations implements Action {
readonly type = REMOVE_PRODUCT_BOUND_CONFIGURATIONS;
+
constructor() {
// Intentional Empty Constructor
}
@@ -341,6 +389,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);
}
@@ -348,6 +397,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 6bc92f3a291..49d660d16a4 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
@@ -10,7 +10,7 @@ import { Store, select } from '@ngrx/store';
import {
FeatureConfigService,
LoggerService,
- normalizeHttpError,
+ tryNormalizeHttpError,
} from '@spartacus/core';
import {
CommonConfigurator,
@@ -86,7 +86,7 @@ export class ConfiguratorBasicEffects {
catchError((error) => [
new ConfiguratorActions.CreateConfigurationFail({
ownerKey: action.payload.owner.key,
- error: normalizeHttpError(error, this.logger),
+ error: tryNormalizeHttpError(error, this.logger),
}),
])
);
@@ -115,7 +115,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),
}),
])
);
@@ -147,7 +147,7 @@ export class ConfiguratorBasicEffects {
});
}),
catchError((error) => {
- const errorPayload = normalizeHttpError(error, this.logger);
+ const errorPayload = tryNormalizeHttpError(error, this.logger);
return [
new ConfiguratorActions.UpdateConfigurationFail({
configuration: payload,
@@ -184,7 +184,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,
@@ -217,7 +217,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,
@@ -253,7 +253,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,
@@ -447,7 +447,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 9e04eb09d5d..3993e512f10 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 224fea6a25a..a2e8f61aa7a 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, map, 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 9289a983dd0..aa24e9c4009 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 { ErrorAction, 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 }
) {
@@ -48,8 +49,12 @@ export class CreateConfiguration extends StateUtils.LoaderLoadAction {
}
}
-export class CreateConfigurationFail extends StateUtils.LoaderFailAction {
+export class CreateConfigurationFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = CREATE_CONFIGURATION_FAIL;
+
constructor(public payload: any) {
super(CONFIGURATION_TEXTFIELD_DATA, payload);
}
@@ -57,6 +62,7 @@ export class CreateConfigurationFail extends StateUtils.LoaderFailAction {
export class CreateConfigurationSuccess extends StateUtils.LoaderSuccessAction {
readonly type = CREATE_CONFIGURATION_SUCCESS;
+
constructor(public payload: ConfiguratorTextfield.Configuration) {
super(CONFIGURATION_TEXTFIELD_DATA);
}
@@ -64,6 +70,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,13 +78,18 @@ 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);
}
}
-export class AddToCartFail extends StateUtils.LoaderFailAction {
+export class AddToCartFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = ADD_TO_CART_FAIL;
+
constructor(public payload: any) {
super(CONFIGURATION_TEXTFIELD_DATA, payload);
}
@@ -85,13 +97,18 @@ export class AddToCartFail extends StateUtils.LoaderFailAction {
export class UpdateCartEntryConfiguration extends StateUtils.LoaderLoadAction {
readonly type = UPDATE_CART_ENTRY_CONFIGURATION;
+
constructor(public payload: ConfiguratorTextfield.UpdateCartEntryParameters) {
super(CONFIGURATION_TEXTFIELD_DATA);
}
}
-export class UpdateCartEntryConfigurationFail extends StateUtils.LoaderFailAction {
+export class UpdateCartEntryConfigurationFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_CART_ENTRY_CONFIGURATION_FAIL;
+
constructor(public payload: any) {
super(CONFIGURATION_TEXTFIELD_DATA, payload);
}
@@ -99,6 +116,7 @@ export class UpdateCartEntryConfigurationFail extends StateUtils.LoaderFailActio
export class ReadCartEntryConfiguration extends StateUtils.LoaderLoadAction {
readonly type = READ_CART_ENTRY_CONFIGURATION;
+
constructor(
public payload: CommonConfigurator.ReadConfigurationFromCartEntryParameters
) {
@@ -108,13 +126,18 @@ 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);
}
}
-export class ReadCartEntryConfigurationFail extends StateUtils.LoaderFailAction {
+export class ReadCartEntryConfigurationFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = READ_CART_ENTRY_CONFIGURATION_FAIL;
+
constructor(public payload: any) {
super(CONFIGURATION_TEXTFIELD_DATA, payload);
}
@@ -122,6 +145,7 @@ export class ReadCartEntryConfigurationFail extends StateUtils.LoaderFailAction
export class ReadOrderEntryConfiguration extends StateUtils.LoaderLoadAction {
readonly type = READ_ORDER_ENTRY_CONFIGURATION;
+
constructor(
public payload: CommonConfigurator.ReadConfigurationFromOrderEntryParameters
) {
@@ -131,13 +155,18 @@ 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);
}
}
-export class ReadOrderEntryConfigurationFail extends StateUtils.LoaderFailAction {
+export class ReadOrderEntryConfigurationFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = READ_ORDER_ENTRY_CONFIGURATION_FAIL;
+
constructor(public payload: any) {
super(CONFIGURATION_TEXTFIELD_DATA, payload);
}
@@ -145,6 +174,7 @@ export class ReadOrderEntryConfigurationFail extends StateUtils.LoaderFailAction
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 69642535624..cdd9a19dfb8 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)
),
])
);
@@ -169,7 +169,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..72b696059f1 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
@@ -1,6 +1,6 @@
+import { GeoPoint, SearchConfig, StateUtils } from '@spartacus/core';
import { STORE_FINDER_DATA } from '../store-finder-state';
import { StoreFinderActions } from './index';
-import { GeoPoint, SearchConfig, StateUtils } from '@spartacus/core';
describe('Find Stores Actions', () => {
describe('OnHold', () => {
@@ -50,13 +50,14 @@ 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),
+ payload: error,
+ error,
+ meta: StateUtils.failMeta(STORE_FINDER_DATA, error),
});
});
});
@@ -92,15 +93,16 @@ 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),
+ payload: error,
+ 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 2b79993dd1d..e618ed5a632 100644
--- a/feature-libs/storefinder/core/store/actions/find-stores.action.ts
+++ b/feature-libs/storefinder/core/store/actions/find-stores.action.ts
@@ -4,8 +4,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import {
+ ErrorAction,
+ GeoPoint,
+ SearchConfig,
+ StateUtils,
+} from '@spartacus/core';
import { STORE_FINDER_DATA } from '../store-finder-state';
-import { 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;
@@ -40,8 +47,12 @@ export class FindStores extends StateUtils.LoaderLoadAction {
}
}
-export class FindStoresFail extends StateUtils.LoaderFailAction {
+export class FindStoresFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = FIND_STORES_FAIL;
+
constructor(public payload: any) {
super(STORE_FINDER_DATA, payload);
}
@@ -49,6 +60,7 @@ export class FindStoresFail extends StateUtils.LoaderFailAction {
export class FindStoresSuccess extends StateUtils.LoaderSuccessAction {
readonly type = FIND_STORES_SUCCESS;
+
constructor(public payload: any) {
super(STORE_FINDER_DATA);
}
@@ -56,13 +68,18 @@ 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);
}
}
-export class FindStoreByIdFail extends StateUtils.LoaderFailAction {
+export class FindStoreByIdFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = FIND_STORE_BY_ID_FAIL;
+
constructor(public payload: any) {
super(STORE_FINDER_DATA, payload);
}
@@ -70,6 +87,7 @@ export class FindStoreByIdFail extends StateUtils.LoaderFailAction {
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..04c641776c4 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
@@ -1,6 +1,6 @@
+import { StateUtils } from '@spartacus/core';
import { STORE_FINDER_DATA } from '../store-finder-state';
import { StoreFinderActions } from './index';
-import { StateUtils } from '@spartacus/core';
describe('View All Stores Actions', () => {
describe('ViewAllStores', () => {
@@ -16,13 +16,14 @@ 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),
+ payload: error,
+ 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 980bdcb421b..78dbf7a924e 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
@@ -4,9 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { STORE_FINDER_DATA } from '../store-finder-state';
-import { StateUtils } from '@spartacus/core';
import { Action } from '@ngrx/store';
+import { ErrorAction, StateUtils } from '@spartacus/core';
+import { STORE_FINDER_DATA } from '../store-finder-state';
export const VIEW_ALL_STORES = '[StoreFinder] View All Stores';
export const VIEW_ALL_STORES_FAIL = '[StoreFinder] View All Stores Fail';
@@ -15,13 +15,18 @@ 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);
}
}
-export class ViewAllStoresFail extends StateUtils.LoaderFailAction {
+export class ViewAllStoresFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = VIEW_ALL_STORES_FAIL;
+
constructor(public payload: any) {
super(STORE_FINDER_DATA, payload);
}
@@ -29,6 +34,7 @@ export class ViewAllStoresFail extends StateUtils.LoaderFailAction {
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 65311e915e1..495f378d2ae 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 4cf5ef9afc1..6ce3faf31c6 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/feature-libs/user/profile/assets/translations/en/my-account-v2-email.18n.ts b/feature-libs/user/profile/assets/translations/en/my-account-v2-email.18n.ts
index 87734806f5d..48d765bbf85 100644
--- a/feature-libs/user/profile/assets/translations/en/my-account-v2-email.18n.ts
+++ b/feature-libs/user/profile/assets/translations/en/my-account-v2-email.18n.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/assets/translations/en/my-account-v2-password.i18n.ts b/feature-libs/user/profile/assets/translations/en/my-account-v2-password.i18n.ts
index 0e892780d0e..8926650cc7e 100644
--- a/feature-libs/user/profile/assets/translations/en/my-account-v2-password.i18n.ts
+++ b/feature-libs/user/profile/assets/translations/en/my-account-v2-password.i18n.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/assets/translations/en/my-account-v2-user-profile.18n.ts b/feature-libs/user/profile/assets/translations/en/my-account-v2-user-profile.18n.ts
index 253222cb43c..4a4878e5139 100644
--- a/feature-libs/user/profile/assets/translations/en/my-account-v2-user-profile.18n.ts
+++ b/feature-libs/user/profile/assets/translations/en/my-account-v2-user-profile.18n.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts b/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts
index a25f9525980..ded1e6ac8a3 100644
--- a/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts
+++ b/feature-libs/user/profile/components/update-email/my-account-v2-email.component.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
@@ -12,8 +11,8 @@ import {
inject,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
-import { Observable } from 'rxjs';
import { GlobalMessageType, User } from '@spartacus/core';
+import { Observable } from 'rxjs';
import { UserProfileFacade } from '@spartacus/user/profile/root';
import { filter } from 'rxjs/operators';
diff --git a/feature-libs/user/profile/components/update-email/update-email-component.service.ts b/feature-libs/user/profile/components/update-email/update-email-component.service.ts
index f737ecaa0f3..b1bcc78f139 100644
--- a/feature-libs/user/profile/components/update-email/update-email-component.service.ts
+++ b/feature-libs/user/profile/components/update-email/update-email-component.service.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/components/update-email/use-my-account-v2-email.ts.ts b/feature-libs/user/profile/components/update-email/use-my-account-v2-email.ts.ts
index 0750ed5998e..501506ffedd 100644
--- a/feature-libs/user/profile/components/update-email/use-my-account-v2-email.ts.ts
+++ b/feature-libs/user/profile/components/update-email/use-my-account-v2-email.ts.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts
index 60e5acbbbf2..c63b91a0d53 100644
--- a/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts
+++ b/feature-libs/user/profile/components/update-password/my-account-v2-password.component.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/components/update-password/use-my-account-v2-password.ts b/feature-libs/user/profile/components/update-password/use-my-account-v2-password.ts
index cbf0ed0c84b..f091ec91291 100644
--- a/feature-libs/user/profile/components/update-password/use-my-account-v2-password.ts
+++ b/feature-libs/user/profile/components/update-password/use-my-account-v2-password.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/components/update-profile/my-account-v2-profile.component.ts b/feature-libs/user/profile/components/update-profile/my-account-v2-profile.component.ts
index a2e3005e794..f41855b2679 100644
--- a/feature-libs/user/profile/components/update-profile/my-account-v2-profile.component.ts
+++ b/feature-libs/user/profile/components/update-profile/my-account-v2-profile.component.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
@@ -12,8 +11,8 @@ import {
inject,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
-import { Title } from '@spartacus/user/profile/root';
import { User } from '@spartacus/user/account/root';
+import { Title } from '@spartacus/user/profile/root';
import { Observable } from 'rxjs';
import { UpdateProfileComponentService } from './update-profile-component.service';
diff --git a/feature-libs/user/profile/components/update-profile/update-profile-component.service.ts b/feature-libs/user/profile/components/update-profile/update-profile-component.service.ts
index 39cea0418df..0aed02495cf 100644
--- a/feature-libs/user/profile/components/update-profile/update-profile-component.service.ts
+++ b/feature-libs/user/profile/components/update-profile/update-profile-component.service.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/feature-libs/user/profile/components/update-profile/use-my-account-v2-profile.ts b/feature-libs/user/profile/components/update-profile/use-my-account-v2-profile.ts
index 69a7a9c6c72..12f93241a23 100644
--- a/feature-libs/user/profile/components/update-profile/use-my-account-v2-profile.ts
+++ b/feature-libs/user/profile/components/update-profile/use-my-account-v2-profile.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
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 1f700344528..873741e65e4 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,7 @@
*/
import { Action } from '@ngrx/store';
-import { ErrorModel, HttpErrorModel } from '@spartacus/core';
+import { ErrorAction } 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 +19,22 @@ interface LoadUserTokenPayload {
}
interface LoadUserTokenFailurePayload {
- error: ErrorModel | HttpErrorModel | any;
+ error: any;
initialActionPayload: LoadUserTokenPayload;
}
-export class LoadCdcUserTokenFail implements Action {
+export class LoadCdcUserTokenFail implements ErrorAction {
+ public error: any;
readonly type = LOAD_CDC_USER_TOKEN_FAIL;
- constructor(public payload: LoadUserTokenFailurePayload) {}
+
+ constructor(public payload: LoadUserTokenFailurePayload) {
+ this.error = payload.error;
+ }
}
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 cae59a7b807..8441e21ee4d 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/package-lock.json b/package-lock.json
index f07112b6cb1..efcf008fea3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -68,6 +68,7 @@
"@swc/core": "^1.3.85",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
+ "@types/http-proxy": "^1.17.15",
"@types/i18next": "^13.0.0",
"@types/jasmine": "~5.1.0",
"@types/jest": "^29.4.0",
@@ -8377,9 +8378,9 @@
"dev": true
},
"node_modules/@types/http-proxy": {
- "version": "1.17.14",
- "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz",
- "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==",
+ "version": "1.17.15",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz",
+ "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
@@ -11436,6 +11437,25 @@
"node-fetch": "^2.6.12"
}
},
+ "node_modules/cross-fetch/node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -17986,44 +18006,6 @@
"dev": true,
"optional": true
},
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/node-fetch/node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
- },
- "node_modules/node-fetch/node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
- },
- "node_modules/node-fetch/node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
@@ -22645,6 +22627,11 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -23853,6 +23840,20 @@
"node": ">=12"
}
},
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/whatwg-url/node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -29581,9 +29582,9 @@
"dev": true
},
"@types/http-proxy": {
- "version": "1.17.14",
- "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz",
- "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==",
+ "version": "1.17.15",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz",
+ "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==",
"dev": true,
"requires": {
"@types/node": "*"
@@ -31861,6 +31862,16 @@
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
"requires": {
"node-fetch": "^2.6.12"
+ },
+ "dependencies": {
+ "node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ }
}
},
"cross-spawn": {
@@ -36738,35 +36749,6 @@
"dev": true,
"optional": true
},
- "node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "requires": {
- "whatwg-url": "^5.0.0"
- },
- "dependencies": {
- "tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
- },
- "webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
- },
- "whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "requires": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- }
- }
- },
"node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
@@ -40074,6 +40056,11 @@
}
}
},
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -40870,6 +40857,22 @@
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
"dev": true
},
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ },
+ "dependencies": {
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ }
+ }
+ },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index 570b04fd573..6983bb145ab 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,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:ssr:local-http-backend": "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",
@@ -179,6 +179,7 @@
"@swc/core": "^1.3.85",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
+ "@types/http-proxy": "^1.17.15",
"@types/i18next": "^13.0.0",
"@types/jasmine": "~5.1.0",
"@types/jest": "^29.4.0",
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 ba4c8011cb1..5d264c95ad6 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
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import {
AnonymousConsent,
ConsentTemplate,
@@ -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,12 +49,18 @@ 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 {
+
+export class LoadAnonymousConsentTemplatesFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ANONYMOUS_CONSENT_TEMPLATES_FAIL;
+
constructor(payload: any) {
super(ANONYMOUS_CONSENTS, payload);
}
@@ -60,6 +68,7 @@ export class LoadAnonymousConsentTemplatesFail extends StateUtils.LoaderFailActi
export class ResetLoadAnonymousConsentTemplates extends StateUtils.LoaderResetAction {
readonly type = RESET_LOAD_ANONYMOUS_CONSENT_TEMPLATES;
+
constructor() {
super(ANONYMOUS_CONSENTS);
}
@@ -67,6 +76,7 @@ export class ResetLoadAnonymousConsentTemplates extends StateUtils.LoaderResetAc
export class GetAllAnonymousConsents {
readonly type = GET_ALL_ANONYMOUS_CONSENTS;
+
constructor() {
// Intentional empty constructor
}
@@ -74,36 +84,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 59df1c71f78..465994ee766 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
@@ -23,7 +23,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';
@@ -77,7 +77,7 @@ export class AnonymousConsentsEffects {
catchError((error) =>
of(
new AnonymousConsentsActions.LoadAnonymousConsentTemplatesFail(
- normalizeHttpError(error, this.logger)
+ tryNormalizeHttpError(error, this.logger)
)
)
)
@@ -119,7 +119,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..fc08d38eae5 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,12 @@ 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 531462ece1c..15030b8575f 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
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../../error-handling';
import { StateUtils } from '../../../../state/utils/index';
import { ClientToken } from '../../models/client-token.model';
import { CLIENT_TOKEN_DATA } from '../client-auth-state';
@@ -14,13 +15,18 @@ 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);
}
}
-export class LoadClientTokenFail extends StateUtils.LoaderFailAction {
+export class LoadClientTokenFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_CLIENT_TOKEN_FAIL;
+
constructor(public payload: any) {
super(CLIENT_TOKEN_DATA, payload);
}
@@ -28,6 +34,7 @@ export class LoadClientTokenFail extends StateUtils.LoaderFailAction {
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 37b7e58c446..b2b8098f6bd 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 e8ac9825661..714d93f4f76 100644
--- a/projects/core/src/base-core.module.ts
+++ b/projects/core/src/base-core.module.ts
@@ -23,6 +23,7 @@ import { StateModule } from './state/state.module';
@NgModule({
imports: [
+ ErrorHandlingModule.forRoot(), // Import this module before any other interceptor to handle HTTP errors efficiently
StateModule.forRoot(),
ConfigModule.forRoot(),
ConfigInitializerModule.forRoot(),
@@ -37,7 +38,6 @@ import { StateModule } from './state/state.module';
BaseOccModule.forRoot(),
LazyLoadingModule.forRoot(),
HttpModule.forRoot(),
- ErrorHandlingModule.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 1cb4dde2256..9ea1f8b6bd0 100755
--- a/projects/core/src/cms/store/actions/components.action.ts
+++ b/projects/core/src/cms/store/actions/components.action.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { CmsComponent } from '../../../model/cms.model';
import { PageContext } from '../../../routing/index';
import { StateUtils } from '../../../state/utils/index';
@@ -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;
@@ -26,10 +28,31 @@ export class LoadCmsComponent extends StateUtils.EntityLoadAction {
}
}
-export class LoadCmsComponentFail extends StateUtils.EntityFailAction {
+export class LoadCmsComponentFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_CMS_COMPONENT_FAIL;
+
+ constructor(payload: { uid: string; error: any; pageContext: PageContext });
+ /**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor(
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ payload: {
+ uid: string;
+ pageContext: PageContext;
+ }
+ );
constructor(
- public payload: { uid: string; error?: any; pageContext: PageContext }
+ public payload: {
+ uid: string;
+ error?: any;
+ pageContext: PageContext;
+ }
) {
super(COMPONENT_ENTITY, payload.uid, payload.error);
}
@@ -39,6 +62,7 @@ export class LoadCmsComponentSuccess<
T extends CmsComponent,
> extends StateUtils.EntitySuccessAction {
readonly type = LOAD_CMS_COMPONENT_SUCCESS;
+
constructor(
public payload: {
component: T;
@@ -54,6 +78,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..64aa8bfbf82 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,18 @@ 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,
+ payload: error,
+ 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 83d21653fd7..6490359502c 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
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { StateUtils } from '../../../state/utils/index';
import { NAVIGATION_DETAIL_ENTITY } from '../cms-state';
@@ -15,13 +16,18 @@ 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);
}
}
-export class LoadCmsNavigationItemsFail extends StateUtils.EntityFailAction {
+export class LoadCmsNavigationItemsFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_CMS_NAVIGATION_ITEMS_FAIL;
+
constructor(
nodeId: string,
public payload: any
@@ -32,6 +38,7 @@ export class LoadCmsNavigationItemsFail extends StateUtils.EntityFailAction {
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 63be897248f..1eea647ba07 100755
--- a/projects/core/src/cms/store/actions/page.action.ts
+++ b/projects/core/src/cms/store/actions/page.action.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { PageContext } from '../../../routing/index';
import { StateUtils } from '../../../state/utils/index';
import { Page } from '../../model/page.model';
@@ -16,13 +17,18 @@ 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);
}
}
-export class LoadCmsPageDataFail extends StateUtils.EntityFailAction {
+export class LoadCmsPageDataFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = LOAD_CMS_PAGE_DATA_FAIL;
+
constructor(pageContext: PageContext, error: any) {
super(pageContext.type ?? '', pageContext.id, error);
}
@@ -30,6 +36,7 @@ export class LoadCmsPageDataFail extends StateUtils.EntityFailAction {
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,18 +44,27 @@ 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);
}
}
-export class CmsSetPageFailIndex extends StateUtils.EntityFailAction {
+export class CmsSetPageFailIndex
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
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 9b69fb1dcd4..b4635f50fcc 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 38086f889f7..b03bd38edf1 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 20a7272aa45..2297c5b021e 100644
--- a/projects/core/src/cms/store/effects/page.effect.spec.ts
+++ b/projects/core/src/cms/store/effects/page.effect.spec.ts
@@ -3,7 +3,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { Action, StoreModule } from '@ngrx/store';
-import { LoggerService, normalizeHttpError } from '@spartacus/core';
+import { LoggerService, tryNormalizeHttpError } from '@spartacus/core';
import { cold, hot } from 'jasmine-marbles';
import { Observable, of, throwError } from 'rxjs';
import { AuthActions } from '../../../auth/user-auth/store/actions/index';
@@ -153,7 +153,7 @@ describe('Page Effects', () => {
const completion = new CmsActions.LoadCmsPageDataFail(
pageContext,
- normalizeHttpError(error, new MockLoggerService())
+ tryNormalizeHttpError(error, new MockLoggerService())
);
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 ad1f8983bb1..4ae83e1078d 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/cx-error-handler.spec.ts b/projects/core/src/error-handling/cx-error-handler.spec.ts
new file mode 100644
index 00000000000..c7461b48c76
--- /dev/null
+++ b/projects/core/src/error-handling/cx-error-handler.spec.ts
@@ -0,0 +1,46 @@
+import { TestBed } from '@angular/core/testing';
+import { CxErrorHandler } from './cx-error-handler';
+import { MULTI_ERROR_HANDLER, MultiErrorHandler } from './multi-error-handler';
+
+class MockErrorHandler implements MultiErrorHandler {
+ handleError = jasmine.createSpy('handleError');
+}
+
+class MockErrorHandler2 implements MultiErrorHandler {
+ handleError = jasmine.createSpy('handleError');
+}
+
+describe('CxErrorHandler', () => {
+ let cxErrorHandler: CxErrorHandler;
+ let errorHandlers: MultiErrorHandler[];
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ CxErrorHandler,
+ {
+ provide: MULTI_ERROR_HANDLER,
+ useClass: MockErrorHandler,
+ multi: true,
+ },
+ {
+ provide: MULTI_ERROR_HANDLER,
+ useClass: MockErrorHandler2,
+ multi: true,
+ },
+ ],
+ });
+
+ cxErrorHandler = TestBed.inject(CxErrorHandler);
+ errorHandlers = TestBed.inject(MULTI_ERROR_HANDLER);
+ });
+
+ it('should call all error handlers', () => {
+ const error = new Error('test error');
+
+ cxErrorHandler.handleError(error);
+ errorHandlers.forEach((handler) => {
+ expect(handler.handleError).toHaveBeenCalledWith(error);
+ });
+ });
+});
diff --git a/projects/core/src/error-handling/cx-error-handler.ts b/projects/core/src/error-handling/cx-error-handler.ts
index b74bbac9bdd..e9f5cbca0b7 100644
--- a/projects/core/src/error-handling/cx-error-handler.ts
+++ b/projects/core/src/error-handling/cx-error-handler.ts
@@ -5,13 +5,35 @@
*/
import { ErrorHandler, Injectable, inject } from '@angular/core';
-import { LoggerService } from '../logger/logger.service';
+import { LoggerService } from '../logger';
+import { MULTI_ERROR_HANDLER } from './multi-error-handler';
+/**
+ * The CxErrorHandler is the default ErrorHandler for Spartacus.
+ * It is responsible for handling errors and passing them to the registered multi error handlers.
+ *
+ * The prefix `Cx` was used to distinguish it from Angular's `ErrorHandler` class.
+ * For more, see Angular docs: https://angular.dev/api/core/ErrorHandler
+ *
+ */
@Injectable()
export class CxErrorHandler implements ErrorHandler {
- logger = inject(LoggerService);
+ /**
+ * @deprecated Since 2211.29 - `LoggerService` is not used anymore in this class.
+ * Instead it's now used in `LoggingErrorHandler`.
+ * This property will be removed in the future together with removing
+ * the feature toggle `propagateErrorsToServer`.
+ */
+ protected logger = inject(LoggerService);
+ protected errorHandlers = inject(MULTI_ERROR_HANDLER);
- handleError(error: any): void {
- this.logger.error(error);
+ /**
+ * Error handler method. Handles the error by passing it to the registered multi error handlers.
+ * @param error - The error to be handled.
+ */
+ handleError(error: unknown): void {
+ this.errorHandlers.forEach((handler) => {
+ handler.handleError(error);
+ });
}
}
diff --git a/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.spec.ts b/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.spec.ts
new file mode 100644
index 00000000000..b51770da07e
--- /dev/null
+++ b/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.spec.ts
@@ -0,0 +1,96 @@
+import { TestBed } from '@angular/core/testing';
+import { Actions } from '@ngrx/effects';
+import { provideMockActions } from '@ngrx/effects/testing';
+import { Action } from '@ngrx/store';
+import { ErrorAction, FeatureConfigService } from '@spartacus/core';
+import { Observable, of } from 'rxjs';
+import { CxErrorHandlerEffect } from './cx-error-handler.effect';
+import { ErrorActionService } from './error-action.service';
+
+describe('CxErrorHandlerEffect', () => {
+ let effect: CxErrorHandlerEffect;
+ let actions$: Observable;
+ let errorActionService: jasmine.SpyObj;
+ let featureConfigService: FeatureConfigService;
+
+ beforeEach(() => {
+ const errorActionServiceSpy = jasmine.createSpyObj('ErrorActionService', [
+ 'handle',
+ 'isErrorAction',
+ ]);
+ TestBed.configureTestingModule({
+ providers: [
+ CxErrorHandlerEffect,
+ FeatureConfigService,
+ provideMockActions(() => actions$),
+ {
+ provide: ErrorActionService,
+ useValue: errorActionServiceSpy,
+ },
+ ],
+ });
+
+ effect = TestBed.inject(CxErrorHandlerEffect);
+ actions$ = TestBed.inject(Actions);
+ errorActionService = TestBed.inject(
+ ErrorActionService
+ ) as jasmine.SpyObj;
+ featureConfigService = TestBed.inject(FeatureConfigService);
+ });
+
+ it('should be created', () => {
+ expect(effect).toBeTruthy();
+ });
+
+ describe('error$ ', () => {
+ describe('when ssrStrictErrorHandlingForHttpAndNgrx is enabled', () => {
+ beforeEach(() => {
+ spyOn(featureConfigService, 'isEnabled').and.returnValue(true);
+ });
+
+ it('should handle error action', () => {
+ const mockErrorAction: ErrorAction = {
+ type: 'ERROR_ACTION_TYPE',
+ error: new Error(),
+ };
+
+ errorActionService.isErrorAction.and.returnValue(true);
+
+ actions$ = of(mockErrorAction);
+
+ effect.error$.subscribe();
+
+ expect(errorActionService.handle).toHaveBeenCalledWith(mockErrorAction);
+ });
+
+ it('should not handle non-error action', () => {
+ const mockNonErrorAction = {
+ type: 'SOME_ACTION',
+ };
+
+ errorActionService.isErrorAction.and.returnValue(false);
+
+ actions$ = of(mockNonErrorAction);
+
+ effect.error$.subscribe();
+
+ expect(errorActionService.handle).not.toHaveBeenCalled();
+ });
+ });
+ });
+ describe('when ssrStrictErrorHandlingForHttpAndNgrx is disabled', () => {
+ beforeEach(() => {
+ spyOn(featureConfigService, 'isEnabled').and.returnValue(false);
+ });
+ it('should not handle error action', () => {
+ const mockErrorAction: ErrorAction = {
+ type: 'ERROR_ACTION_TYPE',
+ error: new Error(),
+ };
+ errorActionService.isErrorAction.and.returnValue(true);
+ actions$ = of(mockErrorAction);
+ effect.error$.subscribe();
+ expect(errorActionService.handle).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..301d7630bb5
--- /dev/null
+++ b/projects/core/src/error-handling/effects-error-handler/cx-error-handler.effect.ts
@@ -0,0 +1,41 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable, inject } from '@angular/core';
+import { Actions, createEffect } from '@ngrx/effects';
+import { Observable } from 'rxjs';
+import { filter, tap } from 'rxjs/operators';
+import { FeatureConfigService } from '../../features-config';
+import { ErrorAction } from './error-action';
+import { ErrorActionService } from './error-action.service';
+
+/**
+ * Effect that captures in a centralized manner errors occurred in NgRx flow.
+ * When such an action is detected, it delegates the error handling to the `ErrorActionService`.
+ */
+@Injectable()
+export class CxErrorHandlerEffect {
+ protected actions$ = inject(Actions);
+ protected errorActionService = inject(ErrorActionService);
+ private featureConfigService = inject(FeatureConfigService);
+
+ error$: Observable = createEffect(
+ () =>
+ this.actions$.pipe(
+ filter(this.errorActionService.isErrorAction),
+ tap((errorAction: ErrorAction) => {
+ if (
+ this.featureConfigService.isEnabled(
+ 'ssrStrictErrorHandlingForHttpAndNgrx'
+ )
+ ) {
+ this.errorActionService.handle(errorAction);
+ }
+ })
+ ),
+ { dispatch: false }
+ );
+}
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..7a271bc04d8
--- /dev/null
+++ b/projects/core/src/error-handling/effects-error-handler/effects-error-handler.module.ts
@@ -0,0 +1,22 @@
+/*
+ * SPDX-FileCopyrightText: 2024 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 { ErrorActionService } from './error-action.service';
+
+@NgModule({
+ imports: [EffectsModule.forFeature([CxErrorHandlerEffect])],
+})
+export class EffectsErrorHandlerModule {
+ static forRoot(): ModuleWithProviders {
+ return {
+ ngModule: EffectsErrorHandlerModule,
+ providers: [ErrorActionService],
+ };
+ }
+}
diff --git a/projects/core/src/error-handling/effects-error-handler/error-action.service.spec.ts b/projects/core/src/error-handling/effects-error-handler/error-action.service.spec.ts
new file mode 100644
index 00000000000..468a58c9613
--- /dev/null
+++ b/projects/core/src/error-handling/effects-error-handler/error-action.service.spec.ts
@@ -0,0 +1,109 @@
+import { HttpErrorResponse } from '@angular/common/http';
+import { ErrorHandler } from '@angular/core';
+import { TestBed } from '@angular/core/testing';
+import { Action } from '@ngrx/store';
+import { ErrorAction, HttpErrorModel, WindowRef } from '@spartacus/core';
+import { ErrorActionService } from './error-action.service';
+
+describe('ErrorActionService', () => {
+ let errorActionService: ErrorActionService;
+ let windowRef: WindowRef;
+ let errorHandlerSpy: jasmine.SpyObj;
+
+ beforeEach(() => {
+ const errorHandlerSpyObj = jasmine.createSpyObj('ErrorHandler', [
+ 'handleError',
+ ]);
+
+ TestBed.configureTestingModule({
+ providers: [
+ ErrorActionService,
+ { provide: WindowRef, useValue: { isBrowser: () => false } },
+ { provide: ErrorHandler, useValue: errorHandlerSpyObj },
+ ],
+ });
+
+ errorActionService = TestBed.inject(ErrorActionService);
+ windowRef = TestBed.inject(WindowRef);
+ errorHandlerSpy = TestBed.inject(
+ ErrorHandler
+ ) as jasmine.SpyObj;
+ });
+
+ it('should be created', () => {
+ expect(errorActionService).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'),
+ };
+
+ errorActionService.handle(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(),
+ };
+
+ errorActionService.handle(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({}),
+ };
+
+ errorActionService.handle(mockErrorAction);
+
+ expect(errorHandlerSpy.handleError).not.toHaveBeenCalled();
+ });
+
+ it('should not call ErrorHandler.handleError in browser runtime environment', () => {
+ spyOn(windowRef, 'isBrowser').and.returnValue(true);
+
+ const mockErrorAction: ErrorAction = {
+ type: 'ERROR_ACTION_TYPE',
+ error: new Error('Test error'),
+ };
+
+ errorActionService.handle(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 = errorActionService.isErrorAction(mockErrorAction);
+
+ expect(result).toBeTruthy();
+ });
+
+ it('should return false for action not implementing ErrorAction interface', () => {
+ const mockNonErrorAction = { type: 'SOME_ACTION' };
+
+ const result = errorActionService.isErrorAction(
+ mockNonErrorAction as Action
+ );
+
+ expect(result).toBeFalsy();
+ });
+ });
+});
diff --git a/projects/core/src/error-handling/effects-error-handler/error-action.service.ts b/projects/core/src/error-handling/effects-error-handler/error-action.service.ts
new file mode 100644
index 00000000000..6a295ccd898
--- /dev/null
+++ b/projects/core/src/error-handling/effects-error-handler/error-action.service.ts
@@ -0,0 +1,57 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { HttpErrorResponse } from '@angular/common/http';
+import { ErrorHandler, Injectable, inject } from '@angular/core';
+import { Action } from '@ngrx/store';
+import { HttpErrorModel } from '../../model/index';
+import { WindowRef } from '../../window';
+import { ErrorAction } from './error-action';
+
+/**
+ * Service responsible for capturing and handling NgRx error actions that implements `ErrorAction`.
+ * It ensures that HTTP errors, which are already handled by `HttpErrorHandlerInterceptor`, are not processed
+ * again to avoid duplicate error handling.
+ */
+@Injectable()
+export class ErrorActionService {
+ protected errorHandler: ErrorHandler = inject(ErrorHandler);
+ protected windowRef = inject(WindowRef);
+
+ handle(action: ErrorAction): void {
+ const error: unknown = 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.shouldHandleError(error)) {
+ this.errorHandler.handleError(error);
+ }
+ }
+
+ /** Here we want to filter which error actions should be handled.
+ * By default, we check if action implements interface ErrorAction */
+ isErrorAction(action: Action): action is ErrorAction {
+ return 'error' in action;
+ }
+
+ /**
+ * Determine if the error should be handled by the `ErrorHandler`.
+ *
+ * Be default, we avoid sending unpredictable errors to the browser's console, to prevent
+ * possibly exposing there potentially confidential user's data.
+ * This isn't an issue in SSR, where pages are rendered anonymously.
+ * Moreover, in SSR we want to capture all app's errors, so we can potentially send
+ * a HTTP error response (e.g. 500 error page) from SSR to a client.
+ */
+ protected shouldHandleError(_error: unknown): boolean {
+ return !this.windowRef.isBrowser();
+ }
+}
diff --git a/projects/core/src/error-handling/effects-error-handler/error-action.ts b/projects/core/src/error-handling/effects-error-handler/error-action.ts
new file mode 100644
index 00000000000..2e04db6cc34
--- /dev/null
+++ b/projects/core/src/error-handling/effects-error-handler/error-action.ts
@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Action } from '@ngrx/store';
+
+export interface ErrorAction extends Action {
+ error: Object;
+}
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..a3ba7d73555
--- /dev/null
+++ b/projects/core/src/error-handling/effects-error-handler/index.ts
@@ -0,0 +1,9 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './effects-error-handler.module';
+export * from './error-action';
+export * from './error-action.service';
diff --git a/projects/core/src/error-handling/error-handling.module.ts b/projects/core/src/error-handling/error-handling.module.ts
index 9a85e9d5f66..7216f69410a 100644
--- a/projects/core/src/error-handling/error-handling.module.ts
+++ b/projects/core/src/error-handling/error-handling.module.ts
@@ -6,13 +6,22 @@
import { ErrorHandler, ModuleWithProviders, NgModule } from '@angular/core';
import { CxErrorHandler } from './cx-error-handler';
+import { EffectsErrorHandlerModule } from './effects-error-handler';
+import { HttpErrorHandlerModule } from './http-error-handler';
+import { provideMultiErrorHandler } from './multi-error-handler/provide-multi-error-handler';
-@NgModule()
+@NgModule({
+ imports: [
+ EffectsErrorHandlerModule.forRoot(),
+ HttpErrorHandlerModule.forRoot(),
+ ],
+})
export class ErrorHandlingModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: ErrorHandlingModule,
providers: [
+ provideMultiErrorHandler(),
{
provide: ErrorHandler,
useClass: CxErrorHandler,
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..1d9e23332f1
--- /dev/null
+++ b/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.spec.ts
@@ -0,0 +1,159 @@
+import {
+ HttpErrorResponse,
+ HttpEvent,
+ HttpHandler,
+ HttpRequest,
+} from '@angular/common/http';
+import { ErrorHandler, Injectable } from '@angular/core';
+import { TestBed } from '@angular/core/testing';
+import { Observable, throwError } from 'rxjs';
+import { FeatureConfigService } from '../../features-config';
+import { OccEndpointsService } from '../../occ';
+import { WindowRef } from '../../window';
+import { HttpErrorHandlerInterceptor } from './http-error-handler.interceptor';
+import {
+ CmsPageNotFoundOutboundHttpError,
+ OutboundHttpError,
+} from './outbound-http-error';
+
+@Injectable()
+class MockErrorHandler {
+ handleError(_error: any): void {}
+}
+
+@Injectable()
+class MockOccEndpointsService {
+ buildUrl = (val: string) => val;
+}
+
+describe('HttpErrorHandlerInterceptor', () => {
+ let interceptor: HttpErrorHandlerInterceptor;
+ let errorHandler: ErrorHandler;
+ let request: HttpRequest;
+ let next: HttpHandler;
+ let featureConfigService: FeatureConfigService;
+ let windowRef: WindowRef;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ HttpErrorHandlerInterceptor,
+ FeatureConfigService,
+ { provide: OccEndpointsService, useClass: MockOccEndpointsService },
+ { provide: WindowRef, useValue: { isBrowser: () => false } },
+ { provide: ErrorHandler, useClass: MockErrorHandler },
+ ],
+ });
+
+ interceptor = TestBed.inject(HttpErrorHandlerInterceptor);
+ errorHandler = TestBed.inject(ErrorHandler);
+ featureConfigService = TestBed.inject(FeatureConfigService);
+ windowRef = TestBed.inject(WindowRef);
+
+ request = new HttpRequest('GET', 'test-url');
+ next = {
+ handle: () => new Observable>(),
+ } as HttpHandler;
+ });
+
+ it('should create the interceptor', () => {
+ expect(interceptor).toBeTruthy();
+ });
+
+ describe('when ssrStrictErrorHandlingForHttpAndNgrx is enabled', () => {
+ beforeEach(() => {
+ spyOn(featureConfigService, 'isEnabled').and.returnValue(true);
+ });
+ it('should call handleError with OutboundHttpError for any HTTP error except 404 cms page not found', (done) => {
+ const error: HttpErrorResponse = new HttpErrorResponse({
+ status: 500,
+ statusText: 'error',
+ });
+ spyOn(errorHandler, 'handleError');
+
+ next.handle = () => throwError(() => error);
+
+ interceptor.intercept(request, next).subscribe({
+ error: (err) => {
+ expect(err).toEqual(error);
+ expect(errorHandler.handleError).toHaveBeenCalledWith(
+ jasmine.any(OutboundHttpError)
+ );
+ done();
+ },
+ });
+ });
+
+ it('should call handleError with CmsPageNotFoundOutboundHttpError when CMS page not found', (done) => {
+ const error: HttpErrorResponse = new HttpErrorResponse({
+ url: 'pages',
+ status: 404,
+ });
+ spyOn(errorHandler, 'handleError');
+
+ next.handle = () => throwError(() => error);
+
+ interceptor.intercept(request, next).subscribe({
+ error: (err) => {
+ expect(err).toEqual(error);
+ expect(errorHandler.handleError).toHaveBeenCalledWith(
+ jasmine.any(CmsPageNotFoundOutboundHttpError)
+ );
+ done();
+ },
+ });
+ });
+
+ it('should not call handleError when it is not SSR', (done) => {
+ spyOn(errorHandler, 'handleError');
+ spyOn(windowRef, 'isBrowser').and.returnValue(true);
+
+ next.handle = () => throwError(() => new HttpErrorResponse({}));
+
+ interceptor.intercept(request, next).subscribe({
+ error: () => {
+ expect(errorHandler.handleError).not.toHaveBeenCalled();
+ 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();
+ });
+ });
+ });
+
+ describe('when ssrStrictErrorHandlingForHttpAndNgrx is disabled', () => {
+ beforeEach(() => {
+ spyOn(featureConfigService, 'isEnabled').and.returnValue(false);
+ });
+
+ it('should pass through the request when there is an 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).not.toHaveBeenCalled();
+ 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..0b8fa954899
--- /dev/null
+++ b/projects/core/src/error-handling/http-error-handler/http-error-handler.interceptor.ts
@@ -0,0 +1,100 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ HttpErrorResponse,
+ HttpEvent,
+ HttpHandler,
+ HttpInterceptor,
+ HttpRequest,
+} from '@angular/common/http';
+import { ErrorHandler, Injectable, inject } from '@angular/core';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { FeatureConfigService } from '../../features-config';
+import { HttpResponseStatus } from '../../global-message';
+import { OccEndpointsService } from '../../occ';
+import { WindowRef } from '../../window';
+import {
+ CmsPageNotFoundOutboundHttpError,
+ OutboundHttpError,
+} from './outbound-http-error';
+
+/**
+ * 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.
+ *
+ * NOTE: It handles not only HTTP errors, but also any RxJs errors
+ * (e.g., `TimeoutError` from `timeout()` operator) thrown in any subsequent interceptor.
+ *
+ * CAUTION: It MUST be provided as the first one in the application to be able to
+ * catch errors from all subsequent interceptors.
+ */
+@Injectable()
+export class HttpErrorHandlerInterceptor implements HttpInterceptor {
+ protected errorHandler = inject(ErrorHandler);
+ protected occEndpointsService = inject(OccEndpointsService);
+ protected windowRef = inject(WindowRef);
+ private featureService = inject(FeatureConfigService);
+
+ intercept(
+ request: HttpRequest,
+ next: HttpHandler
+ ): Observable> {
+ return next.handle(request).pipe(
+ tap({
+ error: (error: unknown) => {
+ if (
+ this.featureService.isEnabled(
+ 'ssrStrictErrorHandlingForHttpAndNgrx'
+ ) &&
+ this.shouldHandleError(error)
+ ) {
+ this.handleError(error);
+ }
+ },
+ })
+ );
+ }
+
+ /**
+ * Determine if the error should be handled by the `ErrorHandler`.
+ *
+ * Be default, we avoid sending unpredictable errors to the browser's console, to prevent
+ * possibly exposing there potentially confidential user's data.
+ * This isn't an issue in SSR, where pages are rendered anonymously.
+ * Moreover, in SSR we want to capture all app's errors, so we can potentially send
+ * a HTTP error response (e.g. 500 error page) from SSR to a client.
+ */
+ protected shouldHandleError(_error: unknown): boolean {
+ return !this.windowRef.isBrowser();
+ }
+
+ protected handleError(error: unknown): void {
+ error = this.isCmsPageNotFoundHttpError(error)
+ ? new CmsPageNotFoundOutboundHttpError(error)
+ : new OutboundHttpError(error);
+ this.errorHandler.handleError(error);
+ }
+
+ /**
+ * Checks if the error corresponds to a CMS page not found HTTP error.
+ *
+ * @param error - The error object to check.
+ * @returns `true` if the error corresponds to a CMS page not found HTTP error, `false` otherwise.
+ */
+ protected isCmsPageNotFoundHttpError(error: unknown): boolean {
+ const expectedUrl = this.occEndpointsService.buildUrl('pages');
+ return (
+ error instanceof HttpErrorResponse &&
+ error.status === HttpResponseStatus.NOT_FOUND &&
+ (error.url ?? '').startsWith(expectedUrl)
+ );
+ }
+}
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..0f4b65dd9cd
--- /dev/null
+++ b/projects/core/src/error-handling/http-error-handler/http-error-handler.module.ts
@@ -0,0 +1,25 @@
+/*
+ * SPDX-FileCopyrightText: 2024 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..e7f24185fff
--- /dev/null
+++ b/projects/core/src/error-handling/http-error-handler/index.ts
@@ -0,0 +1,9 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './http-error-handler.interceptor';
+export * from './http-error-handler.module';
+export * from './outbound-http-error';
diff --git a/projects/core/src/error-handling/http-error-handler/outbound-http-error.ts b/projects/core/src/error-handling/http-error-handler/outbound-http-error.ts
new file mode 100644
index 00000000000..4dea48f0cdc
--- /dev/null
+++ b/projects/core/src/error-handling/http-error-handler/outbound-http-error.ts
@@ -0,0 +1,25 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * Represents an outbound HTTP error that occurs when communicating with the backend.
+ */
+export class OutboundHttpError extends Error {
+ constructor(cause: unknown) {
+ super('Outbound HTTP Error', { cause });
+ }
+}
+
+/**
+ * Represents an outbound HTTP error specific to a CMS page not found.
+ * Extends the base OutboundHttpError class.
+ */
+export class CmsPageNotFoundOutboundHttpError extends OutboundHttpError {
+ constructor(cause: unknown) {
+ super(cause);
+ this.message = 'CMS Page Not Found';
+ }
+}
diff --git a/projects/core/src/error-handling/index.ts b/projects/core/src/error-handling/index.ts
index caaba103780..25cce8474b3 100644
--- a/projects/core/src/error-handling/index.ts
+++ b/projects/core/src/error-handling/index.ts
@@ -5,4 +5,7 @@
*/
export * from './cx-error-handler';
+export * from './effects-error-handler';
export * from './error-handling.module';
+export * from './http-error-handler';
+export * from './multi-error-handler';
diff --git a/projects/core/src/error-handling/multi-error-handler/index.ts b/projects/core/src/error-handling/multi-error-handler/index.ts
new file mode 100644
index 00000000000..cb3e4b55856
--- /dev/null
+++ b/projects/core/src/error-handling/multi-error-handler/index.ts
@@ -0,0 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './logging-error-handler';
+export * from './multi-error-handler';
diff --git a/projects/core/src/error-handling/multi-error-handler/logging-error-handler.spec.ts b/projects/core/src/error-handling/multi-error-handler/logging-error-handler.spec.ts
new file mode 100644
index 00000000000..5e2f0e27882
--- /dev/null
+++ b/projects/core/src/error-handling/multi-error-handler/logging-error-handler.spec.ts
@@ -0,0 +1,30 @@
+import { TestBed } from '@angular/core/testing';
+import { LoggerService } from '../../logger';
+import { LoggingErrorHandler } from './logging-error-handler';
+
+class MockLoggerService implements Partial {
+ error = jasmine.createSpy('error');
+}
+
+describe('LoggingErrorHandler', () => {
+ let errorHandler: LoggingErrorHandler;
+ let loggerService: LoggerService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ LoggingErrorHandler,
+ { provide: LoggerService, useClass: MockLoggerService },
+ ],
+ });
+
+ loggerService = TestBed.inject(LoggerService);
+ errorHandler = TestBed.inject(LoggingErrorHandler);
+ });
+
+ it('should log the error using the logger service', () => {
+ const error = new Error('Test error');
+ errorHandler.handleError(error);
+ expect(loggerService.error).toHaveBeenCalledWith(error);
+ });
+});
diff --git a/projects/core/src/error-handling/multi-error-handler/logging-error-handler.ts b/projects/core/src/error-handling/multi-error-handler/logging-error-handler.ts
new file mode 100644
index 00000000000..b922c149fbc
--- /dev/null
+++ b/projects/core/src/error-handling/multi-error-handler/logging-error-handler.ts
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable, inject } from '@angular/core';
+import { LoggerService } from '../../logger';
+import { MultiErrorHandler } from './multi-error-handler';
+
+/**
+ * An error handler that logs errors using a logger service.
+ * Intended to be used as part of a multi-error handler strategy.
+ *
+ * @see MultiErrorHandler
+ */
+@Injectable({
+ providedIn: 'root',
+})
+export class LoggingErrorHandler implements MultiErrorHandler {
+ protected logger = inject(LoggerService);
+
+ handleError(error: Error): void {
+ this.logger.error(error);
+ }
+}
diff --git a/projects/core/src/error-handling/multi-error-handler/multi-error-handler.ts b/projects/core/src/error-handling/multi-error-handler/multi-error-handler.ts
new file mode 100644
index 00000000000..5d3a05fa9a7
--- /dev/null
+++ b/projects/core/src/error-handling/multi-error-handler/multi-error-handler.ts
@@ -0,0 +1,27 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { InjectionToken } from '@angular/core';
+
+/**
+ * Multi error handlers are responsible for act with errors handled by the CxErrorHandler.
+ *
+ * @method handleError - Handles the error.
+ *
+ * @public
+ */
+export interface MultiErrorHandler {
+ handleError(error: unknown): void;
+}
+
+/**
+ * Injection token for multi error handlers.
+ * Multi provided error handlers will be called in the order they are provided.
+ */
+
+export const MULTI_ERROR_HANDLER = new InjectionToken(
+ 'MULTI_ERROR_HANDLER'
+);
diff --git a/projects/core/src/error-handling/multi-error-handler/provide-multi-error-handler.ts b/projects/core/src/error-handling/multi-error-handler/provide-multi-error-handler.ts
new file mode 100644
index 00000000000..43e59eb1cae
--- /dev/null
+++ b/projects/core/src/error-handling/multi-error-handler/provide-multi-error-handler.ts
@@ -0,0 +1,19 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Provider } from '@angular/core';
+import { LoggingErrorHandler } from './logging-error-handler';
+import { MULTI_ERROR_HANDLER } from './multi-error-handler';
+
+export function provideMultiErrorHandler(): Provider[] {
+ return [
+ {
+ provide: MULTI_ERROR_HANDLER,
+ useExisting: LoggingErrorHandler,
+ multi: true,
+ },
+ ];
+}
diff --git a/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts b/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts
index 1d45cae3b4f..3358622788d 100644
--- a/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts
+++ b/projects/core/src/features-config/feature-toggles/config/feature-toggles.ts
@@ -115,6 +115,25 @@ export interface FeatureTogglesInterface {
*/
productConfiguratorAttributeTypesV2?: boolean;
+ /**
+ * In a server environment (SSR or Prerendering) it propagates all errors caught in Angular app
+ * (in the Angular's `ErrorHandler` class) to the server layer.
+ *
+ * In SSR, such a propagation allows the server layer (e.g. ExpressJS) for handling those errors,
+ * e.g. sending a proper Error Page in response to the client,
+ * instead of a rendered HTML that is possibly malformed due to the occurred error.
+ */
+ propagateErrorsToServer?: boolean;
+
+ /**
+ * In SSR, the following errors will be printed to logs (and additionally can also
+ * be forwarded to ExpressJS if only the other feature toggle `propagateErrorsToServer` is enabled):
+ *
+ * 1. any outgoing HTTP request error (4xx-5xx status)
+ * 2. any NgRx action with the `error` property
+ */
+ ssrStrictErrorHandlingForHttpAndNgrx?: boolean;
+
/**
* The product configuration UI is completely re-rendered after each UI interaction. This may lead to performance issues for large configuration models,
* where a lot of attributes (>50) and/or a lot of possible values per attribute (>50) are rendered on the UI.
@@ -524,6 +543,8 @@ export const defaultFeatureToggles: Required = {
storeFrontLibCardParagraphTruncated: true,
useProductCarouselBatchApi: false,
productConfiguratorAttributeTypesV2: true,
+ propagateErrorsToServer: false,
+ ssrStrictErrorHandlingForHttpAndNgrx: false,
productConfiguratorDeltaRendering: false,
a11yRequiredAsterisks: false,
a11yQuantityOrderTabbing: false,
diff --git a/projects/core/src/model/index.ts b/projects/core/src/model/index.ts
index ae21915d691..3266e9dcd37 100644
--- a/projects/core/src/model/index.ts
+++ b/projects/core/src/model/index.ts
@@ -5,7 +5,6 @@
*/
export * from './address.model';
-export * from './payment.model';
export * from './cms.model';
export * from './consent.model';
export * from './customer-coupon.model';
@@ -13,6 +12,7 @@ export * from './image.model';
export * from './misc.model';
export * from './notification-preference.model';
export * from './org-unit.model';
+export * from './payment.model';
export * from './point-of-service.model';
export * from './product-interest.model';
export * from './product-search.model';
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..145f5872603 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,12 @@ 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,
+ payload: error,
+ 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 8e663653256..7bb6c155f32 100644
--- a/projects/core/src/product/store/actions/product-references.action.ts
+++ b/projects/core/src/product/store/actions/product-references.action.ts
@@ -5,7 +5,7 @@
*/
import { Action } from '@ngrx/store';
-import { ErrorModel } from '../../../model/misc.model';
+import { ErrorAction } from '../../../error-handling';
import { ProductReference } from '../../../model/product.model';
export const LOAD_PRODUCT_REFERENCES = '[Product] Load Product References Data';
@@ -17,6 +17,7 @@ export const CLEAN_PRODUCT_REFERENCES = '[Product] Clean Product References';
export class LoadProductReferences implements Action {
readonly type = LOAD_PRODUCT_REFERENCES;
+
constructor(
public payload: {
productCode: string;
@@ -26,13 +27,26 @@ 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) {}
+ public error: any;
+
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ constructor(payload: any);
+ /**
+ * @deprecated Please pass the argument `payload` (i.e. the error object).
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor();
+ constructor(public payload?: any) {
+ this.error = payload;
+ }
}
export class LoadProductReferencesSuccess implements Action {
readonly type = LOAD_PRODUCT_REFERENCES_SUCCESS;
+
constructor(
public payload: {
productCode: string;
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..b3026e65e25 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,12 @@ 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,
+ payload: error,
+ 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 bb8ef91155f..8c6b8857cd6 100644
--- a/projects/core/src/product/store/actions/product-reviews.action.ts
+++ b/projects/core/src/product/store/actions/product-reviews.action.ts
@@ -5,7 +5,7 @@
*/
import { Action } from '@ngrx/store';
-import { ErrorModel, HttpErrorModel } from '../../../model/misc.model';
+import { ErrorAction } from '../../../error-handling';
import { Review } from '../../../model/product.model';
export const LOAD_PRODUCT_REVIEWS = '[Product] Load Product Reviews Data';
@@ -20,31 +20,59 @@ export const POST_PRODUCT_REVIEW_SUCCESS =
export class LoadProductReviews implements Action {
readonly type = LOAD_PRODUCT_REVIEWS;
+
constructor(public payload: string) {}
}
-export class LoadProductReviewsFail implements Action {
+export class LoadProductReviewsFail implements ErrorAction {
readonly type = LOAD_PRODUCT_REVIEWS_FAIL;
- constructor(public payload?: ErrorModel) {}
+ public error: any;
+
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ constructor(payload: any);
+ /**
+ * @deprecated Please pass the argument `payload` (i.e. the error object).
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor();
+ constructor(public payload?: any) {
+ this.error = payload;
+ }
}
export class LoadProductReviewsSuccess implements Action {
readonly type = LOAD_PRODUCT_REVIEWS_SUCCESS;
+
constructor(public payload: { productCode: string; list: Review[] }) {}
}
export class PostProductReview implements Action {
readonly type = POST_PRODUCT_REVIEW;
+
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?: HttpErrorModel) {}
+ public error: any;
+
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ constructor(payload: any);
+ /**
+ * @deprecated Please pass the argument `payload` (i.e. the error object).
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor();
+ constructor(public payload?: any) {
+ this.error = payload;
+ }
}
export class PostProductReviewSuccess implements Action {
readonly type = POST_PRODUCT_REVIEW_SUCCESS;
+
constructor(public payload: Review) {}
}
diff --git a/projects/core/src/product/store/actions/product-search-by-code.action.spec.ts b/projects/core/src/product/store/actions/product-search-by-code.action.spec.ts
index 2e6d56bcc75..6b47944919a 100644
--- a/projects/core/src/product/store/actions/product-search-by-code.action.spec.ts
+++ b/projects/core/src/product/store/actions/product-search-by-code.action.spec.ts
@@ -1,7 +1,7 @@
-import * as fromProductSearchByCode from './product-search-by-code.action';
-import { PRODUCT_SEARCH_RESULTS_BY_CODES_ENTITY } from '../product-state';
-import { EntityScopedLoaderActions } from '../../../state/utils/scoped-loader/entity-scoped-loader.actions';
import { StateUtils } from '@spartacus/core';
+import { EntityScopedLoaderActions } from '../../../state/utils/scoped-loader/entity-scoped-loader.actions';
+import { PRODUCT_SEARCH_RESULTS_BY_CODES_ENTITY } from '../product-state';
+import * as fromProductSearchByCode from './product-search-by-code.action';
describe('ProductSearchLoadByCode Actions', () => {
describe('ProductSearchLoadByCode', () => {
@@ -62,6 +62,7 @@ describe('ProductSearchLoadByCode Actions', () => {
payload.scope,
payload.error
),
+ error: payload.error,
});
});
});
diff --git a/projects/core/src/product/store/actions/product-search-by-code.action.ts b/projects/core/src/product/store/actions/product-search-by-code.action.ts
index c34310c336a..763f05145bf 100644
--- a/projects/core/src/product/store/actions/product-search-by-code.action.ts
+++ b/projects/core/src/product/store/actions/product-search-by-code.action.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { Product } from '../../../model/product.model';
import { StateUtils } from '../../../state/utils';
import { EntityScopedLoaderActions } from '../../../state/utils/scoped-loader/entity-scoped-loader.actions';
@@ -42,7 +43,10 @@ export class ProductSearchLoadByCodeSuccess extends EntityScopedLoaderActions.En
}
}
-export class ProductSearchLoadByCodeFail extends EntityScopedLoaderActions.EntityScopedFailAction {
+export class ProductSearchLoadByCodeFail
+ extends EntityScopedLoaderActions.EntityScopedFailAction
+ implements ErrorAction
+{
readonly type = PRODUCT_SEARCH_LOAD_BY_CODE_FAIL;
constructor(payload: { code: string; scope: string; error: any }) {
super(
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..f5ecf93498a 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,13 @@ 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,
+ payload: error,
+ error,
auxiliary: undefined,
});
});
@@ -94,12 +95,13 @@ 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,
+ payload: error,
+ 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 bd2f269dd40..5c1dc8b0c30 100644
--- a/projects/core/src/product/store/actions/product-search.action.ts
+++ b/projects/core/src/product/store/actions/product-search.action.ts
@@ -5,7 +5,7 @@
*/
import { Action } from '@ngrx/store';
-import { ErrorModel } from '../../../model/misc.model';
+import { ErrorAction } from '../../../error-handling';
import {
ClearSearch,
ProductSearchPage,
@@ -32,12 +32,16 @@ export class SearchProducts implements Action {
) {}
}
-export class SearchProductsFail implements Action {
+export class SearchProductsFail implements ErrorAction {
readonly type = SEARCH_PRODUCTS_FAIL;
+ public error: any;
+
constructor(
- public payload: ErrorModel | undefined,
+ public payload: any,
public auxiliary?: boolean
- ) {}
+ ) {
+ this.error = payload;
+ }
}
export class SearchProductsSuccess implements Action {
@@ -58,9 +62,12 @@ export class GetProductSuggestionsSuccess implements Action {
constructor(public payload: Suggestion[]) {}
}
-export class GetProductSuggestionsFail implements Action {
+export class GetProductSuggestionsFail implements ErrorAction {
+ public error: any;
readonly type = GET_PRODUCT_SUGGESTIONS_FAIL;
- constructor(public payload: ErrorModel | undefined) {}
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
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 772193e6410..a9b7afeb885 100644
--- a/projects/core/src/product/store/actions/product.action.ts
+++ b/projects/core/src/product/store/actions/product.action.ts
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import { Product } from '../../../model/product.model';
import { EntityLoaderMeta } from '../../../state/utils/entity-loader/entity-loader.action';
import { EntityScopedLoaderActions } from '../../../state/utils/scoped-loader/entity-scoped-loader.actions';
@@ -27,6 +28,7 @@ export interface EntityScopedLoaderAction extends Action {
export class LoadProduct extends EntityScopedLoaderActions.EntityScopedLoadAction {
readonly type = LOAD_PRODUCT;
+
constructor(
public payload: string,
scope = ''
@@ -35,8 +37,12 @@ export class LoadProduct extends EntityScopedLoaderActions.EntityScopedLoadActio
}
}
-export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction {
+export class LoadProductFail
+ extends EntityScopedLoaderActions.EntityScopedFailAction
+ implements ErrorAction
+{
readonly type = LOAD_PRODUCT_FAIL;
+
constructor(
productCode: string,
public payload: any,
@@ -48,6 +54,7 @@ export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailA
export class LoadProductSuccess extends EntityScopedLoaderActions.EntityScopedSuccessAction {
readonly type = LOAD_PRODUCT_SUCCESS;
+
constructor(
public payload: Product,
scope = ''
@@ -58,6 +65,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-references.effect.ts b/projects/core/src/product/store/effects/product-references.effect.ts
index 3e15b768e97..0b214fe9ba7 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 '../../../util/try-normalize-http-error';
@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 eb5f62be9f4..c58d175714d 100644
--- a/projects/core/src/product/store/effects/product-reviews.effect.ts
+++ b/projects/core/src/product/store/effects/product-reviews.effect.ts
@@ -14,8 +14,8 @@ import {
GlobalMessageService,
GlobalMessageType,
} from '../../../global-message/index';
-import { normalizeHttpError } from '../../../util/normalize-http-error';
import { LoggerService } from '../../../logger';
+import { tryNormalizeHttpError } from '../../../util/try-normalize-http-error';
@Injectable()
export class ProductReviewsEffects {
@@ -38,7 +38,7 @@ export class ProductReviewsEffects {
catchError((error) =>
of(
new ProductActions.LoadProductReviewsFail(
- normalizeHttpError(error, this.logger)
+ tryNormalizeHttpError(error, this.logger)
)
)
)
@@ -66,7 +66,7 @@ export class ProductReviewsEffects {
catchError((error) =>
of(
new ProductActions.PostProductReviewFail(
- normalizeHttpError(error, this.logger)
+ tryNormalizeHttpError(error, this.logger)
)
)
)
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 e273c8e2b6e..eb74099484b 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 313a9958498..0fd7b79cd30 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..bd037b4076a 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,13 @@ 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,
+ payload: error,
+ error,
});
});
});
@@ -53,12 +54,13 @@ 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,
+ payload: error,
+ 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 ac10dc1f82d..593437e4633 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
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import { BaseSite } from '../../../model/misc.model';
export const LOAD_BASE_SITE = '[Site-context] Load BaseSite';
@@ -22,13 +23,18 @@ export class LoadBaseSite implements Action {
readonly type = LOAD_BASE_SITE;
}
-export class LoadBaseSiteFail implements Action {
+export class LoadBaseSiteFail implements ErrorAction {
+ public error: any;
readonly type = LOAD_BASE_SITE_FAIL;
- constructor(public payload: any) {}
+
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
export class LoadBaseSiteSuccess implements Action {
readonly type = LOAD_BASE_SITE_SUCCESS;
+
constructor(public payload: BaseSite) {}
}
@@ -36,18 +42,24 @@ export class LoadBaseSites implements Action {
readonly type = LOAD_BASE_SITES;
}
-export class LoadBaseSitesFail implements Action {
+export class LoadBaseSitesFail implements ErrorAction {
+ public error: any;
readonly type = LOAD_BASE_SITES_FAIL;
- constructor(public payload: any) {}
+
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
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..a88b39e8a3e 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,13 @@ 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,
+ payload: error,
+ 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 1cf47245ffe..725d8eaf84a 100644
--- a/projects/core/src/site-context/store/actions/currencies.action.ts
+++ b/projects/core/src/site-context/store/actions/currencies.action.ts
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import { Currency } from '../../../model/misc.model';
export const LOAD_CURRENCIES = '[Site-context] Load Currencies';
@@ -17,9 +18,12 @@ export class LoadCurrencies implements Action {
readonly type = LOAD_CURRENCIES;
}
-export class LoadCurrenciesFail implements Action {
+export class LoadCurrenciesFail implements ErrorAction {
+ public error: any;
readonly type = LOAD_CURRENCIES_FAIL;
- constructor(public payload: any) {}
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
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..f7b63f7cc63 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,13 @@ 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,
+ payload: error,
+ 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 90db4b2695b..2bdca3b571f 100644
--- a/projects/core/src/site-context/store/actions/languages.action.ts
+++ b/projects/core/src/site-context/store/actions/languages.action.ts
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import { Language } from '../../../model/misc.model';
export const LOAD_LANGUAGES = '[Site-context] Load Languages';
@@ -17,23 +18,30 @@ export class LoadLanguages implements Action {
readonly type = LOAD_LANGUAGES;
}
-export class LoadLanguagesFail implements Action {
+export class LoadLanguagesFail implements ErrorAction {
+ public error: any;
readonly type = LOAD_LANGUAGES_FAIL;
- constructor(public payload: any) {}
+
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
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 ac3c6a03ca5..e89d0384f29 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 b3f96caff35..5cd2403482b 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 04466c514b8..2161543abcf 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 cb709ca51d3..7d3badac45c 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
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import { EntityId, entityMeta, EntityMeta } from '../entity/entity.action';
import {
failMeta,
@@ -36,6 +37,21 @@ export function entityLoadMeta(
};
}
+export function entityFailMeta(
+ entityType: string,
+ id: EntityId,
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ error: any
+): EntityLoaderMeta;
+/**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+export function entityFailMeta(
+ entityType: string,
+ id: EntityId
+): EntityLoaderMeta;
export function entityFailMeta(
entityType: string,
id: EntityId,
@@ -75,11 +91,22 @@ export class EntityLoadAction implements EntityLoaderAction {
}
}
-export class EntityFailAction implements EntityLoaderAction {
+export class EntityFailAction implements EntityLoaderAction, ErrorAction {
type = ENTITY_FAIL_ACTION;
readonly meta: EntityLoaderMeta;
+ public error: any;
+
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ constructor(entityType: string, id: EntityId, error: any);
+ /**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor(entityType: string, id: EntityId);
constructor(entityType: string, id: EntityId, error?: any) {
this.meta = entityFailMeta(entityType, id, error);
+ this.error = error;
}
}
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 74a2785ae49..e81a35531e3 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 } from '../../../error-handling';
export const LOADER_LOAD_ACTION = '[LOADER] LOAD';
export const LOADER_FAIL_ACTION = '[LOADER] FAIL';
@@ -36,6 +37,17 @@ export function loadMeta(entityType: string): LoaderMeta {
};
}
+export function failMeta(
+ entityType: string,
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ error: any
+): LoaderMeta;
+/**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+export function failMeta(entityType: string): LoaderMeta;
export function failMeta(entityType: string, error?: any): LoaderMeta {
return {
entityType: entityType,
@@ -60,25 +72,39 @@ 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;
+ public error: any;
readonly meta: LoaderMeta;
+
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ constructor(entityType: string, error: any);
+ /**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor(entityType: string);
constructor(entityType: string, error?: any) {
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 +113,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..985ac6f1537 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
@@ -1,10 +1,10 @@
-import { EntityScopedLoaderActions } from './entity-scoped-loader.actions';
import {
ENTITY_FAIL_ACTION,
ENTITY_LOAD_ACTION,
ENTITY_RESET_ACTION,
ENTITY_SUCCESS_ACTION,
} from '../entity-loader/entity-loader.action';
+import { EntityScopedLoaderActions } from './entity-scoped-loader.actions';
describe('EntityScopedLoaderActions', () => {
const TEST_ENTITY_TYPE = 'test';
@@ -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
);
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 b7d83f33c66..80631ea1707 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
@@ -5,16 +5,17 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import {
- entityFailMeta,
- EntityLoaderMeta,
- entityLoadMeta,
- entityResetMeta,
- entitySuccessMeta,
ENTITY_FAIL_ACTION,
ENTITY_LOAD_ACTION,
ENTITY_RESET_ACTION,
ENTITY_SUCCESS_ACTION,
+ EntityLoaderMeta,
+ entityFailMeta,
+ entityLoadMeta,
+ entityResetMeta,
+ entitySuccessMeta,
} from '../entity-loader/entity-loader.action';
export namespace EntityScopedLoaderActions {
@@ -38,6 +39,33 @@ export namespace EntityScopedLoaderActions {
};
}
+ export function entityScopedFailMeta(
+ entityType: string,
+ id: string | string[],
+ scope: string | undefined,
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ error: any
+ ): EntityScopedLoaderMeta;
+ /**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ export function entityScopedFailMeta(
+ entityType: string,
+ id: string | string[],
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ scope: string | undefined
+ ): EntityScopedLoaderMeta;
+ /**
+ * @deprecated Please pass the argument `scope` and `error`
+ * They will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ export function entityScopedFailMeta(
+ entityType: string,
+ id: string | string[]
+ ): EntityScopedLoaderMeta;
export function entityScopedFailMeta(
entityType: string,
id: string | string[],
@@ -75,14 +103,44 @@ 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;
+ public error: any;
readonly meta: EntityScopedLoaderMeta;
+
+ constructor(
+ entityType: string,
+ id: string | string[],
+ scope: string | undefined,
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ error: any
+ );
+ /**
+ * @deprecated Please pass the argument `error`.
+ * It will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ constructor(
+ entityType: string,
+ id: string | string[],
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
+ scope: string | undefined
+ );
+ /**
+ * @deprecated Please pass the argument `scope` and `error`.
+ * They will become mandatory along with removing
+ * the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
+ */
+ constructor(entityType: string, id: string | string[]);
constructor(
entityType: string,
id: string | string[],
@@ -90,12 +148,14 @@ export namespace EntityScopedLoaderActions {
error?: any
) {
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 +169,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 e25acf02fff..7bcb9b7873a 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..c42d6791fb5 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,13 @@ 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,
+ payload: error,
+ 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 b18521cccbe..b1f8ec923d4 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 '../../../error-handling';
export const LOAD_BILLING_COUNTRIES = '[User] Load Billing Countries';
export const LOAD_BILLING_COUNTRIES_FAIL = '[User] Load Billing Countries Fail';
@@ -18,9 +19,13 @@ 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) {}
+ public error: any;
+
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
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 fd54d2c6a00..c934adee2ea 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
@@ -1,23 +1,23 @@
import {
- CUSTOMER_COUPONS,
- SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID,
- UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID,
- CLAIM_CUSTOMER_COUPON_PROCESS_ID,
- DISCLAIM_CUSTOMER_COUPON_PROCESS_ID,
-} from '../user-state';
+ CustomerCoupon,
+ CustomerCoupon2Customer,
+ CustomerCouponNotification,
+ CustomerCouponSearchResult,
+} from '../../../model/customer-coupon.model';
+import { StateUtils } from '../../../state/utils/index';
import {
- loadMeta,
failMeta,
- successMeta,
+ loadMeta,
resetMeta,
+ successMeta,
} from '../../../state/utils/loader/loader.action';
-import { StateUtils } from '../../../state/utils/index';
import {
- CustomerCoupon,
- CustomerCouponSearchResult,
- CustomerCouponNotification,
- CustomerCoupon2Customer,
-} from '../../../model/customer-coupon.model';
+ CLAIM_CUSTOMER_COUPON_PROCESS_ID,
+ CUSTOMER_COUPONS,
+ DISCLAIM_CUSTOMER_COUPON_PROCESS_ID,
+ SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID,
+ UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID,
+} from '../user-state';
import { PROCESS_FEATURE } from '../../../process/store';
@@ -68,6 +68,7 @@ const customerCoupon2Customer: CustomerCoupon2Customer = {
coupon: coupon1,
customer: {},
};
+const error = new Error('mockError');
describe('Customer Coupon Actions', () => {
describe('LoadCustomerCoupons Action', () => {
@@ -95,6 +96,7 @@ describe('Customer Coupon Actions', () => {
expect({ ...action }).toEqual({
type: UserActions.LOAD_CUSTOMER_COUPONS_FAIL,
payload: error,
+ error,
meta: failMeta(CUSTOMER_COUPONS, error),
});
});
@@ -145,12 +147,12 @@ 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,
@@ -211,12 +213,12 @@ 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,
@@ -276,12 +278,12 @@ 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 f2d0295f18f..72acd8593fb 100644
--- a/projects/core/src/user/store/actions/customer-coupon.action.ts
+++ b/projects/core/src/user/store/actions/customer-coupon.action.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import {
CustomerCoupon2Customer,
CustomerCouponNotification,
@@ -78,7 +79,10 @@ 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);
@@ -112,7 +116,10 @@ export class SubscribeCustomerCoupon extends EntityLoadAction {
}
}
-export class SubscribeCustomerCouponFail extends EntityFailAction {
+export class SubscribeCustomerCouponFail
+ extends EntityFailAction
+ implements ErrorAction
+{
readonly type = SUBSCRIBE_CUSTOMER_COUPON_FAIL;
constructor(public payload: any) {
super(PROCESS_FEATURE, SUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, payload);
@@ -145,7 +152,10 @@ export class UnsubscribeCustomerCoupon extends EntityLoadAction {
}
}
-export class UnsubscribeCustomerCouponFail extends EntityFailAction {
+export class UnsubscribeCustomerCouponFail
+ extends EntityFailAction
+ implements ErrorAction
+{
readonly type = UNSUBSCRIBE_CUSTOMER_COUPON_FAIL;
constructor(public payload: any) {
super(PROCESS_FEATURE, UNSUBSCRIBE_CUSTOMER_COUPON_PROCESS_ID, payload);
@@ -197,7 +207,10 @@ export class ResetDisclaimCustomerCoupon extends EntityLoaderResetAction {
}
}
-export class DisclaimCustomerCouponFail extends EntityFailAction {
+export class DisclaimCustomerCouponFail
+ extends EntityFailAction
+ implements ErrorAction
+{
readonly type = DISCLAIM_CUSTOMER_COUPON_FAIL;
constructor(public payload: any) {
super(PROCESS_FEATURE, DISCLAIM_CUSTOMER_COUPON_PROCESS_ID, payload);
@@ -211,7 +224,10 @@ export class DisclaimCustomerCouponSuccess extends EntitySuccessAction {
}
}
-export class ClaimCustomerCouponFail extends EntityFailAction {
+export class ClaimCustomerCouponFail
+ extends EntityFailAction
+ implements ErrorAction
+{
readonly type = CLAIM_CUSTOMER_COUPON_FAIL;
constructor(public payload: any) {
super(PROCESS_FEATURE, CLAIM_CUSTOMER_COUPON_PROCESS_ID, payload);
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..2b371ee7a1d 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,13 @@ 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 8bb34ae8c2d..778f9465ab9 100644
--- a/projects/core/src/user/store/actions/delivery-countries.action.ts
+++ b/projects/core/src/user/store/actions/delivery-countries.action.ts
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import { Country } from '../../../model/address.model';
export const LOAD_DELIVERY_COUNTRIES = '[User] Load Delivery Countries';
@@ -15,18 +16,24 @@ 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 {
+ public error: any;
readonly type = LOAD_DELIVERY_COUNTRIES_FAIL;
- constructor(public payload: any) {}
+
+ constructor(public payload: any) {
+ this.error = payload;
+ }
}
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..4c212ce6706 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
@@ -1,12 +1,12 @@
-import { UserActions } from './index';
import { NotificationPreference } from '../../../model/notification-preference.model';
-import { StateUtils } from '../../../state/utils/index';
import { PROCESS_FEATURE } from '../../../process/store/process-state';
+import { StateUtils } from '../../../state/utils/index';
+import { resetMeta } from '../../../state/utils/loader/loader.action';
import {
- UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID,
NOTIFICATION_PREFERENCES,
+ UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID,
} from '../user-state';
-import { resetMeta } from '../../../state/utils/loader/loader.action';
+import { UserActions } from './index';
const userId = 'testUser';
const mockNotificationPreference: NotificationPreference[] = [
@@ -17,7 +17,7 @@ const mockNotificationPreference: NotificationPreference[] = [
visible: true,
},
];
-const error = 'anError';
+const error = new Error('anError');
describe('Notification Preference Actions', () => {
describe('LoadNotificationPreferences', () => {
@@ -37,6 +37,7 @@ describe('Notification Preference Actions', () => {
expect({ ...action }).toEqual({
type: UserActions.LOAD_NOTIFICATION_PREFERENCES_FAIL,
payload: error,
+ error,
meta: StateUtils.failMeta(NOTIFICATION_PREFERENCES, error),
});
});
@@ -81,6 +82,7 @@ describe('Notification Preference Actions', () => {
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 164c3443a49..5f096590b11 100644
--- a/projects/core/src/user/store/actions/notification-preference.action.ts
+++ b/projects/core/src/user/store/actions/notification-preference.action.ts
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { StateUtils } from '../../../state/utils/index';
+import { ErrorAction } from '../../../error-handling';
+import { NotificationPreference } from '../../../model/notification-preference.model';
import { PROCESS_FEATURE } from '../../../process/store/process-state';
import {
EntityFailAction,
@@ -12,11 +13,11 @@ import {
EntityLoaderResetAction,
EntitySuccessAction,
} from '../../../state/utils/entity-loader/entity-loader.action';
+import { StateUtils } from '../../../state/utils/index';
import {
- UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID,
NOTIFICATION_PREFERENCES,
+ UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID,
} from '../user-state';
-import { NotificationPreference } from '../../../model/notification-preference.model';
export const LOAD_NOTIFICATION_PREFERENCES =
'[User] Load Notification Preferences';
@@ -37,13 +38,18 @@ export const CLEAR_NOTIFICATION_PREFERENCES =
export class LoadNotificationPreferences extends StateUtils.LoaderLoadAction {
readonly type = LOAD_NOTIFICATION_PREFERENCES;
+
constructor(public payload: string) {
super(NOTIFICATION_PREFERENCES);
}
}
-export class LoadNotificationPreferencesFail extends StateUtils.LoaderFailAction {
+export class LoadNotificationPreferencesFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_NOTIFICATION_PREFERENCES_FAIL;
+
constructor(public payload: any) {
super(NOTIFICATION_PREFERENCES, payload);
}
@@ -51,6 +57,7 @@ export class LoadNotificationPreferencesFail extends StateUtils.LoaderFailAction
export class LoadNotificationPreferencesSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_NOTIFICATION_PREFERENCES_SUCCESS;
+
constructor(public payload: NotificationPreference[]) {
super(NOTIFICATION_PREFERENCES);
}
@@ -58,6 +65,7 @@ export class LoadNotificationPreferencesSuccess extends StateUtils.LoaderSuccess
export class UpdateNotificationPreferences extends EntityLoadAction {
readonly type = UPDATE_NOTIFICATION_PREFERENCES;
+
constructor(
public payload: { userId: string; preferences: NotificationPreference[] }
) {
@@ -65,8 +73,12 @@ export class UpdateNotificationPreferences extends EntityLoadAction {
}
}
-export class UpdateNotificationPreferencesFail extends EntityFailAction {
+export class UpdateNotificationPreferencesFail
+ extends EntityFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_NOTIFICATION_PREFERENCES_FAIL;
+
constructor(public payload: any) {
super(PROCESS_FEATURE, UPDATE_NOTIFICATION_PREFERENCES_PROCESS_ID, payload);
}
@@ -74,6 +86,7 @@ export class UpdateNotificationPreferencesFail extends EntityFailAction {
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 +94,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 +102,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..df3f32316b0 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,11 +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,
+ error,
payload: error,
meta: StateUtils.failMeta(USER_PAYMENT_METHODS, error),
});
@@ -62,11 +63,12 @@ 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({
+ payload: error,
+ 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 +97,12 @@ 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),
+ payload: error,
+ 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 2121b27eb67..b174c079e05 100644
--- a/projects/core/src/user/store/actions/payment-methods.action.ts
+++ b/projects/core/src/user/store/actions/payment-methods.action.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { PaymentDetails } from '../../../model/payment.model';
import { StateUtils } from '../../../state/utils/index';
import { USER_PAYMENT_METHODS } from '../user-state';
@@ -29,13 +30,18 @@ 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);
}
}
-export class LoadUserPaymentMethodsFail extends StateUtils.LoaderFailAction {
+export class LoadUserPaymentMethodsFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_PAYMENT_METHODS_FAIL;
+
constructor(public payload: any) {
super(USER_PAYMENT_METHODS, payload);
}
@@ -43,6 +49,7 @@ export class LoadUserPaymentMethodsFail extends StateUtils.LoaderFailAction {
export class LoadUserPaymentMethodsSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_USER_PAYMENT_METHODS_SUCCESS;
+
constructor(public payload: PaymentDetails[]) {
super(USER_PAYMENT_METHODS);
}
@@ -50,13 +57,18 @@ 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);
}
}
-export class SetDefaultUserPaymentMethodFail extends StateUtils.LoaderFailAction {
+export class SetDefaultUserPaymentMethodFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = SET_DEFAULT_USER_PAYMENT_METHOD_FAIL;
+
constructor(public payload: any) {
super(USER_PAYMENT_METHODS, payload);
}
@@ -64,6 +76,7 @@ export class SetDefaultUserPaymentMethodFail extends StateUtils.LoaderFailAction
export class SetDefaultUserPaymentMethodSuccess extends StateUtils.LoaderSuccessAction {
readonly type = SET_DEFAULT_USER_PAYMENT_METHOD_SUCCESS;
+
constructor(public payload: any) {
super(USER_PAYMENT_METHODS);
}
@@ -71,13 +84,18 @@ 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);
}
}
-export class DeleteUserPaymentMethodFail extends StateUtils.LoaderFailAction {
+export class DeleteUserPaymentMethodFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = DELETE_USER_PAYMENT_METHOD_FAIL;
+
constructor(public payload: any) {
super(USER_PAYMENT_METHODS, payload);
}
@@ -85,6 +103,7 @@ export class DeleteUserPaymentMethodFail extends StateUtils.LoaderFailAction {
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..f494a32b541 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
@@ -1,24 +1,25 @@
+import { NotificationType } from '../../../model/product-interest.model';
+import { PROCESS_FEATURE } from '../../../process/store/process-state';
import {
- PRODUCT_INTERESTS,
- REMOVE_PRODUCT_INTERESTS_PROCESS_ID,
- ADD_PRODUCT_INTEREST_PROCESS_ID,
-} from '../user-state';
-import { UserActions } from './index';
+ entityFailMeta,
+ entityLoadMeta,
+ entityResetMeta,
+ entitySuccessMeta,
+} from '../../../state/utils/entity-loader/entity-loader.action';
import {
- loadMeta,
failMeta,
- successMeta,
+ loadMeta,
resetMeta,
+ successMeta,
} from '../../../state/utils/loader/loader.action';
-import { NotificationType } from '../../../model/product-interest.model';
import {
- entityLoadMeta,
- entitySuccessMeta,
- entityFailMeta,
- entityResetMeta,
-} from '../../../state/utils/entity-loader/entity-loader.action';
-import { PROCESS_FEATURE } from '../../../process/store/process-state';
+ ADD_PRODUCT_INTEREST_PROCESS_ID,
+ PRODUCT_INTERESTS,
+ REMOVE_PRODUCT_INTERESTS_PROCESS_ID,
+} from '../user-state';
+import { UserActions } from './index';
+const error = new Error('error');
const userId = 'qingyu@sap.com';
const productCode = '343898';
@@ -43,11 +44,11 @@ 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 +106,11 @@ 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 +153,11 @@ 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 f04c73b486a..6c5e7de9195 100644
--- a/projects/core/src/user/store/actions/product-interests.actions.ts
+++ b/projects/core/src/user/store/actions/product-interests.actions.ts
@@ -4,29 +4,30 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import {
- 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,
- LoaderResetAction,
-} from '../../../state/utils/loader/loader.action';
import {
EntityFailAction,
EntityLoadAction,
- EntitySuccessAction,
EntityLoaderResetAction,
+ EntitySuccessAction,
} from '../../../state/utils/entity-loader/entity-loader.action';
+import {
+ LoaderFailAction,
+ LoaderLoadAction,
+ LoaderResetAction,
+ LoaderSuccessAction,
+} from '../../../state/utils/loader/loader.action';
+import {
+ ADD_PRODUCT_INTEREST_PROCESS_ID,
+ PRODUCT_INTERESTS,
+ REMOVE_PRODUCT_INTERESTS_PROCESS_ID,
+} from '../user-state';
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;
@@ -62,8 +64,12 @@ export class LoadProductInterests extends LoaderLoadAction {
}
}
-export class LoadProductInterestsFail extends LoaderFailAction {
+export class LoadProductInterestsFail
+ extends LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_PRODUCT_INTERESTS_FAIL;
+
constructor(public payload: any) {
super(PRODUCT_INTERESTS, payload);
}
@@ -71,6 +77,7 @@ export class LoadProductInterestsFail extends LoaderFailAction {
export class LoadProductInterestsSuccess extends LoaderSuccessAction {
readonly type = LOAD_PRODUCT_INTERESTS_SUCCESS;
+
constructor(public payload: ProductInterestSearchResult) {
super(PRODUCT_INTERESTS);
}
@@ -78,6 +85,7 @@ export class LoadProductInterestsSuccess extends LoaderSuccessAction {
export class RemoveProductInterest extends EntityLoadAction {
readonly type = REMOVE_PRODUCT_INTEREST;
+
constructor(
public payload: {
userId: string;
@@ -91,13 +99,18 @@ 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);
}
}
-export class RemoveProductInterestFail extends EntityFailAction {
+export class RemoveProductInterestFail
+ extends EntityFailAction
+ implements ErrorAction
+{
readonly type = REMOVE_PRODUCT_INTEREST_FAIL;
+
constructor(public payload: any) {
super(PROCESS_FEATURE, REMOVE_PRODUCT_INTERESTS_PROCESS_ID, payload);
}
@@ -105,6 +118,7 @@ export class RemoveProductInterestFail extends EntityFailAction {
export class AddProductInterest extends EntityLoadAction {
readonly type = ADD_PRODUCT_INTEREST;
+
constructor(
public payload: {
userId: string;
@@ -118,13 +132,18 @@ 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);
}
}
-export class AddProductInterestFail extends EntityFailAction {
+export class AddProductInterestFail
+ extends EntityFailAction
+ implements ErrorAction
+{
readonly type = ADD_PRODUCT_INTEREST_FAIL;
+
constructor(public payload: any) {
super(PROCESS_FEATURE, ADD_PRODUCT_INTEREST_PROCESS_ID, payload);
}
@@ -132,6 +151,7 @@ export class AddProductInterestFail extends EntityFailAction {
export class ResetAddInterestState extends EntityLoaderResetAction {
readonly type = ADD_PRODUCT_INTEREST_RESET;
+
constructor() {
super(PROCESS_FEATURE, ADD_PRODUCT_INTEREST_PROCESS_ID);
}
@@ -139,6 +159,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 +167,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..733093e9edc 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,13 @@ 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 2d8364c970f..dba607e68f9 100644
--- a/projects/core/src/user/store/actions/regions.action.ts
+++ b/projects/core/src/user/store/actions/regions.action.ts
@@ -5,6 +5,7 @@
*/
import { Action } from '@ngrx/store';
+import { ErrorAction } from '../../../error-handling';
import { Region } from '../../../model/address.model';
import { StateUtils } from '../../../state/utils/index';
import { REGIONS } from '../user-state';
@@ -16,13 +17,18 @@ export const CLEAR_REGIONS = '[User] Clear Regions';
export class LoadRegions extends StateUtils.LoaderLoadAction {
readonly type = LOAD_REGIONS;
+
constructor(public payload: string) {
super(REGIONS);
}
}
-export class LoadRegionsFail extends StateUtils.LoaderFailAction {
+export class LoadRegionsFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_REGIONS_FAIL;
+
constructor(public payload: any) {
super(REGIONS, payload);
}
@@ -30,6 +36,7 @@ export class LoadRegionsFail extends StateUtils.LoaderFailAction {
export class LoadRegionsSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_REGIONS_SUCCESS;
+
constructor(public payload: { entities: Region[]; country: string }) {
super(REGIONS);
}
@@ -37,6 +44,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..aaadccce2fa 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,12 @@ 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 +71,12 @@ 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 +118,12 @@ 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 5ec4aa8a337..cecd9929d41 100644
--- a/projects/core/src/user/store/actions/user-addresses.action.ts
+++ b/projects/core/src/user/store/actions/user-addresses.action.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { Address } from '../../../model/address.model';
import { StateUtils } from '../../../state/utils/index';
import { USER_ADDRESSES } from '../user-state';
@@ -26,13 +27,18 @@ 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);
}
}
-export class LoadUserAddressesFail extends StateUtils.LoaderFailAction {
+export class LoadUserAddressesFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_ADDRESSES_FAIL;
+
constructor(public payload: any) {
super(USER_ADDRESSES, payload);
}
@@ -40,6 +46,7 @@ export class LoadUserAddressesFail extends StateUtils.LoaderFailAction {
export class LoadUserAddressesSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_USER_ADDRESSES_SUCCESS;
+
constructor(public payload: Address[]) {
super(USER_ADDRESSES);
}
@@ -48,13 +55,18 @@ 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);
}
}
-export class AddUserAddressFail extends StateUtils.LoaderFailAction {
+export class AddUserAddressFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = ADD_USER_ADDRESS_FAIL;
+
constructor(public payload: any) {
super(USER_ADDRESSES, payload);
}
@@ -62,6 +74,7 @@ export class AddUserAddressFail extends StateUtils.LoaderFailAction {
export class AddUserAddressSuccess extends StateUtils.LoaderSuccessAction {
readonly type = ADD_USER_ADDRESS_SUCCESS;
+
constructor(public payload: any) {
super(USER_ADDRESSES);
}
@@ -70,6 +83,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 }
) {
@@ -77,8 +91,12 @@ export class UpdateUserAddress extends StateUtils.LoaderLoadAction {
}
}
-export class UpdateUserAddressFail extends StateUtils.LoaderFailAction {
+export class UpdateUserAddressFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = UPDATE_USER_ADDRESS_FAIL;
+
constructor(public payload: any) {
super(USER_ADDRESSES, payload);
}
@@ -86,6 +104,7 @@ export class UpdateUserAddressFail extends StateUtils.LoaderFailAction {
export class UpdateUserAddressSuccess extends StateUtils.LoaderSuccessAction {
readonly type = UPDATE_USER_ADDRESS_SUCCESS;
+
constructor(public payload: any) {
super(USER_ADDRESSES);
}
@@ -94,13 +113,18 @@ 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);
}
}
-export class DeleteUserAddressFail extends StateUtils.LoaderFailAction {
+export class DeleteUserAddressFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = DELETE_USER_ADDRESS_FAIL;
+
constructor(public payload: any) {
super(USER_ADDRESSES, payload);
}
@@ -108,6 +132,7 @@ export class DeleteUserAddressFail extends StateUtils.LoaderFailAction {
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..1d4f13d69bb 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,13 @@ 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),
+ payload: error,
+ error,
+ meta: StateUtils.failMeta(USER_CONSENTS, error),
});
});
});
@@ -72,14 +73,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 c4179cbcc5b..9a39cf2c332 100644
--- a/projects/core/src/user/store/actions/user-consents.action.ts
+++ b/projects/core/src/user/store/actions/user-consents.action.ts
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { ConsentTemplate } from '../../../model/consent.model';
import { PROCESS_FEATURE } from '../../../process/store/process-state';
import { StateUtils } from '../../../state/utils/index';
@@ -34,13 +35,18 @@ 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);
}
}
-export class LoadUserConsentsFail extends StateUtils.LoaderFailAction {
+export class LoadUserConsentsFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_USER_CONSENTS_FAIL;
+
constructor(public payload: any) {
super(USER_CONSENTS, payload);
}
@@ -48,6 +54,7 @@ export class LoadUserConsentsFail extends StateUtils.LoaderFailAction {
export class LoadUserConsentsSuccess extends StateUtils.LoaderSuccessAction {
readonly type = LOAD_USER_CONSENTS_SUCCESS;
+
constructor(public payload: ConsentTemplate[]) {
super(USER_CONSENTS);
}
@@ -55,6 +62,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 +70,7 @@ export class ResetLoadUserConsents extends StateUtils.LoaderResetAction {
export class GiveUserConsent extends StateUtils.EntityLoadAction {
readonly type = GIVE_USER_CONSENT;
+
constructor(
public payload: {
userId: string;
@@ -73,8 +82,12 @@ export class GiveUserConsent extends StateUtils.EntityLoadAction {
}
}
-export class GiveUserConsentFail extends StateUtils.EntityFailAction {
+export class GiveUserConsentFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = GIVE_USER_CONSENT_FAIL;
+
constructor(payload: any) {
super(PROCESS_FEATURE, GIVE_CONSENT_PROCESS_ID, payload);
}
@@ -82,6 +95,7 @@ export class GiveUserConsentFail extends StateUtils.EntityFailAction {
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 +103,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 +111,7 @@ export class ResetGiveUserConsentProcess extends StateUtils.EntityLoaderResetAct
export class TransferAnonymousConsent {
readonly type = TRANSFER_ANONYMOUS_CONSENT;
+
constructor(
public payload: {
userId: string;
@@ -107,6 +123,7 @@ export class TransferAnonymousConsent {
export class WithdrawUserConsent extends StateUtils.EntityLoadAction {
readonly type = WITHDRAW_USER_CONSENT;
+
constructor(
public payload: {
userId: string;
@@ -118,8 +135,12 @@ export class WithdrawUserConsent extends StateUtils.EntityLoadAction {
}
}
-export class WithdrawUserConsentFail extends StateUtils.EntityFailAction {
+export class WithdrawUserConsentFail
+ extends StateUtils.EntityFailAction
+ implements ErrorAction
+{
readonly type = WITHDRAW_USER_CONSENT_FAIL;
+
constructor(payload: any) {
super(PROCESS_FEATURE, WITHDRAW_CONSENT_PROCESS_ID, payload);
}
@@ -127,6 +148,7 @@ export class WithdrawUserConsentFail extends StateUtils.EntityFailAction {
export class WithdrawUserConsentSuccess extends StateUtils.EntitySuccessAction {
readonly type = WITHDRAW_USER_CONSENT_SUCCESS;
+
constructor() {
super(PROCESS_FEATURE, WITHDRAW_CONSENT_PROCESS_ID);
}
@@ -134,6 +156,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..ebbae394223 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,13 @@ 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 c9665585f04..424c06130c4 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
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { ErrorAction } from '../../../error-handling';
import { CostCenter } from '../../../model/org-unit.model';
import { StateUtils } from '../../../state/utils/index';
import { USER_COST_CENTERS } from '../user-state';
@@ -21,7 +22,10 @@ export class LoadActiveCostCenters extends StateUtils.LoaderLoadAction {
}
}
-export class LoadActiveCostCentersFail extends StateUtils.LoaderFailAction {
+export class LoadActiveCostCentersFail
+ extends StateUtils.LoaderFailAction
+ implements ErrorAction
+{
readonly type = LOAD_ACTIVE_COST_CENTERS_FAIL;
constructor(public payload: any) {
super(USER_COST_CENTERS, payload);
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 b59be7d1391..e298b338f93 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 7b99d2dcd8d..1cda07b887d 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)
)
)
)
@@ -159,7 +159,7 @@ export class CustomerCouponEffects {
catchError((error) =>
of(
new fromCustomerCouponsAction.DisclaimCustomerCouponFail(
- 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 57ede26283b..c66de134353 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 cdb60dbfcb7..d2e80f19430 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 fbc6a5960c1..2c6467e54c9 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 5b24aaf367b..3d286419b16 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 47f211dbdcc..217cb3a1c96 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 b9d0d0c648a..f35e74e63ae 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 9a177d87e69..35b9d0baf2a 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 8e9f5c3a70d..54274d431f7 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 926ee05688a..0594c4d54f0 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
@@ -8,7 +8,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';
@@ -121,7 +121,7 @@ describe('User Consents effect', () => {
consentTemplateVersion,
});
const completion = new UserActions.GiveUserConsentFail(
- normalizeHttpError(mockError, new MockLoggerService())
+ tryNormalizeHttpError(mockError, new MockLoggerService())
);
const closeMessage = new GlobalMessageActions.RemoveMessagesByType(
GlobalMessageType.MSG_TYPE_ERROR
@@ -148,7 +148,7 @@ describe('User Consents effect', () => {
consentTemplateVersion,
});
const completion = new UserActions.GiveUserConsentFail(
- normalizeHttpError(mockError, new MockLoggerService())
+ tryNormalizeHttpError(mockError, new MockLoggerService())
);
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 0b1125f9ef2..9e6b4734de3 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 8a7d6115246..58169441ed8 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 6c5d527cb71..5e72aca6a71 100644
--- a/projects/core/src/util/index.ts
+++ b/projects/core/src/util/index.ts
@@ -21,5 +21,6 @@ export * from './script-loader.service';
export * from './ssr.tokens';
export * from './testing-time-utils';
export * from './time-utils';
+export * from './try-normalize-http-error';
export * from './type-guards';
export * from './type-utils';
diff --git a/projects/core/src/util/normalize-http-error.ts b/projects/core/src/util/normalize-http-error.ts
index 9265f643bcf..37686a54794 100644
--- a/projects/core/src/util/normalize-http-error.ts
+++ b/projects/core/src/util/normalize-http-error.ts
@@ -16,6 +16,9 @@ import { HttpErrorModel } from '../model/misc.model';
* NgRx Action payload, as it will strip potentially unserializable parts from
* it and warn in debug mode if passed error is not instance of HttpErrorModel
* (which usually happens when logic in NgRx Effect is not sealed correctly)
+ *
+ * @deprecated since 2211.29 - use `tryNormalizeHttpError` instead. The `normalizeHttpError` will be removed from public API
+ * together with removing the feature toggle `ssrStrictErrorHandlingForHttpAndNgrx`.
*/
export function normalizeHttpError(
error: HttpErrorResponse | HttpErrorModel | any,
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..09f3324c6c5
--- /dev/null
+++ b/projects/core/src/util/try-normalize-http-error.ts
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { HttpErrorResponse } from '@angular/common/http';
+import { LoggerService } from '../logger';
+import { HttpErrorModel } from '../model';
+import { normalizeHttpError } from './normalize-http-error';
+
+export function tryNormalizeHttpError(
+ error: HttpErrorResponse | HttpErrorModel | any,
+ logger: LoggerService
+): HttpErrorModel | Error {
+ return normalizeHttpError(error, logger) ?? error;
+}
diff --git a/projects/schematics/src/add-ssr/__snapshots__/index_spec.ts.snap b/projects/schematics/src/add-ssr/__snapshots__/index_spec.ts.snap
index bce7410a456..598769c2b12 100644
--- a/projects/schematics/src/add-ssr/__snapshots__/index_spec.ts.snap
+++ b/projects/schematics/src/add-ssr/__snapshots__/index_spec.ts.snap
@@ -205,14 +205,20 @@ exports[`add-ssr server.ts should be configured properly 1`] = `
"import { APP_BASE_HREF } from '@angular/common';
import {
NgExpressEngineDecorator,
+ defaultExpressErrorHandlers,
ngExpressEngine as engine,
} from '@spartacus/setup/ssr';
import express from 'express';
+import { readFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import AppServerModule from './src/main.server';
-const ngExpressEngine = NgExpressEngineDecorator.get(engine);
+const ngExpressEngine = NgExpressEngineDecorator.get(engine, {
+ ssrFeatureToggles: {
+ avoidCachingErrors: true,
+ },
+});
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
@@ -220,6 +226,7 @@ export function app(): express.Express {
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(browserDistFolder, 'index.html');
+ const indexHtmlContent = readFileSync(indexHtml, 'utf-8');
server.set('trust proxy', 'loopback');
@@ -249,6 +256,8 @@ export function app(): express.Express {
});
});
+ server.use(defaultExpressErrorHandlers(indexHtmlContent))
+
return server;
}
diff --git a/projects/schematics/src/add-ssr/files/server.__typescriptExt__ b/projects/schematics/src/add-ssr/files/server.__typescriptExt__
index cb8f879adab..75e2cac08e0 100644
--- a/projects/schematics/src/add-ssr/files/server.__typescriptExt__
+++ b/projects/schematics/src/add-ssr/files/server.__typescriptExt__
@@ -1,14 +1,20 @@
import { APP_BASE_HREF } from '@angular/common';
import {
NgExpressEngineDecorator,
+ defaultExpressErrorHandlers,
ngExpressEngine as engine,
} from '@spartacus/setup/ssr';
import express from 'express';
+import { readFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import AppServerModule from './src/main.server';
-const ngExpressEngine = NgExpressEngineDecorator.get(engine);
+const ngExpressEngine = NgExpressEngineDecorator.get(engine, {
+ ssrFeatureToggles: {
+ avoidCachingErrors: true,
+ },
+});
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
@@ -16,6 +22,7 @@ export function app(): express.Express {
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(browserDistFolder, 'index.html');
+ const indexHtmlContent = readFileSync(indexHtml, 'utf-8');
server.set('trust proxy', 'loopback');
@@ -45,6 +52,8 @@ export function app(): express.Express {
});
});
+ server.use(defaultExpressErrorHandlers(indexHtmlContent))
+
return server;
}
diff --git a/projects/schematics/src/shared/lib-configs/quote-schematics-config.ts b/projects/schematics/src/shared/lib-configs/quote-schematics-config.ts
index 356bfdb1520..7aaf2560f55 100644
--- a/projects/schematics/src/shared/lib-configs/quote-schematics-config.ts
+++ b/projects/schematics/src/shared/lib-configs/quote-schematics-config.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/ssr-tests/README.md b/projects/ssr-tests/README.md
index ed58e9896c4..e95e33c8d9e 100644
--- a/projects/ssr-tests/README.md
+++ b/projects/ssr-tests/README.md
@@ -4,7 +4,7 @@ This project is for testing server-side rendering in Spartacus.
## Running Tests
-Before running the test suite, we first need to build our application with `npm install && npm run build:libs && npm run build:ssr:local-http` from the root of the project.
+Before running the test suite, we first need to build our application with `npm install && npm run build:libs && npm run build:ssr:local-http-backend` from the root of the project.
After the build, use the `npm run test:ssr` and `npm run test:ssr:ci` commands from the root of the project to run the tests.
diff --git a/projects/ssr-tests/src/matchers/matchers.ts b/projects/ssr-tests/src/matchers/matchers.ts
new file mode 100644
index 00000000000..f21d03ba33c
--- /dev/null
+++ b/projects/ssr-tests/src/matchers/matchers.ts
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { getLogMessages } from '../utils/log.utils';
+
+expect.extend({
+ toContainLogs(received: string[], expected: string[]) {
+ const receivedString = received.join('\n');
+ const expectedString = Array.isArray(expected)
+ ? expected.join('\n')
+ : expected;
+ const pass = receivedString.includes(expectedString);
+ if (pass) {
+ return {
+ message: () =>
+ `Expected log messages not to contain:\n${expectedString}`,
+ pass: true,
+ };
+ } else {
+ return {
+ message: () =>
+ `Expected log messages to contain:\n${expectedString}\n\nbut received:\n${receivedString}`,
+ pass: false,
+ };
+ }
+ },
+});
+
+interface CustomMatchers {
+ toContainLogs(expected: string[] | string): R;
+}
+
+declare global {
+ namespace jest {
+ interface Expect extends CustomMatchers {}
+ interface Matchers extends CustomMatchers {}
+ interface InverseAsymmetricMatchers extends CustomMatchers {}
+ }
+}
+
+/**
+ * Return a jest matcher that can be used to check log messages.
+ * @returns The jest matcher.
+ */
+export function expectLogMessages(): jest.JestMatchers {
+ return expect(getLogMessages());
+}
diff --git a/projects/ssr-tests/src/proxy.utils.ts b/projects/ssr-tests/src/proxy.utils.ts
deleted file mode 100644
index 7051b2128d0..00000000000
--- a/projects/ssr-tests/src/proxy.utils.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
- * SPDX-FileCopyrightText: 2024 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
index 75ece7c640c..c4f11b49fcf 100644
--- a/projects/ssr-tests/src/ssr-testing.spec.ts
+++ b/projects/ssr-tests/src/ssr-testing.spec.ts
@@ -1,54 +1,134 @@
-import * as Log from './log.utils';
-import * as ProxyServer from './proxy.utils';
-import * as Ssr from './ssr.utils';
+import { Server } from 'http';
+import { expectLogMessages } from './matchers/matchers';
+import * as HttpUtils from './utils/http.utils';
+import * as LogUtils from './utils/log.utils';
+import * as ProxyUtils from './utils/proxy.utils';
+import * as SsrUtils from './utils/ssr.utils';
const BACKEND_BASE_URL: string = process.env.CX_BASE_URL || '';
+jest.setTimeout(SsrUtils.DEFAULT_SSR_TIMEOUT); // set timeout to at least 1x DEFAULT_SSR_TIMEOUT seconds for each test in this file to increase stability of the tests
+
describe('SSR E2E', () => {
- let proxy: any;
- const REQUEST_PATH = '/electronics-spa/en/USD/';
+ let backendProxy: Server;
+ const REQUEST_PATH = '/contact'; // path to the page that is less "busy" than the homepage
- beforeEach(async () => {
- Log.clearSsrLogFile();
- await Ssr.startSsrServer();
+ beforeEach(() => {
+ LogUtils.clearSsrLogFile();
});
afterEach(async () => {
- await proxy.close();
- await Ssr.killSsrServer();
+ backendProxy.close();
+ await SsrUtils.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})`,
- ]);
- });
+ describe('With SSR error handling', () => {
+ describe('Common behavior', () => {
+ beforeEach(async () => {
+ await SsrUtils.startSsrServer();
+ });
+
+ it('should receive success response with request', async () => {
+ backendProxy = await ProxyUtils.startBackendProxyServer({
+ target: BACKEND_BASE_URL,
+ });
+ const response: any =
+ await HttpUtils.sendRequestToSsrServer(REQUEST_PATH);
+ expect(response.statusCode).toEqual(200);
+
+ expectLogMessages().toContainLogs([
+ `Rendering started (${REQUEST_PATH})`,
+ `Request is waiting for the SSR rendering to complete (${REQUEST_PATH})`,
+ ]);
+ });
+
+ it('should receive response with 404 when page does not exist', async () => {
+ backendProxy = await ProxyUtils.startBackendProxyServer({
+ target: BACKEND_BASE_URL,
+ });
+ const response = await HttpUtils.sendRequestToSsrServer(
+ REQUEST_PATH + 'not-existing-page'
+ );
+ expect(response.statusCode).toEqual(404);
+ });
+
+ it('should receive response with status 404 if HTTP error occurred when calling cms/pages API URL', async () => {
+ backendProxy = await ProxyUtils.startBackendProxyServer({
+ target: BACKEND_BASE_URL,
+ callback: (proxyRes, req) => {
+ if (req.url?.includes('cms/pages')) {
+ proxyRes.statusCode = 404;
+ }
+ },
+ });
+ const response = await HttpUtils.sendRequestToSsrServer(REQUEST_PATH);
+ expect(response.statusCode).toEqual(404);
+ });
- it('should receive 404 response when page is not existing', async () => {
- proxy = await ProxyServer.startProxyServer({
- target: BACKEND_BASE_URL,
+ it('should receive response with status 500 if HTTP error occurred when calling other than cms/pages API URL', async () => {
+ backendProxy = await ProxyUtils.startBackendProxyServer({
+ target: BACKEND_BASE_URL,
+ callback: (proxyRes, req) => {
+ if (req.url?.includes('cms/components')) {
+ proxyRes.statusCode = 404;
+ }
+ },
+ });
+ const response = await HttpUtils.sendRequestToSsrServer(REQUEST_PATH);
+ expect(response.statusCode).toEqual(500);
+ });
});
- 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({
- target: BACKEND_BASE_URL,
- throwStatus: 500,
+ describe('With caching enabled', () => {
+ beforeEach(async () => {
+ await SsrUtils.startSsrServer({ cache: true });
+ });
+
+ it(
+ 'should take the response from cache for the next request if previous render succeeded',
+ async () => {
+ backendProxy = await ProxyUtils.startBackendProxyServer({
+ target: BACKEND_BASE_URL,
+ });
+ let response: HttpUtils.SsrResponse;
+ response = await HttpUtils.sendRequestToSsrServer(REQUEST_PATH);
+ expect(response.statusCode).toEqual(200);
+
+ expectLogMessages().toContainLogs([
+ `Rendering started (${REQUEST_PATH})`,
+ `Request is waiting for the SSR rendering to complete (${REQUEST_PATH})`,
+ ]);
+
+ response = await HttpUtils.sendRequestToSsrServer(REQUEST_PATH);
+ expect(response.statusCode).toEqual(200);
+ expectLogMessages().toContain(`Render from cache (${REQUEST_PATH})`);
+ },
+ 2 * SsrUtils.DEFAULT_SSR_TIMEOUT // increase timeout for this test as it calls the SSR server twice
+ );
+
+ it(
+ 'should render for the next request if previous render failed',
+ async () => {
+ backendProxy = await ProxyUtils.startBackendProxyServer({
+ target: BACKEND_BASE_URL,
+ callback: (proxyRes, req) => {
+ if (req.url?.includes('cms/pages')) {
+ proxyRes.statusCode = 404;
+ }
+ },
+ });
+ let response: HttpUtils.SsrResponse;
+ response = await HttpUtils.sendRequestToSsrServer(REQUEST_PATH);
+ expect(response.statusCode).toEqual(404);
+
+ response = await HttpUtils.sendRequestToSsrServer(REQUEST_PATH);
+ expect(response.statusCode).toEqual(404);
+ expectLogMessages().not.toContain(
+ `Render from cache (${REQUEST_PATH})`
+ );
+ },
+ 2 * SsrUtils.DEFAULT_SSR_TIMEOUT // increase timeout for this test as it calls the SSR server twice
+ );
});
- const response: any = await ProxyServer.sendRequest('/');
- expect(response.statusCode).toEqual(500);
});
});
diff --git a/projects/ssr-tests/src/utils/http.utils.ts b/projects/ssr-tests/src/utils/http.utils.ts
new file mode 100644
index 00000000000..504abe5b4b8
--- /dev/null
+++ b/projects/ssr-tests/src/utils/http.utils.ts
@@ -0,0 +1,51 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as http from 'http';
+
+/**
+ * Default settings to send http requests.
+ */
+const REQUEST_OPTIONS = {
+ host: 'localhost',
+ port: 4000,
+};
+
+/**
+ * Response from SSR server.
+ */
+export interface SsrResponse {
+ statusCode: number | undefined;
+ headers: http.IncomingHttpHeaders;
+ body: string;
+}
+
+/**
+ * Send an http GET request with given URL to the SSR server.
+ */
+export async function sendRequestToSsrServer(
+ path: string
+): Promise {
+ return new Promise((resolve, reject) => {
+ http
+ .get({ ...REQUEST_OPTIONS, path }, (res) => {
+ let body = '';
+ res.on('data', (chunk) => {
+ body += chunk;
+ });
+ res.on('end', () => {
+ resolve({
+ statusCode: res.statusCode,
+ headers: res.headers,
+ body,
+ });
+ });
+ })
+ .on('error', (e: Error) => {
+ reject(e);
+ });
+ });
+}
diff --git a/projects/ssr-tests/src/log.utils.ts b/projects/ssr-tests/src/utils/log.utils.ts
similarity index 84%
rename from projects/ssr-tests/src/log.utils.ts
rename to projects/ssr-tests/src/utils/log.utils.ts
index 9e6284c5c5f..91d03bcdfce 100644
--- a/projects/ssr-tests/src/log.utils.ts
+++ b/projects/ssr-tests/src/utils/log.utils.ts
@@ -43,21 +43,12 @@ export function getLogMessages(): string[] {
// 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)
+ .map((text: any) => {
+ return 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.
diff --git a/projects/ssr-tests/src/utils/proxy.utils.ts b/projects/ssr-tests/src/utils/proxy.utils.ts
new file mode 100644
index 00000000000..931ef6ccfd0
--- /dev/null
+++ b/projects/ssr-tests/src/utils/proxy.utils.ts
@@ -0,0 +1,61 @@
+/*
+ * SPDX-FileCopyrightText: 2023 SAP Spartacus team
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import * as http from 'http';
+import httpProxy from 'http-proxy';
+
+/**
+ * Options to start a proxy server.
+ */
+interface ProxyOptions {
+ /**
+ * The url to reroute requests to.
+ */
+ target: string;
+ /**
+ * Number of seconds to delay requests before sending.
+ */
+ delay?: number;
+ /**
+ * Callback to be executed if the request to the target got a response.
+ */
+ callback?: httpProxy.ProxyResCallback;
+}
+
+/**
+ * Starts an http proxy server on port 9002 with the provided options.
+ */
+export async function startBackendProxyServer(
+ options: ProxyOptions
+): Promise {
+ const proxy = httpProxy.createProxyServer({
+ secure: false,
+ });
+ return new Promise((resolve) => {
+ const server = http.createServer((req, res) => {
+ const forwardRequest = () =>
+ proxy.web(req, res, { target: options.target });
+
+ if (options.callback) {
+ // Add one-time listener for the proxy response that stays until `proxyRes` event is triggered next time.
+ proxy.once('proxyRes', options.callback);
+ }
+
+ if (options.delay) {
+ setTimeout(() => {
+ forwardRequest();
+ }, options.delay);
+ } else {
+ forwardRequest();
+ }
+ });
+
+ server.listen(9002, () => {
+ resolve(server);
+ });
+ });
+}
diff --git a/projects/ssr-tests/src/ssr.utils.ts b/projects/ssr-tests/src/utils/ssr.utils.ts
similarity index 58%
rename from projects/ssr-tests/src/ssr.utils.ts
rename to projects/ssr-tests/src/utils/ssr.utils.ts
index 167751e84ba..3e5c01b671d 100644
--- a/projects/ssr-tests/src/ssr.utils.ts
+++ b/projects/ssr-tests/src/utils/ssr.utils.ts
@@ -12,6 +12,29 @@
import * as childProcess from 'child_process';
import * as Log from './log.utils';
+/**
+ * Default timeout for SSR rendering to happen.
+ */
+export const DEFAULT_SSR_TIMEOUT = 20000;
+
+export interface SsrServerOptions {
+ /**
+ * The port the server should run on.
+ * Default is 4000.
+ */
+ port?: number;
+ /**
+ * Whether to enable caching on the server.
+ * Default is false.
+ */
+ cache?: boolean;
+ /**
+ * Time in milliseconds to wait for SSR rendering to happen.
+ * Default is 20000.
+ */
+ timeout?: number;
+}
+
/**
* Used to track the spawned child process running the server.
*/
@@ -20,11 +43,15 @@ let child: childProcess.ChildProcess | any;
/**
* 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.
+ * Function finishes once the server is initialized.
*/
-export async function startSsrServer(port = 4000) {
+export async function startSsrServer({
+ port = 4000,
+ cache = false,
+ timeout = DEFAULT_SSR_TIMEOUT,
+}: SsrServerOptions = {}) {
child = childProcess.spawn(
- `NODE_TLS_REJECT_UNAUTHORIZED=0 PORT=${port} npm run serve:ssr --prefix ../../> .ssr.log`,
+ `NODE_TLS_REJECT_UNAUTHORIZED=0 SSR_CACHE=${cache} SSR_TIMEOUT=${timeout} PORT=${port} npm run serve:ssr --prefix ../../> .ssr.log`,
{ detached: true, shell: true }
);
diff --git a/projects/ssr-tests/tsconfig.json b/projects/ssr-tests/tsconfig.json
index 74bac1c56e0..8da3769aebe 100644
--- a/projects/ssr-tests/tsconfig.json
+++ b/projects/ssr-tests/tsconfig.json
@@ -17,6 +17,5 @@
"strictNullChecks": true,
"resolveJsonModule": true,
"esModuleInterop": true
- },
- "include": ["src/**.ts"]
+ }
}
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-email-management.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-email-management.ts
index c8c7247a2c5..75bc08b6e3d 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-email-management.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-email-management.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-profile-management.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-profile-management.ts
index a21125ba74b..bfe3d090c3d 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-profile-management.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/my-account-v2-profile-management.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.config.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.config.ts
index af64c5dfb4d..920091a5bcb 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.config.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.config.ts
@@ -1,13 +1,12 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
*/
import {
- TabbingOrderTypes,
TabbingOrderConfig,
+ TabbingOrderTypes,
} from '../../../../helpers/accessibility/tabbing-order.model';
export const tabbingOrderConfig: TabbingOrderConfig = {
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.e2e-my-account-v2.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.e2e-my-account-v2.cy.ts
index a19d1baf7d5..c2cad46bf44 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.e2e-my-account-v2.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/accessibility/tabbing-order.e2e-my-account-v2.cy.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-consent-management.e2e.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-consent-management.e2e.cy.ts
index 6af9b31fc16..1c4b13e57a9 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-consent-management.e2e.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-consent-management.e2e.cy.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-email.e2e.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-email.e2e.cy.ts
index b187ac61267..0834d5dcd59 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-email.e2e.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-email.e2e.cy.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-notification-preference.e2e.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-notification-preference.e2e.cy.ts
index 71de54e30ec..367f0658a8a 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-notification-preference.e2e.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-notification-preference.e2e.cy.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-password.e2e.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-password.e2e.cy.ts
index aa9ff1db434..9c40565e39f 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-password.e2e.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-password.e2e.cy.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-profile.e2e.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-profile.e2e.cy.ts
index ee359627742..bb558efcff6 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-profile.e2e.cy.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/regression/my-account/my-account-v2-profile.e2e.cy.ts
@@ -1,12 +1,11 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
*/
-import * as updateProfile from '../../../helpers/update-profile';
import * as login from '../../../helpers/login';
+import * as updateProfile from '../../../helpers/update-profile';
import { viewportContext } from '../../../helpers/viewport-context';
describe('My Account - Update Profile', () => {
diff --git a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-consent-management.ts b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-consent-management.ts
index 087b046ac32..108c2356b16 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-consent-management.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-consent-management.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-notification-preference.ts b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-notification-preference.ts
index 64a66fb5ded..28859d083af 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-notification-preference.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-notification-preference.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-password.ts b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-password.ts
index 177aa9c57db..a857d99a2ea 100644
--- a/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-password.ts
+++ b/projects/storefrontapp-e2e-cypress/cypress/helpers/accessibility/tabbing-order/my-account/my-account-v2-password.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp/server.ts b/projects/storefrontapp/server.ts
index 470661211a2..77ac8f6a53a 100644
--- a/projects/storefrontapp/server.ts
+++ b/projects/storefrontapp/server.ts
@@ -8,12 +8,13 @@ import { APP_BASE_HREF } from '@angular/common';
import {
NgExpressEngineDecorator,
SsrOptimizationOptions,
+ defaultExpressErrorHandlers,
defaultSsrOptimizationOptions,
ngExpressEngine as engine,
} from '@spartacus/setup/ssr';
import express from 'express';
-import { existsSync } from 'node:fs';
+import { existsSync, readFileSync } from 'node:fs';
import { join } from 'path';
import 'zone.js/node';
import AppServerModule from './src/main.server';
@@ -22,6 +23,10 @@ const ssrOptions: SsrOptimizationOptions = {
timeout: Number(
process.env['SSR_TIMEOUT'] ?? defaultSsrOptimizationOptions.timeout
),
+ cache: process.env['SSR_CACHE'] === 'true',
+ ssrFeatureToggles: {
+ avoidCachingErrors: true,
+ },
};
const ngExpressEngine = NgExpressEngineDecorator.get(engine, ssrOptions);
@@ -33,6 +38,7 @@ export function app(): express.Express {
const indexHtml = existsSync(join(distFolder, 'index.original.html'))
? join(distFolder, 'index.original.html')
: join(distFolder, 'index.html');
+ const indexHtmlContent = readFileSync(indexHtml, 'utf-8');
server.set('trust proxy', 'loopback');
@@ -63,6 +69,8 @@ export function app(): express.Express {
});
});
+ server.use(defaultExpressErrorHandlers(indexHtmlContent));
+
return server;
}
diff --git a/projects/storefrontapp/src/app/spartacus/features/quote-feature.module.ts b/projects/storefrontapp/src/app/spartacus/features/quote-feature.module.ts
index e9562a08414..744728d5a1c 100644
--- a/projects/storefrontapp/src/app/spartacus/features/quote-feature.module.ts
+++ b/projects/storefrontapp/src/app/spartacus/features/quote-feature.module.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts
index 678896c6a4a..36285893250 100644
--- a/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts
+++ b/projects/storefrontapp/src/app/spartacus/spartacus-features.module.ts
@@ -78,6 +78,7 @@ import { CustomerTicketingFeatureModule } from './features/customer-ticketing/cu
import { DigitalPaymentsFeatureModule } from './features/digital-payments/digital-payments-feature.module';
import { EpdVisualizationFeatureModule } from './features/epd-visualization/epd-visualization-feature.module';
import { EstimatedDeliveryDateFeatureModule } from './features/estimated-delivery-date/estimated-delivery-date-feature.module';
+import { OmfFeatureModule } from './features/omf/omf-feature.module';
import { OppsFeatureModule } from './features/opps/opps-feature.module';
import { OrderFeatureModule } from './features/order/order-feature.module';
import { AccountSummaryFeatureModule } from './features/organization/organization-account-summary-feature.module';
@@ -88,6 +89,8 @@ import { PDFInvoicesFeatureModule } from './features/pdf-invoices/pdf-invoices-f
import { PickupInStoreFeatureModule } from './features/pickup-in-store/pickup-in-store-feature.module';
import { ProductConfiguratorRulebasedFeatureModule } from './features/product-configurator/product-configurator-rulebased-feature.module';
import { ProductConfiguratorTextfieldFeatureModule } from './features/product-configurator/product-configurator-textfield-feature.module';
+import { ProductMultiDimensionalListFeatureModule } from './features/product-multi-dimensional/product-multi-dimensional-list-feature.module';
+import { ProductMultiDimensionalSelectorFeatureModule } from './features/product-multi-dimensional/product-multi-dimensional-selector-feature.module';
import { BulkPricingFeatureModule } from './features/product/product-bulk-pricing-feature.module';
import { FutureStockFeatureModule } from './features/product/product-future-stock-feature.module';
import { ImageZoomFeatureModule } from './features/product/product-image-zoom-feature.module';
@@ -103,9 +106,6 @@ import { SmartEditFeatureModule } from './features/smartedit/smartedit-feature.m
import { StorefinderFeatureModule } from './features/storefinder/storefinder-feature.module';
import { TrackingFeatureModule } from './features/tracking/tracking-feature.module';
import { UserFeatureModule } from './features/user/user-feature.module';
-import { OmfFeatureModule } from './features/omf/omf-feature.module';
-import { ProductMultiDimensionalSelectorFeatureModule } from './features/product-multi-dimensional/product-multi-dimensional-selector-feature.module';
-import { ProductMultiDimensionalListFeatureModule } from './features/product-multi-dimensional/product-multi-dimensional-list-feature.module';
const featureModules = [];
@@ -297,6 +297,8 @@ if (environment.cpq) {
storeFrontLibCardParagraphTruncated: true,
useProductCarouselBatchApi: true,
productConfiguratorAttributeTypesV2: true,
+ propagateErrorsToServer: true,
+ ssrStrictErrorHandlingForHttpAndNgrx: true,
productConfiguratorDeltaRendering: true,
a11yRequiredAsterisks: true,
a11yQuantityOrderTabbing: true,
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/consent-form/my-account-v2-consent-management-form.component.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/consent-form/my-account-v2-consent-management-form.component.ts
index 60464a77ba9..0ee5481af9d 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/consent-form/my-account-v2-consent-management-form.component.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/consent-form/my-account-v2-consent-management-form.component.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/my-account-v2-consent-management.component.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/my-account-v2-consent-management.component.ts
index fc7b34f3e51..3997e86821c 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/my-account-v2-consent-management.component.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/components/my-account-v2-consent-management.component.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/index.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/index.ts
index c51abfaac02..7383ae2b91e 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/index.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/index.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/my-account-v2-consent-management.module.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/my-account-v2-consent-management.module.ts
index a23c1cc4eb8..be41a90b59a 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/my-account-v2-consent-management.module.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-consent-management/my-account-v2-consent-management.module.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/index.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/index.ts
index 74a80738c57..1f6cd952968 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/index.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/index.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.component.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.component.ts
index b058d4a6d8d..0358570ab44 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.component.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.component.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.module.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.module.ts
index 43287c4070a..51c70ee3251 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.module.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/my-account-v2-notification-preference/my-account-v2-notification-preference.module.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/cms-components/myaccount/my-account-v2/use-my-account-v2-consent-notification-perference.ts b/projects/storefrontlib/cms-components/myaccount/my-account-v2/use-my-account-v2-consent-notification-perference.ts
index 9fd162949f2..8947f04363e 100644
--- a/projects/storefrontlib/cms-components/myaccount/my-account-v2/use-my-account-v2-consent-notification-perference.ts
+++ b/projects/storefrontlib/cms-components/myaccount/my-account-v2/use-my-account-v2-consent-notification-perference.ts
@@ -1,5 +1,4 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
* SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
diff --git a/projects/storefrontlib/shared/components/media/media-sources.pipe.spec.ts b/projects/storefrontlib/shared/components/media/media-sources.pipe.spec.ts
index 180f64edddc..c2107601c62 100644
--- a/projects/storefrontlib/shared/components/media/media-sources.pipe.spec.ts
+++ b/projects/storefrontlib/shared/components/media/media-sources.pipe.spec.ts
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: 2023 SAP Spartacus team
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
*
* SPDX-License-Identifier: Apache-2.0
*/
diff --git a/tools/eslint-rules/index.ts b/tools/eslint-rules/index.ts
index 7489e4b58e2..a7cedb0a7ba 100644
--- a/tools/eslint-rules/index.ts
+++ b/tools/eslint-rules/index.ts
@@ -24,6 +24,11 @@ import {
RULE_NAME as useProvideDefaultFeatureTogglesFactoryName,
} from './rules/use-provide-default-feature-toggles-factory';
+import {
+ rule as noNgrxFailActionWithoutErrorActionImplementation,
+ RULE_NAME as noNgrxFailActionWithoutErrorActionImplementationName,
+} from './rules/no-ngrx-fail-action-without-error-action-implementation';
+
/**
* Import your custom workspace rules at the top of this file.
*
@@ -55,5 +60,7 @@ module.exports = {
[useProvideDefaultFeatureTogglesName]: useProvideDefaultFeatureToggles,
[useProvideDefaultFeatureTogglesFactoryName]:
useProvideDefaultFeatureTogglesFactory,
+ [noNgrxFailActionWithoutErrorActionImplementationName]:
+ noNgrxFailActionWithoutErrorActionImplementation,
},
};
diff --git a/tools/eslint-rules/rules/no-ngrx-fail-action-without-error-action-implementation.spec.ts b/tools/eslint-rules/rules/no-ngrx-fail-action-without-error-action-implementation.spec.ts
new file mode 100644
index 00000000000..c81fb5d44b5
--- /dev/null
+++ b/tools/eslint-rules/rules/no-ngrx-fail-action-without-error-action-implementation.spec.ts
@@ -0,0 +1,178 @@
+import { convertAnnotatedSourceToFailureCase } from '@angular-eslint/utils';
+import { TSESLint } from '@typescript-eslint/utils';
+import {
+ rule,
+ RULE_NAME,
+} from './no-ngrx-fail-action-without-error-action-implementation';
+
+const ruleTester = new TSESLint.RuleTester({
+ parser: require.resolve('@typescript-eslint/parser'),
+});
+
+ruleTester.run(RULE_NAME, rule, {
+ valid: [
+ // actions with `Fail` in name, that implement `ErrorAction`
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProductFail implements ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProductFail implements Action, ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProductFail implements Action, SomeOtherInterface, ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProductFail extends SuperClass implements ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProductFail extends SuperClass implements SomeOtherInterface, ErrorAction {}
+ `,
+
+ // actions without `Fail` in name, that implement `ErrorAction`
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProduct implements ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProduct implements Action, ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProduct implements Action, SomeOtherInterface, ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProduct extends SuperClass implements ErrorAction {}
+ `,
+ `
+ import { ErrorAction } from '@spartacus/core';
+ export class LoadProduct extends SuperClass implements SomeOtherInterface, ErrorAction {}
+ `,
+
+ // actions without `Fail` in name, that don't implement `ErrorAction`
+ `
+ export class LoadProduct {}
+ `,
+ `
+ export class LoadProduct implements Action {}
+ `,
+ `
+ export class LoadProduct implements Action, SomeOtherInterface {}
+ `,
+ `
+ export class LoadProduct extends SuperClass {}
+ `,
+ `
+ export class LoadProduct extends SuperClass implements SomeOtherInterface {}
+ `,
+ ],
+ invalid: [
+ convertAnnotatedSourceToFailureCase({
+ description:
+ 'Fail action has no `implements ErrorAction`, and no other implements',
+ annotatedSource: `
+ export class LoadProductFail {
+ ~~~~~~~~~~~~~~~
+ }
+ `,
+ annotatedOutput: `
+ import { ErrorAction } from '@spartacus/core';
+export class LoadProductFail implements ErrorAction {
+
+ }
+ `,
+ messageId: 'missingImplementsErrorAction',
+ }) as TSESLint.InvalidTestCase<'missingImplementsErrorAction', never[]>, // type cast used as convertAnnotatedSourceToFailureCase simplifies testing, but TSESLint v6 requires never[] instead of readonly unknown[]
+
+ convertAnnotatedSourceToFailureCase({
+ description:
+ 'Fail action has no `implements ErrorAction`, but only 1 other `implements`',
+ annotatedSource: `
+ export class LoadProductFail implements Action {
+ ~~~~~~~~~~~~~~~
+ }
+ `,
+ annotatedOutput: `
+ import { ErrorAction } from '@spartacus/core';
+export class LoadProductFail implements Action, ErrorAction {
+
+ }
+ `,
+ messageId: 'missingImplementsErrorAction',
+ }) as TSESLint.InvalidTestCase<'missingImplementsErrorAction', never[]>, // type cast used as convertAnnotatedSourceToFailureCase simplifies testing, but TSESLint v6 requires never[] instead of readonly unknown[]
+
+ convertAnnotatedSourceToFailureCase({
+ description:
+ 'Fail action has no `implements ErrorAction`, but only 2 other `implements`',
+ annotatedSource: `
+ export class LoadProductFail implements Action, SomeOtherInterface {
+ ~~~~~~~~~~~~~~~
+ }
+ `,
+ annotatedOutput: `
+ import { ErrorAction } from '@spartacus/core';
+export class LoadProductFail implements Action, SomeOtherInterface, ErrorAction {
+
+ }
+ `,
+ messageId: 'missingImplementsErrorAction',
+ }) as TSESLint.InvalidTestCase<'missingImplementsErrorAction', never[]>, // type cast used as convertAnnotatedSourceToFailureCase simplifies testing, but TSESLint v6 requires never[] instead of readonly unknown[]
+
+ convertAnnotatedSourceToFailureCase({
+ description:
+ 'Fail action has no `implements ErrorAction`, but only 1 other `extends`',
+ annotatedSource: `
+ export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction {
+ ~~~~~~~~~~~~~~~
+ }
+ `,
+ annotatedOutput: `
+ import { ErrorAction } from '@spartacus/core';
+export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction implements ErrorAction {
+
+ }
+ `,
+ messageId: 'missingImplementsErrorAction',
+ }) as TSESLint.InvalidTestCase<'missingImplementsErrorAction', never[]>, // type cast used as convertAnnotatedSourceToFailureCase simplifies testing, but TSESLint v6 requires never[] instead of readonly unknown[]
+
+ convertAnnotatedSourceToFailureCase({
+ description:
+ 'Fail action has no `implements ErrorAction`, but only 1 other `extends` and 1 other `implements`',
+ annotatedSource: `
+ export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction implements Action {
+ ~~~~~~~~~~~~~~~
+ }
+ `,
+ annotatedOutput: `
+ import { ErrorAction } from '@spartacus/core';
+export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction implements Action, ErrorAction {
+
+ }
+ `,
+ messageId: 'missingImplementsErrorAction',
+ }) as TSESLint.InvalidTestCase<'missingImplementsErrorAction', never[]>, // type cast used as convertAnnotatedSourceToFailureCase simplifies testing, but TSESLint v6 requires never[] instead of readonly unknown[]
+
+ convertAnnotatedSourceToFailureCase({
+ description:
+ 'Fail action has no `implements ErrorAction`, but only 1 other `extends` and 2 other `implements`',
+ annotatedSource: `
+ export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction implements Action, SomeOtherInterface {
+ ~~~~~~~~~~~~~~~
+ }
+ `,
+ annotatedOutput: `
+ import { ErrorAction } from '@spartacus/core';
+export class LoadProductFail extends EntityScopedLoaderActions.EntityScopedFailAction implements Action, SomeOtherInterface, ErrorAction {
+
+ }
+ `,
+ messageId: 'missingImplementsErrorAction',
+ }) as TSESLint.InvalidTestCase<'missingImplementsErrorAction', never[]>, // type cast used as convertAnnotatedSourceToFailureCase simplifies testing, but TSESLint v6 requires never[] instead of readonly unknown[]
+ ],
+});
diff --git a/tools/eslint-rules/rules/no-ngrx-fail-action-without-error-action-implementation.ts b/tools/eslint-rules/rules/no-ngrx-fail-action-without-error-action-implementation.ts
new file mode 100644
index 00000000000..4cd07883f54
--- /dev/null
+++ b/tools/eslint-rules/rules/no-ngrx-fail-action-without-error-action-implementation.ts
@@ -0,0 +1,82 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * This file sets you up with structure needed for an ESLint rule.
+ *
+ * It leverages utilities from @typescript-eslint to allow TypeScript to
+ * provide autocompletions etc for the configuration.
+ *
+ * Your rule's custom logic will live within the create() method below
+ * and you can learn more about writing ESLint rules on the official guide:
+ *
+ * https://eslint.org/docs/developer-guide/working-with-rules
+ *
+ */
+
+import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
+import {
+ fixMissingImplementsInterface,
+ hasImplementsInterface,
+} from './utils/implements-interface-utils';
+import { fixPossiblyMissingImport } from './utils/import-utils';
+
+// NOTE: The rule will be available in ESLint configs as "@nrwl/nx/workspace/no-ngrx-fail-action-without-error-action-implementation"
+export const RULE_NAME =
+ 'no-ngrx-fail-action-without-error-action-implementation';
+
+export const rule = ESLintUtils.RuleCreator(() => __filename)({
+ name: RULE_NAME,
+ meta: {
+ type: 'problem',
+ docs: {
+ description: ``,
+ recommended: 'recommended',
+ },
+ schema: [], // no options
+ messages: {
+ missingImplementsErrorAction:
+ '[Spartacus] NgRx Failure Action class should have `implements ErrorAction`. Otherwise it might be not handled properly by `ErrorActionService`',
+ },
+ fixable: 'code',
+ },
+ defaultOptions: [],
+ create(context) {
+ return {
+ 'ClassDeclaration[id.name=/Fail/]'(node: TSESTree.ClassDeclaration) {
+ const Constants = {
+ ErrorAction: 'ErrorAction',
+ SpartacusCore: '@spartacus/core',
+ };
+
+ if (!hasImplementsInterface(node, Constants.ErrorAction)) {
+ context.report({
+ node: node.id ?? node,
+ messageId: 'missingImplementsErrorAction',
+ fix(fixer) {
+ const sourceCode = context.sourceCode;
+ return [
+ ...fixMissingImplementsInterface({
+ node,
+ interfaceName: Constants.ErrorAction,
+ sourceCode,
+ fixer,
+ }),
+
+ ...fixPossiblyMissingImport({
+ importedIdentifier: Constants.ErrorAction,
+ importPath: Constants.SpartacusCore,
+ sourceCode,
+ fixer,
+ }),
+ ];
+ },
+ });
+ }
+ },
+ };
+ },
+});
diff --git a/tools/eslint-rules/rules/utils/implements-interface-utils.spec.ts b/tools/eslint-rules/rules/utils/implements-interface-utils.spec.ts
new file mode 100644
index 00000000000..356b2c210db
--- /dev/null
+++ b/tools/eslint-rules/rules/utils/implements-interface-utils.spec.ts
@@ -0,0 +1,88 @@
+import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
+
+import { TSESLint } from '@typescript-eslint/utils';
+import {
+ fixMissingImplementsInterface,
+ hasImplementsInterface,
+} from './implements-interface-utils';
+
+describe('implements-interface-utils', () => {
+ const rule = ESLintUtils.RuleCreator(() => __filename)({
+ name: 'test-rule',
+ meta: {
+ messages: {
+ missingImplementsMyInterface: 'test error action',
+ },
+ docs: {
+ description: 'test description',
+ },
+ schema: [],
+ type: 'problem',
+ fixable: 'code',
+ },
+ defaultOptions: [],
+ create(context) {
+ return {
+ ClassDeclaration(node: TSESTree.ClassDeclaration) {
+ const interfaceName = 'MyInterface'; // Example interface name to test
+ if (
+ // check if interface is implemented using hasImplementsInterface utility
+ !hasImplementsInterface(node, interfaceName)
+ ) {
+ context.report({
+ node: node.id ?? node,
+ messageId: 'missingImplementsMyInterface',
+ fix: (fixer) => {
+ // fix using fixMissingImplementsInterface utility
+ return fixMissingImplementsInterface({
+ fixer,
+ interfaceName,
+ node,
+ sourceCode: context.sourceCode,
+ });
+ },
+ });
+ }
+ },
+ };
+ },
+ });
+
+ // Instantiate RuleTester with TypeScript parser
+ const ruleTester = new TSESLint.RuleTester({
+ parser: require.resolve('@typescript-eslint/parser'),
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ },
+ });
+
+ ruleTester.run('hasImplementsInterface', rule, {
+ valid: [
+ `class MyClass implements MyInterface {}`,
+ `class MyClass implements AnotherInterface, MyInterface {}`,
+ `class MyClass implements MyInterface, AnotherInterface {}`,
+ `class MyClass extends AnotherClass implements MyInterface {}`,
+ ],
+ invalid: [
+ {
+ // if there is no superclass, it adds the new interface after the class name
+ code: `class MyClass {}`,
+ errors: [{ messageId: 'missingImplementsMyInterface' }],
+ output: `class MyClass implements MyInterface {}`,
+ },
+ {
+ // if there are already implemented interfaces, it adds the new one at the end
+ code: `class MyClass implements AnotherInterface {}`,
+ errors: [{ messageId: 'missingImplementsMyInterface' }],
+ output: `class MyClass implements AnotherInterface, MyInterface {}`,
+ },
+ {
+ // if there is a superclass, it adds the new interface after it
+ code: `class MyClass extends SuperClass {}`,
+ errors: [{ messageId: 'missingImplementsMyInterface' }],
+ output: `class MyClass extends SuperClass implements MyInterface {}`,
+ },
+ ],
+ });
+});
diff --git a/tools/eslint-rules/rules/utils/implements-interface-utils.ts b/tools/eslint-rules/rules/utils/implements-interface-utils.ts
new file mode 100644
index 00000000000..7d2d0a6c825
--- /dev/null
+++ b/tools/eslint-rules/rules/utils/implements-interface-utils.ts
@@ -0,0 +1,85 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils';
+import {
+ RuleFix,
+ RuleFixer,
+ SourceCode,
+} from '@typescript-eslint/utils/ts-eslint';
+
+/**
+ * Tells whether the class declaration `node` has an `implements` clause with the given `interfaceName`.
+ */
+export function hasImplementsInterface(
+ node: TSESTree.ClassDeclaration,
+ interfaceName: string
+): boolean {
+ return node.implements?.some(
+ (impl) =>
+ impl.expression.type === AST_NODE_TYPES.Identifier &&
+ impl.expression.name === interfaceName
+ );
+}
+
+/**
+ * Adds missing `implements` clause for the class declaration.
+ *
+ * - if there are already implemented interfaces, it adds the new one at the end
+ * - if there is a superclass, it adds the new interface after it
+ * - if there is no superclass, it adds the new interface after the class name
+ */
+export function fixMissingImplementsInterface({
+ fixer,
+ interfaceName,
+ node,
+ sourceCode,
+}: {
+ node: TSESTree.ClassDeclaration;
+ interfaceName: string;
+ sourceCode: SourceCode;
+ fixer: RuleFixer;
+}): RuleFix[] {
+ const implementsText = createImplementsText({
+ node,
+ interfaceName,
+ sourceCode,
+ });
+
+ const fixes = [];
+ if (node.implements?.length > 0) {
+ const lastImplementsNode = node.implements[node.implements.length - 1];
+ fixes.push(fixer.insertTextAfter(lastImplementsNode, `, ${interfaceName}`));
+ } else if (node.superClass) {
+ fixes.push(fixer.insertTextAfter(node.superClass, implementsText));
+ } else if (node.id) {
+ fixes.push(fixer.insertTextAfter(node.id, implementsText));
+ }
+ return fixes;
+}
+
+/**
+ * Returns a string with the `implements` clause for the class declaration.
+ *
+ * - if there are already implemented interfaces, it adds the new one at the end
+ * - if there is a superclass, it adds the new interface after it
+ * - if there is no superclass, it adds the new interface after the class name
+ */
+function createImplementsText({
+ node,
+ interfaceName,
+ sourceCode,
+}: {
+ node: TSESTree.ClassDeclaration;
+ interfaceName: string;
+ sourceCode: SourceCode;
+}): string {
+ let otherImplements = node.implements
+ ? node.implements.map((impl) => sourceCode.getText(impl)).join(', ')
+ : '';
+ const optionalComma = otherImplements ? ', ' : '';
+ return ` implements ${otherImplements}${optionalComma}${interfaceName}`;
+}
diff --git a/tools/eslint-rules/rules/utils/import-utils.spec.ts b/tools/eslint-rules/rules/utils/import-utils.spec.ts
new file mode 100644
index 00000000000..89fcc75a85a
--- /dev/null
+++ b/tools/eslint-rules/rules/utils/import-utils.spec.ts
@@ -0,0 +1,96 @@
+import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
+
+import { TSESLint } from '@typescript-eslint/utils';
+import { fixPossiblyMissingImport, isIdentifierImported } from './import-utils';
+
+describe('import-utils', () => {
+ const rule = ESLintUtils.RuleCreator(() => __filename)({
+ name: 'test-rule',
+ meta: {
+ messages: {
+ missingIdentifierImport: 'test error action',
+ },
+ docs: {
+ description: 'test description',
+ },
+ schema: [],
+ type: 'problem',
+ fixable: 'code',
+ },
+ defaultOptions: [],
+ create(context) {
+ return {
+ ClassDeclaration(node: TSESTree.ClassDeclaration) {
+ const superClassName = 'MySuperClass'; // Example super class name to test
+ if (
+ // check if super class is imported using isIdentifierImported utility
+ !isIdentifierImported({
+ importedIdentifier: superClassName,
+ importPath: 'my-superclass',
+ sourceCode: context.sourceCode,
+ })
+ ) {
+ context.report({
+ node: node.id ?? node,
+ messageId: 'missingIdentifierImport',
+ fix: (fixer) => {
+ // fix using fixPossiblyMissingImport utility
+ return fixPossiblyMissingImport({
+ fixer,
+ importedIdentifier: superClassName,
+ importPath: 'my-superclass',
+ sourceCode: context.sourceCode,
+ });
+ },
+ });
+ }
+ },
+ };
+ },
+ });
+
+ const ruleTester = new TSESLint.RuleTester({
+ parser: require.resolve('@typescript-eslint/parser'),
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ },
+ });
+
+ ruleTester.run('isIdentifierImported', rule, {
+ valid: [
+ // This class does not implement 'MyInterface', so it's considered valid under our rule
+ `import { MySuperClass } from 'my-superclass';
+
+ class MyClass extends MySuperClass {}
+ `,
+ ],
+ invalid: [
+ {
+ // adds missing import
+ code: `
+ class MyClass extends MySuperClass {}
+ `,
+ errors: [{ messageId: 'missingIdentifierImport' }],
+ output: `
+ import { MySuperClass } from 'my-superclass';
+class MyClass extends MySuperClass {}
+ `,
+ },
+ {
+ // adds missing import as last import
+ code: `
+ import { MyInterface } from 'my-interface';
+ class MyClass extends MySuperClass implements MyInterface {}
+ `,
+ errors: [{ messageId: 'missingIdentifierImport' }],
+ output: `
+ import { MyInterface } from 'my-interface';
+import { MySuperClass } from 'my-superclass';
+
+ class MyClass extends MySuperClass implements MyInterface {}
+ `,
+ },
+ ],
+ });
+});
diff --git a/tools/eslint-rules/rules/utils/import-utils.ts b/tools/eslint-rules/rules/utils/import-utils.ts
new file mode 100644
index 00000000000..91cfa5c77d6
--- /dev/null
+++ b/tools/eslint-rules/rules/utils/import-utils.ts
@@ -0,0 +1,99 @@
+/*
+ * SPDX-FileCopyrightText: 2024 SAP Spartacus team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { AST_NODE_TYPES } from '@typescript-eslint/utils';
+import {
+ RuleFix,
+ RuleFixer,
+ SourceCode,
+} from '@typescript-eslint/utils/ts-eslint';
+
+/**
+ * Adds an import in the file for `importedIdentifier` from `importPath`,
+ * if it's missing in the file `sourceCode`.
+ */
+export function fixPossiblyMissingImport({
+ fixer,
+ importedIdentifier,
+ importPath,
+ sourceCode,
+}: {
+ fixer: RuleFixer;
+ importedIdentifier: string;
+ importPath: string;
+ sourceCode: SourceCode;
+}): RuleFix[] {
+ if (isIdentifierImported({ sourceCode, importedIdentifier, importPath })) {
+ return [];
+ }
+
+ return fixMissingImport({
+ sourceCode,
+ importedIdentifier,
+ importPath,
+ fixer,
+ });
+}
+
+/**
+ * Tells whether the `importedIdentifier` is imported from `importPath` in the file of `sourceCode`.
+ */
+export function isIdentifierImported({
+ importedIdentifier,
+ importPath,
+ sourceCode,
+}: {
+ importedIdentifier: string;
+ importPath: string;
+ sourceCode: SourceCode;
+}): boolean {
+ const importDeclarations = sourceCode.ast.body.filter(
+ (statement) => statement.type === AST_NODE_TYPES.ImportDeclaration
+ );
+ return importDeclarations.some(
+ (declaration) =>
+ declaration.type === AST_NODE_TYPES.ImportDeclaration &&
+ declaration.source.value === importPath &&
+ declaration.specifiers.some(
+ (specifier) =>
+ specifier.type === AST_NODE_TYPES.ImportSpecifier &&
+ specifier.imported.name === importedIdentifier
+ )
+ );
+}
+
+/**
+ * Adds an import in the file for `importedIdentifier` from `importPath`,
+ * in the file `sourceCode`.
+ */
+export function fixMissingImport({
+ fixer,
+ importedIdentifier,
+ importPath,
+ sourceCode,
+}: {
+ fixer: RuleFixer;
+ importedIdentifier: string;
+ importPath: string;
+ sourceCode: SourceCode;
+}): RuleFix[] {
+ const fixes = [];
+ const importStatementText = `import { ${importedIdentifier} } from '${importPath}';\n`;
+ const importDeclarations = sourceCode.ast.body.filter(
+ (statement) => statement.type === AST_NODE_TYPES.ImportDeclaration
+ );
+
+ if (importDeclarations.length > 0) {
+ const lastImport = importDeclarations[importDeclarations.length - 1];
+ fixes.push(fixer.insertTextAfter(lastImport, `\n${importStatementText}`));
+ } else {
+ fixes.push(
+ fixer.insertTextBefore(sourceCode.ast.body[0], importStatementText)
+ );
+ }
+
+ return fixes;
+}