From b144c6f7da60c0c7457d92de1dfd9df308ef98e8 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 25 Jan 2025 18:20:16 +0100 Subject: [PATCH] feat(cli): Support generating sdls for models with only an id and relation (#11931) --- .changesets/11931.md | 11 ++ .../__tests__/__snapshots__/sdl.test.js.snap | 120 ++++++++++++++++++ .../sdl/__tests__/fixtures/schema.prisma | 16 ++- .../generate/sdl/__tests__/sdl.test.js | 33 +++++ packages/cli/src/commands/generate/sdl/sdl.js | 17 ++- 5 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 .changesets/11931.md diff --git a/.changesets/11931.md b/.changesets/11931.md new file mode 100644 index 000000000000..563e8eb4b6c9 --- /dev/null +++ b/.changesets/11931.md @@ -0,0 +1,11 @@ +- feat(cli): Support generating sdls for models with only an id and relation (#11931) by @Tobbe + +It's now possible to generate SDL files for models that look like this + +```prisma +// This would be seeded with available car brands +model CarBrand { + brand String @id + cars Car[] +} +``` diff --git a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap index 9459e6f4dd78..7d930f2f3493 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap +++ b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap @@ -1572,6 +1572,66 @@ exports[`with graphql documentations > in typescript mode > creates a single wor " `; +exports[`without graphql documentations > in javascript mode > create an sdl file for model with only id and relation 1`] = ` +"export const schema = gql\` + type Car { + id: Int! + brand: String! + carBrand: CarBrand! + } + + type Query { + cars: [Car!]! @requireAuth + car(id: Int!): Car @requireAuth + } + + input CreateCarInput { + brand: String! + } + + input UpdateCarInput { + brand: String + } + + type Mutation { + createCar(input: CreateCarInput!): Car! @requireAuth + updateCar(id: Int!, input: UpdateCarInput!): Car! @requireAuth + deleteCar(id: Int!): Car! @requireAuth + } +\` +" +`; + +exports[`without graphql documentations > in javascript mode > create an sdl file for model with only id and relation 2`] = ` +"export const schema = gql\` + type CarBrand { + brand: String! + cars: [Car]! + } + + type Query { + carBrands: [CarBrand!]! @requireAuth + carBrand(brand: String!): CarBrand @requireAuth + } + + input CreateCarBrandInput { + brand: String! + } + + input UpdateCarBrandInput { + brand: String! + } + + type Mutation { + createCarBrand(input: CreateCarBrandInput!): CarBrand! @requireAuth + updateCarBrand(brand: String!, input: UpdateCarBrandInput!): CarBrand! + @requireAuth + deleteCarBrand(brand: String!): CarBrand! @requireAuth + } +\` +" +`; + exports[`without graphql documentations > in javascript mode > creates a multi word sdl file 1`] = ` "export const schema = gql\` type UserProfile { @@ -1809,6 +1869,66 @@ exports[`without graphql documentations > in javascript mode > creates a single " `; +exports[`without graphql documentations > in typescript mode > create an sdl file for model with only id and relation 1`] = ` +"export const schema = gql\` + type Car { + id: Int! + brand: String! + carBrand: CarBrand! + } + + type Query { + cars: [Car!]! @requireAuth + car(id: Int!): Car @requireAuth + } + + input CreateCarInput { + brand: String! + } + + input UpdateCarInput { + brand: String + } + + type Mutation { + createCar(input: CreateCarInput!): Car! @requireAuth + updateCar(id: Int!, input: UpdateCarInput!): Car! @requireAuth + deleteCar(id: Int!): Car! @requireAuth + } +\` +" +`; + +exports[`without graphql documentations > in typescript mode > create an sdl file for model with only id and relation 2`] = ` +"export const schema = gql\` + type CarBrand { + brand: String! + cars: [Car]! + } + + type Query { + carBrands: [CarBrand!]! @requireAuth + carBrand(brand: String!): CarBrand @requireAuth + } + + input CreateCarBrandInput { + brand: String! + } + + input UpdateCarBrandInput { + brand: String! + } + + type Mutation { + createCarBrand(input: CreateCarBrandInput!): CarBrand! @requireAuth + updateCarBrand(brand: String!, input: UpdateCarBrandInput!): CarBrand! + @requireAuth + deleteCarBrand(brand: String!): CarBrand! @requireAuth + } +\` +" +`; + exports[`without graphql documentations > in typescript mode > creates a multi word sdl file 1`] = ` "export const schema = gql\` type UserProfile { diff --git a/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma b/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma index d31245c497a1..17c3f85b1406 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma +++ b/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma @@ -66,6 +66,18 @@ enum Color { } model CustomData { - id Int @id @default(autoincrement()) - data String + id Int @id @default(autoincrement()) + data String +} + +// This would be seeded with available car brands +model CarBrand { + brand String @id + cars Car[] +} + +model Car { + id Int @id @default(autoincrement()) + brand String + carBrand CarBrand @relation(fields: [brand], references: [brand]) } diff --git a/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js b/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js index 56c86490b432..cb22722f7926 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js +++ b/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js @@ -236,6 +236,37 @@ const itCreatesAnSDLFileWithByteDefinitions = (baseArgs = {}) => { }) } +const itCreatesAnSslFileForModelWithOnlyIdAndRelation = (baseArgs = {}) => { + test('create an sdl file for model with only id and relation', async () => { + const files = { + ...(await sdl.files({ + ...baseArgs, + name: 'Car', + crud: true, + })), + ...(await sdl.files({ + ...baseArgs, + name: 'CarBrand', + crud: true, + })), + } + const extension = extensionForBaseArgs(baseArgs) + + expect( + files[ + path.normalize(`/path/to/project/api/src/graphql/cars.sdl.${extension}`) + ], + ).toMatchSnapshot() + expect( + files[ + path.normalize( + `/path/to/project/api/src/graphql/carBrands.sdl.${extension}`, + ) + ], + ).toMatchSnapshot() + }) +} + describe('without graphql documentations', () => { describe('in javascript mode', () => { const baseArgs = { ...getDefaultArgs(sdl.defaults), tests: true } @@ -249,6 +280,7 @@ describe('without graphql documentations', () => { itCreatesAnSDLFileWithEnumDefinitions(baseArgs) itCreatesAnSDLFileWithJsonDefinitions(baseArgs) itCreatesAnSDLFileWithByteDefinitions(baseArgs) + itCreatesAnSslFileForModelWithOnlyIdAndRelation(baseArgs) }) describe('in typescript mode', () => { @@ -267,6 +299,7 @@ describe('without graphql documentations', () => { itCreatesAnSDLFileWithEnumDefinitions(baseArgs) itCreatesAnSDLFileWithJsonDefinitions(baseArgs) itCreatesAnSDLFileWithByteDefinitions(baseArgs) + itCreatesAnSslFileForModelWithOnlyIdAndRelation(baseArgs) }) }) diff --git a/packages/cli/src/commands/generate/sdl/sdl.js b/packages/cli/src/commands/generate/sdl/sdl.js index cd5fb58bfcc8..4d5ded2967d9 100644 --- a/packages/cli/src/commands/generate/sdl/sdl.js +++ b/packages/cli/src/commands/generate/sdl/sdl.js @@ -75,11 +75,13 @@ const modelFieldToSDL = ({ Bytes: 'Byte', } - const fieldContent = `${field.name}: ${field.isList ? '[' : ''}${ - prismaTypeToGraphqlType[field.type] || field.type - }${field.isList ? ']' : ''}${ - (field.isRequired && required) | field.isList ? '!' : '' - }` + const gqlType = prismaTypeToGraphqlType[field.type] || field.type + const type = field.isList ? `[${gqlType}]` : gqlType + // lists and id fields are always required (lists can be empty, that's fine) + const isRequired = + (field.isRequired && required) || field.isList || field.isId + const fieldContent = `${field.name}: ${type}${isRequired ? '!' : ''}` + if (docs) { return addFieldGraphQLComment(field, fieldContent) } else { @@ -98,7 +100,8 @@ const inputSDL = (model, required, types = {}, docs = false) => { .filter((field) => { const idField = model.fields.find((field) => field.isId) - if (idField) { + // Only ignore the id field if it has a default value + if (idField && idField.default) { ignoredFields.push(idField.name) } @@ -162,7 +165,7 @@ const idName = (model, crud) => { const sdlFromSchemaModel = async (name, crud, docs = false) => { const model = await getSchema(name) - // get models for user-defined types referenced + // get models for referenced user-defined types const types = ( await Promise.all( model.fields