Skip to content

Commit bb64b8a

Browse files
committed
fix: incorrect relation owner analysis
Should check if a `@relation` attribute really has `fields` argument to determine if it holds foreign key.
1 parent ea7de9c commit bb64b8a

File tree

2 files changed

+72
-6
lines changed

2 files changed

+72
-6
lines changed

packages/schema/src/plugins/model-meta/index.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { RuntimeAttribute } from '@zenstackhq/runtime';
1111
import {
1212
createProject,
1313
emitProject,
14+
getAttributeArg,
1415
getAttributeArgs,
1516
getDataModels,
1617
getLiteral,
@@ -182,14 +183,25 @@ function isRelationOwner(field: DataModelField, backLink: DataModelField | undef
182183
return false;
183184
}
184185

185-
if (hasAttribute(field, '@relation')) {
186-
// this field has `@relation` attribute
186+
if (!backLink) {
187+
// CHECKME: can this really happen?
187188
return true;
188-
} else if (!backLink || !hasAttribute(backLink, '@relation')) {
189-
// if the opposite side field doesn't have `@relation` attribute either,
190-
// it's an implicit many-to-many relation, both sides are owners
189+
}
190+
191+
if (!hasAttribute(field, '@relation') && !hasAttribute(backLink, '@relation')) {
192+
// if neither side has `@relation` attribute, it's an implicit many-to-many relation,
193+
// both sides are owners
191194
return true;
192-
} else {
195+
}
196+
197+
return holdsForeignKey(field);
198+
}
199+
200+
function holdsForeignKey(field: DataModelField) {
201+
const relation = field.attributes.find((attr) => attr.decl.ref?.name === '@relation');
202+
if (!relation) {
193203
return false;
194204
}
205+
const fields = getAttributeArg(relation, 'fields');
206+
return !!fields;
195207
}

tests/integration/tests/regression/issues.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,58 @@ describe('GitHub issues regression', () => {
300300
},
301301
});
302302
});
303+
304+
it('issue 609', async () => {
305+
const { withPolicy, prisma } = await loadSchema(
306+
`
307+
model User {
308+
id String @id @default(cuid())
309+
comments Comment[]
310+
}
311+
312+
model Comment {
313+
id String @id @default(cuid())
314+
parentCommentId String?
315+
replies Comment[] @relation("CommentToComment")
316+
parent Comment? @relation("CommentToComment", fields: [parentCommentId], references: [id])
317+
comment String
318+
author User @relation(fields: [authorId], references: [id])
319+
authorId String
320+
321+
@@allow('read,create', true)
322+
@@allow('update,delete', auth() == author)
323+
}
324+
`
325+
);
326+
327+
await prisma.user.create({
328+
data: {
329+
id: '1',
330+
comments: {
331+
create: {
332+
id: '1',
333+
comment: 'Comment 1',
334+
},
335+
},
336+
},
337+
});
338+
339+
await prisma.user.create({
340+
data: {
341+
id: '2',
342+
},
343+
});
344+
345+
// connecting a child comment from a different user to a parent comment should succeed
346+
const db = withPolicy({ id: '2' });
347+
await expect(
348+
db.comment.create({
349+
data: {
350+
comment: 'Comment 2',
351+
author: { connect: { id: '2' } },
352+
parent: { connect: { id: '1' } },
353+
},
354+
})
355+
).toResolveTruthy();
356+
});
303357
});

0 commit comments

Comments
 (0)