Skip to content

Commit

Permalink
fix(delegate): concrete model fields are not properly included if que…
Browse files Browse the repository at this point in the history
…ried from a nested context from a parent concrete model (#1700)
  • Loading branch information
ymc9 authored Sep 15, 2024
2 parents 9e93985 + ab261ae commit 8b56d7d
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 9 deletions.
22 changes: 13 additions & 9 deletions packages/runtime/src/enhancements/node/delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,20 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
}
}

if (value !== undefined) {
if (value?.orderBy) {
// refetch the field select/include value because it may have been
// updated during injection
const fieldValue = args[kind][field];

if (fieldValue !== undefined) {
if (fieldValue.orderBy) {
// `orderBy` may contain fields from base types
this.injectWhereHierarchy(fieldInfo.type, value.orderBy);
this.injectWhereHierarchy(fieldInfo.type, fieldValue.orderBy);
}

if (this.injectBaseFieldSelect(model, field, value, args, kind)) {
if (this.injectBaseFieldSelect(model, field, fieldValue, args, kind)) {
delete args[kind][field];
} else if (fieldInfo.isDataModel) {
let nextValue = value;
let nextValue = fieldValue;
if (nextValue === true) {
// make sure the payload is an object
args[kind][field] = nextValue = {};
Expand Down Expand Up @@ -1158,11 +1162,11 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
const base = this.getBaseModel(model);

if (base) {
// merge base fields
// fully merge base fields
const baseRelationName = this.makeAuxRelationName(base);
const baseData = entity[baseRelationName];
if (baseData && typeof baseData === 'object') {
const baseAssembled = this.assembleUp(base.name, baseData);
const baseAssembled = this.assembleHierarchy(base.name, baseData);
Object.assign(result, baseAssembled);
}
}
Expand Down Expand Up @@ -1209,14 +1213,14 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
const modelInfo = getModelInfo(this.options.modelMeta, model, true);

if (modelInfo.discriminator) {
// model is a delegate, merge sub model fields
// model is a delegate, fully merge concrete model fields
const subModelName = entity[modelInfo.discriminator];
if (subModelName) {
const subModel = getModelInfo(this.options.modelMeta, subModelName, true);
const subRelationName = this.makeAuxRelationName(subModel);
const subData = entity[subRelationName];
if (subData && typeof subData === 'object') {
const subAssembled = this.assembleDown(subModel.name, subData);
const subAssembled = this.assembleHierarchy(subModel.name, subData);
Object.assign(result, subAssembled);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1361,4 +1361,50 @@ describe('Polymorphism Test', () => {
],
});
});

it('merges hierarchy correctly', async () => {
const { enhance } = await loadSchema(
`
model Asset {
id Int @id @default(autoincrement())
type String
viewCount Int
comments Comment[]
@@delegate(type)
}
model Post extends Asset {
title String
}
model Comment {
id Int @id @default(autoincrement())
type String
asset Asset @relation(fields: [assetId], references: [id])
assetId Int
moderated Boolean
@@delegate(type)
}
model TextComment extends Comment {
text String
}
`,
{ enhancements: ['delegate'] }
);

const db = enhance();
const post = await db.post.create({ data: { title: 'Post1', viewCount: 1 } });
const comment = await db.textComment.create({
data: { text: 'Comment1', moderated: true, asset: { connect: { id: post.id } } },
});

// delegate include delegate
let r = await db.asset.findFirst({ include: { comments: true } });
expect(r).toMatchObject({ viewCount: 1, comments: [comment] });

// concrete include delegate
r = await db.post.findFirst({ include: { comments: true } });
expect(r).toMatchObject({ ...post, comments: [comment] });
});
});
74 changes: 74 additions & 0 deletions tests/regression/tests/issue-1698.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { loadSchema } from '@zenstackhq/testtools';
describe('issue 1968', () => {
it('regression', async () => {
const { enhance } = await loadSchema(
`
model House {
id Int @id @default(autoincrement())
doorTypeId Int
door Door @relation(fields: [doorTypeId], references: [id])
houseType String
@@delegate(houseType)
}
model PrivateHouse extends House {
size Int
}
model Skyscraper extends House {
height Int
}
model Door {
id Int @id @default(autoincrement())
color String
doorType String
houses House[]
@@delegate(doorType)
}
model IronDoor extends Door {
strength Int
}
model WoodenDoor extends Door {
texture String
}
`,
{ enhancements: ['delegate'] }
);

const db = enhance();
const door1 = await db.ironDoor.create({
data: { strength: 100, color: 'blue' },
});
console.log(door1);

const door2 = await db.woodenDoor.create({
data: { texture: 'pine', color: 'red' },
});
console.log(door2);

const house1 = await db.privateHouse.create({
data: { size: 5000, door: { connect: { id: door1.id } } },
});
console.log(house1);

const house2 = await db.skyscraper.create({
data: { height: 3000, door: { connect: { id: door2.id } } },
});
console.log(house2);

const r1 = await db.privateHouse.findFirst({ include: { door: true } });
console.log(r1);
expect(r1).toMatchObject({
door: { color: 'blue', strength: 100 },
});

const r2 = (await db.skyscraper.findMany({ include: { door: true } }))[0];
console.log(r2);
expect(r2).toMatchObject({
door: { color: 'red', texture: 'pine' },
});
});
});

0 comments on commit 8b56d7d

Please sign in to comment.