From b602a736de98cd84a6ab5fc2245831bd4af6112d Mon Sep 17 00:00:00 2001 From: Marie Verdonck Date: Tue, 1 Sep 2020 14:33:36 +0200 Subject: [PATCH] 71894: Refactored exact FieldName search for validator after change endpoint --- .../edit-in-place-field.component.spec.ts | 2 +- .../core/data/metadata-field-data.service.ts | 49 +++++++------------ src/app/core/registry/registry.service.ts | 2 +- src/app/core/shared/operators.ts | 8 +-- .../filter-input-suggestions.component.ts | 2 +- .../metadatafield-validator.directive.ts | 14 ++++-- 6 files changed, 33 insertions(+), 44 deletions(-) diff --git a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts index 315b76a1e4..341c3e393b 100644 --- a/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-metadata/edit-in-place-field/edit-in-place-field.component.spec.ts @@ -63,7 +63,7 @@ const fieldUpdate = { }; let scheduler: TestScheduler; -fdescribe('EditInPlaceFieldComponent', () => { +describe('EditInPlaceFieldComponent', () => { beforeEach(async(() => { scheduler = getTestScheduler(); diff --git a/src/app/core/data/metadata-field-data.service.ts b/src/app/core/data/metadata-field-data.service.ts index ceaace024b..af2ab7c45c 100644 --- a/src/app/core/data/metadata-field-data.service.ts +++ b/src/app/core/data/metadata-field-data.service.ts @@ -1,10 +1,9 @@ import { Injectable } from '@angular/core'; -import { hasValue, isNotEmptyOperator } from '../../shared/empty.util'; +import { hasValue } from '../../shared/empty.util'; import { dataService } from '../cache/builders/build-decorators'; -import { RestResponse } from '../cache/response.models'; -import { configureRequest } from '../shared/operators'; import { DataService } from './data.service'; -import { RequestEntry } from './request.reducer'; +import { PaginatedList } from './paginated-list'; +import { RemoteData } from './remote-data'; import { RequestService } from './request.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { Store } from '@ngrx/store'; @@ -17,14 +16,10 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { METADATA_FIELD } from '../metadata/metadata-field.resource-type'; import { MetadataField } from '../metadata/metadata-field.model'; import { MetadataSchema } from '../metadata/metadata-schema.model'; -import { - FindListOptions, - GetMetadataFieldRequest, - RestRequest -} from './request.models'; +import { FindListOptions } from './request.models'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { Observable } from 'rxjs/internal/Observable'; -import { distinctUntilChanged, find, map, switchMap, tap } from 'rxjs/operators'; +import { tap } from 'rxjs/operators'; import { RequestParam } from '../cache/models/request-param.model'; /** @@ -70,43 +65,33 @@ export class MetadataFieldDataService extends DataService { * @param qualifier optional; an exact match of the field's qualifier (e.g. "author", "alternative") * @param query optional (if any of schema, element or qualifier used) - part of the fully qualified field, * should start with the start of the schema, element or qualifier (e.g. “dc.ti”, “contributor”, “auth”, “contributor.ot”) + * @param exactName optional; the exact fully qualified field, should use the syntax schema.element.qualifier or + * schema.element if no qualifier exists (e.g. "dc.title", "dc.contributor.author"). It will only return one value + * if there's an exact match * @param options The options info used to retrieve the fields * @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved */ - searchByFieldNameParams(schema: string, element: string, qualifier: string, query: string, options: FindListOptions = {}, ...linksToFollow: Array>) { + searchByFieldNameParams(schema: string, element: string, qualifier: string, query: string, exactName: string, options: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { const optionParams = Object.assign(new FindListOptions(), options, { searchParams: [ new RequestParam('schema', hasValue(schema) ? schema : ''), new RequestParam('element', hasValue(element) ? element : ''), new RequestParam('qualifier', hasValue(qualifier) ? qualifier : ''), - new RequestParam('query', hasValue(query) ? query : '') + new RequestParam('query', hasValue(query) ? query : ''), + new RequestParam('exactName', hasValue(exactName) ? exactName : '') ] }); return this.searchBy(this.searchByFieldNameLinkPath, optionParams, ...linksToFollow); } /** - * Finds a specific metadata field by name. There's always at most one metadata field per name. - * If the metadata field can be found it is in the response, otherwise the response will have code 404 - * @param exactFieldName Exact metadata field name (ex. dc.title, dc.contributor.other) + * Finds a specific metadata field by name. + * @param exactFieldName The exact fully qualified field, should use the syntax schema.element.qualifier or + * schema.element if no qualifier exists (e.g. "dc.title", "dc.contributor.author"). It will only return one value + * if there's an exact match, empty list if there is no exact match. */ - findByExactFieldName(exactFieldName: string): Observable { - const request$ = this.halService.getEndpoint(this.linkPath).pipe( - isNotEmptyOperator(), - distinctUntilChanged(), - map((endpoint: string) => `${endpoint}/name/${exactFieldName}`), - map((endpointURL: string) => new GetMetadataFieldRequest(this.requestService.generateRequestId(), endpointURL)), - configureRequest(this.requestService) - ); - - const requestEntry$ = request$.pipe( - switchMap((request: RestRequest) => this.requestService.getByHref(request.href)) - ); - - return requestEntry$.pipe( - find((request: RequestEntry) => request.completed), - map((request: RequestEntry) => request.response) - ); + findByExactFieldName(exactFieldName: string): Observable>> { + return this.searchByFieldNameParams(null, null, null, null, exactFieldName, null); } /** diff --git a/src/app/core/registry/registry.service.ts b/src/app/core/registry/registry.service.ts index 899d1582de..f349cf428c 100644 --- a/src/app/core/registry/registry.service.ts +++ b/src/app/core/registry/registry.service.ts @@ -293,6 +293,6 @@ 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, options: FindListOptions = {}, ...linksToFollow: Array>): Observable>> { - return this.metadataFieldService.searchByFieldNameParams(null, null, null, query, options, ...linksToFollow); + return this.metadataFieldService.searchByFieldNameParams(null, null, null, query, null, options, ...linksToFollow); } } diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index 0afd1d97b1..b0ff812be4 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -1,6 +1,6 @@ import { Router, UrlTree } from '@angular/router'; import { Observable, combineLatest as observableCombineLatest } from 'rxjs'; -import { filter, find, flatMap, map, switchMap, take, tap } from 'rxjs/operators'; +import { filter, find, flatMap, map, switchMap, tap } from 'rxjs/operators'; import { hasValue, hasValueOperator, isNotEmpty } from '../../shared/empty.util'; import { SearchResult } from '../../shared/search/search-result.model'; import { DSOSuccessResponse, RestResponse } from '../cache/response.models'; @@ -263,7 +263,7 @@ export const metadataFieldsToString = () => map((fieldRD: RemoteData>) => { return fieldRD.payload.page.filter((object: MetadataField) => hasValue(object)) }), - switchMap((fields: MetadataField[])=> { + switchMap((fields: MetadataField[]) => { const fieldSchemaArray = fields.map((field: MetadataField) => { return field.schema.pipe( getFirstSucceededRemoteDataPayload(), @@ -272,7 +272,7 @@ export const metadataFieldsToString = () => }); return observableCombineLatest(fieldSchemaArray); }), - map((fieldSchemaArray: {field: MetadataField, schema: MetadataSchema}[]): string[] => { - return fieldSchemaArray.map((fieldSchema: {field: MetadataField, schema: MetadataSchema}) => fieldSchema.schema.prefix + '.' + fieldSchema.field.toString()) + map((fieldSchemaArray: Array<{ field: MetadataField, schema: MetadataSchema }>): string[] => { + return fieldSchemaArray.map((fieldSchema: { field: MetadataField, schema: MetadataSchema }) => fieldSchema.schema.prefix + '.' + fieldSchema.field.toString()) }) ); diff --git a/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.ts b/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.ts index 1c18ec4cef..0609a85c37 100644 --- a/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.ts +++ b/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.ts @@ -22,7 +22,7 @@ import { InputSuggestion } from '../input-suggestions.model'; /** * Component representing a form with a autocomplete functionality */ -export class FilterInputSuggestionsComponent extends InputSuggestionsComponent implements OnInit{ +export class FilterInputSuggestionsComponent extends InputSuggestionsComponent implements OnInit { form: FormGroup; diff --git a/src/app/shared/utils/metadatafield-validator.directive.ts b/src/app/shared/utils/metadatafield-validator.directive.ts index ea7e5fc105..4ec6fb24bc 100644 --- a/src/app/shared/utils/metadatafield-validator.directive.ts +++ b/src/app/shared/utils/metadatafield-validator.directive.ts @@ -2,8 +2,11 @@ import { Directive, Injectable } from '@angular/core'; import { AbstractControl, AsyncValidator, NG_VALIDATORS, ValidationErrors } from '@angular/forms'; import { map, switchMap, take } from 'rxjs/operators'; import { of as observableOf, timer as observableTimer, Observable } from 'rxjs'; -import { RestResponse } from '../../core/cache/response.models'; import { MetadataFieldDataService } from '../../core/data/metadata-field-data.service'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { RemoteData } from '../../core/data/remote-data'; +import { MetadataField } from '../../core/metadata/metadata-field.model'; +import { getSucceededRemoteData } from '../../core/shared/operators'; /** * Directive for validating if a ngModel value is a valid metadata field @@ -38,11 +41,12 @@ export class MetadataFieldValidator implements AsyncValidator { const res = this.metadataFieldService.findByExactFieldName(control.value) .pipe( - map((response: RestResponse) => { - if (response.isSuccessful) { - return null; - } else { + getSucceededRemoteData(), + map((matchingFieldRD: RemoteData>) => { + if (matchingFieldRD.payload.pageInfo.totalElements === 0) { return { invalidMetadataField: { value: control.value } }; + } else if (matchingFieldRD.payload.pageInfo.totalElements === 1) { + return null; } }) );