From de983b02479bdb4aee80bd42f6faece62586a45f Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Thu, 22 Feb 2024 14:23:04 +0100 Subject: [PATCH] Add containsSupergraphSpec (#44) This method is meant to quickly check if `transformSupergraphToPublicSchema` is needed. Obviously, in most cases when `transformSupergraphToPublicSchema` is used, a provided schema contains Supergraph spec (join__Graph etc). Why do I create it then? We have a special case in GraphQL Hive where we persist the output of `transformSupergraphToPublicSchema`, but when Apollo Federation adds something new, we might want to run this method again to remove new pieces. #### Performance I used a Supergraph SDL with ~20k LOC and create three copies. First copy used `field(whatever: join__Graph)` as an argument somewhere in the middle. Second copy had `scalar join__DirectiveArguments`, also in the middle of the file. Third copy had `directive @join__directive` definition (yeah yeah, in the middle). I wrote three versions of `containsSupergraphSpec` to make sure it has minimal performance footprint. 1. for-loop over all federation scalars, enums and directives that used `sdl.includes("[name") or sdl.includes(" name")` 2. same for-loop but directives where checked first 3. regex (current implementation). I ran it 1000 times and I got (average): 1. 1.56 ms 2. 0.83 ms 3. 0.59 ms --- .changeset/tricky-badgers-punch.md | 5 +++++ src/graphql/contains-supergraph-spec.ts | 17 +++++++++++++++++ .../transform-supergraph-to-public-schema.ts | 12 +++++++++--- src/index.ts | 1 + 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 .changeset/tricky-badgers-punch.md create mode 100644 src/graphql/contains-supergraph-spec.ts diff --git a/.changeset/tricky-badgers-punch.md b/.changeset/tricky-badgers-punch.md new file mode 100644 index 0000000..dfdfbd5 --- /dev/null +++ b/.changeset/tricky-badgers-punch.md @@ -0,0 +1,5 @@ +--- +'@theguild/federation-composition': patch +--- + +Add containsSupergraphSpec to detect if Supergraph related scalars, enums or directives are used diff --git a/src/graphql/contains-supergraph-spec.ts b/src/graphql/contains-supergraph-spec.ts new file mode 100644 index 0000000..ad5c8c8 --- /dev/null +++ b/src/graphql/contains-supergraph-spec.ts @@ -0,0 +1,17 @@ +import { federationDirectives, federationEnums, federationScalars } from "./transform-supergraph-to-public-schema.js"; + + +const supergraphSpecDetectionRegex = new RegExp( + Array.from(federationScalars) + .concat(Array.from(federationEnums)) + // "[NAME" or " NAME" for scalars and enums + .map(name => [`\\[${name}`, `\\s${name}`]) + .flat(2) + // "@NAME" for directives + .concat(Array.from(federationDirectives).map(name => `@${name}`)) + .join('|'), + ); + + export function containsSupergraphSpec(sdl: string): boolean { + return supergraphSpecDetectionRegex.test(sdl); + } \ No newline at end of file diff --git a/src/graphql/transform-supergraph-to-public-schema.ts b/src/graphql/transform-supergraph-to-public-schema.ts index 71f6b97..fdca822 100644 --- a/src/graphql/transform-supergraph-to-public-schema.ts +++ b/src/graphql/transform-supergraph-to-public-schema.ts @@ -9,15 +9,21 @@ import { type SchemaDefinitionNode, } from 'graphql'; -const federationScalars = new Set(['_FieldSet', 'link__Import', 'join__FieldSet']); -const federationEnums = new Set(['core__Purpose', 'join__Graph', 'link__Purpose']); -const federationDirectives = new Set([ +export const federationScalars = new Set([ + '_FieldSet', + 'link__Import', + 'join__FieldSet', + 'join__DirectiveArguments', +]); +export const federationEnums = new Set(['core__Purpose', 'join__Graph', 'link__Purpose']); +export const federationDirectives = new Set([ 'link', 'tag', 'join__graph', 'join__type', 'join__implements', 'join__unionMember', + 'join__directive', 'join__enumValue', 'join__field', 'inaccessible', diff --git a/src/index.ts b/src/index.ts index 784064b..40293b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,4 +2,5 @@ export * from './compose.js'; export * from './types.js'; export * from './validate.js'; export { transformSupergraphToPublicSchema } from './graphql/transform-supergraph-to-public-schema.js'; +export { containsSupergraphSpec } from './graphql/contains-supergraph-spec.js'; export { sortSDL } from './graphql/sort-sdl.js';