diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts index 080a22dd7c6..5d7d04b690a 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts @@ -23,6 +23,11 @@ import { ConfidenceType } from 'src/app/core/shared/confidence-type'; import { DynamicOneboxModel } from 'src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.model'; import { Observable } from 'rxjs'; import { DynamicScrollableDropdownModel } from 'src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; +import { RegistryService } from 'src/app/core/registry/registry.service'; +import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; +import { createPaginatedList } from 'src/app/shared/testing/utils.test'; +import { MetadataField } from 'src/app/core/metadata/metadata-field.model'; +import { MetadataSchema } from 'src/app/core/metadata/metadata-schema.model'; const EDIT_BTN = 'edit'; const CONFIRM_BTN = 'confirm'; @@ -38,6 +43,8 @@ describe('DsoEditMetadataValueComponent', () => { let dsoNameService: DSONameService; let vocabularyServiceStub: any; let itemService: ItemDataService; + let registryService: RegistryService; + let notificationsService: NotificationsService; let editMetadataValue: DsoEditMetadataValue; let metadataValue: MetadataValue; @@ -107,7 +114,24 @@ describe('DsoEditMetadataValueComponent', () => { } }; + let metadataSchema: MetadataSchema; + let metadataFields: MetadataField[]; + function initServices(): void { + metadataSchema = Object.assign(new MetadataSchema(), { + id: 0, + prefix: 'metadata', + namespace: 'http://example.com/', + }); + metadataFields = [ + Object.assign(new MetadataField(), { + id: 0, + element: 'regular', + qualifier: null, + schema: createSuccessfulRemoteDataObject$(metadataSchema), + }), + ]; + relationshipService = jasmine.createSpyObj('relationshipService', { resolveMetadataRepresentation: of(new ItemMetadataRepresentation(metadataValue)), }); @@ -118,6 +142,10 @@ describe('DsoEditMetadataValueComponent', () => { findByHref: createSuccessfulRemoteDataObject$(item) }); vocabularyServiceStub = new VocabularyServiceStub(); + registryService = jasmine.createSpyObj('registryService', { + queryMetadataFields: createSuccessfulRemoteDataObject$(createPaginatedList(metadataFields)), + }); + notificationsService = jasmine.createSpyObj('notificationsService', ['error', 'success']); } beforeEach(waitForAsync(() => { @@ -143,7 +171,9 @@ describe('DsoEditMetadataValueComponent', () => { { provide: RelationshipDataService, useValue: relationshipService }, { provide: DSONameService, useValue: dsoNameService }, { provide: VocabularyService, useValue: vocabularyServiceStub }, - { provide: ItemDataService, useValue: itemService } + { provide: ItemDataService, useValue: itemService }, + { provide: RegistryService, useValue: registryService }, + { provide: NotificationsService, useValue: notificationsService }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); 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 578d4c3c44d..29429ab3a06 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 @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { DsoEditMetadataChangeType, DsoEditMetadataValue } from '../dso-edit-metadata-form'; import { Observable } from 'rxjs/internal/Observable'; import { @@ -8,7 +8,7 @@ import { import { RelationshipDataService } from '../../../core/data/relationship-data.service'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; -import { map, switchMap } from 'rxjs/operators'; +import { map, switchMap, take } 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'; @@ -17,7 +17,7 @@ import { Vocabulary } from '../../../core/submission/vocabularies/models/vocabul import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model'; import { ConfidenceType } from '../../../core/shared/confidence-type'; -import { getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload, getRemoteDataPayload } from '../../../core/shared/operators'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload, getRemoteDataPayload, metadataFieldsToString } from '../../../core/shared/operators'; import { DsDynamicOneboxModelConfig, DynamicOneboxModel } from '../../../shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.model'; import { DynamicScrollableDropdownModel, DynamicScrollableDropdownModelConfig } from '../../../shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; import { ItemDataService } from '../../../core/data/item-data.service'; @@ -27,6 +27,9 @@ import { Collection } from '../../../core/shared/collection.model'; import { FormFieldMetadataValueObject } from '../../../shared/form/builder/models/form-field-metadata-value.model'; import { isNotEmpty } from '../../../shared/empty.util'; import { of as observableOf } from 'rxjs'; +import { RegistryService } from 'src/app/core/registry/registry.service'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; @Component({ selector: 'ds-dso-edit-metadata-value', @@ -36,7 +39,7 @@ import { of as observableOf } from 'rxjs'; /** * Component displaying a single editable row for a metadata value */ -export class DsoEditMetadataValueComponent implements OnInit { +export class DsoEditMetadataValueComponent implements OnInit, OnChanges { /** * The parent {@link DSpaceObject} to display a metadata form for * Also used to determine metadata-representations in case of virtual metadata @@ -155,7 +158,11 @@ export class DsoEditMetadataValueComponent implements OnInit { constructor(protected relationshipService: RelationshipDataService, protected dsoNameService: DSONameService, protected vocabularyService: VocabularyService, - protected itemService: ItemDataService) { + protected itemService: ItemDataService, + protected cdr: ChangeDetectorRef, + protected registryService: RegistryService, + protected notificationsService: NotificationsService, + protected translate: TranslateService) { } ngOnInit(): void { @@ -275,6 +282,56 @@ export class DsoEditMetadataValueComponent implements OnInit { })); } + /** + * Change callback for the component. Check if the mdField has changed to retrieve whether it is metadata + * that uses a controlled vocabulary and update the related properties + * + * @param {SimpleChanges} changes + */ + ngOnChanges(changes: SimpleChanges): void { + if (isNotEmpty(changes.mdField) && !changes.mdField.firstChange) { + if (isNotEmpty(changes.mdField.currentValue) ) { + if (isNotEmpty(changes.mdField.previousValue) && + changes.mdField.previousValue !== changes.mdField.currentValue) { + // Clear authority value in case it has been assigned with the previous metadataField used + this.mdValue.newValue.authority = null; + this.mdValue.newValue.confidence = ConfidenceType.CF_UNSET; + } + + // Only ask if the current mdField have a period character to reduce request + if (changes.mdField.currentValue.includes('.')) { + this.validateMetadataField().subscribe((isValid: boolean) => { + if (isValid) { + this.initAuthorityProperties(); + this.cdr.detectChanges(); + } + }); + } + } + } + } + + /** + * Validate the metadata field to check if it exists on the server and return an observable boolean for success/error + */ + validateMetadataField(): Observable { + return this.registryService.queryMetadataFields(this.mdField, null, true, false, followLink('schema')).pipe( + getFirstCompletedRemoteData(), + switchMap((rd) => { + if (rd.hasSucceeded) { + return observableOf(rd).pipe( + metadataFieldsToString(), + take(1), + map((fields: string[]) => fields.indexOf(this.mdField) > -1) + ); + } else { + this.notificationsService.error(this.translate.instant(`${this.dsoType}.edit.metadata.metadatafield.error`), rd.errorMessage); + return [false]; + } + }), + ); + } + /** * Checks if this field use a authority vocabulary */ diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html index 8fb676a7247..d6c72abdb94 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.html @@ -40,6 +40,7 @@ [dsoType]="dsoType" [saving$]="savingOrLoadingFieldValidation$" [isOnlyValue]="true" + [mdField]="newMdField" (confirm)="confirmNewValue($event)" (remove)="form.newValue = undefined" (undo)="form.newValue = undefined">