93219: Move APP_INITIALIZER logic into InitService

This commit is contained in:
Yura Bondarenko
2022-07-18 15:55:30 +02:00
parent 572a10db6b
commit 39c2aa85ec
6 changed files with 183 additions and 69 deletions

View File

@@ -7,13 +7,9 @@ import { BrowserModule } from '@angular/platform-browser';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { EffectsModule } from '@ngrx/effects';
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
import { MetaReducer, Store, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
import { MetaReducer, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import {
DYNAMIC_ERROR_MESSAGES_MATCHER,
DYNAMIC_MATCHER_PROVIDERS,
DynamicErrorMessagesMatcher
} from '@ng-dynamic-forms/core';
import { DYNAMIC_ERROR_MESSAGES_MATCHER, DYNAMIC_MATCHER_PROVIDERS, DynamicErrorMessagesMatcher } from '@ng-dynamic-forms/core';
import { TranslateModule } from '@ngx-translate/core';
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
import { AppRoutingModule } from './app-routing.module';
@@ -21,7 +17,6 @@ import { AppComponent } from './app.component';
import { appEffects } from './app.effects';
import { appMetaReducers, debugMetaReducers } from './app.metareducers';
import { appReducers, AppState, storeModuleConfig } from './app.reducer';
import { CheckAuthenticationTokenAction } from './core/auth/auth.actions';
import { CoreModule } from './core/core.module';
import { ClientCookieService } from './core/services/client-cookie.service';
import { NavbarModule } from './navbar/navbar.module';
@@ -36,6 +31,7 @@ import { EagerThemesModule } from '../themes/eager-themes.module';
import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
import { RootModule } from './root.module';
import { InitService } from './init.service';
export function getConfig() {
return environment;
@@ -82,6 +78,12 @@ IMPORTS.push(
);
const PROVIDERS = [
{
provide: APP_INITIALIZER,
useFactory: (initService: InitService) => initService.init(),
deps: [ InitService ],
multi: true,
},
{
provide: APP_CONFIG,
useFactory: getConfig
@@ -101,15 +103,6 @@ const PROVIDERS = [
useClass: DSpaceRouterStateSerializer
},
ClientCookieService,
// Check the authentication token when the app initializes
{
provide: APP_INITIALIZER,
useFactory: (store: Store<AppState>,) => {
return () => store.dispatch(new CheckAuthenticationTokenAction());
},
deps: [Store],
multi: true
},
// register AuthInterceptor as HttpInterceptor
{
provide: HTTP_INTERCEPTORS,

63
src/app/init.service.ts Normal file
View File

@@ -0,0 +1,63 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { Store } from '@ngrx/store';
import { AppState } from './app.reducer';
import { CheckAuthenticationTokenAction } from './core/auth/auth.actions';
import { CorrelationIdService } from './correlation-id/correlation-id.service';
import { DSpaceTransferState } from '../modules/transfer-state/dspace-transfer-state.service';
/**
* Performs the initialization of the app.
*
* Should have distinct extensions for server & browser for the specifics.
* Should be provided in AppModule as follows
* ```
* {
* provide: APP_INITIALIZER
* useFactory: (initService: InitService) => initService.init(),
* deps: [ InitService ],
* multi: true,
* }
* ```
*
* In order to be injected in the common APP_INITIALIZER,
* concrete subclasses should be provided in their respective app modules as
* ```
* { provide: InitService, useClass: SpecificInitService }
* ```
*/
export abstract class InitService {
protected constructor(
protected store: Store<AppState>,
protected correlationIdService: CorrelationIdService,
protected dspaceTransferState: DSpaceTransferState,
) {
}
/**
* Main initialization method, to be used as the APP_INITIALIZER factory.
*
* Note that the body of this method and the callback it returns are called
* at different times.
* This is important to take into account when other providers depend on the
* initialization logic (e.g. APP_BASE_HREF)
*/
abstract init(): () => Promise<boolean>;
protected checkAuthenticationToken(): void {
this.store.dispatch(new CheckAuthenticationTokenAction());
}
protected initCorrelationId(): void {
this.correlationIdService.initCorrelationId();
}
protected async transferAppState(): Promise<unknown> {
return this.dspaceTransferState.transfer();
}
}

View File

@@ -1,5 +1,5 @@
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule, makeStateKey, TransferState } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { REQUEST } from '@nguniversal/express-engine/tokens';
@@ -13,7 +13,6 @@ import { AppComponent } from '../../app/app.component';
import { AppModule } from '../../app/app.module';
import { DSpaceBrowserTransferStateModule } from '../transfer-state/dspace-browser-transfer-state.module';
import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service';
import { ClientCookieService } from '../../app/core/services/client-cookie.service';
import { CookieService } from '../../app/core/services/cookie.service';
import { AuthService } from '../../app/core/auth/auth.service';
@@ -23,21 +22,13 @@ import { StatisticsModule } from '../../app/statistics/statistics.module';
import { BrowserKlaroService } from '../../app/shared/cookies/browser-klaro.service';
import { KlaroService } from '../../app/shared/cookies/klaro.service';
import { HardRedirectService } from '../../app/core/services/hard-redirect.service';
import {
BrowserHardRedirectService,
locationProvider,
LocationToken
} from '../../app/core/services/browser-hard-redirect.service';
import { BrowserHardRedirectService, locationProvider, LocationToken } from '../../app/core/services/browser-hard-redirect.service';
import { LocaleService } from '../../app/core/locale/locale.service';
import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.service';
import { AuthRequestService } from '../../app/core/auth/auth-request.service';
import { BrowserAuthRequestService } from '../../app/core/auth/browser-auth-request.service';
import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface';
import { DefaultAppConfig } from '../../config/default-app-config';
import { extendEnvironmentWithAppConfig } from '../../config/config.util';
import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service';
import { environment } from '../../environments/environment';
import { InitService } from 'src/app/init.service';
import { BrowserInitService } from './browser-init.service';
export const REQ_KEY = makeStateKey<string>('req');
@@ -73,25 +64,8 @@ export function getRequest(transferState: TransferState): any {
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: (
transferState: TransferState,
dspaceTransferState: DSpaceTransferState,
correlationIdService: CorrelationIdService
) => {
if (transferState.hasKey<AppConfig>(APP_CONFIG_STATE)) {
const appConfig = transferState.get<AppConfig>(APP_CONFIG_STATE, new DefaultAppConfig());
// extend environment with app config for browser
extendEnvironmentWithAppConfig(environment, appConfig);
}
return () =>
dspaceTransferState.transfer().then((b: boolean) => {
correlationIdService.initCorrelationId();
return b;
});
},
deps: [TransferState, DSpaceTransferState, CorrelationIdService],
multi: true
provide: InitService,
useClass: BrowserInitService,
},
{
provide: REQUEST,

View File

@@ -0,0 +1,54 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { InitService } from '../../app/init.service';
import { Store } from '@ngrx/store';
import { AppState } from '../../app/app.reducer';
import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service';
import { TransferState } from '@angular/platform-browser';
import { APP_CONFIG_STATE, AppConfig } from '../../config/app-config.interface';
import { DefaultAppConfig } from '../../config/default-app-config';
import { extendEnvironmentWithAppConfig } from '../../config/config.util';
import { environment } from '../../environments/environment';
import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service';
import { Injectable } from '@angular/core';
/**
* Performs client-side initialization.
*/
@Injectable()
export class BrowserInitService extends InitService {
constructor(
protected store: Store<AppState>,
protected correlationIdService: CorrelationIdService,
protected transferState: TransferState,
protected dspaceTransferState: DSpaceTransferState,
) {
super(store, correlationIdService, dspaceTransferState);
}
public init(): () => Promise<boolean> {
// this method must be called before the callback because APP_BASE_HREF depends on it
this.loadAppConfigFromSSR();
return async () => {
await this.transferAppState();
this.checkAuthenticationToken();
this.initCorrelationId();
return true;
};
}
private loadAppConfigFromSSR(): void {
if (this.transferState.hasKey<AppConfig>(APP_CONFIG_STATE)) {
const appConfig = this.transferState.get<AppConfig>(APP_CONFIG_STATE, new DefaultAppConfig());
// extend environment with app config for browser
extendEnvironmentWithAppConfig(environment, appConfig);
}
}
}

View File

@@ -1,9 +1,8 @@
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { NgModule } from '@angular/core';
import { BrowserModule, TransferState } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ServerModule } from '@angular/platform-server';
import { RouterModule } from '@angular/router';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
@@ -14,7 +13,6 @@ import { AppComponent } from '../../app/app.component';
import { AppModule } from '../../app/app.module';
import { DSpaceServerTransferStateModule } from '../transfer-state/dspace-server-transfer-state.module';
import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service';
import { TranslateServerLoader } from '../../ngx-translate-loaders/translate-server.loader';
import { CookieService } from '../../app/core/services/cookie.service';
import { ServerCookieService } from '../../app/core/services/server-cookie.service';
@@ -32,10 +30,8 @@ import { ServerHardRedirectService } from '../../app/core/services/server-hard-r
import { Angulartics2Mock } from '../../app/shared/mocks/angulartics2.service.mock';
import { AuthRequestService } from '../../app/core/auth/auth-request.service';
import { ServerAuthRequestService } from '../../app/core/auth/server-auth-request.service';
import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service';
import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface';
import { environment } from '../../environments/environment';
import { InitService } from '../../app/init.service';
import { ServerInitService } from './server-init.service';
export function createTranslateLoader(transferState: TransferState) {
return new TranslateServerLoader(transferState, 'dist/server/assets/i18n/', '.json5');
@@ -60,21 +56,9 @@ export function createTranslateLoader(transferState: TransferState) {
AppModule
],
providers: [
// Initialize app config and extend environment
{
provide: APP_INITIALIZER,
useFactory: (
transferState: TransferState,
dspaceTransferState: DSpaceTransferState,
correlationIdService: CorrelationIdService,
) => {
transferState.set<AppConfig>(APP_CONFIG_STATE, environment as AppConfig);
dspaceTransferState.transfer();
correlationIdService.initCorrelationId();
return () => true;
},
deps: [TransferState, DSpaceTransferState, CorrelationIdService],
multi: true
provide: InitService,
useClass: ServerInitService,
},
{
provide: Angulartics2,

View File

@@ -0,0 +1,46 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
import { InitService } from '../../app/init.service';
import { Store } from '@ngrx/store';
import { AppState } from '../../app/app.reducer';
import { TransferState } from '@angular/platform-browser';
import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service';
import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service';
import { APP_CONFIG_STATE, AppConfig } from '../../config/app-config.interface';
import { environment } from '../../environments/environment';
import { Injectable } from '@angular/core';
/**
* Performs server-side initialization.
*/
@Injectable()
export class ServerInitService extends InitService {
constructor(
protected store: Store<AppState>,
protected correlationIdService: CorrelationIdService,
protected transferState: TransferState,
protected dspaceTransferState: DSpaceTransferState,
) {
super(store, correlationIdService, dspaceTransferState);
}
public init(): () => Promise<boolean> {
return async () => {
this.checkAuthenticationToken();
this.saveAppConfigForCSR();
this.transferAppState(); // todo: SSR breaks if we await this (why?)
this.initCorrelationId();
return true;
};
}
private saveAppConfigForCSR(): void {
this.transferState.set<AppConfig>(APP_CONFIG_STATE, environment as AppConfig);
}
}