From 3b0ec852994f86dd84bdccf77829fb81f8455579 Mon Sep 17 00:00:00 2001 From: Patrick Arminio Date: Fri, 23 Dec 2022 17:09:50 +0000 Subject: [PATCH] Update `schemaIsSubgraph` to also support non nullable `_Service.sdl` (#7274) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related to https://github.com/apollographql/apollo-federation-subgraph-compatibility/pull/302 We found a discrepancy in how libraries implement the federation specification (`_Service.sdl` is `String` in Federation 1 and is `String` in Federation 2). I'm doing this PR mostly to check how easy it would be to support both specs at once, if I understood correctly the code `schemaIsSubgraph` might also be used outside Apollo Server 😊 --- .changeset/quiet-pears-float.md | 5 + .../__tests__/plugin/schemaIsSubgraph.test.ts | 122 ++++++++++++++++++ .../server/src/plugin/schemaIsSubgraph.ts | 13 +- 3 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 .changeset/quiet-pears-float.md create mode 100644 packages/server/src/__tests__/plugin/schemaIsSubgraph.test.ts diff --git a/.changeset/quiet-pears-float.md b/.changeset/quiet-pears-float.md new file mode 100644 index 00000000000..b7feaa1aa42 --- /dev/null +++ b/.changeset/quiet-pears-float.md @@ -0,0 +1,5 @@ +--- +'@apollo/server': patch +--- + +Update schemaIsSubgraph to also support non nullable \_Service.sdl diff --git a/packages/server/src/__tests__/plugin/schemaIsSubgraph.test.ts b/packages/server/src/__tests__/plugin/schemaIsSubgraph.test.ts new file mode 100644 index 00000000000..0dcefdc803e --- /dev/null +++ b/packages/server/src/__tests__/plugin/schemaIsSubgraph.test.ts @@ -0,0 +1,122 @@ +import { schemaIsSubgraph } from '../../plugin/schemaIsSubgraph'; +import { describe, it, expect } from '@jest/globals'; + +import { + GraphQLSchema, + GraphQLObjectType, + GraphQLString, + GraphQLInt, + GraphQLNonNull, +} from 'graphql'; + +describe('schemaIsSubgraph', () => { + it('returns false when there is no service field', async () => { + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'QueryType', + fields: { + hello: { + type: GraphQLString, + }, + }, + }), + }); + + expect(schemaIsSubgraph(schema)).toBe(false); + }); + + it('returns false when the sdl field is a not string', async () => { + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'QueryType', + fields: { + _service: { + type: new GraphQLObjectType({ + name: '_Service', + fields: { + sdl: { + type: GraphQLInt, + }, + }, + }), + }, + }, + }), + }); + + expect(schemaIsSubgraph(schema)).toBe(false); + }); + + it('returns false when the sdl field is a scalar', async () => { + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'QueryType', + fields: { + _service: { + type: new GraphQLObjectType({ + name: '_Service', + fields: { + sdl: { + type: new GraphQLObjectType({ + name: 'Scalar', + fields: { + value: { + type: GraphQLString, + }, + }, + }), + }, + }, + }), + }, + }, + }), + }); + + expect(schemaIsSubgraph(schema)).toBe(false); + }); + + it('returns true when the sdl field is a string', async () => { + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'QueryType', + fields: { + _service: { + type: new GraphQLObjectType({ + name: '_Service', + fields: { + sdl: { + type: GraphQLString, + }, + }, + }), + }, + }, + }), + }); + + expect(schemaIsSubgraph(schema)).toBe(true); + }); + + it('returns true when the sdl field is a non nullable string', async () => { + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'QueryType', + fields: { + _service: { + type: new GraphQLObjectType({ + name: '_Service', + fields: { + sdl: { + type: new GraphQLNonNull(GraphQLString), + }, + }, + }), + }, + }, + }), + }); + + expect(schemaIsSubgraph(schema)).toBe(true); + }); +}); diff --git a/packages/server/src/plugin/schemaIsSubgraph.ts b/packages/server/src/plugin/schemaIsSubgraph.ts index 88e75aecbc5..580926d57f7 100644 --- a/packages/server/src/plugin/schemaIsSubgraph.ts +++ b/packages/server/src/plugin/schemaIsSubgraph.ts @@ -1,4 +1,9 @@ -import { GraphQLSchema, isObjectType, isScalarType } from 'graphql'; +import { + GraphQLSchema, + isObjectType, + isScalarType, + isNonNullType, +} from 'graphql'; // Returns true if it appears that the schema was appears to be of a subgraph // (eg, returned from @apollo/subgraph's buildSubgraphSchema). This strategy @@ -24,7 +29,11 @@ export function schemaIsSubgraph(schema: GraphQLSchema): boolean { if (!sdlField) { return false; } - const sdlFieldType = sdlField.type; + + let sdlFieldType = sdlField.type; + if (isNonNullType(sdlFieldType)) { + sdlFieldType = sdlFieldType.ofType; + } if (!isScalarType(sdlFieldType)) { return false; }