From ec470d582b271dfc5cf6c108dbc22c50e8f17b02 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Tue, 24 Sep 2024 19:50:40 +0100 Subject: [PATCH] Add more missing behaviors, and fix behavior of connection-incapable lists (use 'array' instead) --- .../src/plugins/PgCustomTypeFieldPlugin.ts | 40 +++++++++++++++++-- graphile-build/graphile-build/src/behavior.ts | 25 ++++++++---- .../src/plugins/CommonBehaviorsPlugin.ts | 13 +++++- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/graphile-build/graphile-build-pg/src/plugins/PgCustomTypeFieldPlugin.ts b/graphile-build/graphile-build-pg/src/plugins/PgCustomTypeFieldPlugin.ts index 11156dc1d..5b2590409 100644 --- a/graphile-build/graphile-build-pg/src/plugins/PgCustomTypeFieldPlugin.ts +++ b/graphile-build/graphile-build-pg/src/plugins/PgCustomTypeFieldPlugin.ts @@ -65,6 +65,14 @@ declare global { queryField: true; mutationField: true; typeField: true; + "typeField:resource:connection": true; + "typeField:resource:list": true; + "typeField:resource:array": true; + "queryField:resource:connection": true; + "queryField:resource:list": true; + "queryField:resource:array": true; + "typeField:single": true; + "queryField:single": true; } interface Build { pgGetArgDetailsFromParameters( @@ -195,7 +203,7 @@ function shouldUseCustomConnection( function defaultProcSourceBehavior( s: PgResource, ): GraphileBuild.BehaviorString { - const behavior: GraphileBuild.BehaviorString[] = []; + const behavior: GraphileBuild.BehaviorString[] = ["-array"]; const firstParameter = ( s as PgResource ).parameters[0]; @@ -233,7 +241,7 @@ function defaultProcSourceBehavior( const canUseConnection = !s.sqlPartitionByIndex && !s.isList && !s.codec.arrayOfCodec; if (!canUseConnection) { - behavior.push("-connection", "list"); + behavior.push("-connection", "-list", "array"); } } @@ -358,6 +366,30 @@ export const PgCustomTypeFieldPlugin: GraphileConfig.Plugin = { 'a "custom field function" - add it to a specific type (aka "computed column")', entities: ["pgResource"], }, + "typeField:resource:connection": { + description: "", + entities: ["pgResource"], + }, + "typeField:resource:list": { + description: "", + entities: ["pgResource"], + }, + "queryField:resource:connection": { + description: "", + entities: ["pgResource"], + }, + "queryField:resource:list": { + description: "", + entities: ["pgResource"], + }, + "typeField:single": { + description: "", + entities: ["pgResource"], + }, + "queryField:single": { + description: "", + entities: ["pgResource"], + }, }, }, entityBehavior: { @@ -1154,7 +1186,9 @@ export const PgCustomTypeFieldPlugin: GraphileConfig.Plugin = { const baseScope = isRootQuery ? `queryField` : `typeField`; const connectionFieldBehaviorScope = `${baseScope}:resource:connection`; - const listFieldBehaviorScope = `${baseScope}:resource:list`; + const listFieldBehaviorScope = canUseConnection + ? `${baseScope}:resource:list` + : `${baseScope}:resource:array`; if ( canUseConnection && build.behavior.pgResourceMatches( diff --git a/graphile-build/graphile-build/src/behavior.ts b/graphile-build/graphile-build/src/behavior.ts index db27012cd..d7abc3f10 100644 --- a/graphile-build/graphile-build/src/behavior.ts +++ b/graphile-build/graphile-build/src/behavior.ts @@ -165,7 +165,10 @@ export class Behavior { for (const [behaviorString, spec] of Object.entries( this.behaviorRegistry, )) { - if (spec.entities[entityType]) { + if ( + spec.entities[entityType] || + true /* This ` || true` is because of inheritance (e.g. unique inherits from resource inherits from codec); it causes a headache if we factor it in */ + ) { const parts = behaviorString.split(":"); const l = parts.length; for (let i = 0; i < l; i++) { @@ -216,7 +219,7 @@ export class Behavior { // ... then remove everything ... "-*", // ... then replace it with what we actually want. - multiplyBehavior(defaultBehavior, behavior), + multiplyBehavior(defaultBehavior, behavior, entityType), ]; }, }, @@ -487,17 +490,19 @@ export class Behavior { } for (const { scope } of result) { const behavior = scope.join(":") as keyof GraphileBuild.BehaviorStrings; - if (!this.behaviorRegistry[behavior]) { + if ( + !Object.keys(this.behaviorRegistry).some((bhv) => + stringMatches(bhv, behavior), + ) + ) { console.trace( `Behavior '${behavior}' has not been registered! (Source: ${source})`, ); } if ( - !Object.entries(this.behaviorRegistry).some( - ([bhv, { entities }]) => - (entities[entityType] && bhv === behavior) || - bhv.endsWith(":" + behavior), + !Object.entries(this.behaviorRegistry).some(([bhv, { entities }]) => + /*entities[entityType] &&*/ stringMatches(bhv, behavior), ) ) { console.trace( @@ -704,7 +709,11 @@ export function isValidBehaviorString( * - Result: concatenate these: * - "-connection -resource:connection +query:resource:connection -list +resource:list -query:resource:list" */ -function multiplyBehavior(preferences: string, inferred: string) { +function multiplyBehavior( + preferences: string, + inferred: string, + entityType: string, +) { const pref = parseSpecs(preferences); const inf = parseSpecs(inferred); const result = inf.flatMap((infEntry) => { diff --git a/graphile-build/graphile-build/src/plugins/CommonBehaviorsPlugin.ts b/graphile-build/graphile-build/src/plugins/CommonBehaviorsPlugin.ts index eac4c808b..33d6fec04 100644 --- a/graphile-build/graphile-build/src/plugins/CommonBehaviorsPlugin.ts +++ b/graphile-build/graphile-build/src/plugins/CommonBehaviorsPlugin.ts @@ -5,8 +5,13 @@ import { version } from "../version.js"; declare global { namespace GraphileBuild { interface BehaviorStrings { + // 'connection' and 'list' are for connection-capable collections. If + // your collection is not connection-capable, it should use 'array' + // instead. connection: true; list: true; + array: true; + single: true; "interface:node": true; @@ -28,7 +33,13 @@ export const CommonBehaviorsPlugin: GraphileConfig.Plugin = { entities: [], }, list: { - description: "represent collection as a list (simple collections)", + description: + "represent collection as a list - only use with collections that can be represented as a connection too", + entities: [], + }, + array: { + description: + "represent an array as a list - use with collections which are not connection-capable (otherwise use list)", entities: [], }, single: {