Skip to content

Commit 7cf0e98

Browse files
committed
fix: don't set default value in nested writes when set through FK
1 parent 30cdcfb commit 7cf0e98

File tree

2 files changed

+152
-9
lines changed

2 files changed

+152
-9
lines changed

packages/runtime/src/enhancements/node/default-auth.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import { ACTIONS_WITH_WRITE_PAYLOAD } from '../../constants';
55
import {
66
FieldInfo,
77
NestedWriteVisitor,
8+
NestedWriteVisitorContext,
89
PrismaWriteActionType,
910
clone,
1011
enumerate,
1112
getFields,
13+
getModelInfo,
1214
getTypeDefInfo,
1315
requireField,
1416
} from '../../cross';
@@ -61,7 +63,7 @@ class DefaultAuthHandler extends DefaultPrismaProxyHandler {
6163
private async preprocessWritePayload(model: string, action: PrismaWriteActionType, args: any) {
6264
const newArgs = clone(args);
6365

64-
const processCreatePayload = (model: string, data: any) => {
66+
const processCreatePayload = (model: string, data: any, context: NestedWriteVisitorContext) => {
6567
const fields = getFields(this.options.modelMeta, model);
6668
for (const fieldInfo of Object.values(fields)) {
6769
if (fieldInfo.isTypeDef) {
@@ -82,24 +84,24 @@ class DefaultAuthHandler extends DefaultPrismaProxyHandler {
8284
const defaultValue = this.getDefaultValue(fieldInfo);
8385
if (defaultValue !== undefined) {
8486
// set field value extracted from `auth()`
85-
this.setDefaultValueForModelData(fieldInfo, model, data, defaultValue);
87+
this.setDefaultValueForModelData(fieldInfo, model, data, defaultValue, context);
8688
}
8789
}
8890
};
8991

9092
// visit create payload and set default value to fields using `auth()` in `@default()`
9193
const visitor = new NestedWriteVisitor(this.options.modelMeta, {
92-
create: (model, data) => {
93-
processCreatePayload(model, data);
94+
create: (model, data, context) => {
95+
processCreatePayload(model, data, context);
9496
},
9597

96-
upsert: (model, data) => {
97-
processCreatePayload(model, data.create);
98+
upsert: (model, data, context) => {
99+
processCreatePayload(model, data.create, context);
98100
},
99101

100-
createMany: (model, args) => {
102+
createMany: (model, args, context) => {
101103
for (const item of enumerate(args.data)) {
102-
processCreatePayload(model, item);
104+
processCreatePayload(model, item, context);
103105
}
104106
},
105107
});
@@ -108,12 +110,32 @@ class DefaultAuthHandler extends DefaultPrismaProxyHandler {
108110
return newArgs;
109111
}
110112

111-
private setDefaultValueForModelData(fieldInfo: FieldInfo, model: string, data: any, authDefaultValue: unknown) {
113+
private setDefaultValueForModelData(
114+
fieldInfo: FieldInfo,
115+
model: string,
116+
data: any,
117+
authDefaultValue: unknown,
118+
context: NestedWriteVisitorContext
119+
) {
112120
if (fieldInfo.isForeignKey && fieldInfo.relationField && fieldInfo.relationField in data) {
113121
// if the field is a fk, and the relation field is already set, we should not override it
114122
return;
115123
}
116124

125+
if (context.field?.backLink) {
126+
const modelInfo = getModelInfo(this.options.modelMeta, model);
127+
const parentModel = modelInfo?.fields[context.field.backLink];
128+
129+
if (
130+
parentModel?.isDataModel &&
131+
parentModel.foreignKeyMapping &&
132+
Object.keys(parentModel.foreignKeyMapping).includes(fieldInfo.name)
133+
) {
134+
// if the field is part of a fk as part of a nested write, then prisma handles setting it
135+
return;
136+
}
137+
}
138+
117139
if (fieldInfo.isForeignKey && !isUnsafeMutate(model, data, this.options.modelMeta)) {
118140
// if the field is a fk, and the create payload is not unsafe, we need to translate
119141
// the fk field setting to a `connect` of the corresponding relation field
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { loadSchema } from '@zenstackhq/testtools';
2+
3+
describe('issue 1989', () => {
4+
it('regression', async () => {
5+
const { prisma, enhance } = await loadSchema(
6+
`
7+
model Tenant {
8+
id String @id @default(uuid())
9+
10+
users User[]
11+
posts Post[]
12+
comments Comment[]
13+
postUserLikes PostUserLikes[]
14+
}
15+
16+
model User {
17+
id String @id @default(uuid())
18+
tenantId String @default(auth().tenantId)
19+
tenant Tenant @relation(fields: [tenantId], references: [id])
20+
posts Post[]
21+
likes PostUserLikes[]
22+
23+
@@allow('all', true)
24+
}
25+
26+
model Post {
27+
tenantId String @default(auth().tenantId)
28+
tenant Tenant @relation(fields: [tenantId], references: [id])
29+
id String @default(uuid())
30+
author User @relation(fields: [authorId], references: [id])
31+
authorId String @default(auth().id)
32+
33+
comments Comment[]
34+
likes PostUserLikes[]
35+
36+
@@id([tenantId, id])
37+
38+
@@allow('all', true)
39+
}
40+
41+
model PostUserLikes {
42+
tenantId String @default(auth().tenantId)
43+
tenant Tenant @relation(fields: [tenantId], references: [id])
44+
id String @default(uuid())
45+
46+
userId String
47+
user User @relation(fields: [userId], references: [id])
48+
49+
postId String
50+
post Post @relation(fields: [tenantId, postId], references: [tenantId, id])
51+
52+
@@id([tenantId, id])
53+
@@unique([tenantId, userId, postId])
54+
55+
@@allow('all', true)
56+
}
57+
58+
model Comment {
59+
tenantId String @default(auth().tenantId)
60+
tenant Tenant @relation(fields: [tenantId], references: [id])
61+
id String @default(uuid())
62+
postId String
63+
post Post @relation(fields: [tenantId, postId], references: [tenantId, id])
64+
65+
@@id([tenantId, id])
66+
67+
@@allow('all', true)
68+
}
69+
`,
70+
{ logPrismaQuery: true }
71+
);
72+
73+
const tenant = await prisma.tenant.create({
74+
data: {},
75+
});
76+
const user = await prisma.user.create({
77+
data: { tenantId: tenant.id },
78+
});
79+
80+
const db = enhance({ id: user.id, tenantId: tenant.id });
81+
82+
await expect(db.post.create({
83+
data: {
84+
likes: {
85+
createMany: {
86+
data: [{
87+
userId: user.id
88+
}]
89+
}
90+
}
91+
},
92+
include: {
93+
likes: true
94+
}
95+
})).resolves.toMatchObject({
96+
authorId: user.id,
97+
likes: [{
98+
tenantId: tenant.id,
99+
userId: user.id
100+
}]
101+
});
102+
103+
await expect(db.post.create({
104+
data: {
105+
comments: {
106+
createMany: {
107+
data: [{}]
108+
}
109+
}
110+
},
111+
include: {
112+
comments: true
113+
}
114+
})).resolves.toMatchObject({
115+
authorId: user.id,
116+
comments: [{
117+
tenantId: tenant.id
118+
}]
119+
});
120+
});
121+
});

0 commit comments

Comments
 (0)