Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
43 changes: 31 additions & 12 deletions packages/runtime/src/enhancements/delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,11 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
);
}

// note that we can't call `createMany` directly because it doesn't support
// nested created, which is needed for creating base entities
// `createMany` doesn't support nested create, which is needed for creating entities
// inheriting a delegate base, so we need to convert it to a regular `create` here.
// Note that the main difference is `create` doesn't support `skipDuplicates` as
// `createMany` does.

return this.queryUtils.transaction(this.prisma, async (tx) => {
const r = await Promise.all(
enumerate(args.data).map(async (item) => {
Expand Down Expand Up @@ -423,17 +426,33 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
this.doProcessCreatePayload(model, args);
},

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

for (const item of enumerate(args?.data)) {
this.doProcessCreatePayload(model, item);
if (this.isDelegateOrDescendantOfDelegate(model)) {
if (args.skipDuplicates) {
throw prismaClientValidationError(
this.prisma,
this.options.prismaModule,
'`createMany` with `skipDuplicates` set to true is not supported for delegated models'
);
}

// convert to regular `create`
let createPayload = context.parent.create ?? [];
if (!Array.isArray(createPayload)) {
createPayload = [createPayload];
}

for (const item of enumerate(args.data)) {
this.doProcessCreatePayload(model, item);
createPayload.push(item);
}
context.parent.create = createPayload;
delete context.parent['createMany'];
}
},
});
Expand Down
70 changes: 70 additions & 0 deletions tests/regression/tests/issue-1520.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { loadSchema } from '@zenstackhq/testtools';

describe('issue 1520', () => {
it('regression', async () => {
const { enhance } = await loadSchema(
`
model Course {
id Int @id @default(autoincrement())
title String
addedToNotifications AddedToCourseNotification[]
}

model Group {
id Int @id @default(autoincrement())
addedToNotifications AddedToGroupNotification[]
}

model Notification {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
type String
senderId Int
receiverId Int
@@delegate (type)
}

model AddedToGroupNotification extends Notification {
groupId Int
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
}

model AddedToCourseNotification extends Notification {
courseId Int
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
}
`,
{ enhancements: ['delegate'] }
);

const db = enhance();
const r = await db.course.create({
data: {
title: 'English classes',
addedToNotifications: {
createMany: {
data: [
{
id: 1,
receiverId: 1,
senderId: 2,
},
],
},
},
},
include: { addedToNotifications: true },
});

expect(r.addedToNotifications).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: 1,
courseId: 1,
receiverId: 1,
senderId: 2,
}),
])
);
});
});