resolvers
resolvers copied to clipboard
class-validator resolver not working properly when using Expose annotations with groups
Describe the bug
When a class is annotated with @Expose({groups: "group-name"}) the validation doesn't work.
With the plain @Expose() annotation there's no problem.
To Reproduce Create a simple class to validate:
class TestDto {
@Expose( {groups: ["find", "create", "update"]} )
@Type(() => Number)
@IsDefined({
message: `All fields must be defined.`,
groups: ["publish"],
})
@IsNumber({}, { message: `Must be a number`, always: true })
@Min(0, { message: `Cannot be lower than 0`, always: true })
@Max(255, { message: `Cannot be greater than 255`, always: true })
random: number;
}
Then just create a simple resolver:
const saveResolver = classValidatorResolver(TestDto , {
groups: ["update"],
skipMissingProperties: true,
});
All the validations are ignored.
My assumption is that the resolver, transforms the value into an instance of the TestDto class without any particular option to expose the given annotated properties. So the resulting instance doesn't have the property. So as skipMissingProperties: true is set, the validations won't run on it, except for the @IsDefined annotation if it was annotated with the same group as the resolver: @IsDefined({groups: ["update"]}).
Expected behavior The validation should be run on the object, and reject it if it doesn't meet the criteria.
I have check the code of the resolver, and indeed, the problematic resides in the transformation of the form values to the instance.
In the code, the transformation is made with plainToClass and it should be done with plainToInstance as the former is deprecated, that's not the problem anyway. I've modified the code inside, to create the instance hardcoded like so, and it works.
export const classValidatorResolver: Resolver =
(schema, schemaOptions = {}, resolverOptions = {}) =>
async (values, _, options) => {
// Just added the groups directly as options to test.
const user = plainToInstance(schema, values, {
groups: ['update'],
})
const rawErrors = await (resolverOptions.mode === 'sync'
? validateSync
: validate)(user, schemaOptions)
if (rawErrors.length) {
return {
values: {},
errors: toNestError(
parseErrors(
rawErrors,
!options.shouldUseNativeValidation && options.criteriaMode === 'all'
),
options
),
}
}
options.shouldUseNativeValidation && validateFieldsNatively({}, options)
return { values, errors: {} }
}
It should be nice to add some extra options to the schemaOptions as a new attribute to include the ClassTransformOptions to give a little bit of flexibility to the transformation.
@ramirezsandin Hey 👋🏻 can you have a look at that PR #524 and tell me if it's what you expect?