diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 98b2d9fe92..4a404e3200 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -156,21 +156,6 @@ const PROVIDERS = [ useClass: LogInterceptor, multi: true }, - // insert the unique id of the user that is using the application utilizing cookies - { - provide: APP_INITIALIZER, - useFactory: (cookieService: CookieService, uuidService: UUIDService) => { - const correlationId = cookieService.get('CORRELATION-ID'); - - // Check if cookie exists, if don't, set it with unique id - if (!correlationId) { - cookieService.set('CORRELATION-ID', uuidService.generate()); - } - return () => true; - }, - multi: true, - deps: [CookieService, UUIDService] - }, { provide: DYNAMIC_ERROR_MESSAGES_MATCHER, useValue: ValidateEmailErrorStateMatcher diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index a02095d834..5bd4f745d9 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -49,6 +49,7 @@ import { import { sidebarReducer, SidebarState } from './shared/sidebar/sidebar.reducer'; import { truncatableReducer, TruncatablesState } from './shared/truncatable/truncatable.reducer'; import { ThemeState, themeReducer } from './shared/theme-support/theme.reducer'; +import { correlationIdReducer } from './correlation-id/correlation-id.reducer'; export interface AppState { router: fromRouter.RouterReducerState; @@ -69,6 +70,7 @@ export interface AppState { communityList: CommunityListState; epeopleRegistry: EPeopleRegistryState; groupRegistry: GroupRegistryState; + correlationId: string; } export const appReducers: ActionReducerMap = { @@ -90,6 +92,7 @@ export const appReducers: ActionReducerMap = { communityList: CommunityListReducer, epeopleRegistry: ePeopleRegistryReducer, groupRegistry: groupRegistryReducer, + correlationId: correlationIdReducer }; export const routerStateSelector = (state: AppState) => state.router; diff --git a/src/app/core/log/log.interceptor.ts b/src/app/core/log/log.interceptor.ts index bf843f1da8..aff1a24963 100644 --- a/src/app/core/log/log.interceptor.ts +++ b/src/app/core/log/log.interceptor.ts @@ -3,9 +3,8 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/c import { Router } from '@angular/router'; import { Observable } from 'rxjs'; - -import { CookieService } from '../services/cookie.service'; import { hasValue } from '../../shared/empty.util'; +import { CorrelationIdService } from '../../correlation-id/correlation-id.service'; /** * Log Interceptor intercepting Http Requests & Responses to @@ -15,12 +14,12 @@ import { hasValue } from '../../shared/empty.util'; @Injectable() export class LogInterceptor implements HttpInterceptor { - constructor(private cookieService: CookieService, private router: Router) {} + constructor(private cidService: CorrelationIdService, private router: Router) {} intercept(request: HttpRequest, next: HttpHandler): Observable> { - // Get Unique id of the user from the cookies - const correlationId = this.cookieService.get('CORRELATION-ID'); + // Get the correlation id for the user from the store + const correlationId = this.cidService.getCorrelationId(); // Add headers from the intercepted request let headers = request.headers; diff --git a/src/app/correlation-id/correlation-id.actions.ts b/src/app/correlation-id/correlation-id.actions.ts new file mode 100644 index 0000000000..0901974a4c --- /dev/null +++ b/src/app/correlation-id/correlation-id.actions.ts @@ -0,0 +1,15 @@ +import { type } from '../shared/ngrx/type'; +import { Action } from '@ngrx/store'; + +export const CorrelationIDActionTypes = { + SET: type('dspace/core/correlationId/SET') +}; + +export class SetCorrelationIdAction implements Action { + type = CorrelationIDActionTypes.SET; + + constructor(public payload: string) { + } +} + +export type CorrelationIdAction = SetCorrelationIdAction; diff --git a/src/app/correlation-id/correlation-id.reducer.ts b/src/app/correlation-id/correlation-id.reducer.ts new file mode 100644 index 0000000000..e8071a3108 --- /dev/null +++ b/src/app/correlation-id/correlation-id.reducer.ts @@ -0,0 +1,21 @@ +import { + CorrelationIdAction, + CorrelationIDActionTypes, + SetCorrelationIdAction +} from './correlation-id.actions'; +import { AppState } from '../app.reducer'; + +const initialState = null; + +export const correlationIdSelector = (state: AppState) => state.correlationId; + +export const correlationIdReducer = (state = initialState, action: CorrelationIdAction): string => { + switch (action.type) { + case CorrelationIDActionTypes.SET: { + return (action as SetCorrelationIdAction).payload; + } + default: { + return state; + } + } +}; diff --git a/src/app/correlation-id/correlation-id.service.ts b/src/app/correlation-id/correlation-id.service.ts new file mode 100644 index 0000000000..6f4b2a5341 --- /dev/null +++ b/src/app/correlation-id/correlation-id.service.ts @@ -0,0 +1,64 @@ +import { CookieService } from '../core/services/cookie.service'; +import { UUIDService } from '../core/shared/uuid.service'; +import { Store, select } from '@ngrx/store'; +import { AppState } from '../app.reducer'; +import { isEmpty } from '../shared/empty.util'; +import { correlationIdSelector } from './correlation-id.reducer'; +import { take } from 'rxjs/operators'; +import { SetCorrelationIdAction } from './correlation-id.actions'; +import { Injectable } from '@angular/core'; + +/** + * Service to manage the correlation id, an id used to give context to server side logs + */ +@Injectable({ + providedIn: 'root' +}) +export class CorrelationIdService { + + constructor( + protected cookieService: CookieService, + protected uuidService: UUIDService, + protected store: Store, + ) { + } + + /** + * Initialize the correlation id based on the cookie or the ngrx store + */ + initCorrelationId(): void { + // first see of there's a cookie with a correlation-id + let correlationId = this.cookieService.get('CORRELATION-ID'); + + // if there isn't see if there's an ID in the store + if (isEmpty(correlationId)) { + correlationId = this.getCorrelationId(); + } + + // if no id was found, create a new id + if (isEmpty(correlationId)) { + correlationId = this.uuidService.generate(); + } + + // Store the correct id both in the store and as a cookie to ensure they're in sync + this.store.dispatch(new SetCorrelationIdAction(correlationId)); + this.cookieService.set('CORRELATION-ID', correlationId); + } + + /** + * Get the correlation id from the store + */ + getCorrelationId(): string { + let correlationId; + + this.store.pipe( + select(correlationIdSelector), + take(1) + ).subscribe((storeId: string) => { + // we can do this because ngrx selects are synchronous + correlationId = storeId; + }); + + return correlationId; + } +} diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index 86497d4599..88a59eb157 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -36,6 +36,7 @@ import { BrowserAuthRequestService } from '../../app/core/auth/browser-auth-requ 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'; @@ -81,16 +82,21 @@ export function getRequest(transferState: TransferState): any { providers: [ { provide: APP_INITIALIZER, - useFactory: (transferState: TransferState) => { + useFactory: ( + transferState: TransferState, + dspaceTransferState: DSpaceTransferState, + correlationIdService: CorrelationIdService + ) => { if (transferState.hasKey(APP_CONFIG_STATE)) { const appConfig = transferState.get(APP_CONFIG_STATE, new DefaultAppConfig()); // extend environment with app config for browser extendEnvironmentWithAppConfig(environment, appConfig); } - + dspaceTransferState.transfer(); + correlationIdService.initCorrelationId(); return () => true; }, - deps: [TransferState], + deps: [TransferState, DSpaceTransferState, CorrelationIdService], multi: true }, { @@ -137,9 +143,4 @@ export function getRequest(transferState: TransferState): any { ] }) export class BrowserAppModule { - constructor( - private transferState: DSpaceTransferState, - ) { - this.transferState.transfer(); - } } diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index 8c49554de9..01a5548948 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -32,6 +32,7 @@ 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'; @@ -65,11 +66,17 @@ export function createTranslateLoader() { // Initialize app config and extend environment { provide: APP_INITIALIZER, - useFactory: (transferState: TransferState) => { + useFactory: ( + transferState: TransferState, + dspaceTransferState: DSpaceTransferState, + correlationIdService: CorrelationIdService, + ) => { transferState.set(APP_CONFIG_STATE, environment as AppConfig); + dspaceTransferState.transfer(); + correlationIdService.initCorrelationId(); return () => true; }, - deps: [TransferState], + deps: [TransferState, DSpaceTransferState, CorrelationIdService], multi: true }, { @@ -117,9 +124,4 @@ export function createTranslateLoader() { ] }) export class ServerAppModule { - constructor( - private transferState: DSpaceTransferState, - ) { - this.transferState.transfer(); - } }