Skip to content

Commit 613ac8d

Browse files
authored
fix: use zod parse result data as mutation input (#997)
1 parent 43eb615 commit 613ac8d

File tree

4 files changed

+106
-6
lines changed

4 files changed

+106
-6
lines changed

packages/runtime/src/enhancements/policy/handler.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
249249
// there's no nested write and we've passed input check, proceed with the create directly
250250

251251
// validate zod schema if any
252-
this.validateCreateInputSchema(this.model, args.data);
252+
args.data = this.validateCreateInputSchema(this.model, args.data);
253253

254254
// make a create args only containing data and ID selection
255255
const createArgs: any = { data: args.data, select: this.utils.makeIdSelection(this.model) };
@@ -305,12 +305,20 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
305305
// visit the create payload
306306
const visitor = new NestedWriteVisitor(this.modelMeta, {
307307
create: async (model, args, context) => {
308-
this.validateCreateInputSchema(model, args);
308+
const validateResult = this.validateCreateInputSchema(model, args);
309+
if (validateResult !== args) {
310+
this.utils.replace(args, validateResult);
311+
}
309312
pushIdFields(model, context);
310313
},
311314

312315
createMany: async (model, args, context) => {
313-
enumerate(args.data).forEach((item) => this.validateCreateInputSchema(model, item));
316+
enumerate(args.data).forEach((item) => {
317+
const r = this.validateCreateInputSchema(model, item);
318+
if (r !== item) {
319+
this.utils.replace(item, r);
320+
}
321+
});
314322
pushIdFields(model, context);
315323
},
316324

@@ -319,7 +327,9 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
319327
throw this.utils.validationError(`'where' field is required for connectOrCreate`);
320328
}
321329

322-
this.validateCreateInputSchema(model, args.create);
330+
if (args.create) {
331+
args.create = this.validateCreateInputSchema(model, args.create);
332+
}
323333

324334
const existing = await this.utils.checkExistence(db, model, args.where);
325335
if (existing) {
@@ -468,6 +478,9 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
468478
parseResult.error
469479
);
470480
}
481+
return parseResult.data;
482+
} else {
483+
return data;
471484
}
472485
}
473486

@@ -495,7 +508,10 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
495508
CrudFailureReason.ACCESS_POLICY_VIOLATION
496509
);
497510
} else if (inputCheck === true) {
498-
this.validateCreateInputSchema(this.model, item);
511+
const r = this.validateCreateInputSchema(this.model, item);
512+
if (r !== item) {
513+
this.utils.replace(item, r);
514+
}
499515
} else if (inputCheck === undefined) {
500516
// static policy check is not possible, need to do post-create check
501517
needPostCreateCheck = true;

packages/runtime/src/enhancements/policy/policy-utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,27 @@ export class PolicyUtil {
12761276
return value ? deepcopy(value) : {};
12771277
}
12781278

1279+
/**
1280+
* Replace content of `target` object with `withObject` in-place.
1281+
*/
1282+
replace(target: any, withObject: any) {
1283+
if (!target || typeof target !== 'object' || !withObject || typeof withObject !== 'object') {
1284+
return;
1285+
}
1286+
1287+
// remove missing keys
1288+
for (const key of Object.keys(target)) {
1289+
if (!(key in withObject)) {
1290+
delete target[key];
1291+
}
1292+
}
1293+
1294+
// overwrite keys
1295+
for (const [key, value] of Object.entries(withObject)) {
1296+
target[key] = value;
1297+
}
1298+
}
1299+
12791300
/**
12801301
* Picks properties from an object.
12811302
*/

packages/schema/src/plugins/zod/generator.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ async function generateModelSchema(model: DataModel, project: Project, output: s
395395
////////////////////////////////////////////////
396396

397397
// schema for validating prisma create input (all fields optional)
398-
let prismaCreateSchema = makePartial('baseSchema');
398+
let prismaCreateSchema = makePassthrough(makePartial('baseSchema'));
399399
if (refineFuncName) {
400400
prismaCreateSchema = `${refineFuncName}(${prismaCreateSchema})`;
401401
}
@@ -501,3 +501,7 @@ function makeOmit(schema: string, fields: string[]) {
501501
function makeMerge(schema1: string, schema2: string): string {
502502
return `${schema1}.merge(${schema2})`;
503503
}
504+
505+
function makePassthrough(schema: string) {
506+
return `${schema}.passthrough()`;
507+
}

tests/integration/tests/enhancements/with-policy/field-validation.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ describe('With Policy: field validation', () => {
3535
text3 String @length(min: 3)
3636
text4 String @length(max: 5)
3737
text5 String? @endsWith('xyz')
38+
text6 String? @trim @lower
39+
text7 String? @upper
3840
3941
@@allow('all', true)
4042
}
@@ -495,4 +497,61 @@ describe('With Policy: field validation', () => {
495497
})
496498
).toResolveTruthy();
497499
});
500+
501+
it('string transformation', async () => {
502+
await db.user.create({
503+
data: {
504+
id: '1',
505+
password: 'abc123!@#',
506+
email: 'who@myorg.com',
507+
handle: 'user1',
508+
},
509+
});
510+
511+
await expect(
512+
db.userData.create({
513+
data: {
514+
userId: '1',
515+
a: 1,
516+
b: 0,
517+
c: -1,
518+
d: 0,
519+
text1: 'abc123',
520+
text2: 'def',
521+
text3: 'aaa',
522+
text4: 'abcab',
523+
text6: ' AbC ',
524+
text7: 'abc',
525+
},
526+
})
527+
).resolves.toMatchObject({ text6: 'abc', text7: 'ABC' });
528+
529+
await expect(
530+
db.user.create({
531+
data: {
532+
id: '2',
533+
password: 'abc123!@#',
534+
email: 'who@myorg.com',
535+
handle: 'user2',
536+
userData: {
537+
create: {
538+
a: 1,
539+
b: 0,
540+
c: -1,
541+
d: 0,
542+
text1: 'abc123',
543+
text2: 'def',
544+
text3: 'aaa',
545+
text4: 'abcab',
546+
text6: ' AbC ',
547+
text7: 'abc',
548+
},
549+
},
550+
},
551+
include: { userData: true },
552+
})
553+
).resolves.toMatchObject({
554+
userData: expect.objectContaining({ text6: 'abc', text7: 'ABC' }),
555+
});
556+
});
498557
});

0 commit comments

Comments
 (0)