From 179e7e0543d4f796ec99119a330c49f717b9fe69 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 15 Mar 2024 16:12:22 +0100 Subject: [PATCH 1/2] 113169: Renamed the service in charge of adding metadata head tags to you HTML to HeadTagService & made its methods protected to make it easier to override them --- src/app/app.component.spec.ts | 6 +- .../community-page.component.ts | 6 -- src/app/core/core.module.ts | 4 +- ...rvice.spec.ts => head-tag.service.spec.ts} | 62 ++++++------- ...etadata.service.ts => head-tag.service.ts} | 86 +++++++++---------- src/app/init.service.spec.ts | 15 ++-- src/app/init.service.ts | 8 +- .../full/full-item-page.component.spec.ts | 4 +- .../simple/item-page.component.spec.ts | 8 -- src/app/root/root.component.spec.ts | 36 +------- src/app/root/root.component.ts | 16 +--- ...rvice.mock.ts => head-tag-service.mock.ts} | 2 +- src/modules/app/browser-init.service.ts | 6 +- src/modules/app/server-init.service.ts | 6 +- 14 files changed, 95 insertions(+), 170 deletions(-) rename src/app/core/metadata/{metadata.service.spec.ts => head-tag.service.spec.ts} (89%) rename src/app/core/metadata/{metadata.service.ts => head-tag.service.ts} (87%) rename src/app/shared/mocks/{metadata-service.mock.ts => head-tag-service.mock.ts} (78%) diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index e921c67ace..ed10627615 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -9,11 +9,11 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { AppComponent } from './app.component'; import { HostWindowState } from './shared/search/host-window.reducer'; import { HostWindowResizeAction } from './shared/host-window.actions'; -import { MetadataService } from './core/metadata/metadata.service'; +import { HeadTagService } from './core/metadata/head-tag.service'; import { NativeWindowRef, NativeWindowService } from './core/services/window.service'; import { TranslateLoaderMock } from './shared/mocks/translate-loader.mock'; -import { MetadataServiceMock } from './shared/mocks/metadata-service.mock'; +import { HeadTagServiceMock } from './shared/mocks/head-tag-service.mock'; import { AngularticsProviderMock } from './shared/mocks/angulartics-provider.service.mock'; import { AuthServiceMock } from './shared/mocks/auth.service.mock'; import { AuthService } from './core/auth/auth.service'; @@ -71,7 +71,7 @@ describe('App component', () => { declarations: [AppComponent], // declare the test component providers: [ { provide: NativeWindowService, useValue: new NativeWindowRef() }, - { provide: MetadataService, useValue: new MetadataServiceMock() }, + { provide: HeadTagService, useValue: new HeadTagServiceMock() }, { provide: Angulartics2DSpace, useValue: new AngularticsProviderMock() }, { provide: AuthService, useValue: new AuthServiceMock() }, { provide: Router, useValue: new RouterMock() }, diff --git a/src/app/community-page/community-page.component.ts b/src/app/community-page/community-page.component.ts index a5bbff3cee..80c311d337 100644 --- a/src/app/community-page/community-page.component.ts +++ b/src/app/community-page/community-page.component.ts @@ -3,14 +3,10 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Observable } from 'rxjs'; -import { CommunityDataService } from '../core/data/community-data.service'; import { RemoteData } from '../core/data/remote-data'; import { Bitstream } from '../core/shared/bitstream.model'; import { Community } from '../core/shared/community.model'; - -import { MetadataService } from '../core/metadata/metadata.service'; - import { fadeInOut } from '../shared/animations/fade'; import { hasValue } from '../shared/empty.util'; import { getAllSucceededRemoteDataPayload} from '../core/shared/operators'; @@ -53,8 +49,6 @@ export class CommunityPageComponent implements OnInit { communityPageRoute$: Observable; constructor( - private communityDataService: CommunityDataService, - private metadata: MetadataService, private route: ActivatedRoute, private router: Router, private authService: AuthService, diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index dbca773375..2eaf1a6f12 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -64,7 +64,7 @@ import { Group } from './eperson/models/group.model'; import { JsonPatchOperationsBuilder } from './json-patch/builder/json-patch-operations-builder'; import { MetadataField } from './metadata/metadata-field.model'; import { MetadataSchema } from './metadata/metadata-schema.model'; -import { MetadataService } from './metadata/metadata.service'; +import { HeadTagService } from './metadata/head-tag.service'; import { RegistryService } from './registry/registry.service'; import { RoleService } from './roles/role.service'; import { FeedbackDataService } from './feedback/feedback-data.service'; @@ -218,7 +218,7 @@ const PROVIDERS = [ HALEndpointService, HostWindowService, ItemDataService, - MetadataService, + HeadTagService, ObjectCacheService, PaginationComponentOptions, ResourcePolicyDataService, diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/head-tag.service.spec.ts similarity index 89% rename from src/app/core/metadata/metadata.service.spec.ts rename to src/app/core/metadata/head-tag.service.spec.ts index 553b437d71..7b71978939 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/head-tag.service.spec.ts @@ -19,7 +19,7 @@ import { PaginatedList } from '../data/paginated-list.model'; import { Bitstream } from '../shared/bitstream.model'; import { MetadataValue } from '../shared/metadata.models'; -import { MetadataService } from './metadata.service'; +import { HeadTagService } from './head-tag.service'; import { RootDataService } from '../data/root-data.service'; import { Bundle } from '../shared/bundle.model'; import { createPaginatedList } from '../../shared/testing/utils.test'; @@ -31,8 +31,8 @@ import { AddMetaTagAction, ClearMetaTagAction } from './meta-tag.actions'; import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; import { AppConfig } from '../../../config/app-config.interface'; -describe('MetadataService', () => { - let metadataService: MetadataService; +describe('HeadTagService', () => { + let headTagService: HeadTagService; let meta: Meta; @@ -41,7 +41,6 @@ describe('MetadataService', () => { let dsoNameService: DSONameService; let bundleDataService; - let bitstreamDataService; let rootService: RootDataService; let translateService: TranslateService; let hardRedirectService: HardRedirectService; @@ -59,9 +58,6 @@ describe('MetadataService', () => { rootService = jasmine.createSpyObj({ findRoot: createSuccessfulRemoteDataObject$({ dspaceVersion: 'mock-dspace-version' }) }); - bitstreamDataService = jasmine.createSpyObj({ - findListByHref: createSuccessfulRemoteDataObject$(createPaginatedList([MockBitstream3])), - }); bundleDataService = jasmine.createSpyObj({ findByItemAndName: mockBundleRD$([MockBitstream3]) }); @@ -102,15 +98,13 @@ describe('MetadataService', () => { } } as any; - metadataService = new MetadataService( + headTagService = new HeadTagService( router, translateService, meta, title, dsoNameService, bundleDataService, - bitstreamDataService, - undefined, rootService, store, hardRedirectService, @@ -120,7 +114,7 @@ describe('MetadataService', () => { }); it('items page should set meta tags', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), @@ -147,7 +141,7 @@ describe('MetadataService', () => { })); it('items page should set meta tags as published Thesis', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(mockPublisher(mockType(ItemMock, 'Thesis'))), @@ -166,7 +160,7 @@ describe('MetadataService', () => { })); it('items page should set meta tags as published Technical Report', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(mockPublisher(mockType(ItemMock, 'Technical Report'))), @@ -182,7 +176,7 @@ describe('MetadataService', () => { it('route titles should overwrite dso titles', fakeAsync(() => { (translateService.get as jasmine.Spy).and.returnValues(of('DSpace :: '), of('Translated Route Title')); - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), @@ -198,7 +192,7 @@ describe('MetadataService', () => { it('other navigation should add title and description', fakeAsync(() => { (translateService.get as jasmine.Spy).and.returnValues(of('DSpace :: '), of('Dummy Title'), of('This is a dummy item component for testing!')); - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { title: 'Dummy Title', @@ -220,14 +214,14 @@ describe('MetadataService', () => { describe(`listenForRouteChange`, () => { it(`should call processRouteChange`, fakeAsync(() => { - spyOn(metadataService as any, 'processRouteChange').and.callFake(() => undefined); - metadataService.listenForRouteChange(); + spyOn(headTagService as any, 'processRouteChange').and.callFake(() => undefined); + headTagService.listenForRouteChange(); tick(); - expect((metadataService as any).processRouteChange).toHaveBeenCalled(); + expect((headTagService as any).processRouteChange).toHaveBeenCalled(); })); it(`should add Generator`, fakeAsync(() => { - spyOn(metadataService as any, 'processRouteChange').and.callFake(() => undefined); - metadataService.listenForRouteChange(); + spyOn(headTagService as any, 'processRouteChange').and.callFake(() => undefined); + headTagService.listenForRouteChange(); tick(); expect(meta.addTag).toHaveBeenCalledWith({ name: 'Generator', @@ -238,7 +232,7 @@ describe('MetadataService', () => { describe('citation_abstract_html_url', () => { it('should use dc.identifier.uri if available', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(mockUri(ItemMock, 'https://ddg.gg')), @@ -253,7 +247,7 @@ describe('MetadataService', () => { })); it('should use current route as fallback', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(mockUri(ItemMock)), @@ -270,7 +264,7 @@ describe('MetadataService', () => { describe('citation_*_institution / citation_publisher', () => { it('should use citation_dissertation_institution tag for dissertations', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(mockPublisher(mockType(ItemMock, 'Thesis'))), @@ -287,7 +281,7 @@ describe('MetadataService', () => { })); it('should use citation_tech_report_institution tag for tech reports', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(mockPublisher(mockType(ItemMock, 'Technical Report'))), @@ -304,7 +298,7 @@ describe('MetadataService', () => { })); it('should use citation_publisher for other item types', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(mockPublisher(mockType(ItemMock, 'Some Other Type'))), @@ -325,7 +319,7 @@ describe('MetadataService', () => { it('should link to primary Bitstream URL regardless of format', fakeAsync(() => { (bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$([], MockBitstream3)); - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), @@ -344,7 +338,7 @@ describe('MetadataService', () => { (bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$([MockBitstream3])); (authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false)); - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), @@ -361,7 +355,7 @@ describe('MetadataService', () => { it('should link to first and only Bitstream regardless of format', fakeAsync(() => { (bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$([MockBitstream3])); - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), @@ -381,13 +375,10 @@ describe('MetadataService', () => { beforeEach(() => { bitstreams = [MockBitstream2, MockBitstream3, MockBitstream1]; (bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$(bitstreams)); - (bitstreamDataService.findListByHref as jasmine.Spy).and.returnValues( - ...mockBitstreamPages$(bitstreams).map(bp => createSuccessfulRemoteDataObject$(bp)), - ); }); it('should link to first Bitstream with allowed format', fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), @@ -412,13 +403,10 @@ describe('MetadataService', () => { beforeEach(() => { bitstreams = [MockBitstream1, MockBitstream3, MockBitstream2]; (bundleDataService.findByItemAndName as jasmine.Spy).and.returnValue(mockBundleRD$(bitstreams)); - (bitstreamDataService.findListByHref as jasmine.Spy).and.returnValues( - ...mockBitstreamPages$(bitstreams).map(bp => createSuccessfulRemoteDataObject$(bp)), - ); }); it(`shouldn't add a citation_pdf_url meta tag`, fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), @@ -437,7 +425,7 @@ describe('MetadataService', () => { describe('tagstore', () => { beforeEach(fakeAsync(() => { - (metadataService as any).processRouteChange({ + (headTagService as any).processRouteChange({ data: { value: { dso: createSuccessfulRemoteDataObject(ItemMock), diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/head-tag.service.ts similarity index 87% rename from src/app/core/metadata/metadata.service.ts rename to src/app/core/metadata/head-tag.service.ts index 204c925e6b..2cdee0ce97 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/head-tag.service.ts @@ -17,8 +17,6 @@ import { filter, map, switchMap, take, mergeMap } from 'rxjs/operators'; import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { DSONameService } from '../breadcrumbs/dso-name.service'; -import { BitstreamDataService } from '../data/bitstream-data.service'; -import { BitstreamFormatDataService } from '../data/bitstream-format-data.service'; import { RemoteData } from '../data/remote-data'; import { BitstreamFormat } from '../shared/bitstream-format.model'; @@ -64,7 +62,7 @@ const tagsInUseSelector = ); @Injectable() -export class MetadataService { +export class HeadTagService { private currentObject: BehaviorSubject = new BehaviorSubject(undefined); @@ -84,19 +82,17 @@ export class MetadataService { ]; constructor( - private router: Router, - private translate: TranslateService, - private meta: Meta, - private title: Title, - private dsoNameService: DSONameService, - private bundleDataService: BundleDataService, - private bitstreamDataService: BitstreamDataService, - private bitstreamFormatDataService: BitstreamFormatDataService, - private rootService: RootDataService, - private store: Store, - private hardRedirectService: HardRedirectService, - @Inject(APP_CONFIG) private appConfig: AppConfig, - private authorizationService: AuthorizationDataService + protected router: Router, + protected translate: TranslateService, + protected meta: Meta, + protected title: Title, + protected dsoNameService: DSONameService, + protected bundleDataService: BundleDataService, + protected rootService: RootDataService, + protected store: Store, + protected hardRedirectService: HardRedirectService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, + protected authorizationService: AuthorizationDataService ) { } @@ -115,7 +111,7 @@ export class MetadataService { }); } - private processRouteChange(routeInfo: any): void { + protected processRouteChange(routeInfo: any): void { this.clearMetaTags(); if (hasValue(routeInfo.data.value.dso) && hasValue(routeInfo.data.value.dso.payload)) { @@ -138,14 +134,14 @@ export class MetadataService { } } - private getCurrentRoute(route: ActivatedRoute): ActivatedRoute { + protected getCurrentRoute(route: ActivatedRoute): ActivatedRoute { while (route.firstChild) { route = route.firstChild; } return route; } - private setDSOMetaTags(): void { + protected setDSOMetaTags(): void { this.setTitleTag(); this.setDescriptionTag(); @@ -187,7 +183,7 @@ export class MetadataService { /** * Add to the */ - private setTitleTag(): void { + protected setTitleTag(): void { const value = this.dsoNameService.getName(this.currentObject.getValue()); this.addMetaTag('title', value); this.title.setTitle(value); @@ -196,7 +192,7 @@ export class MetadataService { /** * Add to the */ - private setDescriptionTag(): void { + protected setDescriptionTag(): void { // TODO: truncate abstract const value = this.getMetaTagValue('dc.description.abstract'); this.addMetaTag('description', value); @@ -205,7 +201,7 @@ export class MetadataService { /** * Add to the */ - private setCitationTitleTag(): void { + protected setCitationTitleTag(): void { const value = this.getMetaTagValue('dc.title'); this.addMetaTag('citation_title', value); } @@ -213,7 +209,7 @@ export class MetadataService { /** * Add to the */ - private setCitationAuthorTags(): void { + protected setCitationAuthorTags(): void { const values: string[] = this.getMetaTagValues(['dc.author', 'dc.contributor.author', 'dc.creator']); this.addMetaTags('citation_author', values); } @@ -221,7 +217,7 @@ export class MetadataService { /** * Add to the */ - private setCitationPublicationDateTag(): void { + protected setCitationPublicationDateTag(): void { const value = this.getFirstMetaTagValue(['dc.date.copyright', 'dc.date.issued', 'dc.date.available', 'dc.date.accessioned']); this.addMetaTag('citation_publication_date', value); } @@ -229,7 +225,7 @@ export class MetadataService { /** * Add to the */ - private setCitationISSNTag(): void { + protected setCitationISSNTag(): void { const value = this.getMetaTagValue('dc.identifier.issn'); this.addMetaTag('citation_issn', value); } @@ -237,7 +233,7 @@ export class MetadataService { /** * Add to the */ - private setCitationISBNTag(): void { + protected setCitationISBNTag(): void { const value = this.getMetaTagValue('dc.identifier.isbn'); this.addMetaTag('citation_isbn', value); } @@ -245,7 +241,7 @@ export class MetadataService { /** * Add to the */ - private setCitationLanguageTag(): void { + protected setCitationLanguageTag(): void { const value = this.getFirstMetaTagValue(['dc.language', 'dc.language.iso']); this.addMetaTag('citation_language', value); } @@ -253,7 +249,7 @@ export class MetadataService { /** * Add to the */ - private setCitationDissertationNameTag(): void { + protected setCitationDissertationNameTag(): void { const value = this.getMetaTagValue('dc.title'); this.addMetaTag('citation_dissertation_name', value); } @@ -261,7 +257,7 @@ export class MetadataService { /** * Add dc.publisher to the . The tag name depends on the item type. */ - private setCitationPublisherTag(): void { + protected setCitationPublisherTag(): void { const value = this.getMetaTagValue('dc.publisher'); if (this.isDissertation()) { this.addMetaTag('citation_dissertation_institution', value); @@ -275,7 +271,7 @@ export class MetadataService { /** * Add to the */ - private setCitationKeywordsTag(): void { + protected setCitationKeywordsTag(): void { const value = this.getMetaTagValuesAndCombine('dc.subject'); this.addMetaTag('citation_keywords', value); } @@ -283,7 +279,7 @@ export class MetadataService { /** * Add to the */ - private setCitationAbstractUrlTag(): void { + protected setCitationAbstractUrlTag(): void { if (this.currentObject.value instanceof Item) { let url = this.getMetaTagValue('dc.identifier.uri'); if (hasNoValue(url)) { @@ -296,7 +292,7 @@ export class MetadataService { /** * Add to the */ - private setCitationPdfUrlTag(): void { + protected setCitationPdfUrlTag(): void { if (this.currentObject.value instanceof Item) { const item = this.currentObject.value as Item; @@ -389,7 +385,7 @@ export class MetadataService { * @param bitstreamRd * @private */ - private getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { + protected getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { if (hasValue(bitstreamRd.payload) && isNotEmpty(bitstreamRd.payload.page)) { // Retrieve the formats of all bitstreams in the page sequentially return observableConcat( @@ -422,13 +418,13 @@ export class MetadataService { /** * Add to the containing the current DSpace version */ - private setGenerator(): void { + protected setGenerator(): void { this.rootService.findRoot().pipe(getFirstSucceededRemoteDataPayload()).subscribe((root) => { this.meta.addTag({ name: 'Generator', content: root.dspaceVersion }); }); } - private hasType(value: string): boolean { + protected hasType(value: string): boolean { return this.currentObject.value.hasMetadata('dc.type', { value: value, ignoreCase: true }); } @@ -438,7 +434,7 @@ export class MetadataService { * @returns {boolean} * true if this._item has a dc.type equal to 'Thesis' */ - private isDissertation(): boolean { + protected isDissertation(): boolean { return this.hasType('thesis'); } @@ -448,27 +444,27 @@ export class MetadataService { * @returns {boolean} * true if this._item has a dc.type equal to 'Technical Report' */ - private isTechReport(): boolean { + protected isTechReport(): boolean { return this.hasType('technical report'); } - private getMetaTagValue(key: string): string { + protected getMetaTagValue(key: string): string { return this.currentObject.value.firstMetadataValue(key); } - private getFirstMetaTagValue(keys: string[]): string { + protected getFirstMetaTagValue(keys: string[]): string { return this.currentObject.value.firstMetadataValue(keys); } - private getMetaTagValuesAndCombine(key: string): string { + protected getMetaTagValuesAndCombine(key: string): string { return this.getMetaTagValues([key]).join('; '); } - private getMetaTagValues(keys: string[]): string[] { + protected getMetaTagValues(keys: string[]): string[] { return this.currentObject.value.allMetadataValues(keys); } - private addMetaTag(name: string, content: string): void { + protected addMetaTag(name: string, content: string): void { if (content) { const tag = { name, content } as MetaDefinition; this.meta.addTag(tag); @@ -476,17 +472,17 @@ export class MetadataService { } } - private addMetaTags(name: string, content: string[]): void { + protected addMetaTags(name: string, content: string[]): void { for (const value of content) { this.addMetaTag(name, value); } } - private storeTag(key: string): void { + protected storeTag(key: string): void { this.store.dispatch(new AddMetaTagAction(key)); } - public clearMetaTags() { + protected clearMetaTags(): void { this.store.pipe( select(tagsInUseSelector), take(1) diff --git a/src/app/init.service.spec.ts b/src/app/init.service.spec.ts index a05f06c78f..ff750b36bc 100644 --- a/src/app/init.service.spec.ts +++ b/src/app/init.service.spec.ts @@ -2,7 +2,7 @@ import { InitService } from './init.service'; import { APP_CONFIG } from 'src/config/app-config.interface'; import { APP_INITIALIZER, Injectable } from '@angular/core'; import { inject, TestBed, waitForAsync } from '@angular/core/testing'; -import { MetadataService } from './core/metadata/metadata.service'; +import { HeadTagService } from './core/metadata/head-tag.service'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { CommonModule } from '@angular/common'; import { Store, StoreModule } from '@ngrx/store'; @@ -31,6 +31,7 @@ import objectContaining = jasmine.objectContaining; import createSpyObj = jasmine.createSpyObj; import SpyObj = jasmine.SpyObj; import { getTestScheduler } from 'jasmine-marbles'; +import { HeadTagServiceMock } from './shared/mocks/head-tag-service.mock'; let spy: SpyObj; @@ -121,7 +122,7 @@ describe('InitService', () => { let correlationIdServiceSpy; let dspaceTransferStateSpy; let transferStateSpy; - let metadataServiceSpy; + let headTagService: HeadTagServiceMock; let breadcrumbsServiceSpy; let menuServiceSpy; @@ -147,9 +148,7 @@ describe('InitService', () => { breadcrumbsServiceSpy = jasmine.createSpyObj('breadcrumbsServiceSpy', [ 'listenForRouteChanges', ]); - metadataServiceSpy = jasmine.createSpyObj('metadataService', [ - 'listenForRouteChange', - ]); + headTagService = new HeadTagServiceMock(); menuServiceSpy = jasmine.createSpyObj('menuServiceSpy', [ 'listenForRouteChanges', ]); @@ -173,7 +172,7 @@ describe('InitService', () => { { provide: APP_CONFIG, useValue: environment }, { provide: LocaleService, useValue: getMockLocaleService() }, { provide: Angulartics2DSpace, useValue: new AngularticsProviderMock() }, - { provide: MetadataService, useValue: metadataServiceSpy }, + { provide: HeadTagService, useValue: headTagService }, { provide: BreadcrumbsService, useValue: breadcrumbsServiceSpy }, { provide: AuthService, useValue: new AuthServiceMock() }, { provide: Router, useValue: new RouterMock() }, @@ -189,9 +188,9 @@ describe('InitService', () => { describe('initRouteListeners', () => { it('should call listenForRouteChanges', inject([InitService], (service) => { - // @ts-ignore + spyOn(headTagService, 'listenForRouteChange'); service.initRouteListeners(); - expect(metadataServiceSpy.listenForRouteChange).toHaveBeenCalledTimes(1); + expect(headTagService.listenForRouteChange).toHaveBeenCalledTimes(1); expect(breadcrumbsServiceSpy.listenForRouteChanges).toHaveBeenCalledTimes(1); expect(breadcrumbsServiceSpy.listenForRouteChanges).toHaveBeenCalledTimes(1); })); diff --git a/src/app/init.service.ts b/src/app/init.service.ts index 9fef2ca4ed..dbd3a8f8f1 100644 --- a/src/app/init.service.ts +++ b/src/app/init.service.ts @@ -17,7 +17,7 @@ import isEqual from 'lodash/isEqual'; import { TranslateService } from '@ngx-translate/core'; import { LocaleService } from './core/locale/locale.service'; import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider'; -import { MetadataService } from './core/metadata/metadata.service'; +import { HeadTagService } from './core/metadata/head-tag.service'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { ThemeService } from './shared/theme-support/theme.service'; import { isAuthenticationBlocking } from './core/auth/selectors'; @@ -49,7 +49,7 @@ export abstract class InitService { protected translate: TranslateService, protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, - protected metadata: MetadataService, + protected headTagService: HeadTagService, protected breadcrumbsService: BreadcrumbsService, protected themeService: ThemeService, protected menuService: MenuService, @@ -178,13 +178,13 @@ export abstract class InitService { /** * Start route-listening subscriptions - * - {@link MetadataService.listenForRouteChange} + * - {@link HeadTagService.listenForRouteChange} * - {@link BreadcrumbsService.listenForRouteChanges} * - {@link ThemeService.listenForRouteChanges} * @protected */ protected initRouteListeners(): void { - this.metadata.listenForRouteChange(); + this.headTagService.listenForRouteChange(); this.breadcrumbsService.listenForRouteChanges(); this.themeService.listenForRouteChanges(); this.menuService.listenForRouteChanges(); diff --git a/src/app/item-page/full/full-item-page.component.spec.ts b/src/app/item-page/full/full-item-page.component.spec.ts index 9fc078c2cd..3c689ef696 100644 --- a/src/app/item-page/full/full-item-page.component.spec.ts +++ b/src/app/item-page/full/full-item-page.component.spec.ts @@ -5,7 +5,7 @@ import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA, PLATFORM_ID } from '@angular/core'; import { TruncatePipe } from '../../shared/utils/truncate.pipe'; import { FullItemPageComponent } from './full-item-page.component'; -import { MetadataService } from '../../core/metadata/metadata.service'; +import { HeadTagService } from '../../core/metadata/head-tag.service'; import { ActivatedRoute } from '@angular/router'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { VarDirective } from '../../shared/utils/var.directive'; @@ -116,7 +116,7 @@ describe('FullItemPageComponent', () => { providers: [ { provide: ActivatedRoute, useValue: routeStub }, { provide: ItemDataService, useValue: {} }, - { provide: MetadataService, useValue: metadataServiceStub }, + { provide: HeadTagService, useValue: metadataServiceStub }, { provide: AuthService, useValue: authService }, { provide: AuthorizationDataService, useValue: authorizationDataService }, { provide: ServerResponseService, useValue: serverResponseService }, diff --git a/src/app/item-page/simple/item-page.component.spec.ts b/src/app/item-page/simple/item-page.component.spec.ts index b3202108f4..b13f82b43a 100644 --- a/src/app/item-page/simple/item-page.component.spec.ts +++ b/src/app/item-page/simple/item-page.component.spec.ts @@ -6,7 +6,6 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA, PLATFORM_ID } from '@angular import { ItemPageComponent } from './item-page.component'; import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; -import { MetadataService } from '../../core/metadata/metadata.service'; import { VarDirective } from '../../shared/utils/var.directive'; import { Item } from '../../core/shared/item.model'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @@ -63,12 +62,6 @@ describe('ItemPageComponent', () => { let signpostingDataService: jasmine.SpyObj; let linkHeadService: jasmine.SpyObj; - const mockMetadataService = { - /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ - processRemoteData: () => { - } - /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ - }; const mockRoute = Object.assign(new ActivatedRouteStub(), { data: observableOf({ dso: createSuccessfulRemoteDataObject(mockItem) }) }); @@ -105,7 +98,6 @@ describe('ItemPageComponent', () => { providers: [ { provide: ActivatedRoute, useValue: mockRoute }, { provide: ItemDataService, useValue: {} }, - { provide: MetadataService, useValue: mockMetadataService }, { provide: Router, useValue: {} }, { provide: AuthService, useValue: authService }, { provide: AuthorizationDataService, useValue: authorizationDataService }, diff --git a/src/app/root/root.component.spec.ts b/src/app/root/root.component.spec.ts index ab148b8ebd..92cc2eb709 100644 --- a/src/app/root/root.component.spec.ts +++ b/src/app/root/root.component.spec.ts @@ -1,29 +1,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RootComponent } from './root.component'; import { CommonModule } from '@angular/common'; -import { StoreModule } from '@ngrx/store'; -import { authReducer } from '../core/auth/auth.reducer'; -import { storeModuleConfig } from '../app.reducer'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock'; -import { NativeWindowRef, NativeWindowService } from '../core/services/window.service'; -import { MetadataService } from '../core/metadata/metadata.service'; -import { MetadataServiceMock } from '../shared/mocks/metadata-service.mock'; -import { AngularticsProviderMock } from '../shared/mocks/angulartics-provider.service.mock'; -import { Angulartics2DSpace } from '../statistics/angulartics/dspace-provider'; -import { AuthService } from '../core/auth/auth.service'; -import { AuthServiceMock } from '../shared/mocks/auth.service.mock'; -import { ActivatedRoute, Router } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; +import { Router } from '@angular/router'; import { RouterMock } from '../shared/mocks/router.mock'; -import { MockActivatedRoute } from '../shared/mocks/active-router.mock'; import { MenuService } from '../shared/menu/menu.service'; import { CSSVariableService } from '../shared/sass-helper/css-variable.service'; import { CSSVariableServiceStub } from '../shared/testing/css-variable-service.stub'; import { HostWindowService } from '../shared/host-window.service'; import { HostWindowServiceStub } from '../shared/testing/host-window-service.stub'; -import { LocaleService } from '../core/locale/locale.service'; -import { provideMockStore } from '@ngrx/store/testing'; -import { RouteService } from '../core/services/route.service'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { MenuServiceStub } from '../shared/testing/menu-service.stub'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -37,29 +22,14 @@ describe('RootComponent', () => { imports: [ CommonModule, NoopAnimationsModule, - StoreModule.forRoot(authReducer, storeModuleConfig), - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - }), + TranslateModule.forRoot(), ], declarations: [RootComponent], // declare the test component providers: [ - { provide: NativeWindowService, useValue: new NativeWindowRef() }, - { provide: MetadataService, useValue: new MetadataServiceMock() }, - { provide: Angulartics2DSpace, useValue: new AngularticsProviderMock() }, - { provide: AuthService, useValue: new AuthServiceMock() }, { provide: Router, useValue: new RouterMock() }, - { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, { provide: MenuService, useValue: new MenuServiceStub() }, { provide: CSSVariableService, useClass: CSSVariableServiceStub }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, - { provide: LocaleService, useValue: {} }, - provideMockStore({ core: { auth: { loading: false } } } as any), - RootComponent, - RouteService ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }).compileComponents(); diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index 3c2d65fc1f..70bf6c112a 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -1,20 +1,12 @@ import { map, startWith } from 'rxjs/operators'; -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { combineLatest as combineLatestObservable, Observable, of } from 'rxjs'; -import { Store } from '@ngrx/store'; -import { TranslateService } from '@ngx-translate/core'; - -import { MetadataService } from '../core/metadata/metadata.service'; -import { HostWindowState } from '../shared/search/host-window.reducer'; -import { NativeWindowRef, NativeWindowService } from '../core/services/window.service'; -import { AuthService } from '../core/auth/auth.service'; import { CSSVariableService } from '../shared/sass-helper/css-variable.service'; import { MenuService } from '../shared/menu/menu.service'; import { HostWindowService } from '../shared/host-window.service'; import { ThemeConfig } from '../../config/theme.model'; -import { Angulartics2DSpace } from '../statistics/angulartics/dspace-provider'; import { environment } from '../../environments/environment'; import { slideSidebarPadding } from '../shared/animations/slide'; import { MenuID } from '../shared/menu/menu-id.model'; @@ -47,12 +39,6 @@ export class RootComponent implements OnInit { @Input() shouldShowRouteLoader: boolean; constructor( - @Inject(NativeWindowService) private _window: NativeWindowRef, - private translate: TranslateService, - private store: Store, - private metadata: MetadataService, - private angulartics2DSpace: Angulartics2DSpace, - private authService: AuthService, private router: Router, private cssService: CSSVariableService, private menuService: MenuService, diff --git a/src/app/shared/mocks/metadata-service.mock.ts b/src/app/shared/mocks/head-tag-service.mock.ts similarity index 78% rename from src/app/shared/mocks/metadata-service.mock.ts rename to src/app/shared/mocks/head-tag-service.mock.ts index f07c923d81..48cb6f7eb0 100644 --- a/src/app/shared/mocks/metadata-service.mock.ts +++ b/src/app/shared/mocks/head-tag-service.mock.ts @@ -1,5 +1,5 @@ -export class MetadataServiceMock { +export class HeadTagServiceMock { // eslint-disable-next-line no-empty, @typescript-eslint/no-empty-function public listenForRouteChange(): void { diff --git a/src/modules/app/browser-init.service.ts b/src/modules/app/browser-init.service.ts index 61d57f10f9..dc9a5a6496 100644 --- a/src/modules/app/browser-init.service.ts +++ b/src/modules/app/browser-init.service.ts @@ -19,7 +19,7 @@ import { TranslateService } from '@ngx-translate/core'; import { LocaleService } from '../../app/core/locale/locale.service'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.service'; -import { MetadataService } from '../../app/core/metadata/metadata.service'; +import { HeadTagService } from '../../app/core/metadata/head-tag.service'; import { BreadcrumbsService } from '../../app/breadcrumbs/breadcrumbs.service'; import { KlaroService } from '../../app/shared/cookies/klaro.service'; import { AuthService } from '../../app/core/auth/auth.service'; @@ -50,7 +50,7 @@ export class BrowserInitService extends InitService { protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, protected googleAnalyticsService: GoogleAnalyticsService, - protected metadata: MetadataService, + protected headTagService: HeadTagService, protected breadcrumbsService: BreadcrumbsService, protected klaroService: KlaroService, protected authService: AuthService, @@ -65,7 +65,7 @@ export class BrowserInitService extends InitService { translate, localeService, angulartics2DSpace, - metadata, + headTagService, breadcrumbsService, themeService, menuService, diff --git a/src/modules/app/server-init.service.ts b/src/modules/app/server-init.service.ts index 715f872cd9..1f257f7bdf 100644 --- a/src/modules/app/server-init.service.ts +++ b/src/modules/app/server-init.service.ts @@ -16,7 +16,7 @@ import { Inject, Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { LocaleService } from '../../app/core/locale/locale.service'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; -import { MetadataService } from '../../app/core/metadata/metadata.service'; +import { HeadTagService } from '../../app/core/metadata/head-tag.service'; import { BreadcrumbsService } from '../../app/breadcrumbs/breadcrumbs.service'; import { ThemeService } from '../../app/shared/theme-support/theme.service'; import { take } from 'rxjs/operators'; @@ -35,7 +35,7 @@ export class ServerInitService extends InitService { protected translate: TranslateService, protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, - protected metadata: MetadataService, + protected headTagService: HeadTagService, protected breadcrumbsService: BreadcrumbsService, protected themeService: ThemeService, protected menuService: MenuService @@ -47,7 +47,7 @@ export class ServerInitService extends InitService { translate, localeService, angulartics2DSpace, - metadata, + headTagService, breadcrumbsService, themeService, menuService, From 7b19047a100596096f0f1968a0f80489462482cd Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 15 Mar 2024 17:12:34 +0100 Subject: [PATCH 2/2] 113169: Moved the MetadataValue#isVirtual & MetadataValue#virtualValue to the new MetadataService --- .../data/relationship-data.service.spec.ts | 37 ++++++++++++------- .../core/data/relationship-data.service.ts | 6 ++- .../core/metadata/metadata.service.spec.ts | 16 ++++++++ src/app/core/metadata/metadata.service.ts | 33 +++++++++++++++++ src/app/core/shared/metadata.models.ts | 19 ---------- .../dso-edit-metadata-value.component.html | 2 +- .../dso-edit-metadata-value.component.ts | 10 +++-- .../metadata-representation-list.component.ts | 4 +- ...amic-form-control-container.component.html | 4 +- ...c-form-control-container.component.spec.ts | 5 --- ...ynamic-form-control-container.component.ts | 28 ++++++-------- 11 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 src/app/core/metadata/metadata.service.spec.ts create mode 100644 src/app/core/metadata/metadata.service.ts diff --git a/src/app/core/data/relationship-data.service.spec.ts b/src/app/core/data/relationship-data.service.spec.ts index 4432d5213a..c8cff74ce0 100644 --- a/src/app/core/data/relationship-data.service.spec.ts +++ b/src/app/core/data/relationship-data.service.spec.ts @@ -23,6 +23,13 @@ import { FindListOptions } from './find-list-options.model'; import { testSearchDataImplementation } from './base/search-data.spec'; import { MetadataValue } from '../shared/metadata.models'; import { MetadataRepresentationType } from '../shared/metadata-representation/metadata-representation.model'; +import { TestBed } from '@angular/core/testing'; +import { ItemDataService } from './item-data.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { PAGINATED_RELATIONS_TO_ITEMS_OPERATOR } from '../../item-page/simple/item-types/shared/item-relationships-utils'; +import { Store } from '@ngrx/store'; +import { provideMockStore } from '@ngrx/store/testing'; describe('RelationshipDataService', () => { let service: RelationshipDataService; @@ -128,18 +135,6 @@ describe('RelationshipDataService', () => { findByHref: createSuccessfulRemoteDataObject$(relatedItems[0]) }); - function initTestService() { - return new RelationshipDataService( - requestService, - rdbService, - halService, - objectCache, - itemService, - null, - jasmine.createSpy('paginatedRelationsToItems').and.returnValue((v) => v), - ); - } - const getRequestEntry$ = (successful: boolean) => { return observableOf({ response: { isSuccessful: successful, payload: relationships } as any @@ -148,11 +143,25 @@ describe('RelationshipDataService', () => { beforeEach(() => { requestService = getMockRequestService(getRequestEntry$(true)); - service = initTestService(); + + TestBed.configureTestingModule({ + providers: [ + { provide: RequestService, useValue: requestService }, + { provide: RemoteDataBuildService, useValue: rdbService }, + { provide: HALEndpointService, useValue: halService }, + { provide: ObjectCacheService, useValue: objectCache }, + { provide: ItemDataService, useValue: itemService }, + { provide: RequestService, useValue: requestService }, + { provide: PAGINATED_RELATIONS_TO_ITEMS_OPERATOR, useValue: jasmine.createSpy('paginatedRelationsToItems').and.returnValue((v) => v) }, + { provide: Store, useValue: provideMockStore() }, + RelationshipDataService, + ], + }); + service = TestBed.inject(RelationshipDataService); }); describe('composition', () => { - const initService = () => new RelationshipDataService(null, null, null, null, null, null, null); + const initService = () => new RelationshipDataService(null, null, null, null, null, null, null, null); testSearchDataImplementation(initService); }); diff --git a/src/app/core/data/relationship-data.service.ts b/src/app/core/data/relationship-data.service.ts index 46a51a2d01..4ca260c1c4 100644 --- a/src/app/core/data/relationship-data.service.ts +++ b/src/app/core/data/relationship-data.service.ts @@ -51,6 +51,7 @@ import { MetadataRepresentation } from '../shared/metadata-representation/metada import { MetadatumRepresentation } from '../shared/metadata-representation/metadatum/metadatum-representation.model'; import { ItemMetadataRepresentation } from '../shared/metadata-representation/item/item-metadata-representation.model'; import { DSpaceObject } from '../shared/dspace-object.model'; +import { MetadataService } from '../metadata/metadata.service'; const relationshipListsStateSelector = (state: AppState) => state.relationshipLists; @@ -89,6 +90,7 @@ export class RelationshipDataService extends IdentifiableDataService, @Inject(PAGINATED_RELATIONS_TO_ITEMS_OPERATOR) private paginatedRelationsToItems: (thisId: string) => (source: Observable>>) => Observable>>, @@ -563,8 +565,8 @@ export class RelationshipDataService extends IdentifiableDataService { - if (metadatum.isVirtual) { - return this.findById(metadatum.virtualValue, true, false, followLink('leftItem'), followLink('rightItem')).pipe( + if (this.metadataService.isVirtual(metadatum)) { + return this.findById(this.metadataService.virtualValue(metadatum), true, false, followLink('leftItem'), followLink('rightItem')).pipe( getFirstSucceededRemoteData(), switchMap((relRD: RemoteData) => observableCombineLatest(relRD.payload.leftItem, relRD.payload.rightItem).pipe( diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts new file mode 100644 index 0000000000..79158c7ec0 --- /dev/null +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MetadataService } from './metadata.service'; + +describe('MetadataService', () => { + let service: MetadataService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MetadataService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts new file mode 100644 index 0000000000..4dd6267675 --- /dev/null +++ b/src/app/core/metadata/metadata.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { hasValue } from '../../shared/empty.util'; +import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../shared/metadata.models'; + +/** + * Service for working with DSpace object metadata. + */ +@Injectable({ + providedIn: 'root', +}) +export class MetadataService { + + /** + * Returns true if this Metadata authority key starts with 'virtual::' + */ + public isVirtual(metadataValue: MetadataValue | undefined): boolean { + return hasValue(metadataValue?.authority) && metadataValue.authority.startsWith(VIRTUAL_METADATA_PREFIX); + } + + /** + * If this is a virtual Metadata, it returns everything in the authority key after 'virtual::'. + * + * Returns undefined otherwise. + */ + public virtualValue(metadataValue: MetadataValue | undefined): string { + if (this.isVirtual) { + return metadataValue.authority.substring(metadataValue.authority.indexOf(VIRTUAL_METADATA_PREFIX) + VIRTUAL_METADATA_PREFIX.length); + } else { + return undefined; + } + } + +} diff --git a/src/app/core/shared/metadata.models.ts b/src/app/core/shared/metadata.models.ts index e74e1f9927..273defd8af 100644 --- a/src/app/core/shared/metadata.models.ts +++ b/src/app/core/shared/metadata.models.ts @@ -1,7 +1,6 @@ /* eslint-disable max-classes-per-file */ import { v4 as uuidv4 } from 'uuid'; import { autoserialize, Serialize, Deserialize } from 'cerialize'; -import { hasValue } from '../../shared/empty.util'; export const VIRTUAL_METADATA_PREFIX = 'virtual::'; @@ -53,24 +52,6 @@ export class MetadataValue implements MetadataValueInterface { @autoserialize confidence: number; - /** - * Returns true if this Metadatum's authority key starts with 'virtual::' - */ - get isVirtual(): boolean { - return hasValue(this.authority) && this.authority.startsWith(VIRTUAL_METADATA_PREFIX); - } - - /** - * If this is a virtual Metadatum, it returns everything in the authority key after 'virtual::'. - * Returns undefined otherwise. - */ - get virtualValue(): string { - if (this.isVirtual) { - return this.authority.substring(this.authority.indexOf(VIRTUAL_METADATA_PREFIX) + VIRTUAL_METADATA_PREFIX.length); - } else { - return undefined; - } - } } /** Constraints for matching metadata values. */ diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html index 525b42610b..854c6e83b2 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html @@ -1,4 +1,4 @@ -
diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts index 3fdcd381ab..06b983fc76 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts @@ -12,6 +12,7 @@ import { map } from 'rxjs/operators'; import { getItemPageRoute } from '../../../item-page/item-page-routing-paths'; import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { EMPTY } from 'rxjs/internal/observable/empty'; +import { MetadataService } from '../../../core/metadata/metadata.service'; @Component({ selector: 'ds-dso-edit-metadata-value', @@ -97,8 +98,11 @@ export class DsoEditMetadataValueComponent implements OnInit { */ mdRepresentationName$: Observable; - constructor(protected relationshipService: RelationshipDataService, - protected dsoNameService: DSONameService) { + constructor( + protected relationshipService: RelationshipDataService, + protected dsoNameService: DSONameService, + protected metadataService: MetadataService, + ) { } ngOnInit(): void { @@ -109,7 +113,7 @@ export class DsoEditMetadataValueComponent implements OnInit { * Initialise potential properties of a virtual metadata value */ initVirtualProperties(): void { - this.mdRepresentation$ = this.mdValue.newValue.isVirtual ? + this.mdRepresentation$ = this.metadataService.isVirtual(this.mdValue.newValue) ? this.relationshipService.resolveMetadataRepresentation(this.mdValue.newValue, this.dso, 'Item') .pipe( map((mdRepresentation: MetadataRepresentation) => diff --git a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts index 59a5377f77..6f8b694412 100644 --- a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts +++ b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts @@ -15,6 +15,7 @@ import { } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; import { BrowseService } from '../../../core/browse/browse.service'; import { BrowseDefinitionDataService } from '../../../core/browse/browse-definition-data.service'; +import { MetadataService } from '../../../core/metadata/metadata.service'; @Component({ selector: 'ds-metadata-representation-list', @@ -62,6 +63,7 @@ export class MetadataRepresentationListComponent extends AbstractIncrementalList constructor( public relationshipService: RelationshipDataService, protected browseDefinitionDataService: BrowseDefinitionDataService, + protected metadataService: MetadataService, ) { super(); } @@ -87,7 +89,7 @@ export class MetadataRepresentationListComponent extends AbstractIncrementalList .slice((this.objects.length * this.incrementBy), (this.objects.length * this.incrementBy) + this.incrementBy) .map((metadatum: any) => Object.assign(new MetadataValue(), metadatum)) .map((metadatum: MetadataValue) => { - if (metadatum.isVirtual) { + if (this.metadataService.isVirtual(metadatum)) { return this.relationshipService.resolveMetadataRepresentation(metadatum, this.parentItem, this.itemType); } else { // Check for a configured browse link and return a standard metadata representation diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html index 9e1f1d48aa..97214d7eaa 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.html @@ -10,7 +10,7 @@
+ 'd-none': this.metadataService.isVirtual(value) && (model.hasSelectableMetadata || context?.index > 0)}">
@@ -51,7 +51,7 @@
- + { { provide: DsDynamicTypeBindRelationService, useValue: getMockDsDynamicTypeBindRelationService() }, { provide: RelationshipDataService, useValue: {} }, { provide: SelectableListService, useValue: {} }, - { provide: ItemDataService, useValue: {} }, { provide: Store, useValue: {} }, { provide: RelationshipDataService, useValue: {} }, { provide: SelectableListService, useValue: {} }, - { provide: FormService, useValue: {} }, { provide: FormBuilderService, useValue: {} }, { provide: SubmissionService, useValue: {} }, { @@ -234,7 +230,6 @@ describe('DsDynamicFormControlContainerComponent test suite', () => { findById: () => observableOf(createSuccessfulRemoteDataObject(testWSI)) } }, - { provide: NgZone, useValue: new NgZone({}) }, { provide: APP_CONFIG, useValue: environment } ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index ff5a119b6f..b786dea103 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -6,7 +6,6 @@ import { ContentChildren, EventEmitter, Inject, Input, - NgZone, OnChanges, OnDestroy, OnInit, @@ -99,7 +98,6 @@ import { } from '../../../../core/shared/operators'; import { RemoteData } from '../../../../core/data/remote-data'; import { Item } from '../../../../core/shared/item.model'; -import { ItemDataService } from '../../../../core/data/item-data.service'; import { Store } from '@ngrx/store'; import { AppState } from '../../../../app.reducer'; import { SubmissionObjectDataService } from '../../../../core/submission/submission-object-data.service'; @@ -109,7 +107,6 @@ import { ItemSearchResult } from '../../../object-collection/shared/item-search- import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { Collection } from '../../../../core/shared/collection.model'; import { MetadataValue, VIRTUAL_METADATA_PREFIX } from '../../../../core/shared/metadata.models'; -import { FormService } from '../../form.service'; import { SelectableListState } from '../../../object-list/selectable-list/selectable-list.reducer'; import { SubmissionService } from '../../../../submission/submission.service'; import { followLink } from '../../../utils/follow-link-config.model'; @@ -120,6 +117,7 @@ import { DYNAMIC_FORM_CONTROL_TYPE_RELATION_GROUP } from './ds-dynamic-form-cons import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; import { itemLinksToFollow } from '../../../utils/relation-query.utils'; +import { MetadataService } from '../../../../core/metadata/metadata.service'; export function dsDynamicFormControlMapFn(model: DynamicFormControlModel): Type | null { switch (model.type) { @@ -250,17 +248,15 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo protected typeBindRelationService: DsDynamicTypeBindRelationService, protected translateService: TranslateService, protected relationService: DynamicFormRelationService, - private modalService: NgbModal, - private relationshipService: RelationshipDataService, - private selectableListService: SelectableListService, - private itemService: ItemDataService, - private zone: NgZone, - private store: Store, - private submissionObjectService: SubmissionObjectDataService, - private ref: ChangeDetectorRef, - private formService: FormService, - public formBuilderService: FormBuilderService, - private submissionService: SubmissionService, + protected modalService: NgbModal, + protected relationshipService: RelationshipDataService, + protected selectableListService: SelectableListService, + protected store: Store, + protected submissionObjectService: SubmissionObjectDataService, + protected ref: ChangeDetectorRef, + protected formBuilderService: FormBuilderService, + protected submissionService: SubmissionService, + protected metadataService: MetadataService, @Inject(APP_CONFIG) protected appConfig: AppConfig, ) { super(ref, componentFactoryResolver, layoutService, validationService, dynamicFormComponentService, relationService); @@ -324,8 +320,8 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo this.value = Object.assign(new FormFieldMetadataValueObject(), this.model.value); } - if (hasValue(this.value) && this.value.isVirtual) { - const relationship$ = this.relationshipService.findById(this.value.virtualValue, + if (hasValue(this.value) && this.metadataService.isVirtual(this.value)) { + const relationship$ = this.relationshipService.findById(this.metadataService.virtualValue(this.value), true, true, ... itemLinksToFollow(this.fetchThumbnail)).pipe(