Skip to content

Commit 9f5e0a7

Browse files
committed
feat: implement field validation
1 parent f1a8cef commit 9f5e0a7

File tree

17 files changed

+734
-86
lines changed

17 files changed

+734
-86
lines changed
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

packages/runtime/src/client/crud/operations/base.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,10 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
131131
model: GetModels<Schema>,
132132
filter: any,
133133
): Promise<unknown | undefined> {
134-
const idFields = requireIdFields(this.schema, model);
135-
const _filter = flattenCompoundUniqueFilters(this.schema, model, filter);
136-
const query = kysely
137-
.selectFrom(model)
138-
.where((eb) => eb.and(_filter))
139-
.select(idFields.map((f) => kysely.dynamic.ref(f)))
140-
.limit(1)
141-
.modifyEnd(this.makeContextComment({ model, operation: 'read' }));
142-
return this.executeQueryTakeFirst(kysely, query, 'exists');
134+
return this.readUnique(kysely, model, {
135+
where: filter,
136+
select: this.makeIdSelect(model),
137+
});
143138
}
144139

145140
protected async read(

packages/runtime/src/client/crud/validator.ts renamed to packages/runtime/src/client/crud/validator/index.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@ import stableStringify from 'json-stable-stringify';
44
import { match, P } from 'ts-pattern';
55
import { z, ZodSchema, ZodType } from 'zod';
66
import {
7+
type AttributeApplication,
78
type BuiltinType,
89
type EnumDef,
910
type FieldDef,
1011
type GetModels,
1112
type ModelDef,
1213
type SchemaDef,
13-
} from '../../schema';
14-
import { enumerate } from '../../utils/enumerate';
15-
import { extractFields } from '../../utils/object-utils';
16-
import { formatError } from '../../utils/zod-utils';
17-
import { AGGREGATE_OPERATORS, LOGICAL_COMBINATORS, NUMERIC_FIELD_TYPES } from '../constants';
14+
} from '../../../schema';
15+
import { enumerate } from '../../../utils/enumerate';
16+
import { extractFields } from '../../../utils/object-utils';
17+
import { formatError } from '../../../utils/zod-utils';
18+
import { AGGREGATE_OPERATORS, LOGICAL_COMBINATORS, NUMERIC_FIELD_TYPES } from '../../constants';
1819
import {
1920
type AggregateArgs,
2021
type CountArgs,
@@ -29,16 +30,17 @@ import {
2930
type UpdateManyAndReturnArgs,
3031
type UpdateManyArgs,
3132
type UpsertArgs,
32-
} from '../crud-types';
33-
import { InputValidationError, InternalError } from '../errors';
33+
} from '../../crud-types';
34+
import { InputValidationError, InternalError } from '../../errors';
3435
import {
3536
fieldHasDefaultValue,
3637
getDiscriminatorField,
3738
getEnum,
3839
getUniqueFields,
3940
requireField,
4041
requireModel,
41-
} from '../query-utils';
42+
} from '../../query-utils';
43+
import { addCustomValidation, addNumberValidation, addStringValidation } from './utils';
4244

4345
type GetSchemaFunc<Schema extends SchemaDef, Options> = (model: GetModels<Schema>, options: Options) => ZodType;
4446

@@ -191,11 +193,14 @@ export class InputValidator<Schema extends SchemaDef> {
191193
schema = getSchema(model, options);
192194
this.schemaCache.set(cacheKey!, schema);
193195
}
194-
const { error } = schema.safeParse(args);
196+
const { error, data } = schema.safeParse(args);
195197
if (error) {
196-
throw new InputValidationError(`Invalid ${operation} args: ${formatError(error)}`, error);
198+
throw new InputValidationError(
199+
`Invalid ${operation} args for model "${model}": ${formatError(error)}`,
200+
error,
201+
);
197202
}
198-
return args as T;
203+
return data as T;
199204
}
200205

201206
// #region Find
@@ -235,13 +240,13 @@ export class InputValidator<Schema extends SchemaDef> {
235240
return result;
236241
}
237242

238-
private makePrimitiveSchema(type: string) {
243+
private makePrimitiveSchema(type: string, attributes?: AttributeApplication[]) {
239244
if (this.schema.typeDefs && type in this.schema.typeDefs) {
240245
return this.makeTypeDefSchema(type);
241246
} else {
242247
return match(type)
243-
.with('String', () => z.string())
244-
.with('Int', () => z.number().int())
248+
.with('String', () => addStringValidation(z.string(), attributes))
249+
.with('Int', () => addNumberValidation(z.number().int(), attributes))
245250
.with('Float', () => z.number())
246251
.with('Boolean', () => z.boolean())
247252
.with('BigInt', () => z.union([z.number().int(), z.bigint()]))
@@ -860,7 +865,7 @@ export class InputValidator<Schema extends SchemaDef> {
860865
uncheckedVariantFields[field] = fieldSchema;
861866
}
862867
} else {
863-
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type);
868+
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type, fieldDef.attributes);
864869

865870
if (fieldDef.array) {
866871
fieldSchema = z
@@ -889,14 +894,17 @@ export class InputValidator<Schema extends SchemaDef> {
889894
}
890895
});
891896

897+
const uncheckedCreateSchema = addCustomValidation(z.strictObject(uncheckedVariantFields), modelDef.attributes);
898+
const checkedCreateSchema = addCustomValidation(z.strictObject(checkedVariantFields), modelDef.attributes);
899+
892900
if (!hasRelation) {
893-
return this.orArray(z.strictObject(uncheckedVariantFields), canBeArray);
901+
return this.orArray(uncheckedCreateSchema, canBeArray);
894902
} else {
895903
return z.union([
896-
z.strictObject(uncheckedVariantFields),
897-
z.strictObject(checkedVariantFields),
898-
...(canBeArray ? [z.array(z.strictObject(uncheckedVariantFields))] : []),
899-
...(canBeArray ? [z.array(z.strictObject(checkedVariantFields))] : []),
904+
uncheckedCreateSchema,
905+
checkedCreateSchema,
906+
...(canBeArray ? [z.array(uncheckedCreateSchema)] : []),
907+
...(canBeArray ? [z.array(checkedCreateSchema)] : []),
900908
]);
901909
}
902910
}
@@ -1112,7 +1120,7 @@ export class InputValidator<Schema extends SchemaDef> {
11121120
uncheckedVariantFields[field] = fieldSchema;
11131121
}
11141122
} else {
1115-
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type).optional();
1123+
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type, fieldDef.attributes).optional();
11161124

11171125
if (this.isNumericField(fieldDef)) {
11181126
fieldSchema = z.union([
@@ -1161,10 +1169,12 @@ export class InputValidator<Schema extends SchemaDef> {
11611169
}
11621170
});
11631171

1172+
const uncheckedUpdateSchema = addCustomValidation(z.strictObject(uncheckedVariantFields), modelDef.attributes);
1173+
const checkedUpdateSchema = addCustomValidation(z.strictObject(checkedVariantFields), modelDef.attributes);
11641174
if (!hasRelation) {
1165-
return z.strictObject(uncheckedVariantFields);
1175+
return uncheckedUpdateSchema;
11661176
} else {
1167-
return z.union([z.strictObject(uncheckedVariantFields), z.strictObject(checkedVariantFields)]);
1177+
return z.union([uncheckedUpdateSchema, checkedUpdateSchema]);
11681178
}
11691179
}
11701180

0 commit comments

Comments
 (0)