### What? Cannot generate GraphQL schema with hyphenated field names Using field names that do not adhere to the GraphQL `_a-z & A-Z` standard prevent you from generating a schema, even though it will work just fine everywhere else. Example: `my-field-name` will prevent schema generation. ### How? Field name sanitization on generation and querying This PR adds sanitization to the schema generation that sanitizes field names. - It formats field names in a GraphQL safe format for schema generation. **It does not change your config.** - It adds resolvers for field names that do not adhere so they can be mapped from the config name to the GraphQL safe name. Example: - `my-field` will turn into `my_field` in the schema generation - `my_field` will resolve from `my-field` when data comes out ### Other notes - Moves code from `packages/graphql/src/schema/buildObjectType.ts` to `packages/graphql/src/schema/fieldToSchemaMap.ts` - Resolvers are only added when necessary: `if (formatName(field.name) !== field.name)`. --------- Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
109 lines
2.6 KiB
TypeScript
109 lines
2.6 KiB
TypeScript
import type { Payload } from 'payload'
|
|
|
|
import path from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
|
|
|
import { idToString } from '../helpers/idToString.js'
|
|
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
|
|
|
let payload: Payload
|
|
let restClient: NextRESTClient
|
|
|
|
const filename = fileURLToPath(import.meta.url)
|
|
const dirname = path.dirname(filename)
|
|
|
|
describe('graphql', () => {
|
|
beforeAll(async () => {
|
|
;({ payload, restClient } = await initPayloadInt(dirname))
|
|
})
|
|
|
|
afterAll(async () => {
|
|
if (typeof payload.db.destroy === 'function') {
|
|
await payload.db.destroy()
|
|
}
|
|
})
|
|
|
|
describe('graphql', () => {
|
|
it('should not be able to query introspection', async () => {
|
|
const query = `query {
|
|
__schema {
|
|
queryType {
|
|
name
|
|
}
|
|
}
|
|
}`
|
|
|
|
const response = await restClient
|
|
.GRAPHQL_POST({
|
|
body: JSON.stringify({ query }),
|
|
})
|
|
.then((res) => res.json())
|
|
|
|
expect(response.errors[0].message).toMatch(
|
|
'GraphQL introspection is not allowed, but the query contained __schema or __type',
|
|
)
|
|
})
|
|
|
|
it('should respect maxComplexity', async () => {
|
|
const post = await payload.create({
|
|
collection: 'posts',
|
|
data: {
|
|
title: 'example post',
|
|
},
|
|
})
|
|
await payload.update({
|
|
collection: 'posts',
|
|
id: post.id,
|
|
data: {
|
|
relationToSelf: post.id,
|
|
},
|
|
})
|
|
|
|
const query = `query {
|
|
Post(id: ${idToString(post.id, payload)}) {
|
|
title
|
|
relationToSelf {
|
|
id
|
|
}
|
|
}
|
|
}`
|
|
|
|
const response = await restClient
|
|
.GRAPHQL_POST({
|
|
body: JSON.stringify({ query }),
|
|
})
|
|
.then((res) => res.json())
|
|
|
|
expect(response.errors[0].message).toMatch(
|
|
'The query exceeds the maximum complexity of 800. Actual complexity is 804',
|
|
)
|
|
})
|
|
|
|
it('should sanitize hyphenated field names to snake case', async () => {
|
|
const post = await payload.create({
|
|
collection: 'posts',
|
|
data: {
|
|
title: 'example post',
|
|
'hyphenated-name': 'example-hyphenated-name',
|
|
},
|
|
})
|
|
|
|
const query = `query {
|
|
Post(id: ${idToString(post.id, payload)}) {
|
|
title
|
|
hyphenated_name
|
|
}
|
|
}`
|
|
|
|
const { data } = await restClient
|
|
.GRAPHQL_POST({ body: JSON.stringify({ query }) })
|
|
.then((res) => res.json())
|
|
const res = data.Post
|
|
|
|
expect(res.hyphenated_name).toStrictEqual('example-hyphenated-name')
|
|
})
|
|
})
|
|
})
|