From efd31bdd5102db1d9427434d0fe51a3add456ecf Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 29 May 2024 20:21:59 +0200 Subject: [PATCH 1/2] [DSC-1529] Fix errors occurred during SSR --- src/app/core/metadata/metadata.service.spec.ts | 3 ++- src/app/core/metadata/metadata.service.ts | 9 +++++---- src/app/core/services/internal-link.service.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index cdf663ec068..3bd00a3d787 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -123,7 +123,8 @@ xdescribe('MetadataService', () => { appConfig, authorizationService, schemaJsonLDService, - 'browser' + 'browser', + null ); }); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 910a6374de4..638f50fb7be 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -42,7 +42,7 @@ import { getDownloadableBitstream } from '../shared/bitstream.operators'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { SchemaJsonLDService } from './schema-json-ld/schema-json-ld.service'; import { ITEM } from '../shared/item.resource-type'; -import { isPlatformServer } from '@angular/common'; +import { DOCUMENT, isPlatformServer } from '@angular/common'; import { Root } from '../data/root.model'; import { environment } from '../../../environments/environment'; @@ -102,6 +102,7 @@ export class MetadataService { private authorizationService: AuthorizationDataService, private schemaJsonLDService: SchemaJsonLDService, @Inject(PLATFORM_ID) private platformId: any, + @Inject(DOCUMENT) private _document: Document, ) { } @@ -656,11 +657,11 @@ export class MetadataService { } private getMetaTagValue(key: string): string { - return this.currentObject.value.firstMetadataValue(key); + return this.currentObject?.value?.firstMetadataValue(key); } private getFirstMetaTagValue(keys: string[]): string { - return this.currentObject.value.firstMetadataValue(keys); + return this.currentObject?.value?.firstMetadataValue(keys); } private getMetaTagValuesAndCombine(key: string): string { @@ -741,7 +742,7 @@ export class MetadataService { private setGenericPageMetaTags() { - const pageDocumentTitle = document.getElementsByTagName('title')[0].innerText; + const pageDocumentTitle = this._document.getElementsByTagName('title')[0].innerText; const pageUrl = new URLCombiner(this.hardRedirectService.getCurrentOrigin(), this.router.url).toString(); const genericPageOpenGraphType = 'website'; diff --git a/src/app/core/services/internal-link.service.ts b/src/app/core/services/internal-link.service.ts index f6a40e16921..39b6283829a 100644 --- a/src/app/core/services/internal-link.service.ts +++ b/src/app/core/services/internal-link.service.ts @@ -7,7 +7,7 @@ import { NativeWindowRef, NativeWindowService } from './window.service'; */ @Injectable() export class InternalLinkService { - currentURL = this._window.nativeWindow.location.origin; + currentURL = this._window.nativeWindow?.location?.origin; constructor( @Inject(NativeWindowService) protected _window: NativeWindowRef, From 484e75e382ccb656cf33db5bd0db211418cf2d0b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 29 May 2024 20:24:19 +0200 Subject: [PATCH 2/2] [DSC-1570] Fix issue with missing providers for klaro service during SSR --- src/app/app.component.spec.ts | 8 +++ src/app/app.component.ts | 4 +- .../browser-datadog-rum.service.ts | 67 ++++++++++++++++++ .../datadog-rum/datadog-rum.service.spec.ts | 11 +-- .../shared/datadog-rum/datadog-rum.service.ts | 68 ++----------------- .../datadog-rum/server-datadog-rum.service.ts | 11 +++ src/modules/app/browser-app.module.ts | 6 ++ src/modules/app/server-app.module.ts | 8 ++- 8 files changed, 112 insertions(+), 71 deletions(-) create mode 100644 src/app/shared/datadog-rum/browser-datadog-rum.service.ts create mode 100644 src/app/shared/datadog-rum/server-datadog-rum.service.ts diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index e3bec87c4f4..c483f97823f 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -38,6 +38,7 @@ import { of } from 'rxjs'; import { APP_CONFIG } from '../config/app-config.interface'; import { environment } from '../environments/environment'; import { KlaroService } from './shared/cookies/klaro.service'; +import { DatadogRumService } from './shared/datadog-rum/datadog-rum.service'; let comp: AppComponent; let fixture: ComponentFixture; @@ -57,6 +58,7 @@ describe('App component', () => { let breadcrumbsServiceSpy; let routeServiceMock; let klaroServiceSpy: jasmine.SpyObj; + let datadogRumServiceSpy: jasmine.SpyObj; const getDefaultTestBedConf = () => { breadcrumbsServiceSpy = jasmine.createSpyObj(['listenForRouteChanges']); @@ -71,6 +73,11 @@ describe('App component', () => { consentsUpdates$: of({}) }); + datadogRumServiceSpy = jasmine.createSpyObj('DatadogRumService', { + initDatadogRum: jasmine.createSpy('initDatadogRum'), + getDatadogRumState: jasmine.createSpy('getDatadogRumState') + }); + return { imports: [ CommonModule, @@ -99,6 +106,7 @@ describe('App component', () => { { provide: RouteService, useValue: routeServiceMock }, { provide: APP_CONFIG, useValue: environment }, { provide: KlaroService, useValue: klaroServiceSpy }, + { provide: DatadogRumService, useValue: datadogRumServiceSpy }, provideMockStore({ initialState }), AppComponent, // RouteService diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5ce1213318b..2b6ad96f315 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -111,9 +111,7 @@ export class AppComponent implements OnInit, AfterViewInit { this.dispatchWindowSize(this._window.nativeWindow.innerWidth, this._window.nativeWindow.innerHeight); - if (isPlatformBrowser(this.platformId)) { - this.datadogRumService.initDatadogRum(); - } + this.datadogRumService.initDatadogRum(); } private storeCSSVariables() { diff --git a/src/app/shared/datadog-rum/browser-datadog-rum.service.ts b/src/app/shared/datadog-rum/browser-datadog-rum.service.ts new file mode 100644 index 00000000000..c44b64fd97a --- /dev/null +++ b/src/app/shared/datadog-rum/browser-datadog-rum.service.ts @@ -0,0 +1,67 @@ +import { Injectable } from '@angular/core'; +import { environment } from '../../../environments/environment'; +import { datadogRum } from '@datadog/browser-rum'; +import { CookieConsents, KlaroService } from '../cookies/klaro.service'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { createSelector, Store } from '@ngrx/store'; +import { setDatadogRumStatusAction } from './datadog-rum.actions'; +import { DatadogRumState } from './datadog-rum.reducer'; +import { distinctUntilChanged, take } from 'rxjs/operators'; +import { coreSelector } from '../../core/core.selectors'; +import { CoreState } from '../../core/core-state.model'; +import { DatadogRumService } from './datadog-rum.service'; + +@Injectable() +export class BrowserDatadogRumService extends DatadogRumService { + + consentsUpdates$: BehaviorSubject; + datadogRumStateSelector = createSelector(coreSelector, (state: CoreState) => state.datadogRum); + + constructor( + private klaroService: KlaroService, + private store: Store + ) { + super(); + } + + initDatadogRum() { + this.klaroService.watchConsentUpdates(); + this.consentsUpdates$ = this.klaroService.consentsUpdates$; + this.consentsUpdates$.subscribe(savedPreferences => { + this.getDatadogRumState().subscribe((state) => { + if (savedPreferences?.datadog && + environment.datadogRum?.clientToken && environment.datadogRum?.applicationId && + environment.datadogRum?.service && environment.datadogRum?.env) { + if (!state.isInitialized) { + this.store.dispatch(new setDatadogRumStatusAction({ + isInitialized: true, + isRunning: true + })); + datadogRum.init(environment.datadogRum); + } else if (!state.isRunning) { + this.store.dispatch(new setDatadogRumStatusAction({ + isRunning: true + })); + datadogRum.startSessionReplayRecording(); + } + } else { + datadogRum.stopSessionReplayRecording(); + this.store.dispatch(new setDatadogRumStatusAction({ + isRunning: false + })); + } + }); + }); + } + + + getDatadogRumState(): Observable { + return this.store + .select(this.datadogRumStateSelector) + .pipe( + distinctUntilChanged(), + take(1), + ); + } +} + diff --git a/src/app/shared/datadog-rum/datadog-rum.service.spec.ts b/src/app/shared/datadog-rum/datadog-rum.service.spec.ts index 548e13e9290..0aedf8c40d7 100644 --- a/src/app/shared/datadog-rum/datadog-rum.service.spec.ts +++ b/src/app/shared/datadog-rum/datadog-rum.service.spec.ts @@ -5,9 +5,10 @@ import { CookieConsents, KlaroService } from '../cookies/klaro.service'; import { of } from 'rxjs'; import { environment } from '../../../environments/environment'; import { setDatadogRumStatusAction } from './datadog-rum.actions'; +import { BrowserDatadogRumService } from './browser-datadog-rum.service'; describe('DatadogRumService', () => { - let service: DatadogRumService; + let service: BrowserDatadogRumService; let store: MockStore; let klaroService: KlaroService; let memoizedSelector; @@ -40,12 +41,12 @@ describe('DatadogRumService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ - DatadogRumService, - provideMockStore({initialState}), - {provide: KlaroService, useValue: klaroServiceSpy}, + { provide: DatadogRumService, useClass: BrowserDatadogRumService }, + provideMockStore({ initialState }), + { provide: KlaroService, useValue: klaroServiceSpy }, ] }); - service = TestBed.inject(DatadogRumService); + service = TestBed.inject(DatadogRumService) as BrowserDatadogRumService; store = TestBed.inject(MockStore); memoizedSelector = store.overrideSelector(service.datadogRumStateSelector, initialState.datadogRum); klaroService = TestBed.inject(KlaroService); diff --git a/src/app/shared/datadog-rum/datadog-rum.service.ts b/src/app/shared/datadog-rum/datadog-rum.service.ts index 4ffce1ee73f..f859a19830b 100644 --- a/src/app/shared/datadog-rum/datadog-rum.service.ts +++ b/src/app/shared/datadog-rum/datadog-rum.service.ts @@ -1,67 +1,11 @@ import { Injectable } from '@angular/core'; -import { environment } from '../../../environments/environment'; -import { datadogRum } from '@datadog/browser-rum'; -import { CookieConsents, KlaroService } from '../cookies/klaro.service'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { createSelector, Store } from '@ngrx/store'; -import { setDatadogRumStatusAction } from './datadog-rum.actions'; -import { DatadogRumState } from './datadog-rum.reducer'; -import { distinctUntilChanged, take } from 'rxjs/operators'; -import { coreSelector } from '../../core/core.selectors'; -import { CoreState } from '../../core/core-state.model'; -@Injectable({ - providedIn: 'root' -}) -export class DatadogRumService { +@Injectable() +export abstract class DatadogRumService { - consentsUpdates$: BehaviorSubject; - datadogRumStateSelector = createSelector(coreSelector, (state: CoreState) => state.datadogRum); - - constructor( - private klaroService: KlaroService, - private store: Store - ) { - } - - initDatadogRum() { - this.klaroService.watchConsentUpdates(); - this.consentsUpdates$ = this.klaroService.consentsUpdates$; - this.consentsUpdates$.subscribe(savedPreferences => { - this.getDatadogRumState().subscribe((state) => { - if (savedPreferences?.datadog && - environment.datadogRum?.clientToken && environment.datadogRum?.applicationId && - environment.datadogRum?.service && environment.datadogRum?.env) { - if (!state.isInitialized) { - this.store.dispatch(new setDatadogRumStatusAction({ - isInitialized: true, - isRunning: true - })); - datadogRum.init(environment.datadogRum); - } else if (!state.isRunning) { - this.store.dispatch(new setDatadogRumStatusAction({ - isRunning: true - })); - datadogRum.startSessionReplayRecording(); - } - } else { - datadogRum.stopSessionReplayRecording(); - this.store.dispatch(new setDatadogRumStatusAction({ - isRunning: false - })); - } - }); - }); - } - - - getDatadogRumState(): Observable { - return this.store - .select(this.datadogRumStateSelector) - .pipe( - distinctUntilChanged(), - take(1), - ); - } + /** + * Initializes the service + */ + abstract initDatadogRum(); } diff --git a/src/app/shared/datadog-rum/server-datadog-rum.service.ts b/src/app/shared/datadog-rum/server-datadog-rum.service.ts new file mode 100644 index 00000000000..2f8d074ab90 --- /dev/null +++ b/src/app/shared/datadog-rum/server-datadog-rum.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@angular/core'; +import { DatadogRumService } from './datadog-rum.service'; + +@Injectable() +export class ServerDatadogRumService extends DatadogRumService { + + initDatadogRum() { + return; + } +} + diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index c9ab093c8c0..8d73cde4ed7 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -35,6 +35,8 @@ import { ReferrerService } from '../../app/core/services/referrer.service'; import { BrowserReferrerService } from '../../app/core/services/browser.referrer.service'; import { MathService } from '../../app/core/shared/math.service'; import { ClientMathService } from '../../app/core/shared/client-math.service'; +import { DatadogRumService } from '../../app/shared/datadog-rum/datadog-rum.service'; +import { BrowserDatadogRumService } from '../../app/shared/datadog-rum/browser-datadog-rum.service'; export const REQ_KEY = makeStateKey('req'); @@ -87,6 +89,10 @@ export function getRequest(transferState: TransferState): any { provide: KlaroService, useClass: BrowserKlaroService }, + { + provide: DatadogRumService, + useClass: BrowserDatadogRumService + }, { provide: SubmissionService, useClass: SubmissionService diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index 45bc4e05532..82c580eebc4 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -39,6 +39,8 @@ import { ReferrerService } from '../../app/core/services/referrer.service'; import { ServerReferrerService } from '../../app/core/services/server.referrer.service'; import { MathService } from '../../app/core/shared/math.service'; import { ServerMathService } from '../../app/core/shared/server-math.service'; +import { DatadogRumService } from '../../app/shared/datadog-rum/datadog-rum.service'; +import { ServerDatadogRumService } from '../../app/shared/datadog-rum/server-datadog-rum.service'; export function createTranslateLoader(transferState: TransferState) { return new TranslateServerLoader(transferState, 'dist/server/assets/i18n/', '.json'); @@ -121,7 +123,11 @@ export function createTranslateLoader(transferState: TransferState) { { provide: MathService, useClass: ServerMathService - } + }, + { + provide: DatadogRumService, + useClass: ServerDatadogRumService + }, ] }) export class ServerAppModule {