Add support for field specific validations

- Allow adding validations to specific fields
- Allow excluding validations to specific fields
- Add string matrix into common
This commit is contained in:
Clemente Raposo 2021-02-26 10:12:22 +00:00 committed by Dillon-Brown
parent 3eb305ba4f
commit 10d4c687dd
4 changed files with 93 additions and 34 deletions

View file

@ -21,6 +21,7 @@ export * from './statistics/statistics.model';
export * from './types/messages'; export * from './types/messages';
export * from './types/overridable-map'; export * from './types/overridable-map';
export * from './types/string-map'; export * from './types/string-map';
export * from './types/string-matrix';
export * from './types/user'; export * from './types/user';
export * from './utils/object-utils'; export * from './utils/object-utils';
export * from './utils/value-utils'; export * from './utils/value-utils';

View file

@ -0,0 +1,5 @@
import {StringMap} from './string-map';
export interface StringMatrix {
[key: string]: StringMap;
}

View file

@ -26,7 +26,6 @@
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
import {shareReplay} from 'rxjs/operators'; import {shareReplay} from 'rxjs/operators';
import {EntityMutationGQL} from '../../../api/graphql-api';
import {FetchResult} from '@apollo/client/core'; import {FetchResult} from '@apollo/client/core';
import {ProcessService} from '../../process.service'; import {ProcessService} from '../../process.service';
import {appStateStoreMock} from '../../../../store/app-state/app-state.store.spec.mock'; import {appStateStoreMock} from '../../../../store/app-state/app-state.store.spec.mock';
@ -34,6 +33,7 @@ import {AsyncActionService} from './async-action';
import {messageServiceMock} from '../../../message/message.service.spec.mock'; import {messageServiceMock} from '../../../message/message.service.spec.mock';
import {redirectBulkActionMock} from './actions/redirect/redirect.async-action.spec.mock'; import {redirectBulkActionMock} from './actions/redirect/redirect.async-action.spec.mock';
import {exportBulkActionMock} from './actions/export/export.async-action.spec.mock'; import {exportBulkActionMock} from './actions/export/export.async-action.spec.mock';
import {EntityMutationGQL} from '../../../api/graphql-api/api.record.create';
export const bulkActionMockData = { export const bulkActionMockData = {
'bulk-merge': { 'bulk-merge': {

View file

@ -26,11 +26,9 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {ValidatorInterface} from './validator.Interface'; import {ValidatorInterface} from './validator.Interface';
import {Record} from 'common'; import {MapEntry, OverridableMap, Record, StringMap, StringMatrix, ViewFieldDefinition} from 'common';
import {ViewFieldDefinition} from 'common';
import {AsyncValidatorFn, ValidatorFn} from '@angular/forms'; import {AsyncValidatorFn, ValidatorFn} from '@angular/forms';
import {AsyncValidatorInterface} from './aync-validator.Interface'; import {AsyncValidatorInterface} from './aync-validator.Interface';
import {MapEntry, OverridableMap} from 'common';
import {RequiredValidator} from './validators/required.validator'; import {RequiredValidator} from './validators/required.validator';
import {CurrencyValidator} from './validators/currency.validator'; import {CurrencyValidator} from './validators/currency.validator';
import {DateValidator} from './validators/date.validator'; import {DateValidator} from './validators/date.validator';
@ -68,6 +66,12 @@ export class ValidationManager implements ValidationManagerInterface {
protected saveValidators: OverridableMap<ValidatorInterface>; protected saveValidators: OverridableMap<ValidatorInterface>;
protected asyncSaveValidators: OverridableMap<AsyncValidatorInterface>; protected asyncSaveValidators: OverridableMap<AsyncValidatorInterface>;
protected filterValidators: OverridableMap<ValidatorInterface>; protected filterValidators: OverridableMap<ValidatorInterface>;
protected filterFieldExclusion: StringMatrix = {
default: {}
};
protected saveFieldExclusions: StringMatrix = {
default: {}
};
constructor( constructor(
protected requiredValidator: RequiredValidator, protected requiredValidator: RequiredValidator,
@ -85,56 +89,81 @@ export class ValidationManager implements ValidationManagerInterface {
this.asyncSaveValidators = new OverridableMap<AsyncValidatorInterface>(); this.asyncSaveValidators = new OverridableMap<AsyncValidatorInterface>();
this.filterValidators = new OverridableMap<ValidatorInterface>(); this.filterValidators = new OverridableMap<ValidatorInterface>();
this.saveValidators.addEntry('default', 'required', requiredValidator); this.saveValidators.addEntry('default', this.getKey('required', 'all'), requiredValidator);
this.saveValidators.addEntry('default', 'range', rangeValidator); this.saveValidators.addEntry('default', this.getKey('range', 'all'), rangeValidator);
this.saveValidators.addEntry('default', 'currency', currencyValidator); this.saveValidators.addEntry('default', this.getKey('currency', 'all'), currencyValidator);
this.saveValidators.addEntry('default', 'date', dateValidator); this.saveValidators.addEntry('default', this.getKey('date', 'all'), dateValidator);
this.saveValidators.addEntry('default', 'datetime', datetimeValidator); this.saveValidators.addEntry('default', this.getKey('datetime', 'all'), datetimeValidator);
this.saveValidators.addEntry('default', 'email', emailValidator); this.saveValidators.addEntry('default', this.getKey('email', 'all'), emailValidator);
this.saveValidators.addEntry('default', 'float', floatValidator); this.saveValidators.addEntry('default', this.getKey('float', 'all'), floatValidator);
this.saveValidators.addEntry('default', 'int', intValidator); this.saveValidators.addEntry('default', this.getKey('int', 'all'), intValidator);
this.saveValidators.addEntry('default', 'phone', phoneValidator); this.saveValidators.addEntry('default', this.getKey('phone', 'all'), phoneValidator);
this.filterValidators.addEntry('default', 'date', dateValidator); this.filterValidators.addEntry('default', this.getKey('date', 'all'), dateValidator);
this.filterValidators.addEntry('default', 'datetime', datetimeValidator); this.filterValidators.addEntry('default', this.getKey('datetime', 'all'), datetimeValidator);
this.filterValidators.addEntry('default', 'float', floatValidator); this.filterValidators.addEntry('default', this.getKey('float', 'all'), floatValidator);
this.filterValidators.addEntry('default', 'currency', currencyValidator); this.filterValidators.addEntry('default', this.getKey('currency', 'all'), currencyValidator);
this.filterValidators.addEntry('default', 'int', intValidator); this.filterValidators.addEntry('default', this.getKey('int', 'all'), intValidator);
this.filterValidators.addEntry('default', 'phone', phoneValidator); this.filterValidators.addEntry('default', this.getKey('phone', 'all'), phoneValidator);
} }
public registerSaveValidator(module: string, key: string, validator: ValidatorInterface): void { public registerFieldSaveValidator(module: string, type: string, field: string, validator: ValidatorInterface): void {
this.filterValidators.addEntry(module, key, validator); this.saveValidators.addEntry(module, this.getKey(type, field), validator);
} }
public registerFilterValidator(module: string, key: string, validator: ValidatorInterface): void { public registerSaveValidator(module: string, type: string, validator: ValidatorInterface): void {
this.saveValidators.addEntry(module, key, validator); this.saveValidators.addEntry(module, this.getKey(type, 'all'), validator);
} }
public excludeSaveValidator(module: string, key: string): void { public registerFieldFilterValidator(module: string, type: string, field: string, validator: ValidatorInterface): void {
this.saveValidators.excludeEntry(module, key); this.filterValidators.addEntry(module, this.getKey(type, field), validator);
} }
public excludeFilterValidator(module: string, key: string): void { public registerFilterValidator(module: string, type: string, validator: ValidatorInterface): void {
this.filterValidators.excludeEntry(module, key); this.filterValidators.addEntry(module, this.getKey(type, 'all'), validator);
} }
public registerAsyncSaveValidator(module: string, key: string, validator: AsyncValidatorInterface): void { public excludeFieldSaveValidator(module: string, type: string, field: string): void {
this.asyncSaveValidators.addEntry(module, key, validator);
const moduleExclusions = this.saveFieldExclusions[module] || {};
const key = this.getKey(type, field);
moduleExclusions[key] = key;
this.saveFieldExclusions[module] = moduleExclusions;
} }
public excludeAsyncSaveValidator(module: string, key: string): void { public excludeSaveValidator(module: string, type: string): void {
this.saveValidators.excludeEntry(module, key); this.saveValidators.excludeEntry(module, this.getKey(type, 'all'));
}
public excludeFieldFilterValidator(module: string, type: string, field: string): void {
const moduleExclusions = this.filterFieldExclusion[module] || {};
const key = this.getKey(type, field);
moduleExclusions[key] = key;
this.filterFieldExclusion[module] = moduleExclusions;
}
public excludeFilterValidator(module: string, type: string): void {
this.filterValidators.excludeEntry(module, this.getKey(type, 'all'));
}
public registerAsyncSaveValidator(module: string, type: string, validator: AsyncValidatorInterface): void {
this.asyncSaveValidators.addEntry(module, this.getKey(type, 'all'), validator);
}
public excludeAsyncSaveValidator(module: string, type: string): void {
this.saveValidators.excludeEntry(module, this.getKey(type, 'all'));
} }
public getSaveValidations(module: string, viewField: ViewFieldDefinition, record: Record): ValidatorFn[] { public getSaveValidations(module: string, viewField: ViewFieldDefinition, record: Record): ValidatorFn[] {
const entries = this.saveValidators.getGroupEntries(module); const entries = this.saveValidators.getGroupEntries(module);
return this.filterValidations(entries, record, viewField); const exclusions = this.getExclusions(module, this.saveFieldExclusions);
return this.filterValidations(entries, exclusions, record, viewField);
} }
public getFilterValidations(module: string, viewField: ViewFieldDefinition, record: Record): ValidatorFn[] { public getFilterValidations(module: string, viewField: ViewFieldDefinition, record: Record): ValidatorFn[] {
const entries = this.filterValidators.getGroupEntries(module); const entries = this.filterValidators.getGroupEntries(module);
return this.filterValidations(entries, record, viewField); const exclusions = this.getExclusions(module, this.filterFieldExclusion);
return this.filterValidations(entries, exclusions, record, viewField);
} }
public getAsyncSaveValidations(module: string, viewField: ViewFieldDefinition, record: Record): AsyncValidatorFn[] { public getAsyncSaveValidations(module: string, viewField: ViewFieldDefinition, record: Record): AsyncValidatorFn[] {
@ -154,10 +183,34 @@ export class ValidationManager implements ValidationManagerInterface {
return validations; return validations;
} }
protected filterValidations(entries: MapEntry<ValidatorInterface>, record: Record, viewField: ViewFieldDefinition): ValidatorFn[] { public getKey(type: string, field: string): string {
return `${type}.${field}`;
}
protected parseType(key: string): string {
const partsType = key.split('.') || [];
return partsType[0] || '';
}
protected getExclusions(module: string, exclusionMap: StringMatrix): StringMap {
const defaultExclusions = exclusionMap['default'] || {};
const moduleExclusions = exclusionMap[module] || {};
return {...defaultExclusions, ...moduleExclusions};
}
protected filterValidations(
entries: MapEntry<ValidatorInterface>,
fieldExclusions: StringMap,
record: Record,
viewField: ViewFieldDefinition
): ValidatorFn[] {
let validations = []; let validations = [];
Object.keys(entries).forEach(validatorKey => { Object.keys(entries).forEach(validatorKey => {
const defaultTypeKey = this.getKey(this.parseType(validatorKey), viewField.name);
if (fieldExclusions[validatorKey] || fieldExclusions[defaultTypeKey]) {
return;
}
const validator = entries[validatorKey]; const validator = entries[validatorKey];