Skip to content

Commit

Permalink
fix(delegate): entity create fails when inheriting from a delegate mo…
Browse files Browse the repository at this point in the history
…del that extends an abstract model
  • Loading branch information
ymc9 committed Jul 7, 2024
1 parent df32680 commit 09d14e7
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 6 deletions.
6 changes: 4 additions & 2 deletions packages/schema/src/plugins/prisma/schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
getAttribute,
getAttributeArg,
getAttributeArgLiteral,
getInheritedFromDelegate,
getLiteral,
getRelationKeyPairs,
isDelegateModel,
Expand Down Expand Up @@ -261,9 +262,10 @@ export class PrismaSchemaGenerator {
const model = decl.isView ? prisma.addView(decl.name) : prisma.addModel(decl.name);
for (const field of decl.fields) {
if (field.$inheritedFrom) {
const inheritedFromDelegate = getInheritedFromDelegate(field);
if (
// abstract inheritance is always kept
field.$inheritedFrom.isAbstract ||
// fields inherited from delegate are excluded from physical schema
!inheritedFromDelegate ||
// logical schema keeps all inherited fields
this.mode === 'logical' ||
// id fields are always kept
Expand Down
9 changes: 5 additions & 4 deletions packages/sdk/src/model-meta-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ import {
getAttributeArgs,
getAuthModel,
getDataModels,
getInheritedFromDelegate,
getLiteral,
getRelationField,
hasAttribute,
isDelegateModel,
isAuthInvocation,
isEnumFieldReference,
isForeignKeyField,
isIdField,
resolved,
TypeScriptExpressionTransformer,
getRelationField,
} from '.';

/**
Expand Down Expand Up @@ -267,9 +267,10 @@ function writeFields(
defaultValueProvider: ${defaultValueProvider},`);
}

if (f.$inheritedFrom && isDelegateModel(f.$inheritedFrom) && !isIdField(f)) {
const inheritedFromDelegate = getInheritedFromDelegate(f);
if (inheritedFromDelegate && !isIdField(f)) {
writer.write(`
inheritedFrom: ${JSON.stringify(f.$inheritedFrom.name)},`);
inheritedFrom: ${JSON.stringify(inheritedFromDelegate.name)},`);
}

if (isAutoIncrement(f)) {
Expand Down
15 changes: 15 additions & 0 deletions packages/sdk/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,3 +554,18 @@ export function getDataSourceProvider(model: Model) {
}
return getLiteral<string>(provider.value);
}

/**
* Finds the original delegate base model that defines the given field.
*/
export function getInheritedFromDelegate(field: DataModelField) {
if (!field.$inheritedFrom) {
return undefined;
}

// find the original base delegate model that defines this field,
// use `findLast` to start from the uppermost base
const bases = getRecursiveBases(field.$container as DataModel, true);
const foundBase = bases.findLast((base) => base.fields.some((f) => f.name === field.name) && isDelegateModel(base));
return foundBase;
}
41 changes: 41 additions & 0 deletions tests/regression/tests/issue-1560.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { loadSchema } from '@zenstackhq/testtools';
describe('issue 1560', () => {
it('regression', async () => {
const { enhance } = await loadSchema(
`
model User {
id String @id @default(cuid())
name String
ownedItems OwnedItem[]
}
abstract model Base {
id String @id @default(cuid())
ownerId String
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
}
model OwnedItem extends Base {
ownedItemType String
@@delegate(ownedItemType)
}
model List extends OwnedItem {
title String
}
`,
{ enhancements: ['delegate'] }
);

const db = enhance();
await db.user.create({ data: { id: '1', name: 'user1' } });
await expect(
db.list.create({ data: { id: '1', title: 'list1', owner: { connect: { id: '1' } } } })
).resolves.toMatchObject({
id: '1',
title: 'list1',
ownerId: '1',
ownedItemType: 'List',
});
});
});

0 comments on commit 09d14e7

Please sign in to comment.