Skip to content

Commit 8589b79

Browse files
authored
fix(delegate): delegate models returned in nested results don't include base fields (#1662)
1 parent 19a3b5d commit 8589b79

File tree

2 files changed

+220
-2
lines changed

2 files changed

+220
-2
lines changed

packages/runtime/src/enhancements/delegate.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,22 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
153153
if (args[kind] && typeof args[kind] === 'object') {
154154
for (const [field, value] of Object.entries<any>(args[kind])) {
155155
const fieldInfo = resolveField(this.options.modelMeta, model, field);
156-
if (fieldInfo && value !== undefined) {
156+
if (!fieldInfo) {
157+
continue;
158+
}
159+
160+
if (this.isDelegateOrDescendantOfDelegate(fieldInfo?.type) && value) {
161+
// delegate model, recursively inject hierarchy
162+
if (args[kind][field]) {
163+
if (args[kind][field] === true) {
164+
// make sure the payload is an object
165+
args[kind][field] = {};
166+
}
167+
this.injectSelectIncludeHierarchy(fieldInfo.type, args[kind][field]);
168+
}
169+
}
170+
171+
if (value !== undefined) {
157172
if (value?.orderBy) {
158173
// `orderBy` may contain fields from base types
159174
this.injectWhereHierarchy(fieldInfo.type, value.orderBy);
@@ -1127,7 +1142,7 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
11271142
return destination;
11281143
};
11291144

1130-
const result = deepmerge(upMerged, downMerged, {
1145+
const result: any = deepmerge(upMerged, downMerged, {
11311146
arrayMerge: combineMerge,
11321147
isMergeableObject: (v) => isPlainObject(v) || Array.isArray(v), // avoid messing with Decimal, Date, etc.
11331148
});
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import { loadSchema } from '@zenstackhq/testtools';
2+
describe('issue 1645', () => {
3+
it('regression', async () => {
4+
const { enhance } = await loadSchema(
5+
`
6+
model Product {
7+
id String @id @default(cuid())
8+
name String
9+
slug String
10+
description String?
11+
sku String
12+
price Int
13+
onSale Boolean @default(false)
14+
salePrice Int @default(0)
15+
saleStartDateTime DateTime?
16+
saleEndDateTime DateTime?
17+
scheduledAvailability Boolean @default(false)
18+
availabilityStartDateTime DateTime?
19+
availabilityEndDateTime DateTime?
20+
type String @default('VARIABLE')
21+
image String
22+
orderItems OrderItem[]
23+
createdAt DateTime @default(now())
24+
updatedAt DateTime @updatedAt
25+
26+
@@unique([slug])
27+
28+
@@allow('all', true)
29+
}
30+
31+
model BaseOrder {
32+
id String @id @default(cuid())
33+
orderNumber String @unique @default(nanoid(16))
34+
lineItems OrderItem[]
35+
status String @default('PENDING')
36+
type String @default('PARENT')
37+
userType String?
38+
billingAddress BillingAddress @relation(fields: [billingAddressId], references: [id])
39+
billingAddressId String @map("billing_address_id")
40+
shippingAddress ShippingAddress @relation(fields: [shippingAddressId], references: [id])
41+
shippingAddressId String @map("shipping_address_id")
42+
notes String? @default('')
43+
createdAt DateTime @default(now())
44+
updatedAt DateTime @updatedAt
45+
46+
@@allow('all', true)
47+
@@delegate(userType)
48+
}
49+
50+
model Order extends BaseOrder {
51+
parentId String? @map("parent_id")
52+
parent Order? @relation("OrderToParent", fields: [parentId], references: [id])
53+
groupedOrders Order[] @relation("OrderToParent")
54+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
55+
userId String @map("user_id")
56+
}
57+
58+
model User {
59+
id String @id @default(cuid())
60+
name String?
61+
email String? @unique
62+
emailVerified DateTime?
63+
image String?
64+
orders Order[]
65+
billingAddresses BillingAddress[]
66+
shippingAddresses ShippingAddress[]
67+
68+
@@allow('create,read', true)
69+
@@allow('update,delete', auth().id == this.id)
70+
}
71+
72+
model GuestUser {
73+
id String @id @default(cuid())
74+
name String?
75+
email String @unique
76+
orders GuestOrder[]
77+
createdAt DateTime @default(now())
78+
updatedAt DateTime @updatedAt
79+
80+
@@auth
81+
@@allow('all', true)
82+
}
83+
84+
model GuestOrder extends BaseOrder {
85+
guestUser GuestUser @relation(fields: [guestUserId], references: [id], onDelete: Cascade)
86+
guestUserId String @map("guest_user_id")
87+
parentId String? @map("parent_id")
88+
parent GuestOrder? @relation("OrderToParent", fields: [parentId], references: [id])
89+
groupedOrders GuestOrder[] @relation("OrderToParent")
90+
}
91+
92+
model OrderItem {
93+
id String @id @default(cuid())
94+
order BaseOrder @relation(fields: [orderId], references: [id], onDelete: Cascade)
95+
orderId String @map("order_id")
96+
product Product @relation(fields: [productId], references: [id])
97+
productId String @map("product_id")
98+
quantity Int
99+
createdAt DateTime @default(now())
100+
updatedAt DateTime @updatedAt
101+
102+
@@allow('all', true)
103+
}
104+
105+
model OrderAddress {
106+
id String @id @default(cuid())
107+
firstName String
108+
lastName String
109+
address1 String
110+
address2 String?
111+
city String
112+
state String
113+
postalCode String
114+
country String
115+
email String
116+
phone String
117+
type String
118+
createdAt DateTime @default(now())
119+
updatedAt DateTime @updatedAt
120+
121+
@@allow('all', true)
122+
@@delegate(type)
123+
}
124+
125+
model BillingAddress extends OrderAddress {
126+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
127+
userId String? @map("user_id")
128+
order BaseOrder[]
129+
}
130+
131+
model ShippingAddress extends OrderAddress {
132+
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
133+
userId String? @map("user_id")
134+
order BaseOrder[]
135+
}
136+
`
137+
);
138+
139+
const db = enhance();
140+
141+
await db.user.create({ data: { id: '1', name: 'John', email: '[email protected]' } });
142+
143+
const shipping = await db.shippingAddress.create({
144+
data: {
145+
id: '1',
146+
firstName: 'John',
147+
lastName: 'Doe',
148+
address1: '123 Main St',
149+
city: 'Anytown',
150+
state: 'CA',
151+
postalCode: '12345',
152+
country: 'US',
153+
154+
phone: '123-456-7890',
155+
user: { connect: { id: '1' } },
156+
},
157+
});
158+
159+
const billing = await db.billingAddress.create({
160+
data: {
161+
id: '2',
162+
firstName: 'John',
163+
lastName: 'Doe',
164+
address1: '123 Main St',
165+
city: 'Anytown',
166+
state: 'CA',
167+
postalCode: '12345',
168+
country: 'US',
169+
170+
phone: '123-456-7890',
171+
user: { connect: { id: '1' } },
172+
},
173+
});
174+
175+
await db.order.create({
176+
data: {
177+
id: '1',
178+
orderNumber: '1',
179+
status: 'PENDING',
180+
type: 'PARENT',
181+
shippingAddress: { connect: { id: '1' } },
182+
billingAddress: { connect: { id: '2' } },
183+
user: { connect: { id: '1' } },
184+
},
185+
});
186+
187+
const updated = await db.order.update({
188+
where: { id: '1' },
189+
include: {
190+
lineItems: true,
191+
billingAddress: true,
192+
shippingAddress: true,
193+
},
194+
data: {
195+
type: 'CAMPAIGN',
196+
},
197+
});
198+
199+
console.log(updated);
200+
expect(updated.shippingAddress).toEqual(shipping);
201+
expect(updated.billingAddress).toEqual(billing);
202+
});
203+
});

0 commit comments

Comments
 (0)