Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
c87d27f
fix: decorating inherited property #633
NickKelly1 Sep 7, 2020
e4bc6ff
run prettier
NickKelly1 Sep 7, 2020
166f530
fix inherited property validator overrides
NickKelly1 Sep 8, 2020
2f7b814
update readme
NickKelly1 Sep 8, 2020
3e0b95f
build(deps-dev): bump rollup from 2.60.0 to 2.60.1 (#1415)
dependabot[bot] Nov 22, 2021
cbcc0bb
build(deps-dev): bump lint-staged from 12.0.3 to 12.1.2 (#1416)
dependabot[bot] Nov 22, 2021
81f93d0
build(deps-dev): bump eslint-plugin-jest from 25.2.4 to 25.3.0 (#1419)
dependabot[bot] Nov 23, 2021
3d392a7
build(deps-dev): bump @types/node from 16.11.9 to 16.11.10 (#1420)
dependabot[bot] Nov 24, 2021
7adbc06
build(deps-dev): bump prettier from 2.4.1 to 2.5.0 (#1425)
dependabot[bot] Nov 26, 2021
701f9b9
build(deps-dev): bump rollup from 2.60.1 to 2.60.2 (#1429)
dependabot[bot] Nov 30, 2021
699c906
build(deps-dev): bump @types/node from 16.11.10 to 16.11.11 (#1430)
dependabot[bot] Nov 30, 2021
3552c4c
build(deps): bump libphonenumber-js from 1.9.43 to 1.9.44 (#1434)
dependabot[bot] Dec 3, 2021
a35537a
build(deps-dev): bump prettier from 2.5.0 to 2.5.1 (#1436)
dependabot[bot] Dec 6, 2021
b546ca5
build(deps-dev): bump @types/node from 16.11.11 to 16.11.12 (#1437)
dependabot[bot] Dec 7, 2021
071fb68
build(deps-dev): bump rollup from 2.60.2 to 2.61.0 (#1442)
dependabot[bot] Dec 9, 2021
645bc8d
build(deps-dev): bump typescript from 4.5.2 to 4.5.3 (#1446)
dependabot[bot] Dec 10, 2021
aad3fea
build(deps-dev): bump rollup from 2.61.0 to 2.61.1 (#1447)
dependabot[bot] Dec 13, 2021
e38968a
build(deps-dev): bump typescript from 4.5.3 to 4.5.4 (#1448)
dependabot[bot] Dec 14, 2021
e238714
build(deps-dev): bump @rollup/plugin-node-resolve from 13.0.6 to 13.1…
dependabot[bot] Dec 14, 2021
392eaa6
build(deps-dev): bump @types/node from 16.11.12 to 16.11.13 (#1452)
dependabot[bot] Dec 15, 2021
57cff6d
build(deps-dev): bump @types/node from 16.11.13 to 17.0.0 (#1453)
dependabot[bot] Dec 16, 2021
f560b16
build(deps-dev): bump @types/node from 17.0.0 to 17.0.1 (#1456)
dependabot[bot] Dec 20, 2021
4e39a04
build(deps-dev): bump lint-staged from 12.1.2 to 12.1.3 (#1458)
dependabot[bot] Dec 20, 2021
f3b1daa
build(deps-dev): bump @types/node from 17.0.1 to 17.0.2 (#1459)
dependabot[bot] Dec 21, 2021
d8835a9
build(deps-dev): bump @types/node from 17.0.2 to 17.0.4 (#1461)
dependabot[bot] Dec 24, 2021
89280f7
build(deps-dev): bump rollup from 2.61.1 to 2.62.0 (#1462)
dependabot[bot] Dec 24, 2021
d9ab052
build(deps-dev): bump @types/validator from 13.7.0 to 13.7.1 (#1463)
dependabot[bot] Dec 24, 2021
6b126e3
build(deps-dev): bump @types/node from 17.0.4 to 17.0.5 (#1466)
dependabot[bot] Dec 27, 2021
03e0f4d
build(deps-dev): bump lint-staged from 12.1.3 to 12.1.4 (#1465)
dependabot[bot] Dec 27, 2021
e34028a
build(deps-dev): bump eslint-plugin-jest from 25.3.0 to 25.3.2 (#1468)
dependabot[bot] Dec 28, 2021
4d93bde
build(deps-dev): bump eslint-plugin-jest from 25.3.2 to 25.3.3 (#1470)
dependabot[bot] Dec 31, 2021
7123007
build(deps-dev): bump @types/jest from 27.0.3 to 27.4.0 (#1472)
dependabot[bot] Dec 31, 2021
a7fe601
build(deps-dev): bump @rollup/plugin-node-resolve from 13.1.1 to 13.1…
dependabot[bot] Dec 31, 2021
d59502f
build(deps-dev): bump @types/node from 17.0.5 to 17.0.8 (#1481)
dependabot[bot] Jan 5, 2022
87b0701
build(deps-dev): bump rollup from 2.62.0 to 2.63.0 (#1483)
dependabot[bot] Jan 5, 2022
57b8182
build(deps-dev): bump lint-staged from 12.1.4 to 12.1.5 (#1476)
dependabot[bot] Jan 5, 2022
79558ad
build(deps-dev): bump eslint-plugin-jest from 25.3.3 to 25.3.4 (#1479)
dependabot[bot] Jan 5, 2022
fe1546d
build(deps-dev): bump @rollup/plugin-node-resolve from 13.1.2 to 13.1…
dependabot[bot] Jan 6, 2022
2c0fba0
build(deps-dev): bump lint-staged from 12.1.5 to 12.1.7 (#1488)
dependabot[bot] Jan 10, 2022
3925674
build(deps-dev): bump rollup from 2.63.0 to 2.64.0 (#1493)
dependabot[bot] Jan 17, 2022
1dfdd4c
build(deps-dev): bump @types/node from 17.0.8 to 17.0.9 (#1494)
dependabot[bot] Jan 17, 2022
8753936
build(deps-dev): bump eslint-plugin-jest from 25.3.4 to 25.7.0 (#1495)
dependabot[bot] Jan 17, 2022
9fe272f
build(deps-dev): bump @types/node from 17.0.9 to 17.0.10 (#1498)
dependabot[bot] Jan 19, 2022
2c68316
build(deps-dev): bump lint-staged from 12.1.7 to 12.2.0 (#1499)
dependabot[bot] Jan 19, 2022
ce1a782
build(deps-dev): bump lint-staged from 12.2.0 to 12.2.1 (#1501)
dependabot[bot] Jan 20, 2022
5fb4911
build(deps-dev): bump lint-staged from 12.2.1 to 12.2.2 (#1504)
dependabot[bot] Jan 21, 2022
d54b7b8
build(deps-dev): bump typescript from 4.5.4 to 4.5.5 (#1505)
dependabot[bot] Jan 21, 2022
347d49b
build(deps-dev): bump rollup from 2.64.0 to 2.65.0 (#1506)
dependabot[bot] Jan 21, 2022
294c498
build(deps-dev): bump rollup from 2.65.0 to 2.66.0 (#1507)
dependabot[bot] Jan 24, 2022
4fd13ae
build(deps-dev): bump lint-staged from 12.2.2 to 12.3.1 (#1508)
dependabot[bot] Jan 24, 2022
5aaa3f0
build(deps-dev): bump rollup from 2.66.0 to 2.66.1 (#1511)
dependabot[bot] Jan 25, 2022
f41d107
build(deps-dev): bump @types/node from 17.0.10 to 17.0.12 (#1512)
dependabot[bot] Jan 25, 2022
f375a1a
build(deps-dev): bump eslint-plugin-jest from 25.7.0 to 26.0.0 (#1513)
dependabot[bot] Jan 25, 2022
e762627
build(deps-dev): bump lint-staged from 12.3.1 to 12.3.2 (#1515)
dependabot[bot] Jan 27, 2022
d8982d2
build(deps): bump libphonenumber-js from 1.9.44 to 1.9.46 (#1516)
dependabot[bot] Jan 27, 2022
75e135c
build(deps-dev): bump @types/node from 17.0.12 to 17.0.13 (#1518)
dependabot[bot] Jan 28, 2022
ad877db
build(deps-dev): bump @types/node from 17.0.13 to 17.0.14 (#1525)
dependabot[bot] Feb 1, 2022
8212872
build(deps-dev): bump rollup from 2.66.1 to 2.67.0 (#1526)
dependabot[bot] Feb 2, 2022
1f0028e
build(deps): bump libphonenumber-js from 1.9.46 to 1.9.47 (#1528)
dependabot[bot] Feb 2, 2022
e0ec613
build(deps-dev): bump lint-staged from 12.3.2 to 12.3.3 (#1527)
dependabot[bot] Feb 2, 2022
a5e33c4
build(deps-dev): bump @types/node from 17.0.14 to 17.0.15 (#1530)
dependabot[bot] Feb 7, 2022
e5b3e1b
build(deps-dev): bump rollup from 2.67.0 to 2.67.1 (#1534)
dependabot[bot] Feb 7, 2022
14e3955
build(deps): bump libphonenumber-js from 1.9.47 to 1.9.48 (#1531)
dependabot[bot] Feb 7, 2022
42255c8
build(deps-dev): bump eslint-plugin-jest from 26.0.0 to 26.1.0 (#1532)
dependabot[bot] Feb 7, 2022
56b1c24
build(deps-dev): bump ts-node from 10.4.0 to 10.5.0 (#1536)
dependabot[bot] Feb 8, 2022
dfaae24
build(deps-dev): bump @types/node from 17.0.15 to 17.0.16 (#1537)
dependabot[bot] Feb 8, 2022
1c51579
build(deps-dev): bump @types/node from 17.0.16 to 17.0.17 (#1541)
dependabot[bot] Feb 10, 2022
4017036
build(deps-dev): bump rollup from 2.67.1 to 2.67.2 (#1542)
dependabot[bot] Feb 10, 2022
9a0c7dc
build(deps): bump libphonenumber-js from 1.9.48 to 1.9.49 (#1544)
dependabot[bot] Feb 11, 2022
907bc22
build(deps-dev): bump lint-staged from 12.3.3 to 12.3.4 (#1548)
dependabot[bot] Feb 14, 2022
bf0632b
build(deps-dev): bump @types/node from 17.0.17 to 17.0.18 (#1552)
dependabot[bot] Feb 15, 2022
b4796a8
build(deps-dev): bump eslint-plugin-jest from 26.1.0 to 26.1.1 (#1553)
dependabot[bot] Feb 16, 2022
91f8f07
build(deps-dev): bump rollup from 2.67.2 to 2.67.3 (#1556)
dependabot[bot] Feb 18, 2022
29cfc20
build(deps-dev): bump eslint-config-prettier from 8.3.0 to 8.4.0 (#1557)
dependabot[bot] Feb 21, 2022
6930eca
build(deps-dev): bump rollup from 2.67.3 to 2.68.0 (#1560)
dependabot[bot] Feb 22, 2022
597786c
build(deps-dev): bump @types/node from 17.0.18 to 17.0.19 (#1561)
dependabot[bot] Feb 22, 2022
651b723
build(deps-dev): bump @types/jest from 27.4.0 to 27.4.1 (#1563)
dependabot[bot] Feb 23, 2022
f2bc6aa
build(deps-dev): bump @types/node from 17.0.19 to 17.0.21 (#1564)
dependabot[bot] Feb 24, 2022
142af75
build(deps-dev): bump @rollup/plugin-commonjs from 21.0.1 to 21.0.2 (…
dependabot[bot] Feb 24, 2022
e693778
build(deps-dev): bump typescript from 4.5.5 to 4.6.2 (#1570)
dependabot[bot] Mar 1, 2022
89c4a9e
build(deps-dev): bump ts-node from 10.5.0 to 10.6.0 (#1571)
dependabot[bot] Mar 2, 2022
44c07f9
build(deps-dev): bump rollup from 2.68.0 to 2.69.0 (#1572)
dependabot[bot] Mar 3, 2022
5aeca12
build(deps-dev): bump eslint-config-prettier from 8.4.0 to 8.5.0 (#1573)
dependabot[bot] Mar 3, 2022
176fb90
build(deps-dev): bump rollup from 2.69.0 to 2.70.0 (#1577)
dependabot[bot] Mar 7, 2022
d721eb7
build(deps-dev): bump lint-staged from 12.3.4 to 12.3.5 (#1576)
dependabot[bot] Mar 7, 2022
77ef375
build(deps-dev): bump ts-node from 10.6.0 to 10.7.0 (#1578)
dependabot[bot] Mar 7, 2022
c72d1be
build(deps-dev): bump rollup from 2.70.0 to 2.70.1 (#1582)
dependabot[bot] Mar 14, 2022
4d71476
build(deps-dev): bump lint-staged from 12.3.5 to 12.3.6 (#1584)
dependabot[bot] Mar 16, 2022
7d8bd5f
build(deps-dev): bump prettier from 2.5.1 to 2.6.0 (#1585)
dependabot[bot] Mar 16, 2022
303edfd
build(deps): bump libphonenumber-js from 1.9.49 to 1.9.50 (#1589)
dependabot[bot] Mar 18, 2022
fd67e22
build(deps-dev): bump lint-staged from 12.3.6 to 12.3.7 (#1590)
dependabot[bot] Mar 18, 2022
397473b
build(deps-dev): bump eslint-plugin-jest from 26.1.1 to 26.1.2 (#1591)
dependabot[bot] Mar 21, 2022
363a314
build(deps-dev): bump @types/node from 17.0.21 to 17.0.22 (#1592)
dependabot[bot] Mar 22, 2022
4b0114d
build(deps-dev): bump @types/node from 17.0.22 to 17.0.23 (#1594)
dependabot[bot] Mar 24, 2022
dc20354
build(deps): bump minimist from 1.2.5 to 1.2.6 (#1595)
dependabot[bot] Mar 24, 2022
0262804
build(deps-dev): bump typescript from 4.6.2 to 4.6.3 (#1598)
dependabot[bot] Mar 25, 2022
e993a33
build(deps-dev): bump prettier from 2.6.0 to 2.6.1 (#1599)
dependabot[bot] Mar 25, 2022
7098bf0
build(deps-dev): bump eslint-plugin-jest from 26.1.2 to 26.1.3 (#1600)
dependabot[bot] Mar 25, 2022
8927baa
build(deps-dev): bump @types/validator from 13.7.1 to 13.7.2 (#1602)
dependabot[bot] Mar 28, 2022
b00838c
build(deps-dev): bump @rollup/plugin-commonjs from 21.0.2 to 21.0.3 (…
dependabot[bot] Mar 28, 2022
ba43ec3
build(deps-dev): bump prettier from 2.6.1 to 2.6.2 (#1607)
dependabot[bot] Apr 4, 2022
7befb18
build(deps-dev): bump eslint-plugin-jest from 26.1.3 to 26.1.4 (#1613)
dependabot[bot] Apr 8, 2022
10eb3b2
build(deps): bump libphonenumber-js from 1.9.50 to 1.9.51 (#1615)
dependabot[bot] Apr 11, 2022
1dd1012
build(deps-dev): bump @rollup/plugin-node-resolve from 13.1.3 to 13.2…
dependabot[bot] Apr 12, 2022
2ef8ff0
build(deps-dev): bump @types/node from 17.0.23 to 17.0.24 (#1619)
dependabot[bot] Apr 14, 2022
890aa24
Merge branch 'develop' into inherited-validation
TimVanMourik Sep 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ export class Post {

## Inheriting Validation decorators

When you define a subclass which extends from another one, the subclass will automatically inherit the parent's decorators. If a property is redefined in the descendant class decorators will be applied on it both from that and the base class.
When you define a subclass which extends from another one, the subclass will automatically inherit the parent's decorators. If a property is redefined in the descendant class decorators will be applied on it both from that and the base class. If a property-decorator pair is defined in both the sub-class and parent-class, the decorator from the sub-class will be used instead of the parent-class.

```typescript
import { validate } from 'class-validator';
Expand Down Expand Up @@ -726,6 +726,7 @@ Lets create another custom validation decorator called `IsUserAlreadyExist`:
export function IsUserAlreadyExist(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'IsUserAlreadyExist',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
Expand Down
705 changes: 375 additions & 330 deletions package-lock.json

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,30 @@
"test:ci": "jest --runInBand --no-cache --coverage --verbose"
},
"dependencies": {
"libphonenumber-js": "^1.9.43",
"libphonenumber-js": "^1.9.51",
"validator": "^13.7.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.0.6",
"@types/jest": "^27.0.3",
"@types/node": "^16.11.9",
"@types/validator": "^13.7.0",
"@rollup/plugin-commonjs": "^21.0.3",
"@rollup/plugin-node-resolve": "^13.2.0",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.24",
"@types/validator": "^13.7.2",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-jest": "^25.2.4",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jest": "^26.1.4",
"husky": "^4.3.8",
"jest": "^26.6.3",
"lint-staged": "^12.0.3",
"prettier": "^2.2.1",
"lint-staged": "^12.3.7",
"prettier": "^2.6.2",
"reflect-metadata": "0.1.13",
"rimraf": "3.0.2",
"rollup": "^2.60.0",
"rollup": "^2.70.1",
"rollup-plugin-terser": "^7.0.2",
"ts-jest": "^26.5.6",
"ts-node": "^10.4.0",
"typescript": "^4.2.4"
"ts-node": "^10.7.0",
"typescript": "^4.6.3"
}
}
1 change: 1 addition & 0 deletions sample/sample6-custom-decorator/IsLongerThan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ValidationArguments } from '../../src/validation/ValidationArguments';
export function IsLongerThan(property: string, validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'IsLongerThan',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
Expand Down
1 change: 1 addition & 0 deletions src/decorator/common/Allow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getMetadataStorage } from '../../metadata/MetadataStorage';
export function Allow(validationOptions?: ValidationOptions): PropertyDecorator {
return function (object: object, propertyName: string): void {
const args: ValidationMetadataArgs = {
name: 'Allow',
type: ValidationTypes.WHITELIST,
target: object.constructor,
propertyName: propertyName,
Expand Down
1 change: 1 addition & 0 deletions src/decorator/common/IsOptional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getMetadataStorage } from '../../metadata/MetadataStorage';
export function IsOptional(validationOptions?: ValidationOptions): PropertyDecorator {
return function (object: object, propertyName: string): void {
const args: ValidationMetadataArgs = {
name: 'IsOptional',
type: ValidationTypes.CONDITIONAL_VALIDATION,
target: object.constructor,
propertyName: propertyName,
Expand Down
4 changes: 4 additions & 0 deletions src/decorator/common/Validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export function ValidatorConstraint(options?: { name?: string; async?: boolean }
/**
* Performs validation based on the given custom validation class.
* Validation class must be decorated with ValidatorConstraint decorator.
*
* TODO: allow passing in a `name` so the validator instance created can be uniquely identified
* until then, this validator will be overwritten by properties decorated with `Validate` on subclasses
*/
export function Validate(constraintClass: Function, validationOptions?: ValidationOptions): PropertyDecorator;
export function Validate(
Expand All @@ -40,6 +43,7 @@ export function Validate(
): PropertyDecorator {
return function (object: object, propertyName: string): void {
const args: ValidationMetadataArgs = {
name: 'Validate',
type: ValidationTypes.CUSTOM_VALIDATION,
target: object.constructor,
propertyName: propertyName,
Expand Down
4 changes: 4 additions & 0 deletions src/decorator/common/ValidateIf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { getMetadataStorage } from '../../metadata/MetadataStorage';

/**
* Ignores the other validators on a property when the provided condition function returns false.
*
* TODO: allow passing in a `name` so the validator instance created can be uniquely identified
* until then, this validator will be overwritten by properties decorated with `Validate` on subclasses
*/
export function ValidateIf(
condition: (object: any, value: any) => boolean,
validationOptions?: ValidationOptions
): PropertyDecorator {
return function (object: object, propertyName: string): void {
const args: ValidationMetadataArgs = {
name: 'ValidateIf',
type: ValidationTypes.CONDITIONAL_VALIDATION,
target: object.constructor,
propertyName: propertyName,
Expand Down
1 change: 1 addition & 0 deletions src/decorator/common/ValidateNested.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export function ValidateNested(validationOptions?: ValidationOptions): PropertyD

return function (object: object, propertyName: string): void {
const args: ValidationMetadataArgs = {
name: 'ValidateNested',
type: ValidationTypes.NESTED_VALIDATION,
target: object.constructor,
propertyName: propertyName,
Expand Down
1 change: 1 addition & 0 deletions src/decorator/common/ValidatePromise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getMetadataStorage } from '../../metadata/MetadataStorage';
export function ValidatePromise(validationOptions?: ValidationOptions): PropertyDecorator {
return function (object: object, propertyName: string): void {
const args: ValidationMetadataArgs = {
name: 'ValidatePromise',
type: ValidationTypes.PROMISE_VALIDATION,
target: object.constructor,
propertyName: propertyName,
Expand Down
23 changes: 19 additions & 4 deletions src/metadata/MetadataStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,25 @@ export class MetadataStorage {
// filter out duplicate metadatas, prefer original metadatas instead of inherited metadatas
const uniqueInheritedMetadatas = inheritedMetadatas.filter(inheritedMetadata => {
return !originalMetadatas.find(originalMetadata => {
return (
originalMetadata.propertyName === inheritedMetadata.propertyName &&
originalMetadata.type === inheritedMetadata.type
);
// We have no clean way to determine if 2 validators are the same, and thus can't easily determine
// which validators have been overwritten by a subclass
// - Can't use `validatorCls` object/function: it's recreated on a per-usage basis so two decorators will give different instances
// - Can't use `ValidationTypes`: this was useable until 11a7b8bb59c83d55bc723ebb236fdca912f49d88,
// after which 90% of ValidationTypes were removed in favour of type "customValidation". Note that
// some validators, including any custom validators, still had type "customValidation" before this, and therefore
// did not work with inherited validation
// - `name`: can be used to uniquely identify a validator, but is optional to not break backwards compatability
// in a future release, it should be made required
const isSameProperty = originalMetadata.propertyName === inheritedMetadata.propertyName;
const isSameValidator =
originalMetadata.name && inheritedMetadata.name
? // TODO: when names becomes required, ONLY compare by name
originalMetadata.name === inheritedMetadata.name
: // 95% of decorators are of type "customValidation", despite being different decorators
// therefore this equality comparison introduces lots of false positives
originalMetadata.type === inheritedMetadata.type;

return isSameProperty && isSameValidator;
});
});

Expand Down
6 changes: 6 additions & 0 deletions src/metadata/ValidationMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export class ValidationMetadata {
// Properties
// -------------------------------------------------------------------------

/**
* Validation name. Used to uniquely identify this validator.
*/
name?: string;

/**
* Validation type.
*/
Expand Down Expand Up @@ -69,6 +74,7 @@ export class ValidationMetadata {
// -------------------------------------------------------------------------

constructor(args: ValidationMetadataArgs) {
this.name = args.name;
this.type = args.type;
this.target = args.target;
this.propertyName = args.propertyName;
Expand Down
5 changes: 5 additions & 0 deletions src/metadata/ValidationMetadataArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { ValidationOptions } from '../decorator/ValidationOptions';
* Constructor arguments for ValidationMetadata class.
*/
export interface ValidationMetadataArgs {
/**
* Validation name. Used to uniquely identify this validator.
*/
name?: string;

/**
* Validation type.
*/
Expand Down
1 change: 1 addition & 0 deletions src/register-decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function registerDecorator(options: ValidationDecoratorOptions): void {
}

const validationMetadataArgs: ValidationMetadataArgs = {
name: options.name,
type: options.name && ValidationTypes.isValid(options.name) ? options.name : ValidationTypes.CUSTOM_VALIDATION,
target: options.target,
propertyName: options.propertyName,
Expand Down
5 changes: 5 additions & 0 deletions src/validation-schema/ValidationSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export interface ValidationSchema {
* Name of the object's property to be validated which holds an array of validation constraints.
*/
[propertyName: string]: {
/**
* Validation name. Used to uniquely identify this validator.
*/
name?: string;

/**
* Validation type. Should be one of the ValidationTypes value.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class ValidationSchemaToMetadataTransformer {
each: validation.each,
};
const args: ValidationMetadataArgs = {
name: validation.name,
type: validation.type,
target: schema.name,
propertyName: property,
Expand Down
5 changes: 3 additions & 2 deletions test/functional/custom-decorators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ describe('decorator with inline validation', () => {
function IsLongerThan(property: string, validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string): void {
registerDecorator({
name: 'isLongerThan',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [property],
name: 'isLongerThan',
validator: {
validate(value: any, args: ValidationArguments): Promise<boolean> | boolean {
const [relatedPropertyName] = args.constraints;
Expand Down Expand Up @@ -109,11 +109,11 @@ describe('decorator with default message', () => {
function IsLonger(property: string, validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string): void {
registerDecorator({
name: 'isLonger',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [property],
name: 'isLonger',
validator: {
validate(value: any, args: ValidationArguments): boolean {
const [relatedPropertyName] = args.constraints;
Expand Down Expand Up @@ -183,6 +183,7 @@ describe('decorator with separate validation constraint class', () => {
function IsShorterThan(property: string, validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string): void {
registerDecorator({
name: 'IsShorterThan',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
Expand Down
129 changes: 128 additions & 1 deletion test/functional/inherited-validation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Contains, MinLength } from '../../src/decorator/decorators';
import { Contains, MinLength, Equals, Min } from '../../src/decorator/decorators';
import { Validator } from '../../src/validation/Validator';

const validator = new Validator();
Expand Down Expand Up @@ -34,4 +34,131 @@ describe('inherited validation', () => {
expect(errors[1].value).toEqual('helo world');
});
});

it('should use validators from parent and child classes', () => {
expect.assertions(5);

class MyClass {
@Contains('hello')
title: string;
}

class MySubClass extends MyClass {
@MinLength(5)
title: string;
}

const model = new MySubClass();
model.title = 'helo';
return validator.validate(model).then(errors => {
expect(errors.length).toEqual(1);
expect(errors[0].target).toEqual(model);
expect(errors[0].property).toEqual('title');
expect(errors[0].constraints).toEqual({
minLength: 'title must be longer than or equal to 5 characters',
contains: 'title must contain a hello string',
});
expect(errors[0].value).toEqual('helo');
});
});

it('should override inherited validators in sub classes', () => {
expect.assertions(9);

class MyClass {
@Min(30)
age: number;

@Equals('validator')
first_name: string;

@Equals('class')
last_name: string;
}

class MySubClass extends MyClass {
@Min(40)
age: number;

@Equals('class')
first_name: string;

@Equals('validator')
last_name: string;
}

const model = new MySubClass();
model.age = 20; // fail validation (using sub classes constraint)
model.first_name = 'class'; // pass validation (overriding fail from parent)
model.last_name = 'class'; // fail validation (overriding pass from parent)

return validator.validate(model).then(errors => {
expect(errors.length).toEqual(2);
expect(errors[0].target).toEqual(model);
expect(errors[0].property).toEqual('age');
expect(errors[0].constraints).toEqual({
min: 'age must not be less than 40',
});
expect(errors[0].value).toEqual(20);

expect(errors[1].target).toEqual(model);
expect(errors[1].property).toEqual('last_name');
expect(errors[1].constraints).toEqual({
equals: 'last_name must be equal to validator',
});
expect(errors[1].value).toEqual('class');
});
});

it('should not override different validators of inherited properties in the parent class', () => {
expect.assertions(4);

class MyClass {
@Contains('parent-class')
title: string;
}

class MySubClass extends MyClass {
@Equals('sub-class')
title: string;
}

const model = new MySubClass();
model.title = 'sub-class';

return validator.validate(model).then(errors => {
expect(errors.length).toEqual(1);
expect(errors[0].target).toEqual(model);
expect(errors[0].property).toEqual('title');
expect(errors[0].constraints).toEqual({
contains: 'title must contain a parent-class string',
});
});
});

it('should not override different validators of inherited properties in the sub class', () => {
expect.assertions(4);

class MyClass {
@Contains('parent-class')
title: string;
}

class MySubClass extends MyClass {
@Equals('sub-class')
title: string;
}

const model = new MySubClass();
model.title = 'parent-class';

return validator.validate(model).then(errors => {
expect(errors.length).toEqual(1);
expect(errors[0].target).toEqual(model);
expect(errors[0].property).toEqual('title');
expect(errors[0].constraints).toEqual({
equals: 'title must be equal to sub-class',
});
});
});
});
Loading