71894: Start of mdField validator; todo: show error & some backend changes

This commit is contained in:
Marie Verdonck
2020-07-16 18:07:21 +02:00
parent 77b2506112
commit a6c0a60bd7
8 changed files with 72 additions and 42 deletions

View File

@@ -12,7 +12,6 @@
(dsClickOutside)="checkValidity(suggestionControl)"
(findSuggestions)="findMetadataFieldSuggestions($event)"
#suggestionControl="ngModel"
[dsInListValidator]="metadataFields"
[valid]="(valid | async) !== false"
dsAutoFocus autoFocusSelector=".suggestion_input"
[ngModelOptions]="{standalone: true}"

View File

@@ -37,11 +37,6 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
*/
@Input() url: string;
/**
* List of strings with all metadata field keys available
*/
@Input() metadataFields: string[];
/**
* The metadatum of this field
*/
@@ -65,7 +60,6 @@ export class EditInPlaceFieldComponent implements OnInit, OnChanges {
constructor(
private registryService: RegistryService,
private objectUpdatesService: ObjectUpdatesService,
private metadataSchemaService: MetadataSchemaDataService
) {
}

View File

@@ -33,7 +33,6 @@
<tr *ngFor="let updateValue of ((updates$ | async)| dsObjectValues); trackBy: trackUpdate"
ds-edit-in-place-field
[fieldUpdate]="updateValue || {}"
[metadataFields]="metadataFields$ | async"
[url]="url"
[ngClass]="{
'table-warning': updateValue.changeType === 0,

View File

@@ -42,11 +42,6 @@ export class ItemMetadataComponent extends AbstractItemUpdateComponent {
*/
@Input() updateService: UpdateDataService<Item>;
/**
* Observable with a list of strings with all existing metadata field keys
*/
metadataFields$: Observable<string[]>;
constructor(
public itemService: ItemDataService,
public objectUpdatesService: ObjectUpdatesService,
@@ -54,7 +49,6 @@ export class ItemMetadataComponent extends AbstractItemUpdateComponent {
public notificationsService: NotificationsService,
public translateService: TranslateService,
public route: ActivatedRoute,
public metadataFieldService: RegistryService,
) {
super(itemService, objectUpdatesService, router, notificationsService, translateService, route);
}
@@ -64,7 +58,6 @@ export class ItemMetadataComponent extends AbstractItemUpdateComponent {
*/
ngOnInit(): void {
super.ngOnInit();
this.metadataFields$ = this.findMetadataFields();
if (hasNoValue(this.updateService)) {
this.updateService = this.itemService;
}
@@ -130,16 +123,6 @@ export class ItemMetadataComponent extends AbstractItemUpdateComponent {
});
}
/**
* Method to request all metadata fields and convert them to a list of strings
*/
findMetadataFields(): Observable<string[]> {
return this.metadataFieldService.getAllMetadataFields().pipe(
getSucceededRemoteData(),
take(1),
map((remoteData$) => remoteData$.payload.page.map((field: MetadataField) => field.toString())));
}
/**
* Check for empty metadata UUIDs and fix them (empty UUIDs would break the object-update service)
*/

View File

@@ -90,20 +90,6 @@ export class RegistryService {
return this.metadataFieldService.findBySchema(schema, options, ...linksToFollow);
}
/**
* Retrieve all existing metadata fields as a paginated list
* @param options Options to determine which page of metadata fields should be requested
* When no options are provided, all metadata fields are requested in one large page
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which {@link HALLink}s should be automatically resolved
* @returns an observable that emits a remote data object with a page of metadata fields
*/
// TODO this is temporarily disabled. The performance is too bad.
// It is used down the line for validation. That validation will have to be rewritten against a new rest endpoint.
// Not by downloading the list of all fields.
public getAllMetadataFields(options?: FindListOptions, ...linksToFollow: Array<FollowLinkConfig<MetadataField>>): Observable<RemoteData<PaginatedList<MetadataField>>> {
return createSuccessfulRemoteDataObject$(new PaginatedList<MetadataField>(null, []));
}
public editMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistryEditSchemaAction(schema));
}

View File

@@ -8,7 +8,8 @@
[ngClass]="{'is-invalid': !valid}"
[dsDebounce]="debounceTime" (onDebounce)="find($event)"
[placeholder]="placeholder"
[ngModelOptions]="{standalone: true}" autocomplete="off"/>
[ngModelOptions]="{standalone: true}" autocomplete="off"
dsMetadataFieldValidator />
<input type="submit" class="d-none"/>
<div class="autocomplete dropdown-menu" [ngClass]="{'show': (show | async) && isNotEmpty(suggestions)}">
<div class="dropdown-list">

View File

@@ -21,6 +21,7 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { EnumKeysPipe } from './utils/enum-keys-pipe';
import { FileSizePipe } from './utils/file-size-pipe';
import { MetadataFieldValidator } from './utils/metadatafield-validator.directive';
import { SafeUrlPipe } from './utils/safe-url-pipe';
import { ConsolePipe } from './utils/console.pipe';
@@ -516,7 +517,8 @@ const DIRECTIVES = [
FileValueAccessorDirective,
FileValidator,
ClaimedTaskActionsDirective,
NgForTrackByIdDirective
NgForTrackByIdDirective,
MetadataFieldValidator
];
@NgModule({

View File

@@ -0,0 +1,66 @@
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 { MetadataFieldDataService } from '../../core/data/metadata-field-data.service';
import { getSucceededRemoteData } from '../../core/shared/operators';
/**
* Directive for validating if a ngModel value is a valid metadata field
*/
@Directive({
selector: '[ngModel][dsMetadataFieldValidator]',
// We add our directive to the list of existing validators
providers: [
{ provide: NG_VALIDATORS, useExisting: MetadataFieldValidator, multi: true }
]
})
@Injectable({ providedIn: 'root' })
export class MetadataFieldValidator implements AsyncValidator {
constructor(private metadataFieldService: MetadataFieldDataService) {
}
/**
* The function that checks if the form control's value is currently valid
* @param control The FormControl
*/
validate(control: AbstractControl): Observable<ValidationErrors | null> {
const resTimer = observableTimer(500).pipe(
switchMap(() => {
console.log('control', control)
if (!control.value) {
return observableOf({ invalidMetadataField: { value: control.value } });
}
const mdFieldNameParts = control.value.split('.');
if (mdFieldNameParts.length < 2) {
console.log('not enough parts')
return observableOf({ invalidMetadataField: { value: control.value } });
}
const res = this.metadataFieldService.findByFieldName(mdFieldNameParts[0], mdFieldNameParts[1], mdFieldNameParts.length == 3 ? mdFieldNameParts[2] : '', '')
.pipe(
getSucceededRemoteData(),
map((results) => {
console.log('results', results)
// TODO: - Currently its valid if the search returns at least one matching mdField; but this does mean that if for example a mdField named schema.elementEx.qualifierEx exists, but you fill in schema. or schema.elementEx then there is at least one result, but this doesnt mean this is the whole of the field (the suggestion does show the options); alternatively it is valid if exact one matching field but then dc.title isnt valid because the search schema=dc & element=title also returns for example dc.title.alternative
// - So the endpoint / restcontract should probably be changed to accommodate and exact search? Or was that already what was wanted and did I interpret it wrong?
if (results.payload.totalElements > 0) {
console.log('VALID')
return null;
} else {
console.log('NOT VALID')
return { invalidMetadataField: { value: control.value } };
}
})
);
res.pipe(take(1)).subscribe();
return res;
})
);
resTimer.pipe(take(1)).subscribe();
return resTimer;
}
}