diff --git a/packages/runtime/src/enhancements/policy/policy-utils.ts b/packages/runtime/src/enhancements/policy/policy-utils.ts index 12c8c57be..98c49957c 100644 --- a/packages/runtime/src/enhancements/policy/policy-utils.ts +++ b/packages/runtime/src/enhancements/policy/policy-utils.ts @@ -515,7 +515,7 @@ export class PolicyUtil { throw this.unknownError(`missing backLink field ${currField.backLink} in ${currField.type}`); } - if (backLinkField.isArray) { + if (backLinkField.isArray && !mutating) { // many-side of relationship, wrap with "some" query currQuery[currField.backLink] = { some: { ...visitWhere } }; } else { diff --git a/tests/integration/tests/cli/plugins.test.ts b/tests/integration/tests/cli/plugins.test.ts index d2461c4ec..005a0f69b 100644 --- a/tests/integration/tests/cli/plugins.test.ts +++ b/tests/integration/tests/cli/plugins.test.ts @@ -71,7 +71,7 @@ describe('CLI Plugins Tests', () => { 'zod@3.21.1', 'react', 'swr', - '@tanstack/react-query', + '@tanstack/react-query@^4.0.0', '@trpc/server', '@prisma/client@^4.0.0', `${path.join(__dirname, '../../../../.build/zenstackhq-language-' + ver + '.tgz')}`, diff --git a/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts b/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts index fa7059faa..b112aeeb1 100644 --- a/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts +++ b/tests/integration/tests/enhancements/with-policy/nested-to-many.test.ts @@ -284,7 +284,7 @@ describe('With Policy:nested to-many', () => { expect(r.m2).toEqual(expect.arrayContaining([expect.objectContaining({ id: '2', value: 3 })])); }); - it('update with create', async () => { + it('update with create from one to many', async () => { const { withPolicy } = await loadSchema( ` model M1 { @@ -341,6 +341,56 @@ describe('With Policy:nested to-many', () => { expect(r.m2).toHaveLength(3); }); + it('update with create from many to one', async () => { + const { withPolicy } = await loadSchema( + ` + model M1 { + id String @id @default(uuid()) + value Int + m2 M2[] + + @@allow('read', true) + @@allow('create', value > 0) + @@allow('update', value > 1) + } + + model M2 { + id String @id @default(uuid()) + m1 M1? @relation(fields: [m1Id], references:[id]) + m1Id String? + + @@allow('all', true) + } + ` + ); + + const db = withPolicy(); + + await db.m2.create({ data: { id: '1' } }); + + await expect( + db.m2.update({ + where: { id: '1' }, + data: { + m1: { + create: { value: 0 }, + }, + }, + }) + ).toBeRejectedByPolicy(); + + await expect( + db.m2.update({ + where: { id: '1' }, + data: { + m1: { + create: { value: 1 }, + }, + }, + }) + ).toResolveTruthy(); + }); + it('update with delete', async () => { const { withPolicy, prisma } = await loadSchema( ` diff --git a/tests/integration/tests/regression/issue-764.test.ts b/tests/integration/tests/regression/issue-764.test.ts new file mode 100644 index 000000000..8e64a15d8 --- /dev/null +++ b/tests/integration/tests/regression/issue-764.test.ts @@ -0,0 +1,49 @@ +import { loadSchema } from '@zenstackhq/testtools'; + +describe('Regression: issue 764', () => { + it('regression', async () => { + const { prisma, enhance } = await loadSchema( + ` + model User { + id Int @id @default(autoincrement()) + name String + + post Post? @relation(fields: [postId], references: [id]) + postId Int? + + @@allow('all', true) + } + + model Post { + id Int @id @default(autoincrement()) + title String + User User[] + + @@allow('all', true) + } + ` + ); + + const db = enhance(); + + const user = await prisma.user.create({ + data: { name: 'Me' }, + }); + + await db.user.update({ + where: { id: user.id }, + data: { + post: { + upsert: { + create: { + title: 'Hello World', + }, + update: { + title: 'Hello World', + }, + }, + }, + }, + }); + }); +});