Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/language/res/stdlib.zmodel
Original file line number Diff line number Diff line change
Expand Up @@ -543,22 +543,22 @@ attribute @upper() @@@targetField([StringField]) @@@validation
/**
* Validates a number field is greater than the given value.
*/
attribute @gt(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField]) @@@validation
attribute @gt(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField, BigIntField]) @@@validation

/**
* Validates a number field is greater than or equal to the given value.
*/
attribute @gte(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField]) @@@validation
attribute @gte(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField, BigIntField]) @@@validation

Comment thread
ymc9 marked this conversation as resolved.
Outdated
/**
* Validates a number field is less than the given value.
*/
attribute @lt(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField]) @@@validation
attribute @lt(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField, BigIntField]) @@@validation

/**
* Validates a number field is less than or equal to the given value.
*/
attribute @lte(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField]) @@@validation
attribute @lte(_ value: Int, _ message: String?) @@@targetField([IntField, FloatField, DecimalField, BigIntField]) @@@validation

/**
* Validates the entity with a complex condition.
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/client/crud/dialects/postgresql.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { invariant } from '@zenstackhq/common-helpers';
import Decimal from 'decimal.js';
import { Decimal } from 'decimal.js';
import {
sql,
type Expression,
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/client/crud/dialects/sqlite.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { invariant } from '@zenstackhq/common-helpers';
import Decimal from 'decimal.js';
import { Decimal } from 'decimal.js';
import {
ExpressionWrapper,
sql,
Expand Down
13 changes: 4 additions & 9 deletions packages/runtime/src/client/crud/operations/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,10 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
model: GetModels<Schema>,
filter: any,
): Promise<unknown | undefined> {
const idFields = requireIdFields(this.schema, model);
const _filter = flattenCompoundUniqueFilters(this.schema, model, filter);
const query = kysely
.selectFrom(model)
.where((eb) => eb.and(_filter))
.select(idFields.map((f) => kysely.dynamic.ref(f)))
.limit(1)
.modifyEnd(this.makeContextComment({ model, operation: 'read' }));
return this.executeQueryTakeFirst(kysely, query, 'exists');
return this.readUnique(kysely, model, {
where: filter,
select: this.makeIdSelect(model),
});
}

protected async read(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { invariant } from '@zenstackhq/common-helpers';
import Decimal from 'decimal.js';
import { Decimal } from 'decimal.js';
Comment thread
ymc9 marked this conversation as resolved.
Outdated
import stableStringify from 'json-stable-stringify';
import { match, P } from 'ts-pattern';
import { z, ZodSchema, ZodType } from 'zod';
import {
type AttributeApplication,
type BuiltinType,
type EnumDef,
type FieldDef,
type GetModels,
type ModelDef,
type SchemaDef,
} from '../../schema';
import { enumerate } from '../../utils/enumerate';
import { extractFields } from '../../utils/object-utils';
import { formatError } from '../../utils/zod-utils';
import { AGGREGATE_OPERATORS, LOGICAL_COMBINATORS, NUMERIC_FIELD_TYPES } from '../constants';
} from '../../../schema';
import { enumerate } from '../../../utils/enumerate';
import { extractFields } from '../../../utils/object-utils';
import { formatError } from '../../../utils/zod-utils';
import { AGGREGATE_OPERATORS, LOGICAL_COMBINATORS, NUMERIC_FIELD_TYPES } from '../../constants';
import {
type AggregateArgs,
type CountArgs,
Expand All @@ -29,16 +30,23 @@ import {
type UpdateManyAndReturnArgs,
type UpdateManyArgs,
type UpsertArgs,
} from '../crud-types';
import { InputValidationError, InternalError } from '../errors';
} from '../../crud-types';
import { InputValidationError, InternalError } from '../../errors';
import {
fieldHasDefaultValue,
getDiscriminatorField,
getEnum,
getUniqueFields,
requireField,
requireModel,
} from '../query-utils';
} from '../../query-utils';
import {
addBigIntValidation,
addCustomValidation,
addDecimalValidation,
addNumberValidation,
addStringValidation,
} from './utils';

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

Expand Down Expand Up @@ -191,11 +199,14 @@ export class InputValidator<Schema extends SchemaDef> {
schema = getSchema(model, options);
this.schemaCache.set(cacheKey!, schema);
}
const { error } = schema.safeParse(args);
const { error, data } = schema.safeParse(args);
if (error) {
throw new InputValidationError(`Invalid ${operation} args: ${formatError(error)}`, error);
throw new InputValidationError(
`Invalid ${operation} args for model "${model}": ${formatError(error)}`,
error,
);
}
return args as T;
return data as T;
}

// #region Find
Expand Down Expand Up @@ -235,17 +246,28 @@ export class InputValidator<Schema extends SchemaDef> {
return result;
}

private makePrimitiveSchema(type: string) {
private makePrimitiveSchema(type: string, attributes?: AttributeApplication[]) {
if (this.schema.typeDefs && type in this.schema.typeDefs) {
return this.makeTypeDefSchema(type);
} else {
return match(type)
.with('String', () => z.string())
.with('Int', () => z.number().int())
.with('Float', () => z.number())
.with('String', () => addStringValidation(z.string(), attributes))
.with('Int', () => addNumberValidation(z.number().int(), attributes))
.with('Float', () => addNumberValidation(z.number(), attributes))
.with('Boolean', () => z.boolean())
Comment thread
ymc9 marked this conversation as resolved.
.with('BigInt', () => z.union([z.number().int(), z.bigint()]))
.with('Decimal', () => z.union([z.number(), z.instanceof(Decimal), z.string()]))
.with('BigInt', () =>
z.union([
addNumberValidation(z.number().int(), attributes),
addBigIntValidation(z.bigint(), attributes),
]),
)
.with('Decimal', () =>
z.union([
addNumberValidation(z.number(), attributes),
addDecimalValidation(z.instanceof(Decimal), attributes),
addDecimalValidation(z.string(), attributes),
]),
)
.with('DateTime', () => z.union([z.date(), z.string().datetime()]))
.with('Bytes', () => z.instanceof(Uint8Array))
.otherwise(() => z.unknown());
Expand Down Expand Up @@ -860,7 +882,7 @@ export class InputValidator<Schema extends SchemaDef> {
uncheckedVariantFields[field] = fieldSchema;
}
} else {
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type);
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type, fieldDef.attributes);

if (fieldDef.array) {
fieldSchema = z
Expand Down Expand Up @@ -889,14 +911,17 @@ export class InputValidator<Schema extends SchemaDef> {
}
});

const uncheckedCreateSchema = addCustomValidation(z.strictObject(uncheckedVariantFields), modelDef.attributes);
const checkedCreateSchema = addCustomValidation(z.strictObject(checkedVariantFields), modelDef.attributes);

if (!hasRelation) {
return this.orArray(z.strictObject(uncheckedVariantFields), canBeArray);
return this.orArray(uncheckedCreateSchema, canBeArray);
} else {
return z.union([
z.strictObject(uncheckedVariantFields),
z.strictObject(checkedVariantFields),
...(canBeArray ? [z.array(z.strictObject(uncheckedVariantFields))] : []),
...(canBeArray ? [z.array(z.strictObject(checkedVariantFields))] : []),
uncheckedCreateSchema,
checkedCreateSchema,
...(canBeArray ? [z.array(uncheckedCreateSchema)] : []),
...(canBeArray ? [z.array(checkedCreateSchema)] : []),
]);
}
}
Expand Down Expand Up @@ -1112,7 +1137,7 @@ export class InputValidator<Schema extends SchemaDef> {
uncheckedVariantFields[field] = fieldSchema;
}
} else {
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type).optional();
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type, fieldDef.attributes).optional();

if (this.isNumericField(fieldDef)) {
fieldSchema = z.union([
Expand Down Expand Up @@ -1161,10 +1186,12 @@ export class InputValidator<Schema extends SchemaDef> {
}
});

const uncheckedUpdateSchema = addCustomValidation(z.strictObject(uncheckedVariantFields), modelDef.attributes);
const checkedUpdateSchema = addCustomValidation(z.strictObject(checkedVariantFields), modelDef.attributes);
if (!hasRelation) {
return z.strictObject(uncheckedVariantFields);
return uncheckedUpdateSchema;
} else {
return z.union([z.strictObject(uncheckedVariantFields), z.strictObject(checkedVariantFields)]);
return z.union([uncheckedUpdateSchema, checkedUpdateSchema]);
}
}

Expand Down
Loading
Loading