diff --git a/src/app/core/data/relationship-data.service.spec.ts b/src/app/core/data/relationship-data.service.spec.ts index 4432d5213ae..c8cff74ce02 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 46a51a2d010..4ca260c1c4e 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 00000000000..79158c7ec0f --- /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 00000000000..4dd6267675a --- /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 e74e1f99275..273defd8af7 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 525b42610b4..854c6e83b2a 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 3fdcd381abc..06b983fc76f 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 59a5377f772..6f8b6944123 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 9e1f1d48aa1..97214d7eaa8 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 ff5a119b6fc..b786dea1035 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(