Skip to content

Commit fbcc6e4

Browse files
ymc9Copilot
andauthored
feat(orm): add settings to disallow query-time omit override (#443)
* feat(orm): add settings to disallow query-time omit override * Update packages/orm/src/client/options.ts Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 2d740f1 commit fbcc6e4

File tree

6 files changed

+67
-11
lines changed

6 files changed

+67
-11
lines changed

packages/orm/src/client/client-impl.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,13 @@ export class ClientImpl {
299299
return this.auth;
300300
}
301301

302+
$setOptions<Options extends ClientOptions<SchemaDef>>(options: Options): ClientContract<SchemaDef, Options> {
303+
return new ClientImpl(this.schema, options as ClientOptions<SchemaDef>, this) as unknown as ClientContract<
304+
SchemaDef,
305+
Options
306+
>;
307+
}
308+
302309
$setInputValidation(enable: boolean) {
303310
const newOptions: ClientOptions<SchemaDef> = {
304311
...this.options,

packages/orm/src/client/contract.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,20 @@ export type ClientContract<Schema extends SchemaDef, Options extends ClientOptio
114114
*/
115115
$setAuth(auth: AuthType<Schema> | undefined): ClientContract<Schema, Options>;
116116

117+
/**
118+
* Returns a new client with new options applied.
119+
* @example
120+
* ```
121+
* const dbNoValidation = db.$setOptions({ ...db.$options, validateInput: false });
122+
* ```
123+
*/
124+
$setOptions<Options extends ClientOptions<Schema>>(options: Options): ClientContract<Schema, Options>;
125+
117126
/**
118127
* Returns a new client enabling/disabling input validations expressed with attributes like
119128
* `@email`, `@regex`, `@@validate`, etc.
129+
*
130+
* @deprecated Use `$setOptions` instead.
120131
*/
121132
$setInputValidation(enable: boolean): ClientContract<Schema, Options>;
122133

packages/orm/src/client/crud/validator/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ export class InputValidator<Schema extends SchemaDef> {
6161
return this.client.$schema;
6262
}
6363

64+
private get options() {
65+
return this.client.$options;
66+
}
67+
6468
private get extraValidationsEnabled() {
6569
return this.client.$options.validateInput !== false;
6670
}
@@ -783,7 +787,13 @@ export class InputValidator<Schema extends SchemaDef> {
783787
for (const field of Object.keys(modelDef.fields)) {
784788
const fieldDef = requireField(this.schema, model, field);
785789
if (!fieldDef.relation) {
786-
fields[field] = z.boolean().optional();
790+
if (this.options.allowQueryTimeOmitOverride !== false) {
791+
// if override is allowed, use boolean
792+
fields[field] = z.boolean().optional();
793+
} else {
794+
// otherwise only allow true
795+
fields[field] = z.literal(true).optional();
796+
}
787797
}
788798
}
789799
return z.strictObject(fields);

packages/orm/src/client/options.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,26 @@ export type ClientOptions<Schema extends SchemaDef> = {
7878
* `@@validate`, etc. Defaults to `true`.
7979
*/
8080
validateInput?: boolean;
81-
} & {
81+
8282
/**
8383
* Options for omitting fields in ORM query results.
8484
*/
8585
omit?: OmitOptions<Schema>;
86+
87+
/**
88+
* Whether to allow overriding omit settings at query time. Defaults to `true`. When set to
89+
* `false`, an `omit` clause that sets field to `false` (not omitting) will trigger a validation
90+
* error.
91+
*/
92+
allowQueryTimeOmitOverride?: boolean;
8693
} & (HasComputedFields<Schema> extends true
87-
? {
88-
/**
89-
* Computed field definitions.
90-
*/
91-
computedFields: ComputedFieldsOptions<Schema>;
92-
}
93-
: {}) &
94+
? {
95+
/**
96+
* Computed field definitions.
97+
*/
98+
computedFields: ComputedFieldsOptions<Schema>;
99+
}
100+
: {}) &
94101
(HasProcedures<Schema> extends true
95102
? {
96103
/**

tests/e2e/orm/client-api/omit.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,23 @@ describe('Field omission tests', () => {
140140
const read1 = await db.sub.findFirstOrThrow({ omit: { content: false } });
141141
expect(read1.content).toBe('Foo');
142142
});
143+
144+
it('respects query-level omit override settings', async () => {
145+
const base = await createTestClient(schema);
146+
const db = base.$setOptions({ ...base.$options, allowQueryTimeOmitOverride: false });
147+
await expect(
148+
db.user.create({
149+
data: { id: 1, name: 'User1', password: 'abc' },
150+
omit: { password: false },
151+
}),
152+
).toBeRejectedByValidation();
153+
154+
await db.user.create({ data: { id: 1, name: 'User1', password: 'abc' } });
155+
await expect(db.user.findFirstOrThrow({ omit: { password: false } })).toBeRejectedByValidation();
156+
157+
// allow set omit as true
158+
const user = await db.user.findFirstOrThrow({ omit: { name: true } });
159+
// @ts-expect-error
160+
expect(user.name).toBeUndefined();
161+
});
143162
});

tests/e2e/orm/validation/custom-validation.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,10 @@ describe('Custom validation tests', () => {
143143
}),
144144
).toBeRejectedByValidation();
145145

146+
const dbNoValidation = db.$setOptions({ ...db.$options, validateInput: false });
147+
146148
await expect(
147-
db.$setInputValidation(false).user.create({
149+
dbNoValidation.user.create({
148150
data: {
149151
id: 1,
150152
email: 'xyz',
@@ -153,7 +155,7 @@ describe('Custom validation tests', () => {
153155
).toResolveTruthy();
154156

155157
await expect(
156-
db.$setInputValidation(false).user.update({
158+
dbNoValidation.user.update({
157159
where: { id: 1 },
158160
data: {
159161

0 commit comments

Comments
 (0)