diff --git a/packages/runtime/src/enhancements/node/delegate.ts b/packages/runtime/src/enhancements/node/delegate.ts index 06c1526e5..59bc79793 100644 --- a/packages/runtime/src/enhancements/node/delegate.ts +++ b/packages/runtime/src/enhancements/node/delegate.ts @@ -1106,7 +1106,18 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { const entities = await db[model].findMany(findArgs); // recursively delete base entities (they all have the same id values) - await Promise.all(entities.map((entity) => this.doDelete(db, model, { where: entity }))); + + await Promise.all( + entities.map((entity) => { + let deleteFilter = entity; + if (Object.keys(deleteFilter).length > 1) { + // if the model has compound id fields, we need to compose a compound key filter, + // otherwise calling Prisma's `delete` won't work + deleteFilter = this.queryUtils.composeCompoundUniqueField(model, deleteFilter); + } + return this.doDelete(db, model, { where: deleteFilter }); + }) + ); return { count: entities.length }; } @@ -1114,7 +1125,13 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler { private async deleteBaseRecursively(db: CrudContract, model: string, idValues: any) { let base = this.getBaseModel(model); while (base) { - await db[base.name].delete({ where: idValues }); + let deleteFilter = idValues; + if (Object.keys(idValues).length > 1) { + // if the model has compound id fields, we need to compose a compound key filter, + // otherwise calling Prisma's `delete` won't work + deleteFilter = this.queryUtils.composeCompoundUniqueField(base.name, deleteFilter); + } + await db[base.name].delete({ where: deleteFilter }); base = this.getBaseModel(base.name); } } diff --git a/tests/regression/tests/issue-1998.test.ts b/tests/regression/tests/issue-1998.test.ts new file mode 100644 index 000000000..a2810dfea --- /dev/null +++ b/tests/regression/tests/issue-1998.test.ts @@ -0,0 +1,59 @@ +import { loadSchema } from '@zenstackhq/testtools'; + +describe('issue 1998', () => { + it('regression', async () => { + const { enhance } = await loadSchema( + ` + model Entity { + id String @id + type String + updatable Boolean + children Relation[] @relation("children") + parents Relation[] @relation("parents") + + @@delegate(type) + @@allow('create,read', true) + @@allow('update', updatable) + } + + model A extends Entity {} + + model B extends Entity {} + + model Relation { + parent Entity @relation("children", fields: [parentId], references: [id]) + parentId String + child Entity @relation("parents", fields: [childId], references: [id]) + childId String + + @@allow('create', true) + @@allow('read', check(parent, 'read') && check(child, 'read')) + @@allow('delete', check(parent, 'update') && check(child, 'update')) + + @@id([parentId, childId]) + } + ` + ); + + const db = enhance(); + + await db.a.create({ data: { id: '1', updatable: true } }); + await db.b.create({ data: { id: '2', updatable: true } }); + await db.relation.create({ data: { parentId: '1', childId: '2' } }); + + await expect( + db.relation.deleteMany({ + where: { parentId: '1', childId: '2' }, + }) + ).resolves.toEqual({ count: 1 }); + + await db.a.create({ data: { id: '3', updatable: false } }); + await db.b.create({ data: { id: '4', updatable: false } }); + await db.relation.create({ data: { parentId: '3', childId: '4' } }); + await expect( + db.relation.deleteMany({ + where: { parentId: '3', childId: '4' }, + }) + ).resolves.toEqual({ count: 0 }); + }); +});