Skip to content

Commit

Permalink
Merge pull request #34 from foo-bar-sketch/feat/class-validator/allow…
Browse files Browse the repository at this point in the history
…-non-unique-decorator-names

feat(classValidatorDecorators): adds support for conflicting decorator names from other libraries
  • Loading branch information
darraghoriordan authored Nov 3, 2022
2 parents ac65e30 + 8cb5b87 commit 6d7b6d7
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ ruleTester.run("all-properties-are-whitelisted", rule, {
valid: [
{
code: `
import { A } from 'class-validator';
class A {
@A
b: string
Expand All @@ -17,6 +19,8 @@ class A {
},
{
code: `
import { A } from 'class-validator';
class A {
@A()
b: string
Expand All @@ -25,18 +29,34 @@ class A {
},
{
code: `
import { A } from 'class-validator';
class A {
b: string
}
`,
},
{
code: `
import { IsString, Allow } from 'class-validator';
class A {
@IsString()
b: string
@Allow()
b: string
}
`,
},
{
code: `
import { IsInt } from 'sequelize-typescript';
class A {
@IsInt()
b: string
b: string
}
`,
Expand All @@ -45,7 +65,9 @@ class A {
invalid: [
{
code: `
class A {
import { Allow } from 'class-validator';
class MyClass {
@Allow()
b: string
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {AST_NODE_TYPES, TSESTree, TSESLint} from "@typescript-eslint/utils";
import {AST_NODE_TYPES, TSESLint, TSESTree} from "@typescript-eslint/utils";
import {createRule} from "../../utils/createRule";
import {classValidatorDecorators} from "../../utils/classValidatorDecorators";

const CLASS_VALIDATOR_DECORATOR_NAMES = new Set(classValidatorDecorators);
import {typedTokenHelpers} from "../../utils/typedTokenHelpers";

const rule = createRule({
name: "all-properties-are-whitelisted",
Expand All @@ -29,6 +27,7 @@ const rule = createRule({
return {
// eslint-disable-next-line @typescript-eslint/naming-convention
ClassDeclaration(node) {
const program = typedTokenHelpers.getRootProgram(node);
const withDecorator: TSESTree.PropertyDefinition[] = [];
const withoutDecorator: TSESTree.PropertyDefinition[] = [];
for (const element of node.body.body) {
Expand All @@ -41,8 +40,9 @@ const rule = createRule({
AST_NODE_TYPES.CallExpression &&
decorator.expression.callee.type ===
AST_NODE_TYPES.Identifier &&
CLASS_VALIDATOR_DECORATOR_NAMES.has(
decorator.expression.callee.name
typedTokenHelpers.decoratorIsClassValidatorDecorator(
program,
decorator
)
);
if (hasDecorator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
// is a primitive type date with custom transform - https://github.com/darraghoriordan/eslint-plugin-nestjs-typed/issues/32
options: [{additionalTypeDecorators: ["TransformDate"]}],
code: `
import { IsOptional, IsDate } from 'class-validator';
class ExampleDto {
@ApiPropertyOptional(
{
Expand Down Expand Up @@ -72,12 +74,27 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// no validation decorator
code: `
import { IsDefined } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@IsDefined()
@Type(()=> Person)
members!: Person;
}
`,
},
{
// sequelize-typescript validator with class-validator name conflict
code: `
import { IsInt } from 'sequelize-typescript'
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@IsDefined()
@IsInt
members!: Person;
}
`,
},
{
Expand All @@ -92,6 +109,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// has the type decorator already
code: `
import { IsArray } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@ValidateNested({each:true})
Expand All @@ -104,6 +123,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is a primitive type
code: `
import { IsBoolean } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@ValidateNested({each:true})
Expand All @@ -115,6 +136,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is not a primitive type so skip
code: `
import { Allow } from 'class-validator';
enum Foo {
BAR
}
Expand All @@ -129,6 +152,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is an array - should have type and has it so pass!
code: `
import { ValidateNested } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@ValidateNested({each:true})
Expand All @@ -142,6 +167,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is an array - should have type
code: `
import { ValidateNested } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@ValidateNested({each:true})
Expand All @@ -157,6 +184,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is an OPTIONAL array - should have type
code: `
import { ValidateNested } from 'class-validator';
export class Foo {}
export class CreateOrganisationDto {
Expand All @@ -174,6 +203,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is an array with union - should have type
code: `
import { ValidateNested, IsArray } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@ValidateNested({each:true})
Expand All @@ -190,6 +221,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is not a primitive type
code: `
import { ValidateNested, IsDate } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@ValidateNested({each:true})
Expand All @@ -206,6 +239,8 @@ ruleTester.run("validated-non-primitive-property-needs-type-decorator", rule, {
{
// is a custom class
code: `
import { ValidateNested, IsDefined } from 'class-validator';
export class CreateOrganisationDto {
@ApiProperty({ type: Person, isArray: true })
@ValidateNested({each:true})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from "@typescript-eslint/utils";
import {createRule} from "../../utils/createRule";
import {typedTokenHelpers} from "../../utils/typedTokenHelpers";
import {classValidatorDecorators} from "../../utils/classValidatorDecorators";

const primitiveTypes = new Set([
AST_NODE_TYPES.TSStringKeyword,
Expand Down Expand Up @@ -141,10 +140,23 @@ const rule = createRule({
// property has a validation decorator but not IsEnum
// (we don't care about un-validated properties and enums don't need Type())
const foundClassValidatorDecorators =
typedTokenHelpers.getDecoratorsNamed(
node,
classValidatorDecorators.filter((x) => x !== "IsEnum")
);
typedTokenHelpers.getImportedClassValidatorDecorators(node);
const hasEnum = foundClassValidatorDecorators.some(
(foundClassValidatorDecorator) =>
typedTokenHelpers.decoratorIsIsEnum(
foundClassValidatorDecorator
)
);

if (hasEnum) {
return;
}

// const foundClassValidatorDecorators =
// typedTokenHelpers.getDecoratorsNamed(
// node,
// classValidatorDecorators.filter((x) => x !== "IsEnum")
// );
if (foundClassValidatorDecorators.length === 0) {
return;
}
Expand Down
100 changes: 0 additions & 100 deletions src/utils/classValidatorDecorators.ts

This file was deleted.

Loading

0 comments on commit 6d7b6d7

Please sign in to comment.