Skip to content

Commit

Permalink
fix: createManyAndReturn doesn't work for polymorphic models
Browse files Browse the repository at this point in the history
  • Loading branch information
ymc9 committed Jul 14, 2024
1 parent 6439fd6 commit 43b90f7
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 7 deletions.
41 changes: 39 additions & 2 deletions packages/runtime/src/enhancements/delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,46 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
return this.doCreate(tx, this.model, { data: item });
})
);
return { count: r.length };
});
}

override createManyAndReturn(args: { data: any; select?: any; skipDuplicates?: boolean }): Promise<unknown[]> {
if (!args) {
throw prismaClientValidationError(this.prisma, this.options.prismaModule, 'query argument is required');
}
if (!args.data) {
throw prismaClientValidationError(
this.prisma,
this.options.prismaModule,
'data field is required in query argument'
);
}

if (!this.involvesDelegateModel(this.model)) {
return super.createManyAndReturn(args);
}

// filter out undefined value (due to skipping duplicates)
return { count: r.filter((item) => !!item).length };
if (this.isDelegateOrDescendantOfDelegate(this.model) && args.skipDuplicates) {
throw prismaClientValidationError(
this.prisma,
this.options.prismaModule,
'`createManyAndReturn` with `skipDuplicates` set to true is not supported for delegated models'
);
}

// `createManyAndReturn` 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
// `createManyAndReturn` does.

return this.queryUtils.transaction(this.prisma, async (tx) => {
const r = await Promise.all(
enumerate(args.data).map(async (item) => {
return this.doCreate(tx, this.model, { data: item, select: args.select });
})
);
return r;
});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/enhancements/policy/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
});
}

createManyAndReturn(args: { select: any; include: any; data: any; skipDuplicates?: boolean }) {
createManyAndReturn(args: { data: any; select?: any; skipDuplicates?: boolean }) {
if (!args) {
throw prismaClientValidationError(this.prisma, this.prismaModule, 'query argument is required');
}
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/enhancements/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export interface PrismaProxyHandler {

createMany(args: { data: any; skipDuplicates?: boolean }): Promise<BatchResult>;

createManyAndReturn(args: { data: any; select: any; include: any; skipDuplicates?: boolean }): Promise<unknown[]>;
createManyAndReturn(args: { data: any; select?: any; skipDuplicates?: boolean }): Promise<unknown[]>;

update(args: any): Promise<unknown>;

Expand Down Expand Up @@ -124,7 +124,7 @@ export class DefaultPrismaProxyHandler implements PrismaProxyHandler {
return this.deferred<{ count: number }>('createMany', args, false);
}

createManyAndReturn(args: { data: any; select: any; include: any; skipDuplicates?: boolean }) {
createManyAndReturn(args: { data: any; select?: any; skipDuplicates?: boolean }) {
return this.deferred<unknown[]>('createManyAndReturn', args);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,44 @@ describe('Polymorphism Test', () => {
db.ratedVideo.createMany({ data: { viewCount: 1, duration: 100, url: 'xyz', rating: 100 } })
).resolves.toMatchObject({ count: 1 });

await expect(
db.ratedVideo.createManyAndReturn({ data: { viewCount: 1, duration: 100, url: 'xyz', rating: 100 } })
).resolves.toEqual(
expect.arrayContaining([
expect.objectContaining({
assetType: 'Video',
videoType: 'RatedVideo',
viewCount: 1,
duration: 100,
url: 'xyz',
rating: 100,
}),
])
);

await expect(
db.ratedVideo.createMany({
data: [
{ viewCount: 2, duration: 200, url: 'xyz', rating: 100 },
{ viewCount: 3, duration: 300, url: 'xyz', rating: 100 },
{ viewCount: 3, duration: 300, url: 'xyz', rating: 200 },
],
})
).resolves.toMatchObject({ count: 2 });

await expect(
db.ratedVideo.createManyAndReturn({
data: [
{ viewCount: 2, duration: 200, url: 'xyz', rating: 100 },
{ viewCount: 3, duration: 300, url: 'xyz', rating: 200 },
],
select: { videoType: true, viewCount: true, rating: true },
})
).resolves.toEqual(
expect.arrayContaining([
{ videoType: 'RatedVideo', viewCount: 2, rating: 100 },
{ videoType: 'RatedVideo', viewCount: 3, rating: 200 },
])
);
});

it('create many polymorphic relation', async () => {
Expand All @@ -154,7 +184,7 @@ describe('Polymorphism Test', () => {
data: { viewCount: 1, duration: 100, url: 'xyz', rating: 100 },
});
await expect(
db.user.createMany({ data: [{ id: 2, assets: { connect: { id: video2.id } } }, { id: 3 }] })
db.user.createMany({ data: [{ id: 3, assets: { connect: { id: video2.id } } }, { id: 3 }] })
).resolves.toMatchObject({ count: 2 });
});

Expand Down
63 changes: 63 additions & 0 deletions tests/regression/tests/issue-1576.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { loadSchema } from '@zenstackhq/testtools';
describe('issue 1576', () => {
it('regression', async () => {
const { enhance } = await loadSchema(
`
model Profile {
id Int @id @default(autoincrement())
name String
items Item[]
type String
@@delegate(type)
@@allow('all', true)
}
model GoldProfile extends Profile {
ticket Int
}
model Item {
id Int @id @default(autoincrement())
profileId Int
profile Profile @relation(fields: [profileId], references: [id])
type String
@@delegate(type)
@@allow('all', true)
}
model GoldItem extends Item {
inventory Boolean
}
`
);

const db = enhance();

const profile = await db.goldProfile.create({
data: {
name: 'hello',
ticket: 5,
},
});

await expect(
db.goldItem.createManyAndReturn({
data: [
{
profileId: profile.id,
inventory: true,
},
{
profileId: profile.id,
inventory: true,
},
],
})
).resolves.toEqual(
expect.arrayContaining([
expect.objectContaining({ profileId: profile.id, type: 'GoldItem', inventory: true }),
expect.objectContaining({ profileId: profile.id, type: 'GoldItem', inventory: true }),
])
);
});
});

0 comments on commit 43b90f7

Please sign in to comment.