diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/fixtures/schema.prisma b/packages/cli/src/commands/generate/scaffold/__tests__/fixtures/schema.prisma index 70fc02557b92..252c42590cf4 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/fixtures/schema.prisma +++ b/packages/cli/src/commands/generate/scaffold/__tests__/fixtures/schema.prisma @@ -72,3 +72,8 @@ model Tag { post Post? @relation(fields: [postId], references: [id]) postId Int? } + +model CustomIdField { + uuid String @id @default(uuid()) + name String +} diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldCustomIdName.test.js b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldCustomIdName.test.js new file mode 100644 index 000000000000..a138fae5ff9c --- /dev/null +++ b/packages/cli/src/commands/generate/scaffold/__tests__/scaffoldCustomIdName.test.js @@ -0,0 +1,91 @@ +globalThis.__dirname = __dirname +import path from 'path' + +// Load mocks +import '../../../../lib/test' + +import { getDefaultArgs } from '../../../../lib' +import { yargsDefaults as defaults } from '../../helpers' +import * as scaffold from '../scaffold' + +jest.mock('execa') + +describe('support custom @id name', () => { + let files + + beforeAll(async () => { + files = await scaffold.files({ + ...getDefaultArgs(defaults), + typescript: true, + model: 'CustomIdField', + tests: true, + nestScaffoldByModel: true, + }) + }) + + test('creates routes with the custom id name', async () => { + const customIdFieldRoutes = await scaffold.routes({ + model: 'CustomIdField', + nestScaffoldByModel: true, + }) + expect(customIdFieldRoutes).toEqual([ + '', + '', + '', + '', + ]) + }) + + test('creates a cell with the custom id name', () => { + const customIdFieldCellPath = + '/path/to/project/web/src/components/CustomIdField/CustomIdFieldCell/CustomIdFieldCell.tsx' + + const cell = files[path.normalize(customIdFieldCellPath)] + expect(cell).toContain('FindCustomIdFieldByUuid($uuid: String!)') + expect(cell).toContain('customIdField: customIdField(uuid: $uuid)') + }) + + test('creates an edit cell with the custom id name', () => { + const customIdFieldEditCellPath = + '/path/to/project/web/src/components/CustomIdField/EditCustomIdFieldCell/EditCustomIdFieldCell.tsx' + + const cell = files[path.normalize(customIdFieldEditCellPath)] + expect(cell).toContain('query EditCustomIdFieldByUuid($uuid: String!)') + }) + + test('creates a component with the custom id name', () => { + const customIdFieldComponentPath = + '/path/to/project/web/src/components/CustomIdField/CustomIdField/CustomIdField.tsx' + + const cell = files[path.normalize(customIdFieldComponentPath)] + expect(cell).toContain('DeleteCustomIdFieldMutation($uuid: String!)') + expect(cell).toContain('deleteCustomIdField(uuid: $uuid)') + expect(cell).toContain('deleteCustomIdField({ variables: { uuid } })') + }) + + test('creates a form with the custom id name', () => { + const customIdFieldFormPath = + '/path/to/project/web/src/components/CustomIdField/CustomIdFieldForm/CustomIdFieldForm.tsx' + + const cell = files[path.normalize(customIdFieldFormPath)] + expect(cell).toContain('props.onSave(data, props?.customIdField?.uuid)') + }) + + test('creates a sdl with the custom id name', () => { + const customIdFieldSdlPath = + '/path/to/project/api/src/graphql/customIdFields.sdl.ts' + + const sdl = files[path.normalize(customIdFieldSdlPath)] + const match = sdl.match(/uuid: String!/g) + expect(match).toHaveLength(4) + }) + + test('creates a service with the custom id name', () => { + const customIdFieldServicePath = + '/path/to/project/api/src/graphql/customIdFields.sdl.ts' + + const sdl = files[path.normalize(customIdFieldServicePath)] + const match = sdl.match(/uuid: String!/g) + expect(match).toHaveLength(4) + }) +}) diff --git a/packages/cli/src/commands/generate/scaffold/scaffold.js b/packages/cli/src/commands/generate/scaffold/scaffold.js index 8762ab04da05..ad7b72d83f03 100644 --- a/packages/cli/src/commands/generate/scaffold/scaffold.js +++ b/packages/cli/src/commands/generate/scaffold/scaffold.js @@ -56,6 +56,10 @@ const getIdType = (model) => { return model.fields.find((field) => field.isId)?.type } +const getIdName = (model) => { + return model.fields.find((field) => field.isId)?.name +} + const filterAutoGeneratedColumnsForScaffold = (column) => { const autoGeneratedFunctions = ['now', 'autoincrement'] return !( @@ -495,6 +499,7 @@ const pageFiles = async ( const model = await getSchema(singularName) const idType = getIdType(model) const idTsType = mapPrismaScalarToPagePropTsType(idType) + const idName = getIdName(model) let fileList = {} @@ -531,6 +536,7 @@ const pageFiles = async ( }), { idTsType, + idName, name, pascalScaffoldPath, ...templateStrings, @@ -557,6 +563,8 @@ const componentFiles = async ( const singularName = pascalcase(singularize(name)) const model = await getSchema(singularName) const idType = getIdType(model) + const idName = getIdName(model) + const pascalIdName = pascalcase(idName) const intForeignKeys = intForeignKeysForModel(model) let fileList = {} @@ -594,6 +602,8 @@ const componentFiles = async ( { name, idType, + idName, + pascalIdName, intForeignKeys, pascalScaffoldPath, ...templateStrings, @@ -623,6 +633,7 @@ export const routes = async ({ const nameVars = nameVariants(name) const model = await getSchema(nameVars.singularPascalName) const idRouteParam = getIdType(model) === 'Int' ? ':Int' : '' + const idName = getIdName(model) const paramScaffoldPath = scaffoldPath === '' @@ -638,9 +649,9 @@ export const routes = async ({ // new ``, // edit - ``, + ``, // singular - ``, + ``, // plural ``, ] diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/EditNameCell.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/EditNameCell.tsx.template index a19836a80250..e22087511ff8 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/EditNameCell.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/EditNameCell.tsx.template @@ -1,4 +1,4 @@ -import type { Edit${singularPascalName}ById, Update${singularPascalName}Input } from 'types/graphql' +import type { Edit${singularPascalName}By${pascalIdName}, Update${singularPascalName}Input } from 'types/graphql' import { navigate, routes } from '@redwoodjs/router' import type { CellSuccessProps, CellFailureProps } from '@redwoodjs/web' @@ -8,15 +8,15 @@ import { toast } from '@redwoodjs/web/toast' import ${singularPascalName}Form from '${importComponentNameForm}' export const QUERY = gql` - query Edit${singularPascalName}ById($id: ${idType}!) { - ${singularCamelName}: ${singularCamelName}(id: $id) {<% columns.forEach(column => { %> + query Edit${singularPascalName}By${pascalIdName}($${idName}: ${idType}!) { + ${singularCamelName}: ${singularCamelName}(${idName}: $${idName}) {<% columns.forEach(column => { %> <%= column.name %><% }) %> } } ` const UPDATE_${singularConstantName}_MUTATION = gql` - mutation Update${singularPascalName}Mutation($id: ${idType}!, $input: Update${singularPascalName}Input!) { - update${singularPascalName}(id: $id, input: $input) {<% columns.forEach(column => { %> + mutation Update${singularPascalName}Mutation($${idName}: ${idType}!, $input: Update${singularPascalName}Input!) { + update${singularPascalName}(${idName}: $${idName}, input: $input) {<% columns.forEach(column => { %> <%= column.name %><% }) %> } } @@ -28,7 +28,7 @@ export const Failure = ({ error }: CellFailureProps) => (
{error?.message}
) -export const Success = ({ ${singularCamelName} }: CellSuccessProps) => { +export const Success = ({ ${singularCamelName} }: CellSuccessProps) => { const [update${singularPascalName}, { loading, error }] = useMutation( UPDATE_${singularConstantName}_MUTATION, { @@ -44,7 +44,7 @@ export const Success = ({ ${singularCamelName} }: CellSuccessProps { update${singularPascalName}({ variables: { id, input } }) } diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template index 3ea1e078bb90..bb979071d86b 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template @@ -5,18 +5,18 @@ import { toast } from '@redwoodjs/web/toast' import { ${formattersImports} } from 'src/lib/formatters' -import type { Delete${singularPascalName}MutationVariables, Find${singularPascalName}ById } from 'types/graphql' +import type { Delete${singularPascalName}MutationVariables, Find${singularPascalName}By${pascalIdName} } from 'types/graphql' const DELETE_${singularConstantName}_MUTATION = gql` - mutation Delete${singularPascalName}Mutation($id: ${idType}!) { - delete${singularPascalName}(id: $id) { - id + mutation Delete${singularPascalName}Mutation($${idName}: ${idType}!) { + delete${singularPascalName}(${idName}: $${idName}) { + ${idName} } } ` interface Props { - ${singularCamelName}: NonNullable + ${singularCamelName}: NonNullable } const ${singularPascalName} = ({ ${singularCamelName} }: Props) => { @@ -30,9 +30,9 @@ const ${singularPascalName} = ({ ${singularCamelName} }: Props) => { }, }) - const onDeleteClick = (id: Delete${singularPascalName}MutationVariables['id']) => { - if (confirm('Are you sure you want to delete ${singularCamelName} ' + id + '?')) { - delete${singularPascalName}({ variables: { id } }) + const onDeleteClick = (${idName}: Delete${singularPascalName}MutationVariables['${idName}']) => { + if (confirm('Are you sure you want to delete ${singularCamelName} ' + ${idName} + '?')) { + delete${singularPascalName}({ variables: { ${idName} } }) } } @@ -41,7 +41,7 @@ const ${singularPascalName} = ({ ${singularCamelName} }: Props) => {

- ${singularPascalName} {${singularCamelName}.id} Detail + ${singularPascalName} {${singularCamelName}.${idName}} Detail

@@ -59,7 +59,7 @@ const ${singularPascalName} = ({ ${singularCamelName} }: Props) => { {${pluralCamelName}.map((${singularCamelName}) => ( - <% columns.forEach(column => { %> + <% columns.forEach(column => { %> <% }) %>
{${column.listDisplayFunction}(${singularCamelName}.${column.name})}