Pesquisa de site

typeorm - typescript - class validator - validar com base em tipo diferente


Estou trabalhando com TypeORM e gostaria de fornecer diferentes valores para um campo com base no valor de outro campo. Para explicar, aqui está como meu modelo DTO se parece:

import { IsString, IsOptional, IsNumber, IsEnum, IsObject, IsBoolean, ValidateNested } from 'class-validator';

export enum AttributeTypes {
    DATE = 'DATE',
    TIME = 'TIME',
    NUMBERS = 'NUMBERS',
}

export class BaseValidation {
    @IsOptional()
    @IsBoolean()
    required: boolean;
}

export class modelCreate {
    @IsOptional()
    @IsNumber()
    id: number;

    @IsOptional()
    @IsString()
    label: string;

    @IsOptional()
    @IsEnum(AttributeTypes)
    type: AttributeTypes;

    @IsOptional()
    @IsObject()
    @ValidateNested()
    validation: BaseValidation;
}

O problema aqui é que eu tenho este campo: validation em modelCreate, & esse campo é um objeto e pode ter várias propriedades & pode se parecer com isto no banco de dados:

validation: {
   required: true,
   text: 2
}

ou pode parecer assim:

   validation: {
       required: false,
       number: 1,
       maxNumber: 10
    }

e isso dependeria da propriedade type de modelCreate, porque se o type for 'TIME', eu gostaria de ter validação para isso:

BaseValidation {
    @IsBoolean()
     required: true,
    @IsString()
    text: 2
}

e se o tipo for 'NÚMEROS', eu gostaria de ter uma validação assim

  BaseValidation {
           @IsBoolean()
           required: boolean,
           @IsNumber()
           number: number,
           @IsNumber()
           maxNumber: number
        }

Então a questão é como eu alteraria diferentes classes em um campo de validação com base no valor do campo tipo no class validator, e isso é realmente possível?

Resposta #1

para isso você precisa criar 2 classes, para cada caso, string e números. Então mude o tipo validation: StringBaseValidation | NumberBaseValidation; agora o class-validator pode diferenciar os casos.

Se os dados vêm de uma solicitação, você precisa usar @Type((obj) => obj.writeConditionToDetectString ? StringBaseValidation : NumberBaseValidation) para ajudar a biblioteca a entender de qual tipo são os dados da solicitação.

Resposta #2

Você deve ter um campo discriminativo, para permitir que o class-transformer identifique o tipo a ser convertido e usado na validação. O mesmo pode ser feito com objetos de array aninhados. No seu caso, você pode usar o campo "required".

No exemplo abaixo, se a propriedade required for igual a true, então usamos a classe A; por outro lado, se a propriedade required for igual a false, a classe a ser usada é B.
Na propriedade discriminator você deve definir um campo a ser usado para identificar as diferentes formas polimórficas dos objetos aninhados. O subTypes.[].value é a classe a ser usada na validação e o subTypes.[].name é o valor que o campo discriminator deve ter para assumir a classe adicionada no campo subTypes.[].name.

import { Type } from 'class-transformer';
import { IsBoolean, IsNumber, IsString, ValidateNested } from 'class-validator';

class Discriminator {
  @IsBoolean()
  required: boolean;
}

class A extends Discriminator {  
  @IsString()
  text: string;
}

class B extends Discriminator {  
  @IsNumber()
  number: number;

  @IsNumber()
  maxNumber: number
}

export class C {

  // Other fields...

  @ValidateNested()
  @Type(() => Discriminator, {
    discriminator: {
      property: 'required',
      subTypes: [
        { value: A, name: true as unknown as string },
        { value: B, name: false  as unknown as string },
      ],
    },
    keepDiscriminatorProperty: true,
  })
  validation: A | B;
}

Agora seu controlador deve se parecer com isto:

@Controller()
export class AppController {
  @Post()
  public example(
    @Body(new ValidationPipe({ transform: true }))
    body: C,
  ) {
    return body;
  }
}

E você pode fazer seus pedidos:

curl --location 'localhost:3000/' \
--header 'Content-Type: application/json' \
--data '{
  "validation": {
    "required": true,
    "text": "Some text..."
  } 
}'

Se você alterar o segundo elemento da propriedade required para false, então você verá um erro:

{
- "required": true,
+ "required": false,
  "text": "Some text..."
}
{
  "statusCode": 400,
  "message": [
    "validation.number must be a number conforming to the specified constraints",
    "validation.maxNumber must be a number conforming to the specified constraints"
  ],
  "error": "Bad Request"
}

Referências: https://github.com/typestack/class-transformer#providing-more-than-one-type-option

Artigos relacionados