From c91cad9746cee8d3367738bb4c3038ff17c21b3a Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Tue, 9 Jun 2020 15:24:52 -0400 Subject: [PATCH] addResolversToSchema should merge scalar/enum resolvers instead of overwriting closes #1587 --- packages/schema/src/addResolversToSchema.ts | 57 +++++++++++++++++++ packages/schema/tests/schemaGenerator.test.ts | 29 ++++++++++ 2 files changed, 86 insertions(+) diff --git a/packages/schema/src/addResolversToSchema.ts b/packages/schema/src/addResolversToSchema.ts index 42657629caf..a49f1202c4e 100644 --- a/packages/schema/src/addResolversToSchema.ts +++ b/packages/schema/src/addResolversToSchema.ts @@ -28,6 +28,7 @@ import { healSchema, parseInputValue, forEachField, + mergeDeep, } from '@graphql-tools/utils'; import { checkForResolveTypeResolver } from './checkForResolveTypeResolver'; @@ -163,6 +164,20 @@ function addResolversToExistingSchema( Object.keys(resolverValue).forEach(fieldName => { if (fieldName.startsWith('__')) { type[fieldName.substring(2)] = resolverValue[fieldName]; + } else if (fieldName === 'astNode' && type.astNode != null) { + type.astNode = { + ...type.astNode, + description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? type.astNode.description, + directives: (type.astNode.directives ?? []).concat( + (resolverValue as GraphQLScalarType)?.astNode?.directives ?? [] + ), + }; + } else if (fieldName === 'extensionASTNodes' && type.extensionASTNodes != null) { + type.extensionASTNodes = ([] ?? type.extensionASTNodes).concat( + (resolverValue as GraphQLScalarType)?.extensionASTNodes ?? [] + ); + } else if (fieldName === 'extensions' && type.extensions != null) { + type.extensions = mergeDeep({}, type.extensions, (resolverValue as GraphQLScalarType).extensions); } else { type[fieldName] = resolverValue[fieldName]; } @@ -174,6 +189,20 @@ function addResolversToExistingSchema( Object.keys(resolverValue).forEach(fieldName => { if (fieldName.startsWith('__')) { config[fieldName.substring(2)] = resolverValue[fieldName]; + } else if (fieldName === 'astNode' && config.astNode != null) { + config.astNode = { + ...config.astNode, + description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? config.astNode.description, + directives: (config.astNode.directives ?? []).concat( + (resolverValue as GraphQLEnumType)?.astNode?.directives ?? [] + ), + }; + } else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) { + config.extensionASTNodes = config.extensionASTNodes.concat( + (resolverValue as GraphQLEnumType)?.extensionASTNodes ?? [] + ); + } else if (fieldName === 'extensions' && config.extensions != null) { + type.extensions = mergeDeep({}, config.extensions, (resolverValue as GraphQLEnumType).extensions); } else if (enumValueConfigMap[fieldName]) { enumValueConfigMap[fieldName].value = resolverValue[fieldName]; } @@ -242,6 +271,20 @@ function createNewSchemaWithResolvers( Object.keys(resolverValue).forEach(fieldName => { if (fieldName.startsWith('__')) { config[fieldName.substring(2)] = resolverValue[fieldName]; + } else if (fieldName === 'astNode' && config.astNode != null) { + config.astNode = { + ...config.astNode, + description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? config.astNode.description, + directives: (config.astNode.directives ?? []).concat( + (resolverValue as GraphQLScalarType)?.astNode?.directives ?? [] + ), + }; + } else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) { + config.extensionASTNodes = config.extensionASTNodes.concat( + (resolverValue as GraphQLScalarType)?.extensionASTNodes ?? [] + ); + } else if (fieldName === 'extensions' && config.extensions != null) { + config.extensions = mergeDeep({}, type.extensions, (resolverValue as GraphQLScalarType).extensions); } else { config[fieldName] = resolverValue[fieldName]; } @@ -260,6 +303,20 @@ function createNewSchemaWithResolvers( Object.keys(resolverValue).forEach(fieldName => { if (fieldName.startsWith('__')) { config[fieldName.substring(2)] = resolverValue[fieldName]; + } else if (fieldName === 'astNode' && config.astNode != null) { + config.astNode = { + ...config.astNode, + description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? config.astNode.description, + directives: (config.astNode.directives ?? []).concat( + (resolverValue as GraphQLEnumType)?.astNode?.directives ?? [] + ), + }; + } else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) { + config.extensionASTNodes = config.extensionASTNodes.concat( + (resolverValue as GraphQLEnumType)?.extensionASTNodes ?? [] + ); + } else if (fieldName === 'extensions' && config.extensions != null) { + config.extensions = mergeDeep({}, type.extensions, (resolverValue as GraphQLEnumType).extensions); } else if (enumValueConfigMap[fieldName]) { enumValueConfigMap[fieldName].value = resolverValue[fieldName]; } diff --git a/packages/schema/tests/schemaGenerator.test.ts b/packages/schema/tests/schemaGenerator.test.ts index b8c5dc609ef..c59f32e8126 100644 --- a/packages/schema/tests/schemaGenerator.test.ts +++ b/packages/schema/tests/schemaGenerator.test.ts @@ -838,6 +838,35 @@ describe('generating schema from shorthand', () => { }); }); + test('retains original scalar directives when passing in scalars in resolve functions', () => { + const schema = makeExecutableSchema({ + typeDefs: ` + directive @test on SCALAR + + scalar Test @test + + type Query { + test: Test + } + `, + resolvers: { + Test: new GraphQLScalarType({ + name: 'Test', + description: 'Test resolver', + serialize: (value) => value, + parseValue: (value) => value, + }), + Query: { + test: () => 42, + }, + }, + }); + + const testType = schema.getType('Test'); + expect(testType).toBeInstanceOf(GraphQLScalarType); + expect(testType.astNode.directives.length).toBe(1); + }); + test('retains scalars after walking/recreating the schema', () => { const shorthand = ` scalar Test