From 617c7d87568ffdaa3979f3e88d1f84ae59d090c6 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 9 May 2023 17:20:54 +0200 Subject: [PATCH 1/4] make a call to ensure a correct XSRF token before performing any non-GET requests --- .../group-form/group-form.component.spec.ts | 2 + src/app/core/data/request.service.spec.ts | 8 +++ src/app/core/data/request.service.ts | 14 +++- .../core/xsrf/browser-xsrf.service.spec.ts | 64 +++++++++++++++++++ src/app/core/xsrf/browser-xsrf.service.ts | 26 ++++++++ src/app/core/xsrf/server-xsrf.service.spec.ts | 32 ++++++++++ src/app/core/xsrf/server-xsrf.service.ts | 14 ++++ src/app/core/xsrf/xsrf.service.spec.ts | 20 ++++++ src/app/core/xsrf/xsrf.service.ts | 10 +++ src/modules/app/browser-app.module.ts | 19 +++++- src/modules/app/server-app.module.ts | 6 ++ 11 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 src/app/core/xsrf/browser-xsrf.service.spec.ts create mode 100644 src/app/core/xsrf/browser-xsrf.service.ts create mode 100644 src/app/core/xsrf/server-xsrf.service.spec.ts create mode 100644 src/app/core/xsrf/server-xsrf.service.ts create mode 100644 src/app/core/xsrf/xsrf.service.spec.ts create mode 100644 src/app/core/xsrf/xsrf.service.ts diff --git a/src/app/access-control/group-registry/group-form/group-form.component.spec.ts b/src/app/access-control/group-registry/group-form/group-form.component.spec.ts index 0c8c64a4706..02de06f4155 100644 --- a/src/app/access-control/group-registry/group-form/group-form.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/group-form.component.spec.ts @@ -53,6 +53,7 @@ import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { NoContent } from '../../../core/shared/NoContent.model'; import { PageInfo } from '../../../core/shared/page-info.model'; import { UUIDService } from '../../../core/shared/uuid.service'; +import { XSRFService } from '../../../core/xsrf/xsrf.service'; import { AlertComponent } from '../../../shared/alert/alert.component'; import { ContextHelpDirective } from '../../../shared/context-help.directive'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; @@ -244,6 +245,7 @@ describe('GroupFormComponent', () => { { provide: HttpClient, useValue: {} }, { provide: ObjectCacheService, useValue: {} }, { provide: UUIDService, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: Store, useValue: {} }, { provide: RemoteDataBuildService, useValue: {} }, { provide: HALEndpointService, useValue: {} }, diff --git a/src/app/core/data/request.service.spec.ts b/src/app/core/data/request.service.spec.ts index 76e70e8a6d1..92442854ba1 100644 --- a/src/app/core/data/request.service.spec.ts +++ b/src/app/core/data/request.service.spec.ts @@ -16,6 +16,7 @@ import { getTestScheduler, } from 'jasmine-marbles'; import { + BehaviorSubject, EMPTY, Observable, of as observableOf, @@ -32,6 +33,7 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { coreReducers } from '../core.reducers'; import { CoreState } from '../core-state.model'; import { UUIDService } from '../shared/uuid.service'; +import { XSRFService } from '../xsrf/xsrf.service'; import { RequestConfigureAction, RequestExecuteAction, @@ -59,6 +61,7 @@ describe('RequestService', () => { let uuidService: UUIDService; let store: Store; let mockStore: MockStore; + let xsrfService: XSRFService; const testUUID = '5f2a0d2a-effa-4d54-bd54-5663b960f9eb'; const testHref = 'https://rest.api/endpoint/selfLink'; @@ -104,10 +107,15 @@ describe('RequestService', () => { store = TestBed.inject(Store); mockStore = store as MockStore; mockStore.setState(initialState); + xsrfService = { + tokenInitialized$: new BehaviorSubject(false), + } as XSRFService; + service = new RequestService( objectCache, uuidService, store, + xsrfService, undefined, ); serviceAsAny = service as any; diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index 9bd262b1adb..6ce37d35450 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -42,6 +42,7 @@ import { requestIndexSelector, } from '../index/index.selectors'; import { UUIDService } from '../shared/uuid.service'; +import { XSRFService } from '../xsrf/xsrf.service'; import { RequestConfigureAction, RequestExecuteAction, @@ -168,6 +169,7 @@ export class RequestService { constructor(private objectCache: ObjectCacheService, private uuidService: UUIDService, private store: Store, + protected xsrfService: XSRFService, private indexStore: Store) { } @@ -450,7 +452,17 @@ export class RequestService { */ private dispatchRequest(request: RestRequest) { this.store.dispatch(new RequestConfigureAction(request)); - this.store.dispatch(new RequestExecuteAction(request.uuid)); + // If it's a GET request, or we have an XSRF token, dispatch it immediately + if (request.method === RestRequestMethod.GET || this.xsrfService.tokenInitialized$.getValue() === true) { + this.store.dispatch(new RequestExecuteAction(request.uuid)); + } else { + // Otherwise wait for the XSRF token first + this.xsrfService.tokenInitialized$.pipe( + find((hasInitialized: boolean) => hasInitialized === true), + ).subscribe(() => { + this.store.dispatch(new RequestExecuteAction(request.uuid)); + }); + } } /** diff --git a/src/app/core/xsrf/browser-xsrf.service.spec.ts b/src/app/core/xsrf/browser-xsrf.service.spec.ts new file mode 100644 index 00000000000..378df0e46ba --- /dev/null +++ b/src/app/core/xsrf/browser-xsrf.service.spec.ts @@ -0,0 +1,64 @@ +import { BrowserXSRFService } from './browser-xsrf.service'; +import { HttpClient } from '@angular/common/http'; +import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +describe(`BrowserXSRFService`, () => { + let service: BrowserXSRFService; + let httpClient: HttpClient; + let httpTestingController: HttpTestingController; + + const endpointURL = new RESTURLCombiner('/security/csrf').toString(); + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpClientTestingModule ], + providers: [ BrowserXSRFService ] + }); + httpClient = TestBed.inject(HttpClient); + httpTestingController = TestBed.inject(HttpTestingController); + service = TestBed.inject(BrowserXSRFService); + }); + + describe(`initXSRFToken`, () => { + it(`should perform a POST to the csrf endpoint`, () => { + service.initXSRFToken(httpClient)(); + + const req = httpTestingController.expectOne({ + url: endpointURL, + method: 'POST' + }); + + req.flush({}); + httpTestingController.verify(); + }); + + describe(`when the POST succeeds`, () => { + it(`should set tokenInitialized$ to true`, () => { + service.initXSRFToken(httpClient)(); + + const req = httpTestingController.expectOne(endpointURL); + + req.flush({}); + httpTestingController.verify(); + + expect(service.tokenInitialized$.getValue()).toBeTrue(); + }); + }); + + describe(`when the POST fails`, () => { + it(`should set tokenInitialized$ to true`, () => { + service.initXSRFToken(httpClient)(); + + const req = httpTestingController.expectOne(endpointURL); + + req.error(new ErrorEvent('415')); + httpTestingController.verify(); + + expect(service.tokenInitialized$.getValue()).toBeTrue(); + }); + }); + + }); +}); diff --git a/src/app/core/xsrf/browser-xsrf.service.ts b/src/app/core/xsrf/browser-xsrf.service.ts new file mode 100644 index 00000000000..271e8dca47a --- /dev/null +++ b/src/app/core/xsrf/browser-xsrf.service.ts @@ -0,0 +1,26 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; +import { take, catchError } from 'rxjs/operators'; +import { of as observableOf } from 'rxjs'; +import { XSRFService } from './xsrf.service'; + +@Injectable() +export class BrowserXSRFService extends XSRFService { + initXSRFToken(httpClient: HttpClient): () => Promise { + return () => new Promise((resolve) => { + httpClient.post(new RESTURLCombiner('/security/csrf').toString(), undefined).pipe( + // errors are to be expected if the token and the cookie don't match, that's what we're + // trying to fix for future requests, so just emit any observable to end up in the + // subscribe + catchError(() => observableOf(null)), + take(1), + ).subscribe(() => { + this.tokenInitialized$.next(true); + }); + + // return immediately, the rest of the app doesn't need to wait for this to finish + resolve(); + }); + } +} diff --git a/src/app/core/xsrf/server-xsrf.service.spec.ts b/src/app/core/xsrf/server-xsrf.service.spec.ts new file mode 100644 index 00000000000..b2ace67dd0c --- /dev/null +++ b/src/app/core/xsrf/server-xsrf.service.spec.ts @@ -0,0 +1,32 @@ +import { ServerXSRFService } from './server-xsrf.service'; +import { HttpClient } from '@angular/common/http'; + +describe(`ServerXSRFService`, () => { + let service: ServerXSRFService; + let httpClient: HttpClient; + + beforeEach(() => { + httpClient = jasmine.createSpyObj(['post', 'get', 'request']); + service = new ServerXSRFService(); + }); + + describe(`initXSRFToken`, () => { + it(`shouldn't perform any requests`, (done: DoneFn) => { + service.initXSRFToken(httpClient)().then(() => { + for (const prop in httpClient) { + if (httpClient.hasOwnProperty(prop)) { + expect(httpClient[prop]).not.toHaveBeenCalled(); + } + } + done(); + }); + }); + + it(`should leave tokenInitialized$ on false`, (done: DoneFn) => { + service.initXSRFToken(httpClient)().then(() => { + expect(service.tokenInitialized$.getValue()).toBeFalse(); + done(); + }); + }); + }); +}); diff --git a/src/app/core/xsrf/server-xsrf.service.ts b/src/app/core/xsrf/server-xsrf.service.ts new file mode 100644 index 00000000000..1577893f952 --- /dev/null +++ b/src/app/core/xsrf/server-xsrf.service.ts @@ -0,0 +1,14 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { XSRFService } from './xsrf.service'; + +@Injectable() +export class ServerXSRFService extends XSRFService { + initXSRFToken(httpClient: HttpClient): () => Promise { + return () => new Promise((resolve) => { + // return immediately, and keep tokenInitialized$ false. The server side can make only GET + // requests, since it can never get a valid XSRF cookie + resolve(); + }); + } +} diff --git a/src/app/core/xsrf/xsrf.service.spec.ts b/src/app/core/xsrf/xsrf.service.spec.ts new file mode 100644 index 00000000000..a7c5c01cb7d --- /dev/null +++ b/src/app/core/xsrf/xsrf.service.spec.ts @@ -0,0 +1,20 @@ +import { XSRFService } from './xsrf.service'; +import { HttpClient } from '@angular/common/http'; + +class XSRFServiceImpl extends XSRFService { + initXSRFToken(httpClient: HttpClient): () => Promise { + return () => null; + } +} + +describe(`XSRFService`, () => { + let service: XSRFService; + + beforeEach(() => { + service = new XSRFServiceImpl(); + }); + + it(`should start with tokenInitialized$.hasValue() === false`, () => { + expect(service.tokenInitialized$.getValue()).toBeFalse(); + }); +}); diff --git a/src/app/core/xsrf/xsrf.service.ts b/src/app/core/xsrf/xsrf.service.ts new file mode 100644 index 00000000000..fb8dfe74b33 --- /dev/null +++ b/src/app/core/xsrf/xsrf.service.ts @@ -0,0 +1,10 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; + +@Injectable() +export abstract class XSRFService { + public tokenInitialized$: BehaviorSubject = new BehaviorSubject(false); + + abstract initXSRFToken(httpClient: HttpClient): () => Promise; +} diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index cb8431beb8c..48b40dcddb6 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -2,7 +2,10 @@ import { HttpClient, HttpClientModule, } from '@angular/common/http'; -import { NgModule } from '@angular/core'; +import { + APP_INITIALIZER, + NgModule, +} from '@angular/core'; import { BrowserModule, BrowserTransferStateModule, @@ -48,13 +51,15 @@ import { ClientCookieService } from '../../app/core/services/client-cookie.servi import { CookieService } from '../../app/core/services/cookie.service'; import { HardRedirectService } from '../../app/core/services/hard-redirect.service'; import { ReferrerService } from '../../app/core/services/referrer.service'; +import { BrowserXSRFService } from '../../app/core/xsrf/browser-xsrf.service'; +import { XSRFService } from '../../app/core/xsrf/xsrf.service'; import { BrowserKlaroService } from '../../app/shared/cookies/browser-klaro.service'; import { KlaroService } from '../../app/shared/cookies/klaro.service'; import { MissingTranslationHelper } from '../../app/shared/translate/missing-translation.helper'; import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.service'; import { SubmissionService } from '../../app/submission/submission.service'; import { TranslateBrowserLoader } from '../../ngx-translate-loaders/translate-browser.loader'; -import { BrowserInitService } from './browser-init.service'; +import { BrowserInitService } from './browser-init.service' export const REQ_KEY = makeStateKey('req'); @@ -98,6 +103,16 @@ export function getRequest(transferState: TransferState): any { useFactory: getRequest, deps: [TransferState], }, + { + provide: APP_INITIALIZER, + useFactory: (xsrfService: XSRFService, httpClient: HttpClient) => xsrfService.initXSRFToken(httpClient), + deps: [ XSRFService, HttpClient ], + multi: true, + }, + { + provide: XSRFService, + useClass: BrowserXSRFService, + }, { provide: AuthService, useClass: AuthService, diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index 4de5687d041..20db68c6711 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -46,6 +46,8 @@ import { ServerReferrerService } from '../../app/core/services/server.referrer.s import { ServerCookieService } from '../../app/core/services/server-cookie.service'; import { ServerHardRedirectService } from '../../app/core/services/server-hard-redirect.service'; import { ServerXhrService } from '../../app/core/services/server-xhr.service'; +import { ServerXSRFService } from '../../app/core/xsrf/server-xsrf.service'; +import { XSRFService } from '../../app/core/xsrf/xsrf.service'; import { AngularticsProviderMock } from '../../app/shared/mocks/angulartics-provider.service.mock'; import { Angulartics2Mock } from '../../app/shared/mocks/angulartics2.service.mock'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; @@ -112,6 +114,10 @@ export function createTranslateLoader(transferState: TransferState) { provide: AuthRequestService, useClass: ServerAuthRequestService, }, + { + provide: XSRFService, + useClass: ServerXSRFService, + }, { provide: LocaleService, useClass: ServerLocaleService, From cc6b159a8ae7462c28bc4e5317cb566f12f44a56 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 3 Apr 2024 10:25:43 -0500 Subject: [PATCH 2/4] Update code to use GET request. Cleanup lint errors & add in basic TypeDocs --- .../core/xsrf/browser-xsrf.service.spec.ts | 36 ++++++++----------- src/app/core/xsrf/browser-xsrf.service.ts | 20 ++++++----- src/app/core/xsrf/server-xsrf.service.spec.ts | 3 +- src/app/core/xsrf/server-xsrf.service.ts | 7 +++- src/app/core/xsrf/xsrf.service.spec.ts | 3 +- src/app/core/xsrf/xsrf.service.ts | 5 +++ src/modules/app/browser-app.module.ts | 2 +- 7 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/app/core/xsrf/browser-xsrf.service.spec.ts b/src/app/core/xsrf/browser-xsrf.service.spec.ts index 378df0e46ba..aba3edd3305 100644 --- a/src/app/core/xsrf/browser-xsrf.service.spec.ts +++ b/src/app/core/xsrf/browser-xsrf.service.spec.ts @@ -1,8 +1,12 @@ -import { BrowserXSRFService } from './browser-xsrf.service'; import { HttpClient } from '@angular/common/http'; -import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; +import { + HttpClientTestingModule, + HttpTestingController, +} from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; +import { BrowserXSRFService } from './browser-xsrf.service'; describe(`BrowserXSRFService`, () => { let service: BrowserXSRFService; @@ -14,7 +18,7 @@ describe(`BrowserXSRFService`, () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule ], - providers: [ BrowserXSRFService ] + providers: [ BrowserXSRFService ], }); httpClient = TestBed.inject(HttpClient); httpTestingController = TestBed.inject(HttpTestingController); @@ -22,20 +26,22 @@ describe(`BrowserXSRFService`, () => { }); describe(`initXSRFToken`, () => { - it(`should perform a POST to the csrf endpoint`, () => { + it(`should perform a GET to the csrf endpoint`, (done: DoneFn) => { service.initXSRFToken(httpClient)(); const req = httpTestingController.expectOne({ url: endpointURL, - method: 'POST' + method: 'GET', }); req.flush({}); httpTestingController.verify(); + expect().nothing(); + done(); }); - describe(`when the POST succeeds`, () => { - it(`should set tokenInitialized$ to true`, () => { + describe(`when the GET succeeds`, () => { + it(`should set tokenInitialized$ to true`, (done: DoneFn) => { service.initXSRFToken(httpClient)(); const req = httpTestingController.expectOne(endpointURL); @@ -44,19 +50,7 @@ describe(`BrowserXSRFService`, () => { httpTestingController.verify(); expect(service.tokenInitialized$.getValue()).toBeTrue(); - }); - }); - - describe(`when the POST fails`, () => { - it(`should set tokenInitialized$ to true`, () => { - service.initXSRFToken(httpClient)(); - - const req = httpTestingController.expectOne(endpointURL); - - req.error(new ErrorEvent('415')); - httpTestingController.verify(); - - expect(service.tokenInitialized$.getValue()).toBeTrue(); + done(); }); }); diff --git a/src/app/core/xsrf/browser-xsrf.service.ts b/src/app/core/xsrf/browser-xsrf.service.ts index 271e8dca47a..121defc061b 100644 --- a/src/app/core/xsrf/browser-xsrf.service.ts +++ b/src/app/core/xsrf/browser-xsrf.service.ts @@ -1,21 +1,25 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { take } from 'rxjs/operators'; + import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; -import { take, catchError } from 'rxjs/operators'; -import { of as observableOf } from 'rxjs'; import { XSRFService } from './xsrf.service'; +/** + * Browser (CSR) Service to obtain a new CSRF/XSRF token when needed by our RequestService + * to perform a modify request (e.g. POST/PUT/DELETE). + * NOTE: This is primarily necessary before the *first* modifying request, as the CSRF + * token may not yet be initialized. + */ @Injectable() export class BrowserXSRFService extends XSRFService { initXSRFToken(httpClient: HttpClient): () => Promise { - return () => new Promise((resolve) => { - httpClient.post(new RESTURLCombiner('/security/csrf').toString(), undefined).pipe( - // errors are to be expected if the token and the cookie don't match, that's what we're - // trying to fix for future requests, so just emit any observable to end up in the - // subscribe - catchError(() => observableOf(null)), + return () => new Promise((resolve) => { + // Force a new token to be created by calling the CSRF endpoint + httpClient.get(new RESTURLCombiner('/security/csrf').toString(), undefined).pipe( take(1), ).subscribe(() => { + // Once token is returned, set tokenInitialized to true. this.tokenInitialized$.next(true); }); diff --git a/src/app/core/xsrf/server-xsrf.service.spec.ts b/src/app/core/xsrf/server-xsrf.service.spec.ts index b2ace67dd0c..05728edb423 100644 --- a/src/app/core/xsrf/server-xsrf.service.spec.ts +++ b/src/app/core/xsrf/server-xsrf.service.spec.ts @@ -1,6 +1,7 @@ -import { ServerXSRFService } from './server-xsrf.service'; import { HttpClient } from '@angular/common/http'; +import { ServerXSRFService } from './server-xsrf.service'; + describe(`ServerXSRFService`, () => { let service: ServerXSRFService; let httpClient: HttpClient; diff --git a/src/app/core/xsrf/server-xsrf.service.ts b/src/app/core/xsrf/server-xsrf.service.ts index 1577893f952..f729aa49a7d 100644 --- a/src/app/core/xsrf/server-xsrf.service.ts +++ b/src/app/core/xsrf/server-xsrf.service.ts @@ -1,11 +1,16 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; + import { XSRFService } from './xsrf.service'; +/** + * Server (SSR) Service to obtain a new CSRF/XSRF token. Because SSR only triggers GET + * requests a CSRF token is never needed. + */ @Injectable() export class ServerXSRFService extends XSRFService { initXSRFToken(httpClient: HttpClient): () => Promise { - return () => new Promise((resolve) => { + return () => new Promise((resolve) => { // return immediately, and keep tokenInitialized$ false. The server side can make only GET // requests, since it can never get a valid XSRF cookie resolve(); diff --git a/src/app/core/xsrf/xsrf.service.spec.ts b/src/app/core/xsrf/xsrf.service.spec.ts index a7c5c01cb7d..56564a294ca 100644 --- a/src/app/core/xsrf/xsrf.service.spec.ts +++ b/src/app/core/xsrf/xsrf.service.spec.ts @@ -1,6 +1,7 @@ -import { XSRFService } from './xsrf.service'; import { HttpClient } from '@angular/common/http'; +import { XSRFService } from './xsrf.service'; + class XSRFServiceImpl extends XSRFService { initXSRFToken(httpClient: HttpClient): () => Promise { return () => null; diff --git a/src/app/core/xsrf/xsrf.service.ts b/src/app/core/xsrf/xsrf.service.ts index fb8dfe74b33..99b27021b66 100644 --- a/src/app/core/xsrf/xsrf.service.ts +++ b/src/app/core/xsrf/xsrf.service.ts @@ -2,6 +2,11 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; +/** + * Abstract CSRF/XSRF Service used to track whether a CSRF token has been received + * from the DSpace REST API. Once it is received, the "tokenInitialized$" flag will + * be set to "true". + */ @Injectable() export abstract class XSRFService { public tokenInitialized$: BehaviorSubject = new BehaviorSubject(false); diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index 48b40dcddb6..97e6e0e8603 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -59,7 +59,7 @@ import { MissingTranslationHelper } from '../../app/shared/translate/missing-tra import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.service'; import { SubmissionService } from '../../app/submission/submission.service'; import { TranslateBrowserLoader } from '../../ngx-translate-loaders/translate-browser.loader'; -import { BrowserInitService } from './browser-init.service' +import { BrowserInitService } from './browser-init.service'; export const REQ_KEY = makeStateKey('req'); From f7d31fd34150f61716cd3150986e187af807f94d Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 3 Apr 2024 11:48:13 -0500 Subject: [PATCH 3/4] Fix tests. Add XSRFService to all specs which need it to be initialized --- .../bitstream-formats/bitstream-formats.component.spec.ts | 2 ++ ...search-result-admin-workflow-list-element.component.spec.ts | 2 ++ ...son-search-result-list-submission-element.component.spec.ts | 2 ++ .../edit-relationship-list.component.spec.ts | 2 ++ .../file-section/file-section.component.spec.ts | 2 ++ src/app/login-page/login-page.component.spec.ts | 2 ++ src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts | 2 ++ .../shared/auth-nav-menu/user-menu/user-menu.component.spec.ts | 2 ++ .../relation-group/dynamic-relation-group.component.spec.ts | 2 ++ .../dynamic-lookup-relation-modal.component.spec.ts | 2 ++ src/app/shared/form/form.component.spec.ts | 2 ++ .../listable-object-component-loader.component.spec.ts | 2 ++ .../item-detail-preview/item-detail-preview.component.spec.ts | 2 ++ .../collection-search-result-grid-element.component.spec.ts | 2 ++ .../community-search-result-grid-element.component.spec.ts | 2 ++ .../item-types/item/item-list-element.component.spec.ts | 2 ++ src/app/shared/search/search.component.spec.ts | 2 ++ src/app/submission/edit/submission-edit.component.spec.ts | 2 ++ .../sections/accesses/section-accesses.component.spec.ts | 3 +++ .../sections/license/section-license.component.spec.ts | 2 ++ .../file/edit/section-upload-file-edit.component.spec.ts | 2 ++ 21 files changed, 43 insertions(+) diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts index 321d9469072..9ee767cda05 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts @@ -31,6 +31,7 @@ import { GroupDataService } from '../../../core/eperson/group-data.service'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { BitstreamFormat } from '../../../core/shared/bitstream-format.model'; import { BitstreamFormatSupportLevel } from '../../../core/shared/bitstream-format-support-level'; +import { XSRFService } from '../../../core/xsrf/xsrf.service'; import { HostWindowService } from '../../../shared/host-window.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { PaginationComponent } from '../../../shared/pagination/pagination.component'; @@ -143,6 +144,7 @@ describe('BitstreamFormatsComponent', () => { { provide: PaginationService, useValue: paginationService }, { provide: GroupDataService, useValue: groupDataService }, { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: XSRFService, useValue: {} }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts index fbb936080fd..cd722542f63 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts @@ -17,6 +17,7 @@ import { AuthorizationDataService } from '../../../../../core/data/feature-autho import { Item } from '../../../../../core/shared/item.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model'; +import { XSRFService } from '../../../../../core/xsrf/xsrf.service'; import { AuthServiceMock } from '../../../../../shared/mocks/auth.service.mock'; import { DSONameServiceMock } from '../../../../../shared/mocks/dso-name.service.mock'; import { getMockLinkService } from '../../../../../shared/mocks/link-service.mock'; @@ -67,6 +68,7 @@ describe('WorkflowItemSearchResultAdminWorkflowListElementComponent', () => { { provide: ThemeService, useValue: getMockThemeService() }, { provide: AuthService, useValue: new AuthServiceMock() }, { provide: AuthorizationDataService, useValue: {} }, + { provide: XSRFService, useValue: {} }, ], schemas: [NO_ERRORS_SCHEMA], }) diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts index b041215c9cb..b3c9ab1d9bf 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts @@ -35,6 +35,7 @@ import { Bitstream } from '../../../../../core/shared/bitstream.model'; import { HALEndpointService } from '../../../../../core/shared/hal-endpoint.service'; import { Item } from '../../../../../core/shared/item.model'; import { UUIDService } from '../../../../../core/shared/uuid.service'; +import { XSRFService } from '../../../../../core/xsrf/xsrf.service'; import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock'; import { CollectionElementLinkType } from '../../../../../shared/object-collection/collection-element-link.type'; import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model'; @@ -138,6 +139,7 @@ describe('PersonSearchResultListElementSubmissionComponent', () => { { provide: Store, useValue: {} }, { provide: ObjectCacheService, useValue: {} }, { provide: UUIDService, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: RemoteDataBuildService, useValue: {} }, { provide: CommunityDataService, useValue: {} }, { provide: HALEndpointService, useValue: {} }, diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts index 0396f23b6d0..fd6a8fe0509 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts @@ -38,6 +38,7 @@ import { ItemType } from '../../../../core/shared/item-relationships/item-type.m import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; +import { XSRFService } from '../../../../core/xsrf/xsrf.service'; import { HostWindowService } from '../../../../shared/host-window.service'; import { RouterMock } from '../../../../shared/mocks/router.mock'; import { SelectableListService } from '../../../../shared/object-list/selectable-list/selectable-list.service'; @@ -257,6 +258,7 @@ describe('EditRelationshipListComponent', () => { { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, { provide: AuthRequestService, useValue: new AuthRequestServiceStub() }, { provide: HardRedirectService, useValue: hardRedirectService }, + { provide: XSRFService, useValue: {} }, { provide: APP_CONFIG, useValue: environmentUseThumbs }, { provide: REQUEST, useValue: {} }, CookieService, diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts index 91eb255afd7..b2fb2bf29f0 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts @@ -22,6 +22,7 @@ import { import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; import { Bitstream } from '../../../../core/shared/bitstream.model'; import { PageInfo } from '../../../../core/shared/page-info.model'; +import { XSRFService } from '../../../../core/xsrf/xsrf.service'; import { MetadataFieldWrapperComponent } from '../../../../shared/metadata-field-wrapper/metadata-field-wrapper.component'; import { MockBitstreamFormat1 } from '../../../../shared/mocks/item.mock'; import { getMockThemeService } from '../../../../shared/mocks/theme-service.mock'; @@ -83,6 +84,7 @@ describe('FileSectionComponent', () => { }), BrowserAnimationsModule, FileSectionComponent, VarDirective, FileSizePipe], providers: [ { provide: APP_DATA_SERVICES_MAP, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: BitstreamDataService, useValue: bitstreamDataService }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: APP_CONFIG, useValue: environment }, diff --git a/src/app/login-page/login-page.component.spec.ts b/src/app/login-page/login-page.component.spec.ts index 74aeddfe0cf..6cb4098c4de 100644 --- a/src/app/login-page/login-page.component.spec.ts +++ b/src/app/login-page/login-page.component.spec.ts @@ -12,6 +12,7 @@ import { of as observableOf } from 'rxjs'; import { APP_DATA_SERVICES_MAP } from '../../config/app-config.interface'; import { AuthService } from '../core/auth/auth.service'; +import { XSRFService } from '../core/xsrf/xsrf.service'; import { AuthServiceMock } from '../shared/mocks/auth.service.mock'; import { ActivatedRouteStub } from '../shared/testing/active-router.stub'; import { LoginPageComponent } from './login-page.component'; @@ -39,6 +40,7 @@ describe('LoginPageComponent', () => { providers: [ { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: AuthService, useValue: new AuthServiceMock() }, + { provide: XSRFService, useValue: {} }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, provideMockStore({}), ], diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts index 224620c6e1f..a969b6f49bc 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts @@ -27,6 +27,7 @@ import { } from '../../core/auth/auth.reducer'; import { AuthService } from '../../core/auth/auth.service'; import { AuthTokenInfo } from '../../core/auth/models/auth-token-info.model'; +import { XSRFService } from '../../core/xsrf/xsrf.service'; import { HostWindowService } from '../host-window.service'; import { ActivatedRouteStub } from '../testing/active-router.stub'; import { BrowserOnlyMockPipe } from '../testing/browser-only-mock.pipe'; @@ -102,6 +103,7 @@ describe('AuthNavMenuComponent', () => { { provide: HostWindowService, useValue: window }, { provide: AuthService, useValue: authService }, { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, + { provide: XSRFService, useValue: {} }, ], schemas: [ CUSTOM_ELEMENTS_SCHEMA, diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts index d57db27684f..c7ddf2ac34a 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts @@ -29,6 +29,7 @@ import { } from '../../../core/auth/auth.reducer'; import { AuthService } from '../../../core/auth/auth.service'; import { AuthTokenInfo } from '../../../core/auth/models/auth-token-info.model'; +import { XSRFService } from '../../../core/xsrf/xsrf.service'; import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; import { ActivatedRouteStub } from '../../testing/active-router.stub'; import { EPersonMock } from '../../testing/eperson.mock'; @@ -91,6 +92,7 @@ describe('UserMenuComponent', () => { providers: [ { provide: AuthService, useValue: authService }, { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, + { provide: XSRFService, useValue: {} }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, ], schemas: [ diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts index 535b88561a4..05da3cc5833 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts @@ -43,6 +43,7 @@ import { FormRowModel } from '../../../../../../core/config/models/config-submis import { SubmissionFormsModel } from '../../../../../../core/config/models/config-submission-forms.model'; import { SubmissionObjectDataService } from '../../../../../../core/submission/submission-object-data.service'; import { VocabularyService } from '../../../../../../core/submission/vocabularies/vocabulary.service'; +import { XSRFService } from '../../../../../../core/xsrf/xsrf.service'; import { SubmissionService } from '../../../../../../submission/submission.service'; import { createTestComponent } from '../../../../../testing/utils.test'; import { VocabularyServiceStub } from '../../../../../testing/vocabulary-service.stub'; @@ -180,6 +181,7 @@ describe('DsDynamicRelationGroupComponent test suite', () => { { provide: DsDynamicTypeBindRelationService, useClass: DsDynamicTypeBindRelationService }, { provide: SubmissionObjectDataService, useValue: {} }, { provide: SubmissionService, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: APP_CONFIG, useValue: environment }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, { provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: dsDynamicFormControlMapFn }, diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts index 4995b36f116..94de9eaaa7b 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts @@ -33,6 +33,7 @@ import { ExternalSource } from '../../../../../core/shared/external-source.model import { Item } from '../../../../../core/shared/item.model'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; +import { XSRFService } from '../../../../../core/xsrf/xsrf.service'; import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model'; import { SelectableListService } from '../../../../object-list/selectable-list/selectable-list.service'; import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; @@ -147,6 +148,7 @@ describe('DsDynamicLookupRelationModalComponent', () => { }, }, }, + { provide: XSRFService, useValue: {} }, { provide: NgZone, useValue: new NgZone({}) }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, NgbActiveModal, diff --git a/src/app/shared/form/form.component.spec.ts b/src/app/shared/form/form.component.spec.ts index f88d63f4c24..431ae3387f1 100644 --- a/src/app/shared/form/form.component.spec.ts +++ b/src/app/shared/form/form.component.spec.ts @@ -33,6 +33,7 @@ import { BehaviorSubject } from 'rxjs'; import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface'; import { storeModuleConfig } from '../../app.reducer'; +import { XSRFService } from '../../core/xsrf/xsrf.service'; import { StoreMock } from '../testing/store.mock'; import { createTestComponent } from '../testing/utils.test'; import { DsDynamicFormComponent } from './builder/ds-dynamic-form-ui/ds-dynamic-form.component'; @@ -176,6 +177,7 @@ describe('FormComponent test suite', () => { FormComponent, FormService, { provide: Store, useClass: StoreMock }, + { provide: XSRFService, useValue: {} }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts index 495450be75e..82d44cb58c8 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts @@ -18,6 +18,7 @@ import { of } from 'rxjs'; import { AuthRequestService } from 'src/app/core/auth/auth-request.service'; import { CookieService } from 'src/app/core/services/cookie.service'; import { HardRedirectService } from 'src/app/core/services/hard-redirect.service'; +import { XSRFService } from 'src/app/core/xsrf/xsrf.service'; import { CookieServiceMock } from 'src/app/shared/mocks/cookie.service.mock'; import { getMockThemeService } from 'src/app/shared/mocks/theme-service.mock'; import { AuthRequestServiceStub } from 'src/app/shared/testing/auth-request-service.stub'; @@ -70,6 +71,7 @@ describe('ListableObjectComponentLoaderComponent', () => { { provide: HardRedirectService, useValue: jasmine.createSpyObj('hardRedirectService', ['redirect']) }, { provide: AuthRequestService, useValue: new AuthRequestServiceStub() }, { provide: CookieService, useValue: new CookieServiceMock() }, + { provide: XSRFService, useValue: {} }, { provide: REQUEST, useValue: {} }, { provide: ActivatedRoute, diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts index b7eb800764f..77225f3ed5e 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts @@ -32,6 +32,7 @@ import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service import { Item } from '../../../../core/shared/item.model'; import { SearchService } from '../../../../core/shared/search/search.service'; import { UUIDService } from '../../../../core/shared/uuid.service'; +import { XSRFService } from '../../../../core/xsrf/xsrf.service'; import { AuthServiceMock } from '../../../../shared/mocks/auth.service.mock'; import { getMockThemeService } from '../../../../shared/mocks/theme-service.mock'; import { SearchServiceStub } from '../../../../shared/testing/search-service.stub'; @@ -114,6 +115,7 @@ describe('ItemDetailPreviewComponent', () => { { provide: HALEndpointService, useValue: new HALEndpointServiceStub('workspaceitems') }, { provide: ObjectCacheService, useValue: {} }, { provide: UUIDService, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: Store, useValue: {} }, { provide: RemoteDataBuildService, useValue: {} }, { provide: CommunityDataService, useValue: {} }, diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts index f8a40249b09..bfec6ebeac7 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts @@ -26,6 +26,7 @@ import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.ser import { Collection } from '../../../../core/shared/collection.model'; import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; import { UUIDService } from '../../../../core/shared/uuid.service'; +import { XSRFService } from '../../../../core/xsrf/xsrf.service'; import { NotificationsService } from '../../../notifications/notifications.service'; import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model'; import { ActivatedRouteStub } from '../../../testing/active-router.stub'; @@ -95,6 +96,7 @@ describe('CollectionSearchResultGridElementComponent', () => { { provide: DSOChangeAnalyzer, useValue: {} }, { provide: DefaultChangeAnalyzer, useValue: {} }, { provide: BitstreamFormatDataService, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: LinkService, useValue: linkService }, provideMockStore({}), ], diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts index f60d0c5454e..86d757a0308 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts @@ -26,6 +26,7 @@ import { DSOChangeAnalyzer } from '../../../../core/data/dso-change-analyzer.ser import { Community } from '../../../../core/shared/community.model'; import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; import { UUIDService } from '../../../../core/shared/uuid.service'; +import { XSRFService } from '../../../../core/xsrf/xsrf.service'; import { AuthServiceMock } from '../../../../shared/mocks/auth.service.mock'; import { getMockThemeService } from '../../../../shared/mocks/theme-service.mock'; import { StoreMock } from '../../../../shared/testing/store.mock'; @@ -100,6 +101,7 @@ describe('CommunitySearchResultGridElementComponent', () => { { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, { provide: ThemeService, useValue: getMockThemeService() }, { provide: AuthService, useValue: new AuthServiceMock() }, + { provide: XSRFService, useValue: {} }, ], schemas: [NO_ERRORS_SCHEMA], }).overrideComponent(CommunitySearchResultGridElementComponent, { diff --git a/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts index 597b3cfae1a..47a8aa65028 100644 --- a/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts @@ -17,6 +17,7 @@ import { AuthService } from '../../../../../core/auth/auth.service'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { AuthorizationDataService } from '../../../../../core/data/feature-authorization/authorization-data.service'; import { Item } from '../../../../../core/shared/item.model'; +import { XSRFService } from '../../../../../core/xsrf/xsrf.service'; import { AuthServiceMock } from '../../../../../shared/mocks/auth.service.mock'; import { mockTruncatableService } from '../../../../../shared/mocks/mock-trucatable.service'; import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock'; @@ -78,6 +79,7 @@ describe('ItemListElementComponent', () => { { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, { provide: AuthService, useValue: new AuthServiceMock() }, { provide: AuthorizationDataService, useValue: {} }, + { provide: XSRFService, useValue: {} }, ], schemas: [NO_ERRORS_SCHEMA], }).overrideComponent(ItemListElementComponent, { diff --git a/src/app/shared/search/search.component.spec.ts b/src/app/shared/search/search.component.spec.ts index fec23281cdb..8c7b5b06ff6 100644 --- a/src/app/shared/search/search.component.spec.ts +++ b/src/app/shared/search/search.component.spec.ts @@ -46,6 +46,7 @@ import { SearchConfig, SortConfig, } from '../../core/shared/search/search-filters/search-config.model'; +import { XSRFService } from '../../core/xsrf/xsrf.service'; import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-configuration.service'; import { HostWindowService } from '../host-window.service'; import { PaginationComponentOptions } from '../pagination/pagination-component-options.model'; @@ -237,6 +238,7 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar provide: SearchFilterService, useValue: {}, }, + { provide: XSRFService, useValue: {} }, { provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigurationServiceStub, diff --git a/src/app/submission/edit/submission-edit.component.spec.ts b/src/app/submission/edit/submission-edit.component.spec.ts index ffac7959ebc..81c5c1ddeac 100644 --- a/src/app/submission/edit/submission-edit.component.spec.ts +++ b/src/app/submission/edit/submission-edit.component.spec.ts @@ -22,6 +22,7 @@ import { AuthService } from '../../core/auth/auth.service'; import { ItemDataService } from '../../core/data/item-data.service'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { SubmissionJsonPatchOperationsService } from '../../core/submission/submission-json-patch-operations.service'; +import { XSRFService } from '../../core/xsrf/xsrf.service'; import { mockSubmissionObject } from '../../shared/mocks/submission.mock'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -83,6 +84,7 @@ describe('SubmissionEditComponent Component', () => { { provide: HALEndpointService, useValue: halService }, { provide: SectionsService, useValue: new SectionsServiceStub() }, { provide: ThemeService, useValue: themeService }, + { provide: XSRFService, useValue: {} }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, provideMockStore(), ], diff --git a/src/app/submission/sections/accesses/section-accesses.component.spec.ts b/src/app/submission/sections/accesses/section-accesses.component.spec.ts index 68287149505..c49bd74e0d6 100644 --- a/src/app/submission/sections/accesses/section-accesses.component.spec.ts +++ b/src/app/submission/sections/accesses/section-accesses.component.spec.ts @@ -24,6 +24,7 @@ import { SubmissionAccessesConfigDataService } from '../../../core/config/submis import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/json-patch-operations-builder'; import { SubmissionJsonPatchOperationsService } from '../../../core/submission/submission-json-patch-operations.service'; import { SubmissionObjectDataService } from '../../../core/submission/submission-object-data.service'; +import { XSRFService } from '../../../core/xsrf/xsrf.service'; import { dsDynamicFormControlMapFn } from '../../../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-map-fn'; import { DsDynamicTypeBindRelationService } from '../../../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; @@ -120,6 +121,7 @@ describe('SubmissionSectionAccessesComponent', () => { { provide: DsDynamicTypeBindRelationService, useValue: getMockDsDynamicTypeBindRelationService() }, { provide: SubmissionObjectDataService, useValue: {} }, { provide: SubmissionService, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: APP_CONFIG, useValue: environment }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, { provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: dsDynamicFormControlMapFn }, @@ -216,6 +218,7 @@ describe('SubmissionSectionAccessesComponent', () => { { provide: DsDynamicTypeBindRelationService, useValue: getMockDsDynamicTypeBindRelationService() }, { provide: SubmissionObjectDataService, useValue: {} }, { provide: SubmissionService, useValue: {} }, + { provide: XSRFService, useValue: {} }, { provide: APP_CONFIG, useValue: environment }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, { provide: DYNAMIC_FORM_CONTROL_MAP_FN, useValue: dsDynamicFormControlMapFn }, diff --git a/src/app/submission/sections/license/section-license.component.spec.ts b/src/app/submission/sections/license/section-license.component.spec.ts index c3cb329a504..95b2e7f50ab 100644 --- a/src/app/submission/sections/license/section-license.component.spec.ts +++ b/src/app/submission/sections/license/section-license.component.spec.ts @@ -38,6 +38,7 @@ import { JsonPatchOperationsBuilder } from '../../../core/json-patch/builder/jso import { Collection } from '../../../core/shared/collection.model'; import { License } from '../../../core/shared/license.model'; import { SubmissionObjectDataService } from '../../../core/submission/submission-object-data.service'; +import { XSRFService } from '../../../core/xsrf/xsrf.service'; import { dsDynamicFormControlMapFn } from '../../../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-map-fn'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; @@ -191,6 +192,7 @@ describe('SubmissionSectionLicenseComponent test suite', () => { findById: () => observableOf(createSuccessfulRemoteDataObject(mockSubmissionObject)), }, }, + { provide: XSRFService, useValue: {} }, SubmissionSectionLicenseComponent, ], schemas: [NO_ERRORS_SCHEMA], diff --git a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts index 8543ee40891..015ccd4ae41 100644 --- a/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts +++ b/src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts @@ -38,6 +38,7 @@ import { environment } from '../../../../../../environments/environment.test'; import { JsonPatchOperationPathCombiner } from '../../../../../core/json-patch/builder/json-patch-operation-path-combiner'; import { JsonPatchOperationsBuilder } from '../../../../../core/json-patch/builder/json-patch-operations-builder'; import { SubmissionJsonPatchOperationsService } from '../../../../../core/submission/submission-json-patch-operations.service'; +import { XSRFService } from '../../../../../core/xsrf/xsrf.service'; import { dateToISOFormat } from '../../../../../shared/date.util'; import { DsDynamicTypeBindRelationService } from '../../../../../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service'; import { DynamicCustomSwitchModel } from '../../../../../shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model'; @@ -154,6 +155,7 @@ describe('SubmissionSectionUploadFileEditComponent test suite', () => { { provide: DsDynamicTypeBindRelationService, useValue: getMockDsDynamicTypeBindRelationService() }, { provide: APP_CONFIG, useValue: environment }, { provide: APP_DATA_SERVICES_MAP, useValue: {} }, + { provide: XSRFService, useValue: {} }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents().then(); From bbab56a7dad44b0521c0acc02904cbfa995fee1a Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Wed, 3 Apr 2024 14:00:42 -0500 Subject: [PATCH 4/4] Update build script to use "docker compose" instead of "docker-compose". Fixes random failures starting e2e test backend via Docker --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52f20470a3c..d71c031b930 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: #CHROME_VERSION: "90.0.4430.212-1" # Bump Node heap size (OOM in CI after upgrading to Angular 15) NODE_OPTIONS: '--max-old-space-size=4096' - # Project name to use when running docker-compose prior to e2e tests + # Project name to use when running "docker compose" prior to e2e tests COMPOSE_PROJECT_NAME: 'ci' strategy: # Create a matrix of Node versions to test against (in parallel) @@ -108,12 +108,12 @@ jobs: path: 'coverage/dspace-angular/lcov.info' retention-days: 14 - # Using docker-compose start backend using CI configuration + # Using "docker compose" start backend using CI configuration # and load assetstore from a cached copy - name: Start DSpace REST Backend via Docker (for e2e tests) run: | - docker-compose -f ./docker/docker-compose-ci.yml up -d - docker-compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli + docker compose -f ./docker/docker-compose-ci.yml up -d + docker compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli docker container ls # Run integration tests via Cypress.io @@ -182,7 +182,7 @@ jobs: run: kill -9 $(lsof -t -i:4000) - name: Shutdown Docker containers - run: docker-compose -f ./docker/docker-compose-ci.yml down + run: docker compose -f ./docker/docker-compose-ci.yml down # Codecov upload is a separate job in order to allow us to restart this separate from the entire build/test # job above. This is necessary because Codecov uploads seem to randomly fail at times.