Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions packages/orm/src/client/client-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,13 @@ export class ClientImpl {
return this.auth;
}

$setOptions<Options extends ClientOptions<SchemaDef>>(options: Options): ClientContract<SchemaDef, Options> {
return new ClientImpl(this.schema, options as ClientOptions<SchemaDef>, this) as unknown as ClientContract<
SchemaDef,
Options
>;
}

$setInputValidation(enable: boolean) {
const newOptions: ClientOptions<SchemaDef> = {
...this.options,
Expand Down
11 changes: 11 additions & 0 deletions packages/orm/src/client/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,20 @@ export type ClientContract<Schema extends SchemaDef, Options extends ClientOptio
*/
$setAuth(auth: AuthType<Schema> | undefined): ClientContract<Schema, Options>;

/**
* Returns a new client with new options applied.
* @example
* ```
* const dbNoValidation = db.$setOptions({ ...db.$options, validateInput: false });
* ```
*/
$setOptions<Options extends ClientOptions<Schema>>(options: Options): ClientContract<Schema, Options>;

/**
* Returns a new client enabling/disabling input validations expressed with attributes like
* `@email`, `@regex`, `@@validate`, etc.
*
* @deprecated Use `$setOptions` instead.
*/
$setInputValidation(enable: boolean): ClientContract<Schema, Options>;

Expand Down
12 changes: 11 additions & 1 deletion packages/orm/src/client/crud/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export class InputValidator<Schema extends SchemaDef> {
return this.client.$schema;
}

private get options() {
return this.client.$options;
}

private get extraValidationsEnabled() {
return this.client.$options.validateInput !== false;
}
Expand Down Expand Up @@ -783,7 +787,13 @@ export class InputValidator<Schema extends SchemaDef> {
for (const field of Object.keys(modelDef.fields)) {
const fieldDef = requireField(this.schema, model, field);
if (!fieldDef.relation) {
fields[field] = z.boolean().optional();
if (this.options.allowQueryTimeOmitOverride !== false) {
// if override is allowed, use boolean
fields[field] = z.boolean().optional();
} else {
// otherwise only allow true
fields[field] = z.literal(true).optional();
}
}
}
return z.strictObject(fields);
Expand Down
23 changes: 15 additions & 8 deletions packages/orm/src/client/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,26 @@ export type ClientOptions<Schema extends SchemaDef> = {
* `@@validate`, etc. Defaults to `true`.
*/
validateInput?: boolean;
} & {

/**
* Options for omitting fields in ORM query results.
*/
omit?: OmitOptions<Schema>;

/**
* Whether to allow overriding omit settings at query time. Defaults to `true`. When set to
* `false`, an `omit` clause that set field to `false` (not omitting) will trigger a validation
* error.
*/
allowQueryTimeOmitOverride?: boolean;
} & (HasComputedFields<Schema> extends true
? {
/**
* Computed field definitions.
*/
computedFields: ComputedFieldsOptions<Schema>;
}
: {}) &
? {
/**
* Computed field definitions.
*/
computedFields: ComputedFieldsOptions<Schema>;
}
: {}) &
(HasProcedures<Schema> extends true
? {
/**
Expand Down
19 changes: 19 additions & 0 deletions tests/e2e/orm/client-api/omit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,23 @@ describe('Field omission tests', () => {
const read1 = await db.sub.findFirstOrThrow({ omit: { content: false } });
expect(read1.content).toBe('Foo');
});

it('respects query-level omit override settings', async () => {
const base = await createTestClient(schema);
const db = base.$setOptions({ ...base.$options, allowQueryTimeOmitOverride: false });
await expect(
db.user.create({
data: { id: 1, name: 'User1', password: 'abc' },
omit: { password: false },
}),
).toBeRejectedByValidation();

await db.user.create({ data: { id: 1, name: 'User1', password: 'abc' } });
await expect(db.user.findFirstOrThrow({ omit: { password: false } })).toBeRejectedByValidation();

// allow set omit as true
const user = await db.user.findFirstOrThrow({ omit: { name: true } });
// @ts-expect-error
expect(user.name).toBeUndefined();
});
});
6 changes: 4 additions & 2 deletions tests/e2e/orm/validation/custom-validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,10 @@ describe('Custom validation tests', () => {
}),
).toBeRejectedByValidation();

const dbNoValidation = db.$setOptions({ ...db.$options, validateInput: false });

await expect(
db.$setInputValidation(false).user.create({
dbNoValidation.user.create({
data: {
id: 1,
email: 'xyz',
Expand All @@ -153,7 +155,7 @@ describe('Custom validation tests', () => {
).toResolveTruthy();

await expect(
db.$setInputValidation(false).user.update({
dbNoValidation.user.update({
where: { id: 1 },
data: {
email: '[email protected]',
Expand Down
Loading