Skip to content

Commit ab261ae

Browse files
committed
fix(delegate): concrete model fields are not properly included if queried from a nested context from a parent concrete model
Fixes #1968
1 parent 9e93985 commit ab261ae

File tree

3 files changed

+133
-9
lines changed

3 files changed

+133
-9
lines changed

packages/runtime/src/enhancements/node/delegate.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -168,16 +168,20 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
168168
}
169169
}
170170

171-
if (value !== undefined) {
172-
if (value?.orderBy) {
171+
// refetch the field select/include value because it may have been
172+
// updated during injection
173+
const fieldValue = args[kind][field];
174+
175+
if (fieldValue !== undefined) {
176+
if (fieldValue.orderBy) {
173177
// `orderBy` may contain fields from base types
174-
this.injectWhereHierarchy(fieldInfo.type, value.orderBy);
178+
this.injectWhereHierarchy(fieldInfo.type, fieldValue.orderBy);
175179
}
176180

177-
if (this.injectBaseFieldSelect(model, field, value, args, kind)) {
181+
if (this.injectBaseFieldSelect(model, field, fieldValue, args, kind)) {
178182
delete args[kind][field];
179183
} else if (fieldInfo.isDataModel) {
180-
let nextValue = value;
184+
let nextValue = fieldValue;
181185
if (nextValue === true) {
182186
// make sure the payload is an object
183187
args[kind][field] = nextValue = {};
@@ -1158,11 +1162,11 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
11581162
const base = this.getBaseModel(model);
11591163

11601164
if (base) {
1161-
// merge base fields
1165+
// fully merge base fields
11621166
const baseRelationName = this.makeAuxRelationName(base);
11631167
const baseData = entity[baseRelationName];
11641168
if (baseData && typeof baseData === 'object') {
1165-
const baseAssembled = this.assembleUp(base.name, baseData);
1169+
const baseAssembled = this.assembleHierarchy(base.name, baseData);
11661170
Object.assign(result, baseAssembled);
11671171
}
11681172
}
@@ -1209,14 +1213,14 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
12091213
const modelInfo = getModelInfo(this.options.modelMeta, model, true);
12101214

12111215
if (modelInfo.discriminator) {
1212-
// model is a delegate, merge sub model fields
1216+
// model is a delegate, fully merge concrete model fields
12131217
const subModelName = entity[modelInfo.discriminator];
12141218
if (subModelName) {
12151219
const subModel = getModelInfo(this.options.modelMeta, subModelName, true);
12161220
const subRelationName = this.makeAuxRelationName(subModel);
12171221
const subData = entity[subRelationName];
12181222
if (subData && typeof subData === 'object') {
1219-
const subAssembled = this.assembleDown(subModel.name, subData);
1223+
const subAssembled = this.assembleHierarchy(subModel.name, subData);
12201224
Object.assign(result, subAssembled);
12211225
}
12221226
}

tests/integration/tests/enhancements/with-delegate/enhanced-client.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,4 +1361,50 @@ describe('Polymorphism Test', () => {
13611361
],
13621362
});
13631363
});
1364+
1365+
it('merges hierarchy correctly', async () => {
1366+
const { enhance } = await loadSchema(
1367+
`
1368+
model Asset {
1369+
id Int @id @default(autoincrement())
1370+
type String
1371+
viewCount Int
1372+
comments Comment[]
1373+
@@delegate(type)
1374+
}
1375+
1376+
model Post extends Asset {
1377+
title String
1378+
}
1379+
1380+
model Comment {
1381+
id Int @id @default(autoincrement())
1382+
type String
1383+
asset Asset @relation(fields: [assetId], references: [id])
1384+
assetId Int
1385+
moderated Boolean
1386+
@@delegate(type)
1387+
}
1388+
1389+
model TextComment extends Comment {
1390+
text String
1391+
}
1392+
`,
1393+
{ enhancements: ['delegate'] }
1394+
);
1395+
1396+
const db = enhance();
1397+
const post = await db.post.create({ data: { title: 'Post1', viewCount: 1 } });
1398+
const comment = await db.textComment.create({
1399+
data: { text: 'Comment1', moderated: true, asset: { connect: { id: post.id } } },
1400+
});
1401+
1402+
// delegate include delegate
1403+
let r = await db.asset.findFirst({ include: { comments: true } });
1404+
expect(r).toMatchObject({ viewCount: 1, comments: [comment] });
1405+
1406+
// concrete include delegate
1407+
r = await db.post.findFirst({ include: { comments: true } });
1408+
expect(r).toMatchObject({ ...post, comments: [comment] });
1409+
});
13641410
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { loadSchema } from '@zenstackhq/testtools';
2+
describe('issue 1968', () => {
3+
it('regression', async () => {
4+
const { enhance } = await loadSchema(
5+
`
6+
model House {
7+
id Int @id @default(autoincrement())
8+
doorTypeId Int
9+
door Door @relation(fields: [doorTypeId], references: [id])
10+
houseType String
11+
@@delegate(houseType)
12+
}
13+
14+
model PrivateHouse extends House {
15+
size Int
16+
}
17+
18+
model Skyscraper extends House {
19+
height Int
20+
}
21+
22+
model Door {
23+
id Int @id @default(autoincrement())
24+
color String
25+
doorType String
26+
houses House[]
27+
@@delegate(doorType)
28+
}
29+
30+
model IronDoor extends Door {
31+
strength Int
32+
}
33+
34+
model WoodenDoor extends Door {
35+
texture String
36+
}
37+
`,
38+
{ enhancements: ['delegate'] }
39+
);
40+
41+
const db = enhance();
42+
const door1 = await db.ironDoor.create({
43+
data: { strength: 100, color: 'blue' },
44+
});
45+
console.log(door1);
46+
47+
const door2 = await db.woodenDoor.create({
48+
data: { texture: 'pine', color: 'red' },
49+
});
50+
console.log(door2);
51+
52+
const house1 = await db.privateHouse.create({
53+
data: { size: 5000, door: { connect: { id: door1.id } } },
54+
});
55+
console.log(house1);
56+
57+
const house2 = await db.skyscraper.create({
58+
data: { height: 3000, door: { connect: { id: door2.id } } },
59+
});
60+
console.log(house2);
61+
62+
const r1 = await db.privateHouse.findFirst({ include: { door: true } });
63+
console.log(r1);
64+
expect(r1).toMatchObject({
65+
door: { color: 'blue', strength: 100 },
66+
});
67+
68+
const r2 = (await db.skyscraper.findMany({ include: { door: true } }))[0];
69+
console.log(r2);
70+
expect(r2).toMatchObject({
71+
door: { color: 'red', texture: 'pine' },
72+
});
73+
});
74+
});

0 commit comments

Comments
 (0)