Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node.js exits with error after adding new locale to PWA #1646

Closed
gorpet opened this issue Apr 29, 2024 · 10 comments
Closed

Node.js exits with error after adding new locale to PWA #1646

gorpet opened this issue Apr 29, 2024 · 10 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@gorpet
Copy link

gorpet commented Apr 29, 2024

After adding new locale to PWA and backend and try to switch to new locale, error 'no elements in sequence' is thrown and Node.js processes exits.

Actual Behavior

Error 'no elements in sequence' and Node.js process exits

Expected Behavior

Page is loaded with configured and selected locale (e.g. nl_NL)

Steps to Reproduce the Bug

Steps to reproduce the behavior:

  1. Go to SLDSystem and add new locale to PWA application e.g. nl_NL
  2. Follow steps to add new locale e.g. nl_NL as described here https://github.com/intershop/intershop-pwa/blob/develop/docs/concepts/configuration.md#extend-locales
  3. Open PWA frontpage and switch to new locale nl_NL using language selector
  4. See error printing in PWA log, and also Node.js process is killed

Environment Details

Latest PWA 5.1.0
ICM 7.10.41.0 or any version, also this Mock REST response for /configuration endpoint can be used:

  "data": {
    "application": {
      "id": "application",
      "applicationId": null,
      "applicationType": "intershop.REST",
      "displayName": null,
      "urlIdentifier": "-",
      "default": true
    },
    "basket": {
      "id": "basket",
      "expirationType": "Time",
      "lineItemPositionHandling": "AssignOnly",
      "addProductBehaviour": "MergeQuantities",
      "lifetime": "28800",
      "maxItemSize": "50",
      "maxItemQuantity": "100",
      "minTotalValue": null,
      "maxTotalValue": null,
      "acceleration": true,
      "termsAndConditions": true,
      "emailOptIn": false,
      "displayTaxesAndFees": "ConsolidatedTaxes",
      "desiredDeliveryDate": true,
      "deliveryExcludeSaturday": true,
      "deliveryExcludeSunday": true,
      "packSlipMessage": true,
      "packSlipMessageMaxLength": "1000",
      "giftWrap": false,
      "giftMessage": false,
      "giftMessageMaxLength": null,
      "partialOrderNo": true,
      "customerProductID": true
    },
    "captcha": {
      "id": "captcha",
      "redemptionOfGiftCardsAndCertificates": true,
      "forgotPassword": true,
      "contactUs": true,
      "emailShoppingCart": true,
      "register": true
    },
    "customFieldDefinitions": [
      {
        "name": "commissionNumber",
        "type": "String",
        "description": "Commission number of the line item.",
        "displayName": "Commission number",
        "position": 1,
        "scopes": [
          {
            "name": "BasketLineItem",
            "isEditable": true,
            "isVisible": true
          },
          {
            "name": "OrderLineItem",
            "isEditable": false,
            "isVisible": true
          }
        ]
      },
      {
        "name": "projectNumber",
        "type": "String",
        "description": "Unique identifier to associate the order with a project.",
        "displayName": "Project Number",
        "position": 2,
        "scopes": [
          {
            "name": "Order",
            "isEditable": false,
            "isVisible": true
          },
          {
            "name": "Basket",
            "isEditable": true,
            "isVisible": true
          }
        ]
      }
    ],
    "general": {
      "id": "general",
      "defaultCurrency": "USD",
      "defaultLocale": "en_US",
      "currencies": [
        "USD",
        "EUR"
      ],
      "locales": [
        "de_DE",
        "en_US",
        "fr_FR",
        "nl_NL"
      ],
      "customerTypeForLoginApproval": []
    },
    "marketing": {
      "id": "marketing",
      "newsletterSubscriptionEnabled": true
    },
    "preferences": {
      "id": "preferences",
      "ChannelPreferences": {
        "id": "ChannelPreferences",
        "EnableAdvancedVariationHandling": "false",
        "PasswordRetrievalEmailSubject": "Password Retrieval",
        "ContactFormUserServiceEmailFrom": "info@test.intershop.de",
        "PasswordReminderEmailFrom": "info@test.intershop.de"
      },
      "ShippingPreferences": {
        "id": "ShippingPreferences",
        "MultipleShipmentsSupported": "true"
      },
      "UserCredentialPreferences": {
        "id": "UserCredentialPreferences",
        "UserRegistrationLoginType": "email"
      }
    },
    "pricing": {
      "id": "pricing",
      "priceType": "net",
      "privateCustomerPriceDisplayType": "gross",
      "smbCustomerPriceDisplayType": "net",
      "defaultCustomerTypeForPriceDisplay": "SMB"
    },
    "services": {
      "id": "services",
      "Address_Check_Services": {
        "id": "Address_Check_Services",
        "runnable": "true"
      },
      "ReCaptchaV3ServiceDefinition": {
        "id": "ReCaptchaV3ServiceDefinition",
        "runnable": "true",
        "ScoreThreshold": "0.5",
        "VerificationURL": "https://www.google.com/recaptcha/api/siteverify",
        "SiteKey": "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
      },
      "OrderApprovalServiceDefinition": {
        "id": "OrderApprovalServiceDefinition",
        "runnable": "true"
      }
    },
    "shipping": {
      "deliveryExcludeSaturday": true,
      "deliveryExcludeSunday": true,
      "desiredDeliveryDate": true,
      "desiredDeliveryDaysMax": "90",
      "desiredDeliveryDaysMin": "2",
      "id": "shipping",
      "messageToMerchant": true,
      "multipleShipmentsSupported": true,
      "pickupInStoreEnabled": false,
      "readyForShipmentMaximumTime": "7",
      "readyForShipmentMinimumTime": "3"
    }
  }
}

Additional Context, like Screenshots, Log File Snippets etc.

image

import localeDe from '@angular/common/locales/de';
import localeFr from '@angular/common/locales/fr';
import localeNL from '@angular/common/locales/nl';
import { Inject, LOCALE_ID, NgModule, TransferState } from '@angular/core';
import {
  MissingTranslationHandler,
  TranslateCompiler,
  TranslateLoader,
  TranslateModule,
  TranslateService,
} from '@ngx-translate/core';

import { SSR_LOCALE } from './configurations/state-keys';
import {
  FALLBACK_LANG,
  FallbackMissingTranslationHandler,
} from './utils/translate/fallback-missing-translation-handler';
import { ICMTranslateLoader, LOCAL_TRANSLATIONS } from './utils/translate/icm-translate-loader';
import { PWATranslateCompiler } from './utils/translate/pwa-translate-compiler';
import { TranslationGenerator } from './utils/translate/translations-generator';

@NgModule({
  imports: [
    TranslateModule.forRoot({
      loader: { provide: TranslateLoader, useClass: ICMTranslateLoader },
      missingTranslationHandler: { provide: MissingTranslationHandler, useClass: FallbackMissingTranslationHandler },
      useDefaultLang: false,
      compiler: { provide: TranslateCompiler, useClass: PWATranslateCompiler },
    }),
  ],
  providers: [
    { provide: FALLBACK_LANG, useValue: 'en_US' },
    {
      provide: LOCAL_TRANSLATIONS,
      useValue: {
        useFactory: (lang: string) => {
          switch (lang) {
            case 'en_US':
              return import('../../assets/i18n/en_US.json');
            case 'fr_FR':
              return import('../../assets/i18n/fr_FR.json');
            case 'de_DE':
              return import('../../assets/i18n/de_DE.json');
            case 'nl_NL':
              return import('../../assets/i18n/nl_NL.json');
          }
        },
      },
    },
    TranslationGenerator,
  ],
})
export class InternationalizationModule {
  constructor(
    @Inject(LOCALE_ID) angularDefaultLocale: string,
    translateService: TranslateService,
    transferState: TransferState,
    generator: TranslationGenerator
  ) {
    registerLocaleData(localeDe);
    registerLocaleData(localeFr);
    registerLocaleData(localeNL);

    let defaultLang = angularDefaultLocale.replace(/\-/, '_');
    if (transferState.hasKey(SSR_LOCALE)) {
      defaultLang = transferState.get(SSR_LOCALE, defaultLang);
    }
    translateService.setDefaultLang(defaultLang);
    translateService.use(defaultLang);

    generator.init();
  }
}

AB#96210

@gorpet gorpet added the bug Something isn't working label Apr 29, 2024
@andreassteinmann andreassteinmann self-assigned this May 3, 2024
@andreassteinmann
Copy link
Collaborator

andreassteinmann commented May 3, 2024

@gorpet

Hi,

I used the following setup but the mentioned error cannot be reproduced:

I'm able to switch to nl_NL and all errors in the console are related to REST requests that return a 500 status because the referenced ICM server does not have product or categories data for nl_NL.

Remarks:

  • The JSON data from the mentioned /configurations call is missing an opening curly brace { at the start.
  • The changes specified in src\app\core\internationalization.module.ts are missing the line import { registerLocaleData } from '@angular/common'; at the beginning of the file.

@gorpet
Copy link
Author

gorpet commented May 3, 2024

Hi @andreassteinmann
Both internationalization.module.ts and /configurations are correct, there was just c/p issue.
The only difference here is that I start server without docker and with command: TRUST_ICM=true LOGGING=true npm run start:ssr-dev -- --ssl --host dds-x1e-120.local --port 443
Can you try that?
Thanks for support,
Goran

@andreassteinmann
Copy link
Collaborator

Hi @gorpet
I tried it using TRUST_ICM=true LOGGING=true npm run start:ssr-dev -- --ssl --port 443 and I also do not get the error. The mocked REST call is working and includes the mentioned languages.

I'm still trying to figure out what might cause the issue.

@andreassteinmann
Copy link
Collaborator

andreassteinmann commented May 6, 2024

Hi @gorpet,

I created a temporary branch https://github.com/intershop/intershop-pwa/tree/tmp/new-locale-nl_NL with the changes I made for the new locale nl_NL so you can have a look at it and test it on your side. I used

  • TRUST_ICM=true LOGGING=true npm run start:ssr-dev -- --ssl --port 443
  • docker.compose up --build

Changes:

@andreassteinmann
Copy link
Collaborator

If the described error persists, you can try to change the code in src\app\core\utils\translate\icm-translate-loader.ts to use take(1) instead of first() from

        ofType(routerNavigationAction),
        first(),
        switchMap(() =>
          this.localizations.getServerTranslations(lang).pipe(
            tap(data => {
              this.transferState.set(SSR_TRANSLATIONS, data);
            })
          )
        ),

to

        ofType(routerNavigationAction),
        take(1),
        switchMap(() =>
          this.localizations.getServerTranslations(lang).pipe(
            tap(data => {
              this.transferState.set(SSR_TRANSLATIONS, data);
            })
          )
        ),

@gorpet
Copy link
Author

gorpet commented May 6, 2024

Hi @andreassteinmann
Thank you for your support, I just tried running branch 'new-locale-nl_NL' with mock data provided and it seems it works. Then I removed mock data to try with my local server, and error occurs. After that I copied/pasted server response from /configuration REST call to get.json file and reenabled mock in environment and then it works again without error...
After that I changed back to real server call and changed icm-translate-loader.ts as you suggested and then it works!
Not sure why, do you have idea why real call with take(1) works and with first() it does not? Also what is strange is that exact mocked response work either way...

Thank you again and kind regards,
Goran

@andreassteinmann
Copy link
Collaborator

@gorpet first() expects the source Observable to emit at least one item. If the Observable completes without any emissions, first() will throw a NoSuchElementException to indicate that no elements were emitted. In the context of the issue, if the Observable is expected to emit at least one value but doesn't, first() enforces the emission of at least one item. This could happen if, for example, the translation data isn't loaded as expected due to a network issue, incorrect configuration, or if the data is filtered out before it reaches first().

Maybe we need to change it in the code as well. I need to check this with my colleagues. But until then, you may work with the code changes to take(1).

Kind regards,
Andreas

@gorpet
Copy link
Author

gorpet commented May 6, 2024

Hi @andreassteinmann,
Ok, then until you change it in code, I will override icm-translate-loader.ts and use take(1).

Kind regards,
Goran

@andreassteinmann
Copy link
Collaborator

Hi @gorpet, I'm going to delete the temp branch this afternoon. In case you need to keep the changes, just create a backup ;)

@andreassteinmann
Copy link
Collaborator

The PR #1652 is created.

@shauke shauke added this to the 5.2 milestone May 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants