Skip to content

Commit

Permalink
Merge pull request #1788 from npwork/feature/added_validation_decorators
Browse files Browse the repository at this point in the history
feat: added support for decorators Matches, IsPositive, IsNegative, L…
  • Loading branch information
kamilmysliwiec authored Mar 25, 2022
2 parents 83a46f7 + 1cee333 commit daa95f9
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 8 deletions.
99 changes: 94 additions & 5 deletions lib/plugin/visitors/model-class.visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
replaceImportPath
} from '../utils/plugin-utils';
import { AbstractFileVisitor } from './abstract.visitor';
import { PropertyAssignment } from 'typescript';

type ClassMetadata = Record<string, ts.ObjectLiteralExpression>;

Expand Down Expand Up @@ -396,6 +397,20 @@ export class ModelClassVisitor extends AbstractFileVisitor {
const assignments = [];
const decorators = node.decorators;

this.addPropertyByValidationDecorator(
factory,
'Matches',
'pattern',
decorators,
assignments
);
this.addPropertyByValidationDecorator(
factory,
'IsIn',
'enum',
decorators,
assignments
);
this.addPropertyByValidationDecorator(
factory,
'Min',
Expand Down Expand Up @@ -424,6 +439,59 @@ export class ModelClassVisitor extends AbstractFileVisitor {
decorators,
assignments
);
this.addPropertiesByValidationDecorator(
factory,
'IsPositive',
decorators,
assignments,
() => {
return [
factory.createPropertyAssignment(
'minimum',
createPrimitiveLiteral(factory, 1)
)
];
}
);
this.addPropertiesByValidationDecorator(
factory,
'IsNegative',
decorators,
assignments,
() => {
return [
factory.createPropertyAssignment(
'maximum',
createPrimitiveLiteral(factory, -1)
)
];
}
);
this.addPropertiesByValidationDecorator(
factory,
'Length',
decorators,
assignments,
(decoratorRef: ts.Decorator) => {
const decoratorArguments = getDecoratorArguments(decoratorRef);

const result = [];
result.push(
factory.createPropertyAssignment(
'minLength',
head(decoratorArguments)
)
);

if (decoratorArguments.length > 1) {
result.push(
factory.createPropertyAssignment('maxLength', decoratorArguments[1])
);
}

return result;
}
);

return assignments;
}
Expand All @@ -435,17 +503,38 @@ export class ModelClassVisitor extends AbstractFileVisitor {
decorators: ts.NodeArray<ts.Decorator>,
assignments: ts.PropertyAssignment[]
) {
const decoratorRef = getDecoratorOrUndefinedByNames(
this.addPropertiesByValidationDecorator(
factory,
decoratorName,
decorators,
assignments,
(decoratorRef: ts.Decorator) => {
const argument: ts.Expression = head(
getDecoratorArguments(decoratorRef)
);
if (argument) {
return [factory.createPropertyAssignment(propertyKey, argument)];
}
return [];
}
);
}

addPropertiesByValidationDecorator(
factory: ts.NodeFactory,
decoratorName: string,
decorators: ts.NodeArray<ts.Decorator>,
assignments: ts.PropertyAssignment[],
addPropertyAssignments: (decoratorRef: ts.Decorator) => PropertyAssignment[]
) {
const decoratorRef: ts.Decorator = getDecoratorOrUndefinedByNames(
[decoratorName],
decorators
);
if (!decoratorRef) {
return;
}
const argument: ts.Expression = head(getDecoratorArguments(decoratorRef));
if (argument) {
assignments.push(factory.createPropertyAssignment(propertyKey, argument));
}
assignments.push(...addPropertyAssignments(decoratorRef));
}

addClassMetadata(
Expand Down
38 changes: 35 additions & 3 deletions test/plugin/fixtures/create-cat.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const createCatDtoText = `
import { IsInt, IsString } from 'class-validator';
import { IsInt, IsString, IsPositive, IsNegative, Length, Matches, IsIn } from 'class-validator';
enum Status {
ENABLED,
Expand All @@ -19,10 +19,22 @@ class OtherNode {
}
export class CreateCatDto {
@IsIn(['a', 'b'])
isIn: string;
@Matches(/^[+]?abc$/)
pattern: string;
name: string;
@Min(0)
@Max(10)
age: number = 3;
@IsPositive()
positive: number = 5;
@IsNegative()
negative: number = -1;
@Length(2)
lengthMin: string;
@Length(3, 5)
lengthMinMax: string;
tags: string[];
status: Status = Status.ENABLED;
status2?: Status;
Expand All @@ -49,7 +61,7 @@ export class CreateCatDto {
}
`;

export const createCatDtoTextTranspiled = `import { IsString } from 'class-validator';
export const createCatDtoTextTranspiled = `import { IsString, IsPositive, IsNegative, Length, Matches, IsIn } from 'class-validator';
var Status;
(function (Status) {
Status[Status[\"ENABLED\"] = 0] = \"ENABLED\";
Expand All @@ -67,16 +79,36 @@ class OtherNode {
export class CreateCatDto {
constructor() {
this.age = 3;
this.positive = 5;
this.negative = -1;
this.status = Status.ENABLED;
}
static _OPENAPI_METADATA_FACTORY() {
return { name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3, minimum: 0, maximum: 10 }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, status2: { required: false, enum: Status }, statusArr: { required: false, enum: Status, isArray: true }, oneValueEnum: { required: false, enum: OneValueEnum }, oneValueEnumArr: { required: false, enum: OneValueEnum }, breed: { required: false, type: () => String, title: "this is breed im comment" }, nodes: { required: true, type: () => [Object] }, optionalBoolean: { required: false, type: () => Boolean }, date: { required: true, type: () => Date }, twoDimensionPrimitives: { required: true, type: () => [[String]] }, twoDimensionNodes: { required: true, type: () => [[OtherNode]] } };
return { isIn: { required: true, type: () => String, enum: ['a', 'b'] }, pattern: { required: true, type: () => String, pattern: /^[+]?abc$/ }, name: { required: true, type: () => String }, age: { required: true, type: () => Number, default: 3, minimum: 0, maximum: 10 }, positive: { required: true, type: () => Number, default: 5, minimum: 1 }, negative: { required: true, type: () => Number, default: -1, maximum: -1 }, lengthMin: { required: true, type: () => String, minLength: 2 }, lengthMinMax: { required: true, type: () => String, minLength: 3, maxLength: 5 }, tags: { required: true, type: () => [String] }, status: { required: true, default: Status.ENABLED, enum: Status }, status2: { required: false, enum: Status }, statusArr: { required: false, enum: Status, isArray: true }, oneValueEnum: { required: false, enum: OneValueEnum }, oneValueEnumArr: { required: false, enum: OneValueEnum }, breed: { required: false, type: () => String, title: "this is breed im comment" }, nodes: { required: true, type: () => [Object] }, optionalBoolean: { required: false, type: () => Boolean }, date: { required: true, type: () => Date }, twoDimensionPrimitives: { required: true, type: () => [[String]] }, twoDimensionNodes: { required: true, type: () => [[OtherNode]] } };
}
}
__decorate([
IsIn(['a', 'b'])
], CreateCatDto.prototype, \"isIn\", void 0);
__decorate([
Matches(/^[+]?abc$/)
], CreateCatDto.prototype, \"pattern\", void 0);
__decorate([
Min(0),
Max(10)
], CreateCatDto.prototype, \"age\", void 0);
__decorate([
IsPositive()
], CreateCatDto.prototype, \"positive\", void 0);
__decorate([
IsNegative()
], CreateCatDto.prototype, \"negative\", void 0);
__decorate([
Length(2)
], CreateCatDto.prototype, \"lengthMin\", void 0);
__decorate([
Length(3, 5)
], CreateCatDto.prototype, \"lengthMinMax\", void 0);
__decorate([
ApiProperty({ description: "this is breed", type: String }),
IsString()
Expand Down

0 comments on commit daa95f9

Please sign in to comment.