From 1d0f0b8304976e494f0882b4ca33390d5b0748d3 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 1 Jul 2020 16:11:49 +0200 Subject: [PATCH] move schema on metadatafields to an observable remotedata --- .../metadata-field-form.component.ts | 6 +-- .../metadata-schema.component.spec.ts | 12 ++--- .../metadata-schema.component.ts | 5 +- .../edit-in-place-field.component.html | 2 +- .../edit-in-place-field.component.ts | 6 +-- .../data/metadata-field-data.service.spec.ts | 39 --------------- .../core/data/metadata-field-data.service.ts | 19 +------ src/app/core/metadata/metadata-field.model.ts | 10 ++-- .../core/registry/registry.service.spec.ts | 34 +++++++++---- src/app/core/registry/registry.service.ts | 49 ++++++++++++------- 10 files changed, 78 insertions(+), 104 deletions(-) diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts index 42f6441791..70cdc15b4d 100644 --- a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts @@ -157,19 +157,17 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy { this.registryService.getActiveMetadataField().pipe(take(1)).subscribe( (field) => { const values = { - schema: this.metadataSchema, element: this.element.value, qualifier: this.qualifier.value, scopeNote: this.scopeNote.value }; if (field == null) { - this.registryService.createOrUpdateMetadataField(Object.assign(new MetadataField(), values)).subscribe((newField) => { + this.registryService.createMetadataField(Object.assign(new MetadataField(), values), this.metadataSchema).subscribe((newField) => { this.submitForm.emit(newField); }); } else { - this.registryService.createOrUpdateMetadataField(Object.assign(new MetadataField(), field, { + this.registryService.updateMetadataField(Object.assign(new MetadataField(), field, { id: field.id, - schema: this.metadataSchema, element: (values.element ? values.element : field.element), qualifier: (values.qualifier ? values.qualifier : field.qualifier), scopeNote: (values.scopeNote ? values.scopeNote : field.scopeNote) diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts index 26f4c90b0c..ef85bf5d1e 100644 --- a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts @@ -61,7 +61,7 @@ describe('MetadataSchemaComponent', () => { element: 'contributor', qualifier: 'advisor', scopeNote: null, - schema: mockSchemasList[0] + schema: createSuccessfulRemoteDataObject$(mockSchemasList[0]) }, { id: 2, @@ -73,7 +73,7 @@ describe('MetadataSchemaComponent', () => { element: 'contributor', qualifier: 'author', scopeNote: null, - schema: mockSchemasList[0] + schema: createSuccessfulRemoteDataObject$(mockSchemasList[0]) }, { id: 3, @@ -85,7 +85,7 @@ describe('MetadataSchemaComponent', () => { element: 'contributor', qualifier: 'editor', scopeNote: 'test scope note', - schema: mockSchemasList[1] + schema: createSuccessfulRemoteDataObject$(mockSchemasList[1]) }, { id: 4, @@ -97,15 +97,15 @@ describe('MetadataSchemaComponent', () => { element: 'contributor', qualifier: 'illustrator', scopeNote: null, - schema: mockSchemasList[1] + schema: createSuccessfulRemoteDataObject$(mockSchemasList[1]) } ]; const mockSchemas = createSuccessfulRemoteDataObject$(new PaginatedList(null, mockSchemasList)); /* tslint:disable:no-empty */ const registryServiceStub = { getMetadataSchemas: () => mockSchemas, - getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(new PaginatedList(null, mockFieldsList.filter((value) => value.schema === schema))), - getMetadataSchemaByName: (schemaName: string) => createSuccessfulRemoteDataObject$(mockSchemasList.filter((value) => value.prefix === schemaName)[0]), + getMetadataFieldsBySchema: (schema: MetadataSchema) => createSuccessfulRemoteDataObject$(new PaginatedList(null, mockFieldsList)), + getMetadataSchemaByPrefix: (schemaName: string) => createSuccessfulRemoteDataObject$(mockSchemasList.filter((value) => value.prefix === schemaName)[0]), getActiveMetadataField: () => observableOf(undefined), getSelectedMetadataFields: () => observableOf([]), editMetadataField: (schema) => {}, diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts index 96872573b9..87f4b863c0 100644 --- a/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-schema.component.ts @@ -17,6 +17,7 @@ import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operato import { toFindListOptions } from '../../../shared/pagination/pagination.utils'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { combineLatest } from 'rxjs/internal/observable/combineLatest'; +import { followLink } from '../../../shared/utils/follow-link-config.model'; @Component({ selector: 'ds-metadata-schema', @@ -71,7 +72,7 @@ export class MetadataSchemaComponent implements OnInit { * @param params */ initialize(params) { - this.metadataSchema$ = this.registryService.getMetadataSchemaByName(params.schemaName).pipe(getFirstSucceededRemoteDataPayload()); + this.metadataSchema$ = this.registryService.getMetadataSchemaByPrefix(params.schemaName).pipe(getFirstSucceededRemoteDataPayload()); this.updateFields(); } @@ -91,7 +92,7 @@ export class MetadataSchemaComponent implements OnInit { this.metadataFields$ = combineLatest(this.metadataSchema$, this.needsUpdate$).pipe( switchMap(([schema, update]: [MetadataSchema, boolean]) => { if (update) { - return this.registryService.getMetadataFieldsBySchema(schema, toFindListOptions(this.config)); + return this.registryService.getMetadataFieldsBySchema(schema, toFindListOptions(this.config), followLink('schema')); } }) ); diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html index e8ffc28920..80c78941c8 100644 --- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html +++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.html @@ -67,4 +67,4 @@ - \ No newline at end of file + diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts index e73465f905..7e09ac9bec 100644 --- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts +++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.ts @@ -58,7 +58,7 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges { metadataFieldSuggestions: BehaviorSubject = new BehaviorSubject([]); constructor( - private metadataFieldService: RegistryService, + private registryService: RegistryService, private objectUpdatesService: ObjectUpdatesService, ) { } @@ -128,13 +128,13 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges { */ findMetadataFieldSuggestions(query: string): void { if (isNotEmpty(query)) { - this.metadataFieldService.queryMetadataFields(query).pipe( + this.registryService.queryMetadataFields(query).pipe( // getSucceededRemoteData(), take(1), map((data) => data.payload.page) ).subscribe( (fields: MetadataField[]) => this.metadataFieldSuggestions.next( - fields.filter((field: MetadataField) => field.schema.prefix !== 'relation' && field.schema.prefix !== 'relationship').map((field: MetadataField) => { + fields.map((field: MetadataField) => { return { displayValue: field.toString().split('.').join('.​'), value: field.toString() diff --git a/src/app/core/data/metadata-field-data.service.spec.ts b/src/app/core/data/metadata-field-data.service.spec.ts index 5c17b56845..c4ca7f7efc 100644 --- a/src/app/core/data/metadata-field-data.service.spec.ts +++ b/src/app/core/data/metadata-field-data.service.spec.ts @@ -64,45 +64,6 @@ describe('MetadataFieldDataService', () => { }); }); - describe('createOrUpdateMetadataField', () => { - let field: MetadataField; - - beforeEach(() => { - field = Object.assign(new MetadataField(), { - element: 'identifier', - qualifier: undefined, - schema: schema, - _links: { - self: { href: 'selflink' } - } - }); - }); - - describe('called with a new metadata field', () => { - it('should send a CreateRequest', (done) => { - metadataFieldService.createOrUpdateMetadataField(field).subscribe(() => { - expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(CreateRequest)); - done(); - }); - }); - }); - - describe('called with an existing metadata field', () => { - beforeEach(() => { - field = Object.assign(field, { - id: 'id-of-existing-field' - }); - }); - - it('should send a PutRequest', (done) => { - metadataFieldService.createOrUpdateMetadataField(field).subscribe(() => { - expect(requestService.configure).toHaveBeenCalledWith(jasmine.any(PutRequest)); - done(); - }); - }); - }); - }); - describe('clearRequests', () => { it('should remove requests on the data service\'s endpoint', (done) => { metadataFieldService.clearRequests().subscribe(() => { diff --git a/src/app/core/data/metadata-field-data.service.ts b/src/app/core/data/metadata-field-data.service.ts index f50be20f13..8065e45a9e 100644 --- a/src/app/core/data/metadata-field-data.service.ts +++ b/src/app/core/data/metadata-field-data.service.ts @@ -21,6 +21,7 @@ import { find, skipWhile, switchMap, tap } from 'rxjs/operators'; import { RemoteData } from './remote-data'; import { RequestParam } from '../cache/models/request-param.model'; import { PaginatedList } from './paginated-list'; +import { getFirstSucceededRemoteDataPayload } from '../shared/operators'; /** * A service responsible for fetching/sending data from/to the REST API on the metadatafields endpoint @@ -56,24 +57,6 @@ export class MetadataFieldDataService extends DataService { return this.searchBy(this.searchBySchemaLinkPath, optionsWithSchema, ...linksToFollow); } - /** - * Create or Update a MetadataField - * If the MetadataField contains an id, it is assumed the field already exists and is updated instead - * Since creating or updating is nearly identical, the only real difference is the request (and slight difference in endpoint): - * - On creation, a CreateRequest is used - * - On update, a PutRequest is used - * @param field The MetadataField to create or update - */ - createOrUpdateMetadataField(field: MetadataField): Observable> { - const isUpdate = hasValue(field.id); - - if (isUpdate) { - return this.put(field); - } else { - return this.create(field, new RequestParam('schemaId', field.schema.id)); - } - } - /** * Clear all metadata field requests * Used for refreshing lists after adding/updating/removing a metadata field from a metadata schema diff --git a/src/app/core/metadata/metadata-field.model.ts b/src/app/core/metadata/metadata-field.model.ts index ad7ec59b25..66171141c5 100644 --- a/src/app/core/metadata/metadata-field.model.ts +++ b/src/app/core/metadata/metadata-field.model.ts @@ -9,6 +9,9 @@ import { ResourceType } from '../shared/resource-type'; import { excludeFromEquals } from '../utilities/equals.decorators'; import { METADATA_FIELD } from './metadata-field.resource-type'; import { MetadataSchema } from './metadata-schema.model'; +import { RemoteData } from '../data/remote-data'; +import { Observable } from 'rxjs'; +import { METADATA_SCHEMA } from './metadata-schema.resource-type'; /** * Class the represents a metadata field @@ -61,16 +64,15 @@ export class MetadataField extends ListableObject implements HALResource { * The MetadataSchema for this MetadataField * Will be undefined unless the schema {@link HALLink} has been resolved. */ - // TODO the responseparsingservice assumes schemas are always embedded. This should use remotedata, and be a link instead. - // @link(METADATA_SCHEMA) - schema?: MetadataSchema; + @link(METADATA_SCHEMA) + schema?: Observable>; /** * Method to print this metadata field as a string * @param separator The separator between the schema, element and qualifier in the string */ toString(separator: string = '.'): string { - let key = this.schema.prefix + separator + this.element; + let key = this.element; if (isNotEmpty(this.qualifier)) { key += separator + this.qualifier; } diff --git a/src/app/core/registry/registry.service.spec.ts b/src/app/core/registry/registry.service.spec.ts index 203e58136c..6f6deb3e3f 100644 --- a/src/app/core/registry/registry.service.spec.ts +++ b/src/app/core/registry/registry.service.spec.ts @@ -82,7 +82,7 @@ describe('RegistryService', () => { element: 'contributor', qualifier: 'advisor', scopeNote: null, - schema: mockSchemasList[0], + schema: createSuccessfulRemoteDataObject$(mockSchemasList[0]), type: MetadataField.type }), Object.assign(new MetadataField(), @@ -94,7 +94,7 @@ describe('RegistryService', () => { element: 'contributor', qualifier: 'author', scopeNote: null, - schema: mockSchemasList[0], + schema: createSuccessfulRemoteDataObject$(mockSchemasList[0]), type: MetadataField.type }), Object.assign(new MetadataField(), @@ -106,7 +106,7 @@ describe('RegistryService', () => { element: 'contributor', qualifier: 'editor', scopeNote: 'test scope note', - schema: mockSchemasList[1], + schema: createSuccessfulRemoteDataObject$(mockSchemasList[1]), type: MetadataField.type }), Object.assign(new MetadataField(), @@ -118,7 +118,7 @@ describe('RegistryService', () => { element: 'contributor', qualifier: 'illustrator', scopeNote: null, - schema: mockSchemasList[1], + schema: createSuccessfulRemoteDataObject$(mockSchemasList[1]), type: MetadataField.type }) ]; @@ -134,7 +134,8 @@ describe('RegistryService', () => { metadataFieldService = jasmine.createSpyObj('metadataFieldService', { findAll: createSuccessfulRemoteDataObject$(createPaginatedList(mockFieldsList)), findById: createSuccessfulRemoteDataObject$(mockFieldsList[0]), - createOrUpdateMetadataField: createSuccessfulRemoteDataObject$(mockFieldsList[0]), + create: createSuccessfulRemoteDataObject$(mockFieldsList[0]), + put: createSuccessfulRemoteDataObject$(mockFieldsList[0]), deleteAndReturnResponse: observableOf(new RestResponse(true, 200, 'OK')), clearRequests: observableOf('href') }); @@ -178,7 +179,7 @@ describe('RegistryService', () => { let result; beforeEach(() => { - result = registryService.getMetadataSchemaByName(mockSchemasList[0].prefix); + result = registryService.getMetadataSchemaByPrefix(mockSchemasList[0].prefix); }); it('should call metadataSchemaService.findById with the correct ID', (done) => { @@ -325,14 +326,29 @@ describe('RegistryService', () => { }); }); - describe('when createOrUpdateMetadataField is called', () => { + describe('when createMetadataField is called', () => { let result: Observable; beforeEach(() => { - result = registryService.createOrUpdateMetadataField(mockFieldsList[0]); + result = registryService.createMetadataField(mockFieldsList[0], mockSchemasList[0]); }); - it('should return the created/updated metadata field', (done) => { + it('should return the created metadata field', (done) => { + result.subscribe((field: MetadataField) => { + expect(field).toEqual(mockFieldsList[0]); + done(); + }); + }); + }); + + describe('when updateMetadataField is called', () => { + let result: Observable; + + beforeEach(() => { + result = registryService.updateMetadataField(mockFieldsList[0]); + }); + + it('should return the updated metadata field', (done) => { result.subscribe((field: MetadataField) => { expect(field).toEqual(mockFieldsList[0]); done(); diff --git a/src/app/core/registry/registry.service.ts b/src/app/core/registry/registry.service.ts index 79b982da8a..75d1d5cc74 100644 --- a/src/app/core/registry/registry.service.ts +++ b/src/app/core/registry/registry.service.ts @@ -36,7 +36,8 @@ import { MetadataSchema } from '../metadata/metadata-schema.model'; import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataSchemaDataService } from '../data/metadata-schema-data.service'; import { MetadataFieldDataService } from '../data/metadata-field-data.service'; -import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; +import { FollowLinkConfig, followLink } from '../../shared/utils/follow-link-config.model'; +import { RequestParam } from '../cache/models/request-param.model'; const metadataRegistryStateSelector = (state: AppState) => state.metadataRegistry; const editMetadataSchemaSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.editSchema); @@ -68,11 +69,11 @@ export class RegistryService { } /** - * Retrieves a metadata schema by its name - * @param schemaName The name of the schema to find + * Retrieves a metadata schema by its prefix + * @param prefix The prefux of the schema to find * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved */ - public getMetadataSchemaByName(schemaName: string, ...linksToFollow: Array>): Observable> { + public getMetadataSchemaByPrefix(prefix: string, ...linksToFollow: Array>): Observable> { // Temporary options to get ALL metadataschemas until there's a rest api endpoint for fetching a specific schema const options: FindListOptions = Object.assign(new FindListOptions(), { elementsPerPage: 10000 @@ -81,7 +82,7 @@ export class RegistryService { getFirstSucceededRemoteDataPayload(), map((schemas: PaginatedList) => schemas.page), isNotEmptyOperator(), - map((schemas: MetadataSchema[]) => schemas.filter((schema) => schema.prefix === schemaName)[0]), + map((schemas: MetadataSchema[]) => schemas.filter((schema) => schema.prefix === prefix)[0]), flatMap((schema: MetadataSchema) => this.metadataSchemaService.findById(`${schema.id}`, ...linksToFollow)) ); } @@ -240,25 +241,37 @@ export class RegistryService { } /** - * Create or Update a MetadataField - * If the MetadataField contains an id, it is assumed the field already exists and is updated instead - * Since creating or updating is nearly identical, the only real difference is the request (and slight difference in endpoint): - * - On creation, a CreateRequest is used - * - On update, a PutRequest is used - * @param field The MetadataField to create or update + * Create a MetadataField + * + * @param field The MetadataField to create + * @param schema The MetadataSchema to create the field in */ - public createOrUpdateMetadataField(field: MetadataField): Observable { - const isUpdate = hasValue(field.id); - return this.metadataFieldService.createOrUpdateMetadataField(field).pipe( + public createMetadataField(field: MetadataField, schema: MetadataSchema): Observable { + return this.metadataFieldService.create(field, new RequestParam('schemaId', schema.id)).pipe( getFirstSucceededRemoteDataPayload(), hasValueOperator(), tap(() => { - const fieldString = `${field.schema.prefix}.${field.element}${field.qualifier ? `.${field.qualifier}` : ''}`; - this.showNotifications(true, isUpdate, true, {field: fieldString}); + this.showNotifications(true, false, true, {field: field.toString()}); }) ); } + /** + * Update a MetadataField + * + * @param field The MetadataField to update + */ + public updateMetadataField(field: MetadataField): Observable { + return this.metadataFieldService.put(field).pipe( + getFirstSucceededRemoteDataPayload(), + hasValueOperator(), + tap(() => { + this.showNotifications(true, true, true, {field: field.toString()}); + }) + ); + } + + /** * Method to delete a metadata field * @param id The id of the metadata field to delete @@ -296,12 +309,12 @@ export class RegistryService { * @returns an observable that emits a remote data object with a page of metadata fields that match the query */ queryMetadataFields(query: string): Observable>> { - return this.getAllMetadataFields().pipe( + return this.getAllMetadataFields(undefined, followLink('schema')).pipe( map((rd: RemoteData>) => { const filteredFields: MetadataField[] = rd.payload.page.filter( (field: MetadataField) => field.toString().indexOf(query) >= 0 ); - const page: PaginatedList = new PaginatedList(new PageInfo(), filteredFields) + const page: PaginatedList = new PaginatedList(new PageInfo(), filteredFields); return Object.assign({}, rd, { payload: page }); }) );