mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[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
This commit is contained in:
@@ -129,7 +129,8 @@
|
|||||||
"uploadfileform",
|
"uploadfileform",
|
||||||
"processwithstarteventform",
|
"processwithstarteventform",
|
||||||
"processstring",
|
"processstring",
|
||||||
"typeahed"
|
"typeahed",
|
||||||
|
"minmax"
|
||||||
],
|
],
|
||||||
"dictionaries": [
|
"dictionaries": [
|
||||||
"html",
|
"html",
|
||||||
|
@@ -55,7 +55,7 @@
|
|||||||
class="adf-textitem-editable-error"
|
class="adf-textitem-editable-error"
|
||||||
*ngIf="hasErrors">
|
*ngIf="hasErrors">
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let errorMessage of errorMessages">{{ errorMessage | translate }}</li>
|
<li *ngFor="let error of errors">{{ error.message | translate: error }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
|
||||||
|
@@ -27,6 +27,7 @@ import { MatChipsModule } from '@angular/material/chips';
|
|||||||
import { ClipboardService } from '../../../clipboard/clipboard.service';
|
import { ClipboardService } from '../../../clipboard/clipboard.service';
|
||||||
import { DebugElement } from '@angular/core';
|
import { DebugElement } from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface';
|
||||||
|
|
||||||
describe('CardViewTextItemComponent', () => {
|
describe('CardViewTextItemComponent', () => {
|
||||||
|
|
||||||
@@ -441,7 +442,7 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should set the errorMessages properly if the editedValue is invalid', async () => {
|
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.isValid = () => false;
|
||||||
component.property.getValidationErrors = () => expectedErrorMessages;
|
component.property.getValidationErrors = () => expectedErrorMessages;
|
||||||
|
|
||||||
@@ -450,7 +451,7 @@ describe('CardViewTextItemComponent', () => {
|
|||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
updateTextField(component.property.key, 'updated-value');
|
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 () => {
|
it('should update the property value after a successful update attempt', async () => {
|
||||||
|
@@ -22,6 +22,7 @@ import { BaseCardView } from '../base-card-view';
|
|||||||
import { MatChipInputEvent } from '@angular/material/chips';
|
import { MatChipInputEvent } from '@angular/material/chips';
|
||||||
import { ClipboardService } from '../../../clipboard/clipboard.service';
|
import { ClipboardService } from '../../../clipboard/clipboard.service';
|
||||||
import { TranslationService } from '../../../services/translation.service';
|
import { TranslationService } from '../../../services/translation.service';
|
||||||
|
import { CardViewItemValidator } from '../../interfaces/card-view-item-validator.interface';
|
||||||
|
|
||||||
export const DEFAULT_SEPARATOR = ', ';
|
export const DEFAULT_SEPARATOR = ', ';
|
||||||
const templateTypes = {
|
const templateTypes = {
|
||||||
@@ -55,7 +56,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
multiValueSeparator: string = DEFAULT_SEPARATOR;
|
multiValueSeparator: string = DEFAULT_SEPARATOR;
|
||||||
|
|
||||||
editedValue: string | string[];
|
editedValue: string | string[];
|
||||||
errorMessages: string[];
|
errors: CardViewItemValidator[];
|
||||||
templateType: string;
|
templateType: string;
|
||||||
|
|
||||||
constructor(cardViewUpdateService: CardViewUpdateService,
|
constructor(cardViewUpdateService: CardViewUpdateService,
|
||||||
@@ -92,7 +93,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
private resetErrorMessages() {
|
private resetErrorMessages() {
|
||||||
this.errorMessages = [];
|
this.errors = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
update(): void {
|
update(): void {
|
||||||
@@ -102,7 +103,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
this.property.value = updatedValue;
|
this.property.value = updatedValue;
|
||||||
this.resetErrorMessages();
|
this.resetErrorMessages();
|
||||||
} else {
|
} else {
|
||||||
this.errorMessages = this.property.getValidationErrors(this.editedValue);
|
this.errors = this.property.getValidationErrors(this.editedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +177,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
get hasErrors(): boolean {
|
get hasErrors(): boolean {
|
||||||
return this.errorMessages && this.errorMessages.length > 0;
|
return (!!this.errors?.length) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isChipViewEnabled(): boolean {
|
get isChipViewEnabled(): boolean {
|
||||||
|
@@ -91,7 +91,7 @@ describe('CardViewBaseItemModel', () => {
|
|||||||
const isValid = itemModel.isValid('test-against-this');
|
const isValid = itemModel.isValid('test-against-this');
|
||||||
|
|
||||||
expect(isValid).toBe(false);
|
expect(isValid).toBe(false);
|
||||||
expect(itemModel.getValidationErrors('test-against-this')).toEqual(['validator 1', 'validator 3']);
|
expect(itemModel.getValidationErrors('test-against-this')).toEqual([validator1, validator3 ]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CardViewItemProperties, CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
import { CardViewItemProperties, CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||||
|
import validatorsMap from '../validators/validators.map';
|
||||||
|
|
||||||
export abstract class CardViewBaseItemModel {
|
export abstract class CardViewBaseItemModel {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -38,6 +39,14 @@ export abstract class CardViewBaseItemModel {
|
|||||||
this.icon = cardViewItemProperties.icon || '';
|
this.icon = cardViewItemProperties.icon || '';
|
||||||
this.validators = cardViewItemProperties.validators || [];
|
this.validators = cardViewItemProperties.validators || [];
|
||||||
this.data = cardViewItemProperties.data || null;
|
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 {
|
isEmpty(): boolean {
|
||||||
@@ -54,11 +63,11 @@ export abstract class CardViewBaseItemModel {
|
|||||||
.reduce((isValidUntilNow, isValid) => isValidUntilNow && isValid, true);
|
.reduce((isValidUntilNow, isValid) => isValidUntilNow && isValid, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getValidationErrors(value): string[] {
|
getValidationErrors(value): CardViewItemValidator[] {
|
||||||
if (!this.validators.length) {
|
if (!this.validators.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.validators.filter((validator) => !validator.isValid(value)).map((validator) => validator.message);
|
return this.validators.filter((validator) => !validator.isValid(value)).map((validator) => validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -56,4 +56,24 @@ describe('CardViewFloatItemModel', () => {
|
|||||||
expect(itemModel.isValid('42.3')).toBe(true, 'For "42.3" it should be true');
|
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');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -56,4 +56,24 @@ describe('CardViewIntItemModel', () => {
|
|||||||
expect(itemModel.isValid('42.3')).toBe(false, 'For "42.3" it should be false');
|
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');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -85,4 +85,24 @@ describe('CardViewTextItemModel', () => {
|
|||||||
expect(itemModel.displayValue).toBe('testpiped-testpiped-testpiped-Banuk-1-2-3');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
@@ -17,3 +17,7 @@
|
|||||||
|
|
||||||
export * from './card-view-item-int.validator';
|
export * from './card-view-item-int.validator';
|
||||||
export * from './card-view-item-float.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';
|
||||||
|
27
lib/core/card-view/validators/validators.map.ts
Normal file
27
lib/core/card-view/validators/validators.map.ts
Normal file
@@ -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;
|
@@ -175,7 +175,10 @@
|
|||||||
"NONE": "None",
|
"NONE": "None",
|
||||||
"VALIDATORS": {
|
"VALIDATORS": {
|
||||||
"FLOAT_VALIDATION_ERROR": "Use a number format",
|
"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"
|
"MORE": "More"
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user