From aa3d89034242108e73cf87c4a106992baca789ab Mon Sep 17 00:00:00 2001 From: Cilibiu Bogdan Date: Fri, 24 Jul 2020 18:58:07 +0300 Subject: [PATCH] [ACA-3742] Metadata - constraints validation (#5908) * card min max value validator * card match value validator * card value length validator * map validators to constraint type * add minmax * update exported validators * register validators based on constraint type * translate errors with parameters * tests --- cspell.json | 3 +- .../card-view-textitem.component.html | 2 +- .../card-view-textitem.component.spec.ts | 5 ++- .../card-view-textitem.component.ts | 9 +++-- .../models/card-view-baseitem.model.spec.ts | 2 +- .../models/card-view-baseitem.model.ts | 13 ++++++- .../models/card-view-floatitem.model.spec.ts | 20 ++++++++++ .../models/card-view-intitem.model.spec.ts | 20 ++++++++++ .../models/card-view-textitem.model.spec.ts | 20 ++++++++++ .../card-view-item-length.valiator.ts | 34 +++++++++++++++++ .../card-view-item-match.valiator.ts | 34 +++++++++++++++++ .../card-view-item-minmax.valiator.ts | 37 +++++++++++++++++++ .../validators/card-view.validators.ts | 4 ++ .../card-view/validators/validators.map.ts | 27 ++++++++++++++ lib/core/i18n/en.json | 5 ++- 15 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 lib/core/card-view/validators/card-view-item-length.valiator.ts create mode 100644 lib/core/card-view/validators/card-view-item-match.valiator.ts create mode 100644 lib/core/card-view/validators/card-view-item-minmax.valiator.ts create mode 100644 lib/core/card-view/validators/validators.map.ts diff --git a/cspell.json b/cspell.json index 3acbbe0ee1..b484a24da7 100644 --- a/cspell.json +++ b/cspell.json @@ -129,7 +129,8 @@ "uploadfileform", "processwithstarteventform", "processstring", - "typeahed" + "typeahed", + "minmax" ], "dictionaries": [ "html", diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html index 8c476f1ceb..b14e0dec4e 100644 --- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html +++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html @@ -55,7 +55,7 @@ class="adf-textitem-editable-error" *ngIf="hasErrors"> diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts index c35e7c2495..4f55503887 100644 --- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts +++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts @@ -27,6 +27,7 @@ import { MatChipsModule } from '@angular/material/chips'; import { ClipboardService } from '../../../clipboard/clipboard.service'; import { DebugElement } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; +import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface'; describe('CardViewTextItemComponent', () => { @@ -441,7 +442,7 @@ describe('CardViewTextItemComponent', () => { }); it('should set the errorMessages properly if the editedValue is invalid', async () => { - const expectedErrorMessages = ['Something went wrong']; + const expectedErrorMessages = [{ message: 'Something went wrong' } as CardViewItemValidator]; component.property.isValid = () => false; component.property.getValidationErrors = () => expectedErrorMessages; @@ -450,7 +451,7 @@ describe('CardViewTextItemComponent', () => { await fixture.whenStable(); updateTextField(component.property.key, 'updated-value'); - expect(component.errorMessages).toBe(expectedErrorMessages); + expect(component.errors).toBe(expectedErrorMessages); }); it('should update the property value after a successful update attempt', async () => { diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts index 081148cc09..f77c018468 100644 --- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts +++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts @@ -22,6 +22,7 @@ import { BaseCardView } from '../base-card-view'; import { MatChipInputEvent } from '@angular/material/chips'; import { ClipboardService } from '../../../clipboard/clipboard.service'; import { TranslationService } from '../../../services/translation.service'; +import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface'; export const DEFAULT_SEPARATOR = ', '; const templateTypes = { @@ -55,7 +56,7 @@ export class CardViewTextItemComponent extends BaseCardView 0; + return (!!this.errors?.length) ?? false; } get isChipViewEnabled(): boolean { diff --git a/lib/core/card-view/models/card-view-baseitem.model.spec.ts b/lib/core/card-view/models/card-view-baseitem.model.spec.ts index f1b6f7daf3..39036f2fc9 100644 --- a/lib/core/card-view/models/card-view-baseitem.model.spec.ts +++ b/lib/core/card-view/models/card-view-baseitem.model.spec.ts @@ -91,7 +91,7 @@ describe('CardViewBaseItemModel', () => { const isValid = itemModel.isValid('test-against-this'); expect(isValid).toBe(false); - expect(itemModel.getValidationErrors('test-against-this')).toEqual(['validator 1', 'validator 3']); + expect(itemModel.getValidationErrors('test-against-this')).toEqual([validator1, validator3 ]); }); }); }); diff --git a/lib/core/card-view/models/card-view-baseitem.model.ts b/lib/core/card-view/models/card-view-baseitem.model.ts index 5c09e0a217..87eccf25f2 100644 --- a/lib/core/card-view/models/card-view-baseitem.model.ts +++ b/lib/core/card-view/models/card-view-baseitem.model.ts @@ -16,6 +16,7 @@ */ import { CardViewItemProperties, CardViewItemValidator } from '../interfaces/card-view.interfaces'; +import validatorsMap from '../validators/validators.map'; export abstract class CardViewBaseItemModel { label: string; @@ -38,6 +39,14 @@ export abstract class CardViewBaseItemModel { this.icon = cardViewItemProperties.icon || ''; this.validators = cardViewItemProperties.validators || []; this.data = cardViewItemProperties.data || null; + + if (cardViewItemProperties?.constraints?.length ?? 0) { + for (const constraint of cardViewItemProperties.constraints) { + if (constraint.type !== 'LIST') { + this.validators.push(validatorsMap[constraint.type.toLowerCase()](constraint.parameters)); + } + } + } } isEmpty(): boolean { @@ -54,11 +63,11 @@ export abstract class CardViewBaseItemModel { .reduce((isValidUntilNow, isValid) => isValidUntilNow && isValid, true); } - getValidationErrors(value): string[] { + getValidationErrors(value): CardViewItemValidator[] { if (!this.validators.length) { return []; } - return this.validators.filter((validator) => !validator.isValid(value)).map((validator) => validator.message); + return this.validators.filter((validator) => !validator.isValid(value)).map((validator) => validator); } } diff --git a/lib/core/card-view/models/card-view-floatitem.model.spec.ts b/lib/core/card-view/models/card-view-floatitem.model.spec.ts index 47fbe1acc2..b18a9534c4 100644 --- a/lib/core/card-view/models/card-view-floatitem.model.spec.ts +++ b/lib/core/card-view/models/card-view-floatitem.model.spec.ts @@ -56,4 +56,24 @@ describe('CardViewFloatItemModel', () => { expect(itemModel.isValid('42.3')).toBe(true, 'For "42.3" it should be true'); expect(itemModel.isValid('test')).toBe(false, 'For "test" it should be false'); }); + + it('should validate based on defined constraints', () => { + const constrainedProperties = { + label: 'Tribe', + value: '42.42', + key: 'tribe', + dataType: 'd:float', + constraints: [{ + id: 'constraint-id', + type: 'MINMAX', + parameters: { minValue: 10, maxValue: 40 } + }] + }; + + const itemModel = new CardViewFloatItemModel(constrainedProperties); + expect(itemModel.isValid(itemModel.value)).toBe(false, '42.42 is bigger than maximum allowed'); + + itemModel.value = '9.1'; + expect(itemModel.isValid(itemModel.value)).toBe(false, '9.1 is less than minimum allowed'); + }); }); diff --git a/lib/core/card-view/models/card-view-intitem.model.spec.ts b/lib/core/card-view/models/card-view-intitem.model.spec.ts index 1e92e5e965..e49eb4ae45 100644 --- a/lib/core/card-view/models/card-view-intitem.model.spec.ts +++ b/lib/core/card-view/models/card-view-intitem.model.spec.ts @@ -56,4 +56,24 @@ describe('CardViewIntItemModel', () => { expect(itemModel.isValid('42.3')).toBe(false, 'For "42.3" it should be false'); expect(itemModel.isValid('test')).toBe(false, 'For "test" it should be false'); }); + + it('should validate based on defined constraints', () => { + const constrainedProperties = { + label: 'Tribe', + value: '20', + key: 'tribe', + dataType: 'd:float', + constraints: [{ + id: 'constraint-id', + type: 'MINMAX', + parameters: { minValue: 10, maxValue: 15 } + }] + }; + + const itemModel = new CardViewIntItemModel(constrainedProperties); + expect(itemModel.isValid(itemModel.value)).toBe(false, '20 is bigger than maximum allowed'); + + itemModel.value = '5'; + expect(itemModel.isValid(itemModel.value)).toBe(false, '5 is less than minimum allowed'); + }); }); diff --git a/lib/core/card-view/models/card-view-textitem.model.spec.ts b/lib/core/card-view/models/card-view-textitem.model.spec.ts index a8126efc93..f3f97c20d8 100644 --- a/lib/core/card-view/models/card-view-textitem.model.spec.ts +++ b/lib/core/card-view/models/card-view-textitem.model.spec.ts @@ -85,4 +85,24 @@ describe('CardViewTextItemModel', () => { expect(itemModel.displayValue).toBe('testpiped-testpiped-testpiped-Banuk-1-2-3'); }); }); + + it('should validate based on defined constraints', () => { + const constrainedProperties = { + label: 'Tribe', + value: 'test', + key: 'tribe', + dataType: 'd:text', + constraints: [{ + id: 'constraint-id', + type: 'REGEX', + parameters: { expression: '^(?=.*test).*' } + }] + }; + + const itemModel = new CardViewTextItemModel(constrainedProperties); + expect(itemModel.isValid(itemModel.value)).toBe(true); + + itemModel.value = 'dummy'; + expect(itemModel.isValid(itemModel.value)).toBe(false, '`dummy` is not a constraint expression pattern'); + }); }); diff --git a/lib/core/card-view/validators/card-view-item-length.valiator.ts b/lib/core/card-view/validators/card-view-item-length.valiator.ts new file mode 100644 index 0000000000..a1f77d6f4c --- /dev/null +++ b/lib/core/card-view/validators/card-view-item-length.valiator.ts @@ -0,0 +1,34 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CardViewItemValidator } from '../interfaces/card-view.interfaces'; + +export interface LengthValidatorParams { + minLength: number; + maxLength: number; +} + +export class CardViewItemLengthValidator implements CardViewItemValidator { + message = 'CORE.CARDVIEW.VALIDATORS.LENGTH_VALIDATION_ERROR'; + + constructor(private minLength: number, private maxLength: number) {} + + isValid(value: string = ''): boolean { + const stringLength = value.length; + return stringLength >= this.minLength && stringLength <= this.maxLength; + } +} diff --git a/lib/core/card-view/validators/card-view-item-match.valiator.ts b/lib/core/card-view/validators/card-view-item-match.valiator.ts new file mode 100644 index 0000000000..7c148833a5 --- /dev/null +++ b/lib/core/card-view/validators/card-view-item-match.valiator.ts @@ -0,0 +1,34 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CardViewItemValidator } from '../interfaces/card-view.interfaces'; + +export interface MatchValidatorParams { + expression: string; + flags?: string; +} + +export class CardViewItemMatchValidator implements CardViewItemValidator { + message = 'CORE.CARDVIEW.VALIDATORS.MATCH_VALIDATION_ERROR'; + + constructor(private expression: string, private flags?: string) {} + + isValid(value: string): boolean { + const regex = new RegExp(this.expression, this.flags); + return value === '' || regex.test(value); + } +} diff --git a/lib/core/card-view/validators/card-view-item-minmax.valiator.ts b/lib/core/card-view/validators/card-view-item-minmax.valiator.ts new file mode 100644 index 0000000000..e3d63f08ba --- /dev/null +++ b/lib/core/card-view/validators/card-view-item-minmax.valiator.ts @@ -0,0 +1,37 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CardViewItemValidator } from '../interfaces/card-view.interfaces'; +import { CardViewItemIntValidator } from './card-view-item-int.validator'; + +export interface MinMaxValidatorParams { + minValue: number; + maxValue: number; +} + +export class CardViewItemMinMaxValidator implements CardViewItemValidator { + message = 'CORE.CARDVIEW.VALIDATORS.MINMAX_VALIDATION_ERROR'; + private intValidator: CardViewItemIntValidator; + + constructor(private minValue: number, private maxValue: number) { + this.intValidator = new CardViewItemIntValidator(); + } + + isValid(value: number): boolean { + return this.intValidator.isValid(value) && (value >= this.minValue && value <= this.maxValue); + } +} diff --git a/lib/core/card-view/validators/card-view.validators.ts b/lib/core/card-view/validators/card-view.validators.ts index 4b8e6d355f..b05b5e4999 100644 --- a/lib/core/card-view/validators/card-view.validators.ts +++ b/lib/core/card-view/validators/card-view.validators.ts @@ -17,3 +17,7 @@ export * from './card-view-item-int.validator'; export * from './card-view-item-float.validator'; +export * from './card-view-item-match.valiator'; +export * from './card-view-item-minmax.valiator'; +export * from './card-view-item-length.valiator'; +export * from './validators.map'; diff --git a/lib/core/card-view/validators/validators.map.ts b/lib/core/card-view/validators/validators.map.ts new file mode 100644 index 0000000000..a916d53da2 --- /dev/null +++ b/lib/core/card-view/validators/validators.map.ts @@ -0,0 +1,27 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CardViewItemMatchValidator, MatchValidatorParams } from './card-view-item-match.valiator'; +import { CardViewItemMinMaxValidator, MinMaxValidatorParams } from './card-view-item-minmax.valiator'; +import { CardViewItemLengthValidator, LengthValidatorParams } from './card-view-item-length.valiator'; + +const validators = { + minmax: (parameters: MinMaxValidatorParams) => new CardViewItemMinMaxValidator(parameters.minValue, parameters.maxValue), + regex: (parameters: MatchValidatorParams) => new CardViewItemMatchValidator(parameters.expression), + length: (parameters: LengthValidatorParams) => new CardViewItemLengthValidator(parameters.minLength, parameters.maxLength) +}; +export default validators; diff --git a/lib/core/i18n/en.json b/lib/core/i18n/en.json index da509c57a9..34745a1ec6 100644 --- a/lib/core/i18n/en.json +++ b/lib/core/i18n/en.json @@ -175,7 +175,10 @@ "NONE": "None", "VALIDATORS": { "FLOAT_VALIDATION_ERROR": "Use a number format", - "INT_VALIDATION_ERROR": "Use an integer format" + "INT_VALIDATION_ERROR": "Use an integer format", + "LENGTH_VALIDATION_ERROR": "Value should be minimum {{ minLength }} and maximum {{ maxLength }} in length", + "MATCH_VALIDATION_ERROR": "Value doesn't match pattern: {{ expression }}", + "MINMAX_VALIDATION_ERROR": "Value should be between minimum {{ minValue }} and maximum {{ maxValue }}" }, "MORE": "More" },