move schema on metadatafields to an observable remotedata

This commit is contained in:
Art Lowel
2020-07-01 16:11:49 +02:00
parent e0990aeb4b
commit 1d0f0b8304
10 changed files with 78 additions and 104 deletions

View File

@@ -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)

View File

@@ -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) => {},

View File

@@ -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'));
}
})
);

View File

@@ -67,4 +67,4 @@
<i class="fas fa-undo-alt fa-fw"></i>
</button>
</div>
</td>
</td>

View File

@@ -58,7 +58,7 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
metadataFieldSuggestions: BehaviorSubject<InputSuggestion[]> = 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('.&#8203;'),
value: field.toString()

View File

@@ -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(() => {

View File

@@ -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<MetadataField> {
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<RemoteData<MetadataField>> {
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

View File

@@ -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<RemoteData<MetadataSchema>>;
/**
* 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;
}

View File

@@ -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<MetadataField>;
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<MetadataField>;
beforeEach(() => {
result = registryService.updateMetadataField(mockFieldsList[0]);
});
it('should return the updated metadata field', (done) => {
result.subscribe((field: MetadataField) => {
expect(field).toEqual(mockFieldsList[0]);
done();

View File

@@ -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<FollowLinkConfig<MetadataSchema>>): Observable<RemoteData<MetadataSchema>> {
public getMetadataSchemaByPrefix(prefix: string, ...linksToFollow: Array<FollowLinkConfig<MetadataSchema>>): Observable<RemoteData<MetadataSchema>> {
// 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<MetadataSchema>) => 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<MetadataField> {
const isUpdate = hasValue(field.id);
return this.metadataFieldService.createOrUpdateMetadataField(field).pipe(
public createMetadataField(field: MetadataField, schema: MetadataSchema): Observable<MetadataField> {
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<MetadataField> {
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<RemoteData<PaginatedList<MetadataField>>> {
return this.getAllMetadataFields().pipe(
return this.getAllMetadataFields(undefined, followLink('schema')).pipe(
map((rd: RemoteData<PaginatedList<MetadataField>>) => {
const filteredFields: MetadataField[] = rd.payload.page.filter(
(field: MetadataField) => field.toString().indexOf(query) >= 0
);
const page: PaginatedList<MetadataField> = new PaginatedList<MetadataField>(new PageInfo(), filteredFields)
const page: PaginatedList<MetadataField> = new PaginatedList<MetadataField>(new PageInfo(), filteredFields);
return Object.assign({}, rd, { payload: page });
})
);