From 06137fc18db4291dffb47d42513ed553e955e347 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 15 Jun 2026 03:44:45 +0000 Subject: [PATCH 1/2] feat(meta-schema): expose enum metadata in _meta field-level introspection Add enumValues field to MetaField that exposes PostgreSQL enum type name and allowed values for fields with enum types. Supports direct enum codecs, domain-wrapped enums, and array-of-enum detection. --- .../__snapshots__/meta-schema.test.ts.snap | 97 +++++++++++++++ .../__tests__/meta-schema.test.ts | 116 +++++++++++++++++- .../plugins/meta-schema/graphql-meta-field.ts | 10 ++ .../src/plugins/meta-schema/type-mappings.ts | 18 +++ .../src/plugins/meta-schema/types.ts | 8 ++ .../schema-snapshot.test.ts.snap | 12 ++ .../__snapshots__/graphile-test.test.ts.snap | 63 ++++++++++ 7 files changed, 323 insertions(+), 1 deletion(-) diff --git a/graphile/graphile-settings/__tests__/__snapshots__/meta-schema.test.ts.snap b/graphile/graphile-settings/__tests__/__snapshots__/meta-schema.test.ts.snap index 7bcb6cfcba..bd772942fc 100644 --- a/graphile/graphile-settings/__tests__/__snapshots__/meta-schema.test.ts.snap +++ b/graphile/graphile-settings/__tests__/__snapshots__/meta-schema.test.ts.snap @@ -16,6 +16,9 @@ exports[`MetaSchemaPlugin _meta query contract contains required selection paths "constraints.unique", "constraints.unique.name", "fields", + "fields.enumValues", + "fields.enumValues.name", + "fields.enumValues.values", "fields.name", "fields.type", "fields.type.gqlType", @@ -135,6 +138,10 @@ exports[`MetaSchemaPlugin _meta query contract has stable printed GraphQL text 1 isArray subtype } + enumValues { + name + values + } } indexes { name @@ -283,6 +290,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -302,6 +310,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -329,6 +338,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -348,6 +358,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -376,6 +387,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -398,6 +410,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": true, "isNotNull": true, @@ -414,6 +427,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -430,6 +444,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -446,6 +461,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": true, "isNotNull": true, @@ -466,6 +482,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -485,6 +502,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -512,6 +530,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -531,6 +550,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -563,6 +583,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -603,6 +624,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -636,6 +658,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -662,6 +685,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -700,6 +724,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -719,6 +744,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -747,6 +773,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -769,6 +796,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": true, "isNotNull": true, @@ -785,6 +813,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -801,6 +830,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": false, @@ -817,6 +847,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -837,6 +868,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -856,6 +888,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -888,6 +921,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -928,6 +962,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -961,6 +996,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -989,6 +1025,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1015,6 +1052,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1043,6 +1081,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1069,6 +1108,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1098,6 +1138,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1117,6 +1158,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1143,6 +1185,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "junctionLeftKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1162,6 +1205,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1181,6 +1225,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1207,6 +1252,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "junctionRightKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1228,6 +1274,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "leftKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1246,6 +1293,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "rightKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1280,6 +1328,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1299,6 +1348,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1326,6 +1376,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1345,6 +1396,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1373,6 +1425,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1389,6 +1442,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1411,6 +1465,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": true, "isNotNull": true, @@ -1427,6 +1482,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": true, "isNotNull": true, @@ -1447,6 +1503,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1466,6 +1523,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1493,6 +1551,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1512,6 +1571,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1545,6 +1605,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1561,6 +1622,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1601,6 +1663,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1617,6 +1680,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1650,6 +1714,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1676,6 +1741,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1714,6 +1780,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1736,6 +1803,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1758,6 +1826,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1774,6 +1843,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1798,6 +1868,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1824,6 +1895,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1864,6 +1936,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1898,6 +1971,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1926,6 +2000,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -1955,6 +2030,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -1974,6 +2050,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2000,6 +2077,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "junctionLeftKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -2019,6 +2097,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -2038,6 +2117,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "refFields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2064,6 +2144,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "junctionRightKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -2085,6 +2166,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "leftKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2103,6 +2185,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "rightKeyAttributes": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2133,6 +2216,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -2159,6 +2243,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2181,6 +2266,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -2203,6 +2289,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": false, @@ -2219,6 +2306,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -2235,6 +2323,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult }, { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2259,6 +2348,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, @@ -2285,6 +2375,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2325,6 +2416,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2359,6 +2451,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2385,6 +2478,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2413,6 +2507,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2439,6 +2534,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "keys": [ { "description": null, + "enumValues": null, "hasDefault": true, "isForeignKey": false, "isNotNull": true, @@ -2471,6 +2567,7 @@ exports[`MetaSchemaPlugin snapshot scenarios produces stable metadata for a mult "fields": [ { "description": null, + "enumValues": null, "hasDefault": false, "isForeignKey": false, "isNotNull": true, diff --git a/graphile/graphile-settings/__tests__/meta-schema.test.ts b/graphile/graphile-settings/__tests__/meta-schema.test.ts index e6a90390bc..1ae21fcaa2 100644 --- a/graphile/graphile-settings/__tests__/meta-schema.test.ts +++ b/graphile/graphile-settings/__tests__/meta-schema.test.ts @@ -346,7 +346,7 @@ query MetaContract { name schemaName query { all one create update delete } - fields { name type { pgType gqlType isArray subtype } } + fields { name type { pgType gqlType isArray subtype } enumValues { name values } } indexes { name isUnique isPrimary columns fields { name } } constraints { primaryKey { name } @@ -396,6 +396,7 @@ query MetaContract { } } } + `; const REQUIRED_META_QUERY_PATHS = [ @@ -466,6 +467,8 @@ const REQUIRED_META_QUERY_PATHS = [ 'search.config.boostRecent', 'search.config.boostRecencyField', 'search.config.boostRecencyDecay', + 'fields.enumValues.name', + 'fields.enumValues.values', ]; function collectSelectionPaths(selections: readonly SelectionNode[], prefix = ''): string[] { @@ -829,6 +832,7 @@ describe('MetaSchemaPlugin', () => { isPrimaryKey: false, isForeignKey: false, description: null, + enumValues: null, }); }); @@ -2086,6 +2090,116 @@ describe('MetaSchemaPlugin', () => { }); }); + describe('enum metadata', () => { + it('returns null enumValues for non-enum fields', () => { + const build = createMockBuild({ + user: { + codec: createMockCodec('user', { + id: createMockAttribute('uuid'), + name: createMockAttribute('text'), + }), + uniques: [], + relations: {}, + }, + }); + const tables = callInitHook(build); + for (const field of tables[0].fields) { + expect(field.enumValues).toBeNull(); + } + }); + + it('detects enum type on a field', () => { + const enumCodec = { + name: 'status_enum', + values: [{ value: 'active' }, { value: 'inactive' }, { value: 'pending' }], + }; + const build = createMockBuild({ + task: { + codec: createMockCodec('task', { + id: createMockAttribute('uuid'), + status: createMockAttribute('status_enum', { codec: enumCodec }), + }), + uniques: [], + relations: {}, + }, + }); + const tables = callInitHook(build); + const statusField = tables[0].fields.find((f: any) => f.name === 'status'); + expect(statusField.enumValues).toEqual({ + name: 'status_enum', + values: ['active', 'inactive', 'pending'], + }); + }); + + it('detects enum values as plain strings', () => { + const enumCodec = { + name: 'priority_enum', + values: ['low', 'medium', 'high'], + }; + const build = createMockBuild({ + task: { + codec: createMockCodec('task', { + id: createMockAttribute('uuid'), + priority: createMockAttribute('priority_enum', { codec: enumCodec }), + }), + uniques: [], + relations: {}, + }, + }); + const tables = callInitHook(build); + const priorityField = tables[0].fields.find((f: any) => f.name === 'priority'); + expect(priorityField.enumValues).toEqual({ + name: 'priority_enum', + values: ['low', 'medium', 'high'], + }); + }); + + it('detects enum through domain wrapper', () => { + const innerEnumCodec = { + name: 'color_enum', + values: [{ value: 'red' }, { value: 'green' }, { value: 'blue' }], + }; + const domainCodec = { + name: 'color_domain', + domainOfCodec: innerEnumCodec, + }; + const build = createMockBuild({ + item: { + codec: createMockCodec('item', { + id: createMockAttribute('uuid'), + color: createMockAttribute('color_domain', { codec: domainCodec }), + }), + uniques: [], + relations: {}, + }, + }); + const tables = callInitHook(build); + const colorField = tables[0].fields.find((f: any) => f.name === 'color'); + expect(colorField.enumValues).toEqual({ + name: 'color_enum', + values: ['red', 'green', 'blue'], + }); + }); + + it('does not detect enum on non-enum array codec', () => { + const innerCodec = { name: 'text', arrayOfCodec: null as any }; + const arrayCodec = { name: '_text', arrayOfCodec: innerCodec }; + const build = createMockBuild({ + item: { + codec: createMockCodec('item', { + id: createMockAttribute('uuid'), + tags: createMockAttribute('_text', { codec: arrayCodec }), + }), + uniques: [], + relations: {}, + }, + }); + const tables = callInitHook(build); + const tagsField = tables[0].fields.find((f: any) => f.name === 'tags'); + expect(tagsField.enumValues).toBeNull(); + }); + }); + describe('_meta query contract', () => { it('contains required selection paths', () => { const paths = getMetaQueryTablePaths(META_QUERY_CONTRACT).sort(); diff --git a/graphile/graphile-settings/src/plugins/meta-schema/graphql-meta-field.ts b/graphile/graphile-settings/src/plugins/meta-schema/graphql-meta-field.ts index 50ba9ccb1e..5a5302a6b5 100644 --- a/graphile/graphile-settings/src/plugins/meta-schema/graphql-meta-field.ts +++ b/graphile/graphile-settings/src/plugins/meta-schema/graphql-meta-field.ts @@ -36,6 +36,15 @@ function createMetaSchemaType(): GraphQLObjectType { }), }); + const MetaEnumType = new GraphQLObjectType({ + name: 'MetaEnum', + description: 'Information about a PostgreSQL enum type', + fields: () => ({ + name: { type: nn(GraphQLString), description: 'The PostgreSQL enum type name' }, + values: { type: nnList(GraphQLString), description: 'Allowed values for this enum' }, + }), + }); + const MetaFieldType = new GraphQLObjectType({ name: 'MetaField', description: 'Information about a table field/column', @@ -47,6 +56,7 @@ function createMetaSchemaType(): GraphQLObjectType { isPrimaryKey: { type: nn(GraphQLBoolean) }, isForeignKey: { type: nn(GraphQLBoolean) }, description: { type: GraphQLString }, + enumValues: { type: MetaEnumType, description: 'Enum metadata if this field has an enum type' }, }), }); diff --git a/graphile/graphile-settings/src/plugins/meta-schema/type-mappings.ts b/graphile/graphile-settings/src/plugins/meta-schema/type-mappings.ts index 261f8e334a..234ade1b36 100644 --- a/graphile/graphile-settings/src/plugins/meta-schema/type-mappings.ts +++ b/graphile/graphile-settings/src/plugins/meta-schema/type-mappings.ts @@ -1,4 +1,5 @@ import type { + EnumMeta, FieldMeta, GqlTypeResolverBuild, PgAttribute, @@ -75,6 +76,22 @@ function resolveGqlTypeName( return nestedTypeName ? pgTypeToGqlType(nestedTypeName) : pgTypeName; } +function extractEnumMeta( + codec: PgCodec | null | undefined, +): EnumMeta | null { + if (!codec) return null; + + // Check the codec itself, or unwrap domain/array wrappers + const inner = (codec as any).domainOfCodec ?? (codec as any).arrayOfCodec ?? codec; + const values = (inner as any).values; + if (!Array.isArray(values) || values.length === 0) return null; + + return { + name: inner.name || codec.name || 'unknown', + values: values.map((v: any) => (typeof v === 'string' ? v : v.value)), + }; +} + export interface BuildFieldMetaOptions { isPrimaryKey?: boolean; isForeignKey?: boolean; @@ -106,5 +123,6 @@ export function buildFieldMeta( isPrimaryKey: options?.isPrimaryKey ?? false, isForeignKey: options?.isForeignKey ?? false, description: attr?.description ?? null, + enumValues: extractEnumMeta(attr?.codec), }; } diff --git a/graphile/graphile-settings/src/plugins/meta-schema/types.ts b/graphile/graphile-settings/src/plugins/meta-schema/types.ts index c7e4becd43..bd2838bd7d 100644 --- a/graphile/graphile-settings/src/plugins/meta-schema/types.ts +++ b/graphile/graphile-settings/src/plugins/meta-schema/types.ts @@ -50,6 +50,13 @@ export interface SearchConfigMeta { boostRecencyDecay: number | null; } +export interface EnumMeta { + /** The PostgreSQL enum type name */ + name: string; + /** Allowed values for this enum */ + values: string[]; +} + export interface FieldMeta { name: string; type: TypeMeta; @@ -58,6 +65,7 @@ export interface FieldMeta { isPrimaryKey: boolean; isForeignKey: boolean; description: string | null; + enumValues: EnumMeta | null; } export interface TypeMeta { diff --git a/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap b/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap index aaeddeed17..02d343855d 100644 --- a/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap +++ b/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap @@ -1452,6 +1452,18 @@ type MetaField { isPrimaryKey: Boolean! isForeignKey: Boolean! description: String + + """Enum metadata if this field has an enum type""" + enumValues: MetaEnum +} + +"""Information about a PostgreSQL enum type""" +type MetaEnum { + """The PostgreSQL enum type name""" + name: String! + + """Allowed values for this enum""" + values: [String!]! } """Information about a PostgreSQL type""" diff --git a/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap b/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap index 595c2bf50f..b9165e305c 100644 --- a/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap +++ b/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap @@ -1578,6 +1578,18 @@ based pagination. May not be used with \`last\`.", "ofType": null, }, }, + { + "args": [], + "deprecationReason": null, + "description": "Enum metadata if this field has an enum type", + "isDeprecated": false, + "name": "enumValues", + "type": { + "kind": "OBJECT", + "name": "MetaEnum", + "ofType": null, + }, + }, ], "inputFields": null, "interfaces": [], @@ -1585,6 +1597,57 @@ based pagination. May not be used with \`last\`.", "name": "MetaField", "possibleTypes": null, }, + { + "description": "Information about a PostgreSQL enum type", + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The PostgreSQL enum type name", + "isDeprecated": false, + "name": "name", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + { + "args": [], + "deprecationReason": null, + "description": "Allowed values for this enum", + "isDeprecated": false, + "name": "values", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + }, + }, + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "MetaEnum", + "possibleTypes": null, + }, { "description": "Information about a PostgreSQL type", "enumValues": null, From 7dc2afea6794ee96792995042daee6497e40994b Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Mon, 15 Jun 2026 03:51:39 +0000 Subject: [PATCH 2/2] fix: reorder MetaEnum type after MetaType in introspection snapshots --- .../schema-snapshot.test.ts.snap | 18 ++-- .../__snapshots__/graphile-test.test.ts.snap | 102 +++++++++--------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap b/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap index 02d343855d..a9eca1e520 100644 --- a/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap +++ b/graphql/server-test/__tests__/__snapshots__/schema-snapshot.test.ts.snap @@ -1457,15 +1457,6 @@ type MetaField { enumValues: MetaEnum } -"""Information about a PostgreSQL enum type""" -type MetaEnum { - """The PostgreSQL enum type name""" - name: String! - - """Allowed values for this enum""" - values: [String!]! -} - """Information about a PostgreSQL type""" type MetaType { pgType: String! @@ -1476,6 +1467,15 @@ type MetaType { subtype: String } +"""Information about a PostgreSQL enum type""" +type MetaEnum { + """The PostgreSQL enum type name""" + name: String! + + """Allowed values for this enum""" + values: [String!]! +} + """Information about a database index""" type MetaIndex { name: String! diff --git a/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap b/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap index b9165e305c..86ac19680c 100644 --- a/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap +++ b/graphql/test/__tests__/__snapshots__/graphile-test.test.ts.snap @@ -1597,57 +1597,6 @@ based pagination. May not be used with \`last\`.", "name": "MetaField", "possibleTypes": null, }, - { - "description": "Information about a PostgreSQL enum type", - "enumValues": null, - "fields": [ - { - "args": [], - "deprecationReason": null, - "description": "The PostgreSQL enum type name", - "isDeprecated": false, - "name": "name", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null, - }, - }, - }, - { - "args": [], - "deprecationReason": null, - "description": "Allowed values for this enum", - "isDeprecated": false, - "name": "values", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null, - }, - }, - }, - }, - }, - ], - "inputFields": null, - "interfaces": [], - "kind": "OBJECT", - "name": "MetaEnum", - "possibleTypes": null, - }, { "description": "Information about a PostgreSQL type", "enumValues": null, @@ -1743,6 +1692,57 @@ based pagination. May not be used with \`last\`.", "name": "MetaType", "possibleTypes": null, }, + { + "description": "Information about a PostgreSQL enum type", + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": "The PostgreSQL enum type name", + "isDeprecated": false, + "name": "name", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + { + "args": [], + "deprecationReason": null, + "description": "Allowed values for this enum", + "isDeprecated": false, + "name": "values", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null, + }, + }, + }, + }, + }, + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "MetaEnum", + "possibleTypes": null, + }, { "description": "Information about a database index", "enumValues": null,