From 6f8babe1a668ad1166296aba8ff43d120d4261ff Mon Sep 17 00:00:00 2001 From: Romain Lenzotti Date: Wed, 16 Oct 2024 07:58:47 +0200 Subject: [PATCH] fix(prisma): fix circular reference when column is optional Closes: #2863 --- packages/orm/prisma/package.json | 1 + .../transform/transformScalarToType.spec.ts | 8 ++--- .../transform/transformScalarToType.ts | 10 +++--- .../__snapshots__/generateModels.spec.ts.snap | 20 ++++++------ .../orm/prisma/test/circular-ref/package.json | 10 ++++++ .../test/circular-ref/prisma/schema.prisma | 32 +++++++++++++++++++ 6 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 packages/orm/prisma/test/circular-ref/package.json create mode 100644 packages/orm/prisma/test/circular-ref/prisma/schema.prisma diff --git a/packages/orm/prisma/package.json b/packages/orm/prisma/package.json index d997f696d44..8083130fc7e 100644 --- a/packages/orm/prisma/package.json +++ b/packages/orm/prisma/package.json @@ -25,6 +25,7 @@ "test": "vitest run", "generate:postgres:esm": "yarn build && cd test/postgres-esm && prisma -v && prisma generate", "generate:mongo:esm": "yarn build && cd test/mongo-esm && prisma -v && prisma generate", + "generate:circular:esm": "yarn build && cd test/circular-ref && prisma -v && prisma generate", "test:ci": "vitest run --coverage.thresholds.autoUpdate=true" }, "dependencies": { diff --git a/packages/orm/prisma/src/generator/transform/transformScalarToType.spec.ts b/packages/orm/prisma/src/generator/transform/transformScalarToType.spec.ts index 860e5533743..3a7e43b2eb6 100644 --- a/packages/orm/prisma/src/generator/transform/transformScalarToType.spec.ts +++ b/packages/orm/prisma/src/generator/transform/transformScalarToType.spec.ts @@ -107,11 +107,11 @@ describe("transformScalarToType()", () => { }); // @ts-ignore field.model.name = "User"; - expect(transformScalarToType(field, ctx)).toEqual("UserModel | null"); - expect(field.model.addImportDeclaration).not.toHaveBeenCalled(); + expect(transformScalarToType(field, ctx)).toEqual("Relation | null"); + expect(field.model.addImportDeclaration).toHaveBeenCalledWith("@tsed/core", "Relation", true); }); - it("should transform User to User", () => { + it("should transform Role to User", () => { const ctx = createContextFixture(); const field = createDmmfFieldFixture({ kind: "object", @@ -119,7 +119,7 @@ describe("transformScalarToType()", () => { }); // @ts-ignore field.model.name = "User"; - expect(transformScalarToType(field, ctx)).toEqual("RoleModel | null"); + expect(transformScalarToType(field, ctx)).toEqual("Relation | null"); expect(field.model.addImportDeclaration).toHaveBeenCalledWith("./RoleModel", "RoleModel"); }); }); diff --git a/packages/orm/prisma/src/generator/transform/transformScalarToType.ts b/packages/orm/prisma/src/generator/transform/transformScalarToType.ts index 72ff6568cdd..0a833deb8ee 100644 --- a/packages/orm/prisma/src/generator/transform/transformScalarToType.ts +++ b/packages/orm/prisma/src/generator/transform/transformScalarToType.ts @@ -31,6 +31,11 @@ export function transformScalarToType(field: DmmfField, ctx: TransformContext): TSType += "[]"; } + if (!isList && hasCircularRef && ["inputObjectTypes", "enumTypes"].includes(location)) { + hasCircularRef && !isList && field.model.addImportDeclaration("@tsed/core", "Relation", true); + TSType = `Relation<${TSType}>`; + } + if (!isRequired) { if (model.isInputType) { TSType += " | undefined"; @@ -41,10 +46,7 @@ export function transformScalarToType(field: DmmfField, ctx: TransformContext): TSType += " | null"; } - if (isRequired && !isList && !isNullable && hasCircularRef && ["inputObjectTypes", "enumTypes"].includes(location)) { - hasCircularRef && !isList && field.model.addImportDeclaration("@tsed/core", "Relation", true); - TSType = `Relation<${TSType}>`; - } + return TSType; } diff --git a/packages/orm/prisma/src/generator/utils/__snapshots__/generateModels.spec.ts.snap b/packages/orm/prisma/src/generator/utils/__snapshots__/generateModels.spec.ts.snap index ed135951e28..637d3525e70 100644 --- a/packages/orm/prisma/src/generator/utils/__snapshots__/generateModels.spec.ts.snap +++ b/packages/orm/prisma/src/generator/utils/__snapshots__/generateModels.spec.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`generateModels > should generate models (info) 1`] = ` -"import { Info } from "../client/index.js"; +"import { Info } from "../client/index"; import { Required, Property } from "@tsed/schema"; export class InfoModel implements Info { @@ -18,9 +18,10 @@ export class InfoModel implements Info { `; exports[`generateModels > should generate models (post) 1`] = ` -"import { Post } from "../client/index.js"; +"import { Post } from "../client/index"; import { Integer, Required, Property, Allow } from "@tsed/schema"; -import { UserModel } from "./UserModel.js"; +import { UserModel } from "./UserModel"; +import type { Relation } from "@tsed/core"; export class PostModel implements Post { @Property(Number) @@ -30,7 +31,7 @@ export class PostModel implements Post { @Property(() => UserModel) @Allow(null) - user: UserModel | null; + user: Relation | null; @Property(Number) @Integer() @@ -42,10 +43,11 @@ export class PostModel implements Post { `; exports[`generateModels > should generate models (user) 1`] = ` -"import { User } from "../client/index.js"; +"import { User } from "../client/index"; import { Integer, Required, Property, Groups, Format, Email, Description, Allow, Enum, CollectionOf } from "@tsed/schema"; -import { Role } from "../enums/index.js"; -import { PostModel } from "./PostModel.js"; +import type { Relation } from "@tsed/core"; +import { Role } from "../enums/index"; +import { PostModel } from "./PostModel"; export class UserModel implements User { @Property(Number) @@ -84,11 +86,11 @@ export class UserModel implements User { @Property(() => UserModel) @Allow(null) - successor: UserModel | null; + successor: Relation | null; @Property(() => UserModel) @Allow(null) - predecessor: UserModel | null; + predecessor: Relation | null; @Required() @Enum(Role) diff --git a/packages/orm/prisma/test/circular-ref/package.json b/packages/orm/prisma/test/circular-ref/package.json new file mode 100644 index 00000000000..d72ff0fbc66 --- /dev/null +++ b/packages/orm/prisma/test/circular-ref/package.json @@ -0,0 +1,10 @@ +{ + "name": "@tsed/prisma-test", + "type": "commonjs", + "devDependencies": { + "prisma": "^4.0.0" + }, + "dependencies": { + "@prisma/client": "^4.0.0" + } +} diff --git a/packages/orm/prisma/test/circular-ref/prisma/schema.prisma b/packages/orm/prisma/test/circular-ref/prisma/schema.prisma new file mode 100644 index 00000000000..41ec7537a96 --- /dev/null +++ b/packages/orm/prisma/test/circular-ref/prisma/schema.prisma @@ -0,0 +1,32 @@ + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" + binaryTargets = ["native", "windows", "debian-openssl-1.1.x"] + output = "../prisma/generated/client" +} + +generator tsed { + provider = "node ../../lib/cjs/generator.js" + output = "../prisma/generated/tsed" + emitDMMF = true +} + +model Conversation { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + assignmentId String? @map("assignment_id") @db.Uuid + assignment Assignment? @relation(fields: [assignmentId], references: [id], onDelete: NoAction, onUpdate: NoAction) + + @@map("conversation") +} + +model Assignment { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + conversations Conversation[] + + @@map("assignment") +}