Skip to content

Commit

Permalink
feat(codegen): support sdl generation for model with compound id (red…
Browse files Browse the repository at this point in the history
…woodjs#8556)

partial resolution of redwoodjs#8552 

with this prisma schema:

```
model Temp {
  tenantId  String
  id        String   @default(uuid())
  name String
  @@id([tenantId, id])
}
```

running `yarn rw g sdl temp` now generates: 

```
export const schema = gql`
  type Temp {
    tenantId: String!
    id: String!
    name: String!
  }

  type Query {
    temps: [Temp!]! @requireAuth
    temp(id: TempIdInput!): Temp @requireAuth
  }

  input CreateTempInput {
    tenantId: String!
    name: String!
  }

  input TempIdInput {
    tenantId: String!
    id: String!
  }

  input UpdateTempInput {
    tenantId: String
    name: String
  }

  type Mutation {
    createTemp(input: CreateTempInput!): Temp! @requireAuth
    updateTemp(id: TempIdInput!, input: UpdateTempInput!): Temp! @requireAuth
    deleteTemp(id: TempIdInput!): Temp! @requireAuth
  }
`
```

It's not perfect for the following reasons:
1. Generated service still expects a single { id } argument (but this
may or may not be correct depending on where you get your tenantId from)
2. CreateTempInput is expecting part of the primary key in the request
3. UpdateTempInput duplicates the tenantId property from the TempIdInput

#⁠2 and #⁠3 above are easily fixable. In my opinion #⁠3 above makes
sense to fix, but for both #⁠1 and #⁠2, we cannot draw assumptions about
what the developer intends to take as an input when using a compound
primary key. Sometimes it might be part of the input, sometimes (in the
case of tenantId) it might come from the authorized user and be
invisible to the API consumers.

The important thing is, now you can generate your SDL's and modify from
there. Previously, redwood threw an error and you couldn't use codegen.

This may be considered a breaking change because users who supply their
own `sdl.{j,t}s.template` should now update to support `idInput`.
However, since the feature didn't exist before, I personally don't
consider this breaking.

---------

Co-authored-by: Orta Therox <git@orta.io>
  • Loading branch information
russell-dot-js and orta authored Aug 30, 2024
1 parent 5e6c258 commit c9beece
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .changesets/8556.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
- feat(codegen): support sdl generation for model with compound id (#8556) by @russell-dot-js

The SDL generator will no longer error when handling a table with a composite id, like so:

```prisma
model Order {
tenantId String
id String @unique @default(uuid())
@@id([tenantId, id])
}
```

It will now generate code in the SDL that handles input of both these variables:

```gql
input TempIdInput {
tenantId: String!
id: String!
}
```

If you are using a custom `sdl.{j,t}s.template` then you may have to update it to support the new `idInput`.
1 change: 1 addition & 0 deletions packages/cli/src/commands/generate/cell/cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const files = async ({ name, typescript, ...options }) => {

// needed for the singular cell GQL query find by id case
try {
// todo should pull from graphql schema rather than prisma!
model = await getSchema(pascalcase(singularize(cellName)))
idName = getIdName(model)
idType = getIdType(model)
Expand Down
21 changes: 21 additions & 0 deletions packages/cli/src/commands/generate/sdl/sdl.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ const inputSDL = (model, required, types = {}, docs = false) => {
.map((field) => modelFieldToSDL({ field, required, types, docs }))
}

const idInputSDL = (idType, docs) => {
if (!Array.isArray(idType)) {
return []
}
return idType.map((field) =>
modelFieldToSDL({ field, required: true, types: {}, docs }),
)
}

// creates the CreateInput type (all fields are required)
const createInputSDL = (model, types = {}, docs = false) => {
return inputSDL(model, true, types, docs)
Expand All @@ -122,7 +131,14 @@ const idType = (model, crud) => {
return undefined
}

// When using a composite primary key, we need to return an array of fields
if (model.primaryKey?.fields.length) {
const { fields: fieldNames } = model.primaryKey
return fieldNames.map((name) => model.fields.find((f) => f.name === name))
}

const idField = model.fields.find((field) => field.isId)

if (!idField) {
missingIdConsoleMessage()
throw new Error('Failed: Could not generate SDL')
Expand Down Expand Up @@ -174,12 +190,15 @@ const sdlFromSchemaModel = async (name, crud, docs = false) => {
const modelDescription =
model.documentation || `Representation of ${modelName}.`

const idTypeRes = idType(model, crud)

return {
modelName,
modelDescription,
query: querySDL(model, docs).join('\n '),
createInput: createInputSDL(model, types, docs).join('\n '),
updateInput: updateInputSDL(model, types, docs).join('\n '),
idInput: idInputSDL(idTypeRes, docs).join('\n '),
idType: idType(model, crud),
idName: idName(model, crud),
relations: relationsForModel(model),
Expand All @@ -200,6 +219,7 @@ export const files = async ({
query,
createInput,
updateInput,
idInput,
idType,
idName,
relations,
Expand All @@ -221,6 +241,7 @@ export const files = async ({
query,
createInput,
updateInput,
idInput,
idType,
idName,
enums,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ export const schema = gql`
${createInput}
}

<% if (idInput) { %>
<% if (docs) { %>
"""Autogenerated ID input type of ${singularPascalName}Id."""
<% } %>
input ${singularPascalName}IdInput {
${idInput}
}
<% } %>

<% if (docs) { %>
"""Autogenerated input type of Update${singularPascalName}."""
<% } %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ export const schema = gql`
${createInput}
}

<% if (idInput) { %>
<% if (docs) { %>
"""Autogenerated ID input type of ${singularPascalName}Id."""
<% } %>
input ${singularPascalName}IdInput {
${idInput}
}
<% } %>

<% if (docs) { %>
"""Autogenerated input type of Update${singularPascalName}."""
<% } %>
Expand Down

0 comments on commit c9beece

Please sign in to comment.