From 9652cbb0dbe6527a919d7ec553066b27439904a0 Mon Sep 17 00:00:00 2001 From: JG Date: Tue, 28 Nov 2023 23:19:37 +0000 Subject: [PATCH] feat: Support multiple levels inheritance --- .../src/language-server/zmodel-scope.ts | 33 +++++++++------ .../tests/generator/prisma-generator.test.ts | 40 +++++++++++++++++++ .../prisma/multi-level-inheritance.prisma | 19 +++++++++ packages/schema/tests/schema/abstract.test.ts | 33 +++++++++++++++ 4 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 packages/schema/tests/generator/prisma/multi-level-inheritance.prisma diff --git a/packages/schema/src/language-server/zmodel-scope.ts b/packages/schema/src/language-server/zmodel-scope.ts index 95fecacc7..a227d9d1c 100644 --- a/packages/schema/src/language-server/zmodel-scope.ts +++ b/packages/schema/src/language-server/zmodel-scope.ts @@ -73,22 +73,31 @@ export class ZModelScopeComputation extends DefaultScopeComputation { if (decl.$type === 'DataModel') { const dataModel = decl as DataModel; dataModel.$resolvedFields = [...dataModel.fields]; - dataModel.superTypes.forEach((superType) => { - const superTypeDecl = superType.ref; - if (superTypeDecl) { - superTypeDecl.fields.forEach((field) => { - const cloneField = Object.assign({}, field); - cloneField.$isInherited = true; - const mutable = cloneField as Mutable; - // update container - mutable.$container = dataModel; - dataModel.$resolvedFields.push(cloneField); - }); - } + this.getRecursiveSuperTypes(dataModel).forEach((superType) => { + superType.fields.forEach((field) => { + const cloneField = Object.assign({}, field); + cloneField.$isInherited = true; + const mutable = cloneField as Mutable; + // update container + mutable.$container = dataModel; + dataModel.$resolvedFields.push(cloneField); + }); }); } }); } + + private getRecursiveSuperTypes(dataModel: DataModel): DataModel[] { + const result: DataModel[] = []; + dataModel.superTypes.forEach((superType) => { + const superTypeDecl = superType.ref; + if (superTypeDecl) { + result.push(superTypeDecl); + result.push(...this.getRecursiveSuperTypes(superTypeDecl)); + } + }); + return result; + } } export class ZModelScopeProvider extends DefaultScopeProvider { diff --git a/packages/schema/tests/generator/prisma-generator.test.ts b/packages/schema/tests/generator/prisma-generator.test.ts index 8ba127842..049104b1e 100644 --- a/packages/schema/tests/generator/prisma-generator.test.ts +++ b/packages/schema/tests/generator/prisma-generator.test.ts @@ -355,6 +355,46 @@ describe('Prisma generator test', () => { expect(todo?.documentation?.replace(/\s/g, '')).toBe(`@@allow('read', owner == auth())`.replace(/\s/g, '')); }); + it('multiple level inheritance', async () => { + const model = await loadModel(` + datasource db { + provider = 'postgresql' + url = env('URL') + } + + abstract model Base { + id String @id @default(cuid()) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + } + + abstract model BaseDeletable extends Base { + deleted Boolean @default(false) @omit + + @@deny('read', deleted) + } + + model Test1 extends BaseDeletable { + @@allow('all', true) + } + `); + + const { name } = tmp.fileSync({ postfix: '.prisma' }); + await new PrismaSchemaGenerator().generate(model, { + name: 'Prisma', + provider: '@core/prisma', + schemaPath: 'schema.zmodel', + output: name, + format: true, + }); + + const content = fs.readFileSync(name, 'utf-8'); + const expected = fs.readFileSync(path.join(__dirname, './prisma/multi-level-inheritance.prisma'), 'utf-8'); + + expect(content).toBe(expected); + }); + it('format prisma', async () => { const model = await loadModel(` datasource db { diff --git a/packages/schema/tests/generator/prisma/multi-level-inheritance.prisma b/packages/schema/tests/generator/prisma/multi-level-inheritance.prisma new file mode 100644 index 000000000..7a0707d7a --- /dev/null +++ b/packages/schema/tests/generator/prisma/multi-level-inheritance.prisma @@ -0,0 +1,19 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +datasource db { + provider = "postgresql" + url = env("URL") +} + +/// @@deny('read', deleted) +/// @@allow('all', true) +model Test1 { + id String @id() @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt() + /// @omit + deleted Boolean @default(false) +} diff --git a/packages/schema/tests/schema/abstract.test.ts b/packages/schema/tests/schema/abstract.test.ts index 621e7998a..47d607962 100644 --- a/packages/schema/tests/schema/abstract.test.ts +++ b/packages/schema/tests/schema/abstract.test.ts @@ -28,4 +28,37 @@ describe('Abstract Schema Tests', () => { model Foo extends Base {} `); }); + + it('multiple level inheritance', async () => { + await loadModel(` + datasource db { + provider = 'postgresql' + url = env('DATABASE_URL') + } + + generator js { + provider = 'prisma-client-js' + } + + abstract model Base1 { + id String @id @default(cuid()) + } + + abstract model Base2 extends Base1 { + fieldA String + } + + model A extends Base2 { + field String + b B[] + } + + model B { + id String @id @default(cuid()) + a A @relation(fields: [aId], references: [id]) + aId String + } + + `); + }); });