Skip to content

Commit ac2b291

Browse files
authored
fix(delegate): entity create fails when inheriting from a delegate model that extends an abstract model (#1561)
1 parent df32680 commit ac2b291

File tree

4 files changed

+65
-6
lines changed

4 files changed

+65
-6
lines changed

packages/schema/src/plugins/prisma/schema-generator.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
getAttribute,
4040
getAttributeArg,
4141
getAttributeArgLiteral,
42+
getInheritedFromDelegate,
4243
getLiteral,
4344
getRelationKeyPairs,
4445
isDelegateModel,
@@ -261,9 +262,10 @@ export class PrismaSchemaGenerator {
261262
const model = decl.isView ? prisma.addView(decl.name) : prisma.addModel(decl.name);
262263
for (const field of decl.fields) {
263264
if (field.$inheritedFrom) {
265+
const inheritedFromDelegate = getInheritedFromDelegate(field);
264266
if (
265-
// abstract inheritance is always kept
266-
field.$inheritedFrom.isAbstract ||
267+
// fields inherited from delegate are excluded from physical schema
268+
!inheritedFromDelegate ||
267269
// logical schema keeps all inherited fields
268270
this.mode === 'logical' ||
269271
// id fields are always kept

packages/sdk/src/model-meta-generator.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ import {
2424
getAttributeArgs,
2525
getAuthModel,
2626
getDataModels,
27+
getInheritedFromDelegate,
2728
getLiteral,
29+
getRelationField,
2830
hasAttribute,
29-
isDelegateModel,
3031
isAuthInvocation,
3132
isEnumFieldReference,
3233
isForeignKeyField,
3334
isIdField,
3435
resolved,
3536
TypeScriptExpressionTransformer,
36-
getRelationField,
3737
} from '.';
3838

3939
/**
@@ -267,9 +267,10 @@ function writeFields(
267267
defaultValueProvider: ${defaultValueProvider},`);
268268
}
269269

270-
if (f.$inheritedFrom && isDelegateModel(f.$inheritedFrom) && !isIdField(f)) {
270+
const inheritedFromDelegate = getInheritedFromDelegate(f);
271+
if (inheritedFromDelegate && !isIdField(f)) {
271272
writer.write(`
272-
inheritedFrom: ${JSON.stringify(f.$inheritedFrom.name)},`);
273+
inheritedFrom: ${JSON.stringify(inheritedFromDelegate.name)},`);
273274
}
274275

275276
if (isAutoIncrement(f)) {

packages/sdk/src/utils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,18 @@ export function getDataSourceProvider(model: Model) {
554554
}
555555
return getLiteral<string>(provider.value);
556556
}
557+
558+
/**
559+
* Finds the original delegate base model that defines the given field.
560+
*/
561+
export function getInheritedFromDelegate(field: DataModelField) {
562+
if (!field.$inheritedFrom) {
563+
return undefined;
564+
}
565+
566+
// find the original base delegate model that defines this field,
567+
// use `findLast` to start from the uppermost base
568+
const bases = getRecursiveBases(field.$container as DataModel, true);
569+
const foundBase = bases.findLast((base) => base.fields.some((f) => f.name === field.name) && isDelegateModel(base));
570+
return foundBase;
571+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { loadSchema } from '@zenstackhq/testtools';
2+
describe('issue 1560', () => {
3+
it('regression', async () => {
4+
const { enhance } = await loadSchema(
5+
`
6+
model User {
7+
id String @id @default(cuid())
8+
name String
9+
ownedItems OwnedItem[]
10+
}
11+
12+
abstract model Base {
13+
id String @id @default(cuid())
14+
ownerId String
15+
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
16+
}
17+
18+
model OwnedItem extends Base {
19+
ownedItemType String
20+
@@delegate(ownedItemType)
21+
}
22+
23+
model List extends OwnedItem {
24+
title String
25+
}
26+
`,
27+
{ enhancements: ['delegate'] }
28+
);
29+
30+
const db = enhance();
31+
await db.user.create({ data: { id: '1', name: 'user1' } });
32+
await expect(
33+
db.list.create({ data: { id: '1', title: 'list1', owner: { connect: { id: '1' } } } })
34+
).resolves.toMatchObject({
35+
id: '1',
36+
title: 'list1',
37+
ownerId: '1',
38+
ownedItemType: 'List',
39+
});
40+
});
41+
});

0 commit comments

Comments
 (0)