Skip to content

Commit 3e77974

Browse files
authored
fix(delegate): make sure nested createMany is converted into regular create (#1526)
1 parent 09ebea0 commit 3e77974

File tree

2 files changed

+101
-12
lines changed

2 files changed

+101
-12
lines changed

packages/runtime/src/enhancements/delegate.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,11 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
390390
);
391391
}
392392

393-
// note that we can't call `createMany` directly because it doesn't support
394-
// nested created, which is needed for creating base entities
393+
// `createMany` doesn't support nested create, which is needed for creating entities
394+
// inheriting a delegate base, so we need to convert it to a regular `create` here.
395+
// Note that the main difference is `create` doesn't support `skipDuplicates` as
396+
// `createMany` does.
397+
395398
return this.queryUtils.transaction(this.prisma, async (tx) => {
396399
const r = await Promise.all(
397400
enumerate(args.data).map(async (item) => {
@@ -423,17 +426,33 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
423426
this.doProcessCreatePayload(model, args);
424427
},
425428

426-
createMany: (model, args, _context) => {
427-
if (args.skipDuplicates) {
428-
throw prismaClientValidationError(
429-
this.prisma,
430-
this.options.prismaModule,
431-
'`createMany` with `skipDuplicates` set to true is not supported for delegated models'
432-
);
433-
}
429+
createMany: (model, args, context) => {
430+
// `createMany` doesn't support nested create, which is needed for creating entities
431+
// inheriting a delegate base, so we need to convert it to a regular `create` here.
432+
// Note that the main difference is `create` doesn't support `skipDuplicates` as
433+
// `createMany` does.
434434

435-
for (const item of enumerate(args?.data)) {
436-
this.doProcessCreatePayload(model, item);
435+
if (this.isDelegateOrDescendantOfDelegate(model)) {
436+
if (args.skipDuplicates) {
437+
throw prismaClientValidationError(
438+
this.prisma,
439+
this.options.prismaModule,
440+
'`createMany` with `skipDuplicates` set to true is not supported for delegated models'
441+
);
442+
}
443+
444+
// convert to regular `create`
445+
let createPayload = context.parent.create ?? [];
446+
if (!Array.isArray(createPayload)) {
447+
createPayload = [createPayload];
448+
}
449+
450+
for (const item of enumerate(args.data)) {
451+
this.doProcessCreatePayload(model, item);
452+
createPayload.push(item);
453+
}
454+
context.parent.create = createPayload;
455+
delete context.parent['createMany'];
437456
}
438457
},
439458
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { loadSchema } from '@zenstackhq/testtools';
2+
3+
describe('issue 1520', () => {
4+
it('regression', async () => {
5+
const { enhance } = await loadSchema(
6+
`
7+
model Course {
8+
id Int @id @default(autoincrement())
9+
title String
10+
addedToNotifications AddedToCourseNotification[]
11+
}
12+
13+
model Group {
14+
id Int @id @default(autoincrement())
15+
addedToNotifications AddedToGroupNotification[]
16+
}
17+
18+
model Notification {
19+
id Int @id @default(autoincrement())
20+
createdAt DateTime @default(now())
21+
type String
22+
senderId Int
23+
receiverId Int
24+
@@delegate (type)
25+
}
26+
27+
model AddedToGroupNotification extends Notification {
28+
groupId Int
29+
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
30+
}
31+
32+
model AddedToCourseNotification extends Notification {
33+
courseId Int
34+
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
35+
}
36+
`,
37+
{ enhancements: ['delegate'] }
38+
);
39+
40+
const db = enhance();
41+
const r = await db.course.create({
42+
data: {
43+
title: 'English classes',
44+
addedToNotifications: {
45+
createMany: {
46+
data: [
47+
{
48+
id: 1,
49+
receiverId: 1,
50+
senderId: 2,
51+
},
52+
],
53+
},
54+
},
55+
},
56+
include: { addedToNotifications: true },
57+
});
58+
59+
expect(r.addedToNotifications).toEqual(
60+
expect.arrayContaining([
61+
expect.objectContaining({
62+
id: 1,
63+
courseId: 1,
64+
receiverId: 1,
65+
senderId: 2,
66+
}),
67+
])
68+
);
69+
});
70+
});

0 commit comments

Comments
 (0)