diff --git a/packages/sanity/src/_internal/cli/actions/graphql/extractFromSanitySchema.ts b/packages/sanity/src/_internal/cli/actions/graphql/extractFromSanitySchema.ts index 1ca695844c81..ec9a28d7d43d 100644 --- a/packages/sanity/src/_internal/cli/actions/graphql/extractFromSanitySchema.ts +++ b/packages/sanity/src/_internal/cli/actions/graphql/extractFromSanitySchema.ts @@ -481,7 +481,9 @@ export function extractFromSanitySchema( } try { - unionRecursionGuards.add(guardPathName) + if (guardPathName !== 'reference') { + unionRecursionGuards.add(guardPathName) + } candidates.forEach((def, i) => { if (typeNeedsHoisting(def)) { diff --git a/packages/sanity/test/cli/graphql/extract.test.ts b/packages/sanity/test/cli/graphql/extract.test.ts index 1e189f611cbb..8d2bf0582099 100644 --- a/packages/sanity/test/cli/graphql/extract.test.ts +++ b/packages/sanity/test/cli/graphql/extract.test.ts @@ -4,6 +4,7 @@ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest' import {extractFromSanitySchema} from '../../../src/_internal/cli/actions/graphql/extractFromSanitySchema' import {type ApiSpecification} from '../../../src/_internal/cli/actions/graphql/types' import testStudioSchema from './fixtures/test-studio' +import unionRefsSchema from './fixtures/union-refs' describe('GraphQL - Schema extraction', () => { beforeEach(() => { @@ -15,13 +16,21 @@ describe('GraphQL - Schema extraction', () => { vi.runAllTimers() }) - it('Should be able to extract schema', () => { + it('Should be able to extract schema 1', () => { const extracted = extractFromSanitySchema(testStudioSchema, { nonNullDocumentFields: false, }) expect(sortExtracted(extracted)).toMatchSnapshot() }) + + it('Should be able to extract schema 2', () => { + const extracted = extractFromSanitySchema(unionRefsSchema, { + nonNullDocumentFields: false, + }) + + expect(sortExtracted(extracted)).toMatchSnapshot() + }) }) function sortExtracted(schema: ApiSpecification) { diff --git a/packages/sanity/test/cli/graphql/fixtures/union-refs.ts b/packages/sanity/test/cli/graphql/fixtures/union-refs.ts new file mode 100644 index 000000000000..94cdf6d4b5aa --- /dev/null +++ b/packages/sanity/test/cli/graphql/fixtures/union-refs.ts @@ -0,0 +1,483 @@ +import {Schema} from '@sanity/schema' +import {defineField, defineType} from 'sanity' + +export const root = defineType({ + name: 'title', + type: 'array', + of: [ + { + type: 'reference', + to: [{type: 'union'}, {type: 'a'}], + }, + ], +}) + +export const union = defineType({ + type: 'document', + name: 'union', + fields: [ + defineField({ + name: 'body', + type: 'array', + of: [ + { + type: 'reference', + to: [{type: 'a'}, {type: 'b'}], + }, + {type: 'a'}, + ], + }), + ], +}) + +export const a = defineType({ + type: 'document', + name: 'a', + fields: [defineField({name: 'title', type: 'string'})], +}) +export const b = defineType({ + type: 'document', + name: 'b', + fields: [defineField({name: 'title', type: 'string'})], +}) +//export const schemaTypes = [root, union, a, b] +export default Schema.compile({ + types: [ + root, + union, + a, + b, + { + title: 'Geographical Point', + name: 'geopoint', + type: 'object', + fields: [ + { + name: 'lat', + type: 'number', + title: 'Latitude', + }, + { + name: 'lng', + type: 'number', + title: 'Longitude', + }, + { + name: 'alt', + type: 'number', + title: 'Altitude', + }, + ], + }, + { + name: 'sanity.fileAsset', + title: 'File', + type: 'document', + fieldsets: [ + { + name: 'system', + title: 'System fields', + description: 'These fields are managed by the system and not editable', + }, + ], + fields: [ + { + name: 'originalFilename', + type: 'string', + title: 'Original file name', + readOnly: true, + }, + { + name: 'label', + type: 'string', + title: 'Label', + }, + { + name: 'title', + type: 'string', + title: 'Title', + }, + { + name: 'description', + type: 'string', + title: 'Description', + }, + { + name: 'altText', + type: 'string', + title: 'Alternative text', + }, + { + name: 'sha1hash', + type: 'string', + title: 'SHA1 hash', + readOnly: true, + fieldset: 'system', + }, + { + name: 'extension', + type: 'string', + title: 'File extension', + readOnly: true, + fieldset: 'system', + }, + { + name: 'mimeType', + type: 'string', + title: 'Mime type', + readOnly: true, + fieldset: 'system', + }, + { + name: 'size', + type: 'number', + title: 'File size in bytes', + readOnly: true, + fieldset: 'system', + }, + { + name: 'assetId', + type: 'string', + title: 'Asset ID', + readOnly: true, + fieldset: 'system', + }, + { + name: 'path', + type: 'string', + title: 'Path', + readOnly: true, + fieldset: 'system', + }, + { + name: 'url', + type: 'string', + title: 'Url', + readOnly: true, + fieldset: 'system', + }, + { + name: 'source', + type: 'sanity.assetSourceData', + title: 'Source', + readOnly: true, + fieldset: 'system', + }, + ], + preview: { + select: { + title: 'originalFilename', + path: 'path', + mimeType: 'mimeType', + size: 'size', + }, + prepare(doc: any) { + return { + title: doc.title || doc.path.split('/').slice(-1)[0], + subtitle: `${doc.mimeType} (${(doc.size / 1024 / 1024).toFixed(2)} MB)`, + } + }, + }, + orderings: [ + { + title: 'File size', + name: 'fileSizeDesc', + by: [{field: 'size', direction: 'desc'}], + }, + ], + }, + { + name: 'sanity.imageHotspot', + title: 'Image hotspot', + type: 'object', + fields: [ + { + name: 'x', + type: 'number', + }, + { + name: 'y', + type: 'number', + }, + { + name: 'height', + type: 'number', + }, + { + name: 'width', + type: 'number', + }, + ], + }, + { + name: 'sanity.imageMetadata', + title: 'Image metadata', + type: 'object', + fieldsets: [ + { + name: 'extra', + title: 'Extra metadata…', + options: { + collapsable: true, + }, + }, + ], + fields: [ + { + name: 'location', + type: 'geopoint', + }, + { + name: 'dimensions', + title: 'Dimensions', + type: 'sanity.imageDimensions', + fieldset: 'extra', + }, + { + name: 'palette', + type: 'sanity.imagePalette', + title: 'Palette', + fieldset: 'extra', + }, + { + name: 'lqip', + title: 'LQIP (Low-Quality Image Placeholder)', + type: 'string', + readOnly: true, + }, + { + name: 'blurHash', + title: 'BlurHash', + type: 'string', + readOnly: true, + }, + { + name: 'hasAlpha', + title: 'Has alpha channel', + type: 'boolean', + readOnly: true, + }, + { + name: 'isOpaque', + title: 'Is opaque', + type: 'boolean', + readOnly: true, + }, + ], + }, + { + name: 'sanity.assetSourceData', + title: 'Asset Source Data', + type: 'object', + fields: [ + { + name: 'name', + title: 'Source name', + description: 'A canonical name for the source this asset is originating from', + type: 'string', + }, + { + name: 'id', + title: 'Asset Source ID', + description: + 'The unique ID for the asset within the originating source so you can programatically find back to it', + type: 'string', + }, + { + name: 'url', + title: 'Asset information URL', + description: 'A URL to find more information about this asset in the originating source', + type: 'string', + }, + ], + }, + { + name: 'sanity.imageCrop', + title: 'Image crop', + type: 'object', + fields: [ + { + name: 'top', + type: 'number', + }, + { + name: 'bottom', + type: 'number', + }, + { + name: 'left', + type: 'number', + }, + { + name: 'right', + type: 'number', + }, + ], + }, + { + name: 'sanity.imageDimensions', + type: 'object', + title: 'Image dimensions', + fields: [ + {name: 'height', type: 'number', title: 'Height', readOnly: true}, + {name: 'width', type: 'number', title: 'Width', readOnly: true}, + {name: 'aspectRatio', type: 'number', title: 'Aspect ratio', readOnly: true}, + ], + }, + { + name: 'sanity.imagePalette', + title: 'Image palette', + type: 'object', + fields: [ + {name: 'darkMuted', type: 'sanity.imagePaletteSwatch', title: 'Dark Muted'}, + {name: 'lightVibrant', type: 'sanity.imagePaletteSwatch', title: 'Light Vibrant'}, + {name: 'darkVibrant', type: 'sanity.imagePaletteSwatch', title: 'Dark Vibrant'}, + {name: 'vibrant', type: 'sanity.imagePaletteSwatch', title: 'Vibrant'}, + {name: 'dominant', type: 'sanity.imagePaletteSwatch', title: 'Dominant'}, + {name: 'lightMuted', type: 'sanity.imagePaletteSwatch', title: 'Light Muted'}, + {name: 'muted', type: 'sanity.imagePaletteSwatch', title: 'Muted'}, + ], + }, + { + name: 'sanity.imagePaletteSwatch', + title: 'Image palette swatch', + type: 'object', + fields: [ + {name: 'background', type: 'string', title: 'Background', readOnly: true}, + {name: 'foreground', type: 'string', title: 'Foreground', readOnly: true}, + {name: 'population', type: 'number', title: 'Population', readOnly: true}, + {name: 'title', type: 'string', title: 'String', readOnly: true}, + ], + }, + { + name: 'sanity.imageAsset', + title: 'Image', + type: 'document', + fieldsets: [ + { + name: 'system', + title: 'System fields', + description: 'These fields are managed by the system and not editable', + }, + ], + fields: [ + { + name: 'originalFilename', + type: 'string', + title: 'Original file name', + readOnly: true, + }, + { + name: 'label', + type: 'string', + title: 'Label', + }, + { + name: 'title', + type: 'string', + title: 'Title', + }, + { + name: 'description', + type: 'string', + title: 'Description', + }, + { + name: 'altText', + type: 'string', + title: 'Alternative text', + }, + { + name: 'sha1hash', + type: 'string', + title: 'SHA1 hash', + readOnly: true, + fieldset: 'system', + }, + { + name: 'extension', + type: 'string', + readOnly: true, + title: 'File extension', + fieldset: 'system', + }, + { + name: 'mimeType', + type: 'string', + readOnly: true, + title: 'Mime type', + fieldset: 'system', + }, + { + name: 'size', + type: 'number', + title: 'File size in bytes', + readOnly: true, + fieldset: 'system', + }, + { + name: 'assetId', + type: 'string', + title: 'Asset ID', + readOnly: true, + fieldset: 'system', + }, + { + name: 'uploadId', + type: 'string', + readOnly: true, + hidden: true, + fieldset: 'system', + }, + { + name: 'path', + type: 'string', + title: 'Path', + readOnly: true, + fieldset: 'system', + }, + { + name: 'url', + type: 'string', + title: 'Url', + readOnly: true, + fieldset: 'system', + }, + { + name: 'metadata', + type: 'sanity.imageMetadata', + title: 'Metadata', + }, + { + name: 'source', + type: 'sanity.assetSourceData', + title: 'Source', + readOnly: true, + fieldset: 'system', + }, + ], + preview: { + select: { + id: '_id', + title: 'originalFilename', + mimeType: 'mimeType', + size: 'size', + }, + prepare(doc: any) { + return { + title: doc.title || doc.path.split('/').slice(-1)[0], + media: {asset: {_ref: doc.id}}, + subtitle: `${doc.mimeType} (${(doc.size / 1024 / 1024).toFixed(2)} MB)`, + } + }, + }, + orderings: [ + { + title: 'File size', + name: 'fileSizeDesc', + by: [{field: 'size', direction: 'desc'}], + }, + ], + }, + ], +}) diff --git a/packages/sanity/test/cli/graphql/gen3.test.ts b/packages/sanity/test/cli/graphql/gen3.test.ts index c57d7d7bb5f4..f1316a4078ce 100644 --- a/packages/sanity/test/cli/graphql/gen3.test.ts +++ b/packages/sanity/test/cli/graphql/gen3.test.ts @@ -46,6 +46,7 @@ describe('GraphQL - Generation 3', () => { describe.each([ {name: 'testStudioSchema', sanitySchema: testStudioSchema}, {name: 'manySelfRefsSchema', sanitySchema: manySelfRefsSchema}, + {name: 'unionRefsSchema', sanitySchema: unionRefsSchema}, ])(`Union cache: sanitySchema: $name`, ({sanitySchema}) => { /** * @jest-environment jsdom