Skip to content

Commit

Permalink
Merge pull request DSpace#2897 from 4Science/DURACOM-247
Browse files Browse the repository at this point in the history
  • Loading branch information
tdonohue authored Apr 10, 2024
2 parents 10db33b + b2f94b5 commit 26ecc03
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 169 deletions.
6 changes: 3 additions & 3 deletions src/app/core/cache/builders/link.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ class TestModel implements HALResource {
successor?: TestModel;
}

const mockDataServiceMap: any = {
[TEST_MODEL.value]: () => import('../../../shared/testing/test-data-service.mock').then(m => m.TestDataService),
};
const mockDataServiceMap: any = new Map([
[TEST_MODEL.value, () => import('../../../shared/testing/test-data-service.mock').then(m => m.TestDataService)],
]);

let testDataService: TestDataService;

Expand Down
7 changes: 3 additions & 4 deletions src/app/core/cache/builders/link.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
Inject,
Injectable,
InjectionToken,
Injector,
} from '@angular/core';
import {
Expand All @@ -25,7 +24,7 @@ import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model
import { HALDataService } from '../../data/base/hal-data-service.interface';
import { PaginatedList } from '../../data/paginated-list.model';
import { RemoteData } from '../../data/remote-data';
import { lazyService } from '../../lazy-service';
import { lazyDataService } from '../../lazy-data-service';
import { GenericConstructor } from '../../shared/generic-constructor';
import { HALResource } from '../../shared/hal-resource.model';
import {
Expand All @@ -43,7 +42,7 @@ export class LinkService {

constructor(
protected injector: Injector,
@Inject(APP_DATA_SERVICES_MAP) private map: InjectionToken<LazyDataServicesMap>,
@Inject(APP_DATA_SERVICES_MAP) private map: LazyDataServicesMap,
@Inject(LINK_DEFINITION_FACTORY) private getLinkDefinition: <T extends HALResource>(source: GenericConstructor<T>, linkName: keyof T['_links']) => LinkDefinition<T>,
@Inject(LINK_DEFINITION_MAP_FACTORY) private getLinkDefinitions: <T extends HALResource>(source: GenericConstructor<T>) => Map<keyof T['_links'], LinkDefinition<T>>,
) {
Expand Down Expand Up @@ -73,7 +72,7 @@ export class LinkService {
public resolveLinkWithoutAttaching<T extends HALResource, U extends HALResource>(model, linkToFollow: FollowLinkConfig<T>): Observable<RemoteData<U | PaginatedList<U>>> {
const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name);
if (hasValue(matchingLinkDef)) {
const lazyProvider$: Observable<HALDataService<any>> = lazyService(this.map[matchingLinkDef.resourceType.value], this.injector);
const lazyProvider$: Observable<HALDataService<any>> = lazyDataService(this.map, matchingLinkDef.resourceType.value, this.injector);
return lazyProvider$.pipe(
switchMap((provider: HALDataService<any>) => {
const link = model._links[matchingLinkDef.linkName];
Expand Down
138 changes: 68 additions & 70 deletions src/app/core/data-services-map.ts

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion src/app/core/data/request.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
map,
mergeMap,
take,
withLatestFrom,
} from 'rxjs/operators';

import {
Expand All @@ -25,6 +26,7 @@ import { ParsedResponse } from '../cache/response.models';
import { DSpaceSerializer } from '../dspace-rest/dspace.serializer';
import { DspaceRestService } from '../dspace-rest/dspace-rest.service';
import { RawRestResponse } from '../dspace-rest/raw-rest-response.model';
import { XSRFService } from '../xsrf/xsrf.service';
import {
RequestActionTypes,
RequestErrorAction,
Expand All @@ -35,6 +37,7 @@ import {
import { RequestService } from './request.service';
import { RequestEntry } from './request-entry.model';
import { RequestError } from './request-error.model';
import { RestRequestMethod } from './rest-request-method';
import { RestRequestWithResponseParser } from './rest-request-with-response-parser.model';

@Injectable()
Expand All @@ -48,7 +51,11 @@ export class RequestEffects {
);
}),
filter((entry: RequestEntry) => hasValue(entry)),
map((entry: RequestEntry) => entry.request),
withLatestFrom(this.xsrfService.tokenInitialized$),
// If it's a GET request, or we have an XSRF token, dispatch it immediately
// Otherwise wait for the XSRF token first
filter(([entry, tokenInitialized]: [RequestEntry, boolean]) => entry.request.method === RestRequestMethod.GET || tokenInitialized === true),
map(([entry, tokenInitialized]: [RequestEntry, boolean]) => entry.request),
mergeMap((request: RestRequestWithResponseParser) => {
let body = request.body;
if (isNotEmpty(request.body) && !request.isMultipart) {
Expand Down Expand Up @@ -89,6 +96,7 @@ export class RequestEffects {
private restApi: DspaceRestService,
private injector: Injector,
protected requestService: RequestService,
protected xsrfService: XSRFService,
) { }

}
8 changes: 0 additions & 8 deletions src/app/core/data/request.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
getTestScheduler,
} from 'jasmine-marbles';
import {
BehaviorSubject,
EMPTY,
Observable,
of as observableOf,
Expand All @@ -34,7 +33,6 @@ 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,
Expand Down Expand Up @@ -62,7 +60,6 @@ describe('RequestService', () => {
let uuidService: UUIDService;
let store: Store<CoreState>;
let mockStore: MockStore<CoreState>;
let xsrfService: XSRFService;

const testUUID = '5f2a0d2a-effa-4d54-bd54-5663b960f9eb';
const testHref = 'https://rest.api/endpoint/selfLink';
Expand Down Expand Up @@ -108,16 +105,11 @@ describe('RequestService', () => {
store = TestBed.inject(Store);
mockStore = store as MockStore<CoreState>;
mockStore.setState(initialState);
xsrfService = {
tokenInitialized$: new BehaviorSubject(false),
} as XSRFService;

service = new RequestService(
objectCache,
uuidService,
store,
xsrfService,
undefined,
);
serviceAsAny = service as any;
});
Expand Down
22 changes: 3 additions & 19 deletions src/app/core/data/request.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,12 @@ import { ObjectCacheService } from '../cache/object-cache.service';
import { CommitSSBAction } from '../cache/server-sync-buffer.actions';
import { coreSelector } from '../core.selectors';
import { CoreState } from '../core-state.model';
import {
IndexState,
MetaIndexState,
} from '../index/index.reducer';
import { IndexState } from '../index/index.reducer';
import {
getUrlWithoutEmbedParams,
requestIndexSelector,
} from '../index/index.selectors';
import { UUIDService } from '../shared/uuid.service';
import { XSRFService } from '../xsrf/xsrf.service';
import {
RequestConfigureAction,
RequestExecuteAction,
Expand Down Expand Up @@ -169,9 +165,7 @@ export class RequestService {

constructor(private objectCache: ObjectCacheService,
private uuidService: UUIDService,
private store: Store<CoreState>,
protected xsrfService: XSRFService,
private indexStore: Store<MetaIndexState>) {
private store: Store<CoreState>) {
}

generateRequestId(): string {
Expand Down Expand Up @@ -455,17 +449,7 @@ export class RequestService {
private dispatchRequest(request: RestRequest) {
asapScheduler.schedule(() => {
this.store.dispatch(new RequestConfigureAction(request));
// 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));
});
}
this.store.dispatch(new RequestExecuteAction(request.uuid));
});
}

Expand Down
50 changes: 50 additions & 0 deletions src/app/core/lazy-data-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
Injector,
Type,
} from '@angular/core';
import {
defer,
Observable,
} from 'rxjs';

import { LazyDataServicesMap } from '../../config/app-config.interface';
import { HALDataService } from './data/base/hal-data-service.interface';

/**
* Loads a service lazily. The service is loaded when the observable is subscribed to.
*
* @param dataServicesMap A map of promises returning the data services to load
* @param key The key of the service
* @param injector The injector to use to load the service. If not provided, the current injector is used.
* @returns An observable of the service.
*
* @example
* ```ts
* const dataService$ = lazyDataService({ 'data-service': () => import('./data-service').then((m) => m.MyService)}, 'data-service', this.injector);
* or
* const dataService$ = lazyDataService({'data-service': () => import('./data-service')}, 'data-service', this.injector);
* ```
*/
export function lazyDataService<T>(
dataServicesMap: LazyDataServicesMap,
key: string,
injector: Injector,
): Observable<T> {
return defer(() => {
if (dataServicesMap.has(key) && typeof dataServicesMap.get(key) === 'function') {
const loader: () => Promise<Type<HALDataService<any>> | { default: HALDataService<any> }> = dataServicesMap.get(key);
return loader()
.then((serviceOrDefault) => {
if ('default' in serviceOrDefault) {
return injector!.get(serviceOrDefault.default);
}
return injector!.get(serviceOrDefault);
})
.catch((error) => {
throw error;
});
} else {
return null;
}
});
}
40 changes: 0 additions & 40 deletions src/app/core/lazy-service.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ const REINSTATE_BTN = 'reinstate';
const SAVE_BTN = 'save';
const DISCARD_BTN = 'discard';

const mockDataServiceMap: any = {
[ITEM.value]: () => import('../../shared/testing/test-data-service.mock').then(m => m.TestDataService),
};
const mockDataServiceMap: any = new Map([
[ITEM.value, () => import('../../shared/testing/test-data-service.mock').then(m => m.TestDataService)],
]);

describe('DsoEditMetadataComponent', () => {
let component: DsoEditMetadataComponent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
ChangeDetectorRef,
Component,
Inject,
InjectionToken,
Injector,
Input,
OnDestroy,
Expand Down Expand Up @@ -42,7 +41,7 @@ import {
import { ArrayMoveChangeAnalyzer } from '../../core/data/array-move-change-analyzer.service';
import { RemoteData } from '../../core/data/remote-data';
import { UpdateDataService } from '../../core/data/update-data.service';
import { lazyService } from '../../core/lazy-service';
import { lazyDataService } from '../../core/lazy-data-service';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
import { ResourceType } from '../../core/shared/resource-type';
Expand Down Expand Up @@ -152,7 +151,7 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
protected parentInjector: Injector,
protected arrayMoveChangeAnalyser: ArrayMoveChangeAnalyzer<number>,
protected cdr: ChangeDetectorRef,
@Inject(APP_DATA_SERVICES_MAP) private dataServiceMap: InjectionToken<LazyDataServicesMap>) {
@Inject(APP_DATA_SERVICES_MAP) private dataServiceMap: LazyDataServicesMap) {
}

/**
Expand Down Expand Up @@ -186,7 +185,7 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
*/
retrieveDataService(): Observable<UpdateDataService<DSpaceObject>> {
if (hasNoValue(this.updateDataService)) {
const lazyProvider$: Observable<UpdateDataService<DSpaceObject>> = lazyService(this.dataServiceMap[this.dsoType], this.parentInjector);
const lazyProvider$: Observable<UpdateDataService<DSpaceObject>> = lazyDataService(this.dataServiceMap, this.dsoType, this.parentInjector);
return lazyProvider$;
} else {
return EMPTY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import { cold } from 'jasmine-marbles';
import uniqueId from 'lodash/uniqueId';
import { of as observableOf } from 'rxjs';

import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface';
import {
APP_DATA_SERVICES_MAP,
LazyDataServicesMap,
} from '../../../config/app-config.interface';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { buildPaginatedList } from '../../core/data/paginated-list.model';
import { RequestService } from '../../core/data/request.service';
Expand All @@ -41,10 +44,10 @@ import { SearchEvent } from './eperson-group-list-event-type';
import { EpersonSearchBoxComponent } from './eperson-search-box/eperson-search-box.component';
import { GroupSearchBoxComponent } from './group-search-box/group-search-box.component';

const mockDataServiceMap: any = {
[EPERSON.value]: () => import('../../core/eperson/eperson-data.service').then(m => m.EPersonDataService),
[GROUP.value]: () => import('../../core/eperson/group-data.service').then(m => m.GroupDataService),
};
const mockDataServiceMap: LazyDataServicesMap = new Map([
[EPERSON.value, () => import('../../core/eperson/eperson-data.service').then(m => m.EPersonDataService)],
[GROUP.value, () => import('../../core/eperson/group-data.service').then(m => m.GroupDataService)],
]);

describe('EpersonGroupListComponent test suite', () => {
let comp: EpersonGroupListComponent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
Component,
EventEmitter,
Inject,
InjectionToken,
Injector,
Input,
OnDestroy,
Expand Down Expand Up @@ -35,7 +34,7 @@ import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../core/eperson/group-data.service';
import { EPERSON } from '../../core/eperson/models/eperson.resource-type';
import { GROUP } from '../../core/eperson/models/group.resource-type';
import { lazyService } from '../../core/lazy-service';
import { lazyDataService } from '../../core/lazy-data-service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
Expand Down Expand Up @@ -133,15 +132,15 @@ export class EpersonGroupListComponent implements OnInit, OnDestroy {
constructor(public dsoNameService: DSONameService,
private parentInjector: Injector,
private paginationService: PaginationService,
@Inject(APP_DATA_SERVICES_MAP) private dataServiceMap: InjectionToken<LazyDataServicesMap>) {
@Inject(APP_DATA_SERVICES_MAP) private dataServiceMap: LazyDataServicesMap) {
}

/**
* Initialize the component
*/
ngOnInit(): void {
const resourceType: ResourceType = (this.isListOfEPerson) ? EPERSON : GROUP;
const lazyProvider$: Observable<EPersonDataService | GroupDataService> = lazyService(this.dataServiceMap[resourceType.value], this.parentInjector);
const lazyProvider$: Observable<EPersonDataService | GroupDataService> = lazyDataService(this.dataServiceMap, resourceType.value, this.parentInjector);
lazyProvider$.subscribe((dataService: EPersonDataService | GroupDataService) => {
this.dataService = dataService;
console.log(dataService);
Expand Down
Loading

0 comments on commit 26ecc03

Please sign in to comment.