diff --git a/.changeset/many-lamps-tickle.md b/.changeset/many-lamps-tickle.md new file mode 100644 index 00000000000..c8b8b95df54 --- /dev/null +++ b/.changeset/many-lamps-tickle.md @@ -0,0 +1,5 @@ +--- +'@graphql-tools/stitching-directives': patch +--- + +Allow using merge directive without keyArg argument if resolver takes 0 arguments diff --git a/packages/stitch/tests/typeMerging.test.ts b/packages/stitch/tests/typeMerging.test.ts index 879a211ceab..19fa49f56df 100644 --- a/packages/stitch/tests/typeMerging.test.ts +++ b/packages/stitch/tests/typeMerging.test.ts @@ -114,6 +114,78 @@ describe('merging using type merging', () => { expect(userByIdData.chirps[1].author.email).not.toBe(null); }); + test('works without resolver args', async () => { + let chirpSchema = makeExecutableSchema({ + typeDefs: /* GraphQL */ ` + type Stats { + totalChirps: Int! + } + + type Query { + stats: Stats! + } + `, + }); + + chirpSchema = addMocksToSchema({ schema: chirpSchema }); + + let authorSchema = makeExecutableSchema({ + typeDefs: /* GraphQL */ ` + type Stats { + totalUsers: Int! + } + type Query { + stats: Stats! + } + `, + }); + + authorSchema = addMocksToSchema({ schema: authorSchema }); + + const stitchedSchema = stitchSchemas({ + subschemas: [ + { + schema: chirpSchema, + merge: { + Stats: { + fieldName: 'stats', + args: () => ({}), + }, + }, + batch: true, + }, + { + schema: authorSchema, + merge: { + Stats: { + fieldName: 'stats', + args: () => ({}), + }, + }, + batch: true, + }, + ], + }); + + const query = /* GraphQL */ ` + query { + stats { + totalChirps + } + } + `; + + const result = await graphql({ + schema: stitchedSchema, + source: query, + }); + + expect(result.errors).toBeUndefined(); + assertSome(result.data); + const statsData: any = result.data['stats']; + expect(statsData.totalChirps).not.toBe(null); + }); + test('handle top level failures on subschema queries', async () => { let userSchema = makeExecutableSchema({ typeDefs: /* GraphQL */ ` diff --git a/packages/stitching-directives/src/stitchingDirectivesValidator.ts b/packages/stitching-directives/src/stitchingDirectivesValidator.ts index f5d82b8512f..880b70cbc54 100644 --- a/packages/stitching-directives/src/stitchingDirectivesValidator.ts +++ b/packages/stitching-directives/src/stitchingDirectivesValidator.ts @@ -84,7 +84,7 @@ export function stitchingDirectivesValidator( const keyArg = mergeDirective['keyArg']; if (keyArg == null) { - if (!mergeArgsExpr && args.length !== 1) { + if (!mergeArgsExpr && args.length > 1) { throw new Error( 'Cannot use @merge directive without `keyArg` argument if resolver takes more than one argument.' ); diff --git a/packages/stitching-directives/tests/stitchingDirectivesValidator.test.ts b/packages/stitching-directives/tests/stitchingDirectivesValidator.test.ts index c6433ed347e..064c60f40bf 100644 --- a/packages/stitching-directives/tests/stitchingDirectivesValidator.test.ts +++ b/packages/stitching-directives/tests/stitchingDirectivesValidator.test.ts @@ -156,4 +156,40 @@ describe('type merging directives', () => { expect(() => makeExecutableSchema({ typeDefs })).not.toThrow(); expect(() => stitchingDirectivesValidator(makeExecutableSchema({ typeDefs }))).not.toThrow(); }); + + test('does not throw an error if merge used without arguments on a zero args endpoint', () => { + const typeDefs = /* GraphQL */ ` + ${allStitchingDirectivesTypeDefs} + + type Query { + _stats: Stats @merge + } + + type Stats { + firstStat: Int + secondStat: Int + } + `; + + expect(() => makeExecutableSchema({ typeDefs })).not.toThrow(); + expect(() => stitchingDirectivesValidator(makeExecutableSchema({ typeDefs }))).not.toThrow(); + }); + + test('throws an error if merge used without arguments on a multiple args endpoint', () => { + const typeDefs = /* GraphQL */ ` + ${allStitchingDirectivesTypeDefs} + + type Query { + _user(id: ID, name: String): User @merge + } + + type User @key(selectionSet: "{ id name }") { + id: ID + name: String + } + `; + + expect(() => makeExecutableSchema({ typeDefs })).not.toThrow(); + expect(() => stitchingDirectivesValidator(makeExecutableSchema({ typeDefs }))).toThrow(); + }); });