NestJS/Class-transformer @Type Discriminator object doesn't correctly validate data

Viewed 2641

I have a generic class as following:

import {IsArray, IsNotEmpty, IsString, ValidateNested} from "class-validator";
import {PatientInfoValidator} from "./businessInfo/PatientInfoValidator";
import {TypeValidator} from "./TypeValidator";
import {Type} from "class-transformer";

export class GenericValidator {
    @IsString()
    @IsNotEmpty()
    userId: string;

    @ValidateNested({each:true})
    @Type(() => TypeValidator)
    type: TypeValidator;
}

And two classes which inherit from this class

import {IsArray, IsNotEmpty, IsString, ValidateNested} from "class-validator";
import {PatientInfoValidator} from "./businessInfo/PatientInfoValidator";
import {Type} from "class-transformer";
import {BusinessInfoValidator} from "./BusinessInfoValidator";
import {GenericValidator} from "./GenericValidator";
import {TypeValidator} from "./TypeValidator";

export class BodyValidator extends GenericValidator {
    @IsString()
    @IsNotEmpty()
    userId: string;

    @ValidateNested({each:true})
    @Type(() => TypeValidator)
    type: TypeValidator;

    @ValidateNested({each:true})
    @Type(() => BusinessInfoValidator)
    businessInformation: BusinessInfoValidator;

    @IsArray()
    sheetLink: string[];

    validate: boolean;
}

And:

import {GenericValidator} from "./GenericValidator";
import {IsNotEmpty, IsString, ValidateNested} from "class-validator";
import {Type} from "class-transformer";
import {TypeValidator} from "./TypeValidator";

export class CaseTwoValidator extends GenericValidator {
    @IsString()
    @IsNotEmpty()
    userId: string;

    @ValidateNested({each:true})
    @Type(() => TypeValidator)
    type: TypeValidator;

    newKey: string;
}

These are used in the DTO for validation:

import { ApiProperty } from '@nestjs/swagger';
import {ValidateNested} from "class-validator";
import { Type } from 'class-transformer';
import { BodyValidator} from "../validators/BodyValidator";
import { GenericValidator} from "../validators/GenericValidator";
import { CaseTwoValidator} from "../validators/CaseTwoValidator";

export class CreateSheetsDto {
    @ApiProperty()
    @ValidateNested({ each: true })
    @Type(() => GenericValidator, {
        keepDiscriminatorProperty: true,
        discriminator: {
            property: "type.label",
            subTypes: [
                { value: BodyValidator, name: "Hors dissection et syndrome neurologique" },
                { value: CaseTwoValidator, name: "NewCase" }
            ]
        }
        })
    readonly body: BodyValidator | CaseTwoValidator;
}

I'm expecting the controller having this DTO as params to: Reject as Bad request all request body which are not conform to BodyValidator or CaseTwoValidator format.

Current behavior: API requests are very permissive, and doesn't enforce nested checks.

I was wondering if I'm missing something obvious having spent 3hours on this issue.

1 Answers

I ran into the exact same problem a while back and figured out that decorators are not applied for inherited classes. Decorators i.e. (@IsString) must be applied to the class directly so you unfortunately must redefine the fields in your parent classes to your child DTO's.

See the TypeScript documentation on this here. Specifically this portion

A method decorator cannot be used in a declaration file, on an overload

Related