Skip to content

Commit 2749bd9

Browse files
authored
feat(classValidationResolver): add transform and validator options (#524)
BREAKING CHANGE: schema options now includes validator and transformer Before: ```ts schemaOptions?: ValidatorOptions, ``` After: ```ts schemaOptions?: { validator?: ValidatorOptions; transformer?: ClassTransformOptions; } ```
1 parent f3da212 commit 2749bd9

File tree

4 files changed

+105
-10
lines changed

4 files changed

+105
-10
lines changed

class-validator/src/__tests__/__snapshots__/class-validator.ts.snap

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,38 @@ exports[`classValidatorResolver > should return all the errors from classValidat
205205
"values": {},
206206
}
207207
`;
208+
209+
exports[`validate data with transformer option 1`] = `
210+
{
211+
"errors": {
212+
"random": {
213+
"message": "All fields must be defined.",
214+
"ref": undefined,
215+
"type": "isDefined",
216+
"types": {
217+
"isDefined": "All fields must be defined.",
218+
"isNumber": "Must be a number",
219+
"max": "Cannot be greater than 255",
220+
"min": "Cannot be lower than 0",
221+
},
222+
},
223+
},
224+
"values": {},
225+
}
226+
`;
227+
228+
exports[`validate data with validator option 1`] = `
229+
{
230+
"errors": {
231+
"random": {
232+
"message": "All fields must be defined.",
233+
"ref": undefined,
234+
"type": "isDefined",
235+
"types": {
236+
"isDefined": "All fields must be defined.",
237+
},
238+
},
239+
},
240+
"values": {},
241+
}
242+
`;

class-validator/src/__tests__/class-validator.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import { classValidatorResolver } from '..';
33
import { Schema, validData, fields, invalidData } from './__fixtures__/data';
44
import * as classValidator from 'class-validator';
5+
import { Expose, Type } from 'class-transformer';
6+
import { IsDefined, IsNumber, Max, Min } from 'class-validator';
57

68
const shouldUseNativeValidation = false;
79

@@ -85,3 +87,61 @@ describe('classValidatorResolver', () => {
8587
expect(result).toMatchSnapshot();
8688
});
8789
});
90+
91+
it('validate data with transformer option', async () => {
92+
class SchemaTest {
93+
@Expose({ groups: ['find', 'create', 'update'] })
94+
@Type(() => Number)
95+
@IsDefined({
96+
message: `All fields must be defined.`,
97+
groups: ['publish'],
98+
})
99+
@IsNumber({}, { message: `Must be a number`, always: true })
100+
@Min(0, { message: `Cannot be lower than 0`, always: true })
101+
@Max(255, { message: `Cannot be greater than 255`, always: true })
102+
random: number;
103+
}
104+
105+
const result = await classValidatorResolver(
106+
SchemaTest,
107+
{ transformer: { groups: ['update'] } },
108+
{
109+
mode: 'sync',
110+
},
111+
)(invalidData, undefined, {
112+
fields,
113+
criteriaMode: 'all',
114+
shouldUseNativeValidation,
115+
});
116+
117+
expect(result).toMatchSnapshot();
118+
});
119+
120+
it('validate data with validator option', async () => {
121+
class SchemaTest {
122+
@Expose({ groups: ['find', 'create', 'update'] })
123+
@Type(() => Number)
124+
@IsDefined({
125+
message: `All fields must be defined.`,
126+
groups: ['publish'],
127+
})
128+
@IsNumber({}, { message: `Must be a number`, always: true })
129+
@Min(0, { message: `Cannot be lower than 0`, always: true })
130+
@Max(255, { message: `Cannot be greater than 255`, always: true })
131+
random: number;
132+
}
133+
134+
const result = await classValidatorResolver(
135+
SchemaTest,
136+
{ validator: { stopAtFirstError: true } },
137+
{
138+
mode: 'sync',
139+
},
140+
)(invalidData, undefined, {
141+
fields,
142+
criteriaMode: 'all',
143+
shouldUseNativeValidation,
144+
});
145+
146+
expect(result).toMatchSnapshot();
147+
});

class-validator/src/class-validator.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ const parseErrors = (
3737
export const classValidatorResolver: Resolver =
3838
(schema, schemaOptions = {}, resolverOptions = {}) =>
3939
async (values, _, options) => {
40-
const user = plainToClass(schema, values);
40+
const { transformer, validator } = schemaOptions;
41+
const data = plainToClass(schema, values, transformer);
4142

4243
const rawErrors = await (resolverOptions.mode === 'sync'
4344
? validateSync
44-
: validate)(user, schemaOptions);
45+
: validate)(data, validator);
4546

4647
if (rawErrors.length) {
4748
return {

class-validator/src/types.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import {
2-
FieldValues,
3-
ResolverOptions,
4-
ResolverResult,
5-
} from 'react-hook-form';
1+
import { FieldValues, ResolverOptions, ResolverResult } from 'react-hook-form';
62
import { ValidatorOptions } from 'class-validator';
7-
import { ClassConstructor } from 'class-transformer';
3+
import { ClassConstructor, ClassTransformOptions } from 'class-transformer';
84

95
export type Resolver = <T extends { [_: string]: any }>(
106
schema: ClassConstructor<T>,
11-
schemaOptions?: ValidatorOptions,
12-
resolverOptions?: { mode?: 'async' | 'sync', rawValues?: boolean; },
7+
schemaOptions?: {
8+
validator?: ValidatorOptions;
9+
transformer?: ClassTransformOptions;
10+
},
11+
resolverOptions?: { mode?: 'async' | 'sync'; rawValues?: boolean },
1312
) => <TFieldValues extends FieldValues, TContext>(
1413
values: TFieldValues,
1514
context: TContext | undefined,

0 commit comments

Comments
 (0)