From 075f5347d567876edbb998d3588bbfb845c3d65b Mon Sep 17 00:00:00 2001 From: Sam Boling Date: Thu, 5 Jul 2018 20:41:06 -0600 Subject: [PATCH 1/2] build ASTs directly, use getDirectiveValues to support object args --- src/index.js | 50 +++++++++++++++++++++++---------------- test/index.js | 65 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 80 insertions(+), 35 deletions(-) diff --git a/src/index.js b/src/index.js index 8c5f238..f2b6e53 100755 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import {GraphQLDirective} from 'graphql/type/directives'; -import {GraphQLSchema, parse} from 'graphql'; +import {GraphQLSchema, getDirectiveValues} from 'graphql'; const DEFAULT_DIRECTIVES = ['skip', 'include']; @@ -28,20 +28,22 @@ function resolveWithDirective(resolve, source, directive, context, info) { d => directive.name.value === d.name, )[0]; - let args = {}; - - for (let arg of directive.arguments) { - args[arg.name.value] = arg.value.value; - } + const args = getDirectiveValues( + directiveConfig, + { + directives: [directive], + }, + info.variableValues + ); return directiveConfig.resolve(resolve, source, args, context, info); } /** - * parse directives from a schema defenition form them as graphql directive structure + * parse directives from a schema definition into directive AST nodes */ function parseSchemaDirectives(directives) { - let schemaDirectives = []; + let directiveNodes = []; if ( !directives || @@ -52,22 +54,30 @@ function parseSchemaDirectives(directives) { } for (let directiveName in directives) { - let argsList = [], - args = ''; - - Object.keys(directives[directiveName]).map(key => { - argsList.push(`${key}:"${directives[directiveName][key]}"`); - }); - - if (argsList.length > 0) { - args = `(${argsList.join(',')})`; + let directiveArgNodes = []; + for (let argName in directives.args || []) { + const arg = directives.args[argName]; + directiveArgNodes.push({ + kind: 'InputValueDefinition', + description: arg.description, + name: arg.name, + type: arg.type, + defaultValue: arg.defaultValue, + }); } - schemaDirectives.push(`@${directiveName}${args}`); + directiveNodes.push({ + kind: 'DirectiveDefinition', + description: directives[directiveName].description, + name: { + kind: 'Name', + value: directiveName, + }, + arguments: directiveArgNodes, + }); } - return parse(`{ a: String ${schemaDirectives.join(' ')} }`).definitions[0] - .selectionSet.selections[0].directives; + return directiveNodes; } /** diff --git a/test/index.js b/test/index.js index 876d85a..1415f31 100755 --- a/test/index.js +++ b/test/index.js @@ -4,8 +4,8 @@ import { } from '../src/index'; import { GraphQLInt, + GraphQLInputObjectType, GraphQLSchema, - GraphQLObjectType, GraphQLNonNull, GraphQLList, graphql, @@ -21,14 +21,34 @@ import { import {expect} from 'chai'; +const TestOption = new GraphQLInputObjectType({ + name: 'TestOption', + fields: { + againBy: { + type: GraphQLInt, + description: 'then duplicate again this many times', + }, + } +}); + let GraphQLTestDirective, - GraphQLTestDirectiveTrows, + GraphQLTestDirectiveThrows, GraphQLTestDirectiveCatch, errors, schema; describe('GraphQLCustomDirective', () => { before(() => { + function doDuplicate(input, by) { + let times = []; + + for (let i = 0; i < (by || 2); i++) { + times.push(input); + } + + return times.join(' '); + } + GraphQLTestDirective = new GraphQLCustomDirective({ name: 'duplicate', description: 'duplicate the string sperating them with space', @@ -38,24 +58,26 @@ describe('GraphQLCustomDirective', () => { type: GraphQLInt, description: 'the times to duplicate the string', }, + opts: { + type: new GraphQLList(TestOption), + description: 'additional options', + } }, - resolve: function(resolve, source, {by}, schema, info) { + resolve: function(resolve, source, {by, opts}, schema, info) { return resolve().then(result => { - if (!result) { - return result; + if (result) { + result = doDuplicate(result, by); + if (opts) { + for (let i = 0; i < opts.length; i++) { + result = doDuplicate(result, opts[i].againBy); + } + } } - - let times = []; - - for (let i = 0; i < (by || 2); i++) { - times.push(result); - } - - return times.join(' '); + return result; }); }, }); - GraphQLTestDirectiveTrows = new GraphQLCustomDirective({ + GraphQLTestDirectiveThrows = new GraphQLCustomDirective({ name: 'throws', description: 'throws an error after promise is resolved', locations: [DirectiveLocation.FIELD], @@ -141,6 +163,19 @@ describe('GraphQLCustomDirective', () => { testEqual({directives, query, schema, input, expected, done}); }); + it('expected directive to handle complex argument types', done => { + const query = `{ value @duplicate(opts: [{againBy:2} {againBy:2}]) }`, + schema = ` +type Query { value: String } schema { query: Query } +type TestOption { againBy: Int } +`, + input = {value: 'test'}, + directives = [GraphQLTestDirective], + expected = {value: 'test test test test test test test test'}; + + testEqual({directives, query, schema, input, expected, done}); + }); + it('expected directive to alter execution of graphql and result null', done => { const query = `{ value @duplicate }`, directives = [GraphQLTestDirective], @@ -155,7 +190,7 @@ describe('GraphQLCustomDirective', () => { passServer = true, directives = [ GraphQLTestDirective, - GraphQLTestDirectiveTrows, + GraphQLTestDirectiveThrows, GraphQLTestDirectiveCatch, ]; From fea318b399bb0a913f6f2d6b30f746c8c45263ec Mon Sep 17 00:00:00 2001 From: Sam Boling Date: Thu, 5 Jul 2018 21:28:05 -0600 Subject: [PATCH 2/2] add test for multiple arguments and query variables --- test/index.js | 19 +++++++++++++++---- test/utils.js | 5 ++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/test/index.js b/test/index.js index 1415f31..42b9fb1 100755 --- a/test/index.js +++ b/test/index.js @@ -165,10 +165,7 @@ describe('GraphQLCustomDirective', () => { it('expected directive to handle complex argument types', done => { const query = `{ value @duplicate(opts: [{againBy:2} {againBy:2}]) }`, - schema = ` -type Query { value: String } schema { query: Query } -type TestOption { againBy: Int } -`, + schema = `type Query { value: String } schema { query: Query }`, input = {value: 'test'}, directives = [GraphQLTestDirective], expected = {value: 'test test test test test test test test'}; @@ -176,6 +173,20 @@ type TestOption { againBy: Int } testEqual({directives, query, schema, input, expected, done}); }); + it('expected directive to handle multiple arguments and variables', done => { + const query = ` +query TestVariables($by: Int) { + value @duplicate(by: $by, opts: [{againBy:2}]) +}`, + schema = 'type Query { value(input: Int): String } schema { query: Query }', + input = {value: 'test'}, + variables = {by: 3}, + directives = [GraphQLTestDirective], + expected = {value: 'test test test test test test'}; + + testEqual({schema, directives, query, input, variables, expected, done}); + }); + it('expected directive to alter execution of graphql and result null', done => { const query = `{ value @duplicate }`, directives = [GraphQLTestDirective], diff --git a/test/utils.js b/test/utils.js index e8bd557..025fed2 100755 --- a/test/utils.js +++ b/test/utils.js @@ -21,6 +21,7 @@ const _runQuery = function({ passServer = false, done, context, + variables, }) { let executionSchema = buildSchema(schema || DEFAULT_TEST_SCHEMA); @@ -43,7 +44,7 @@ const _runQuery = function({ applySchemaCustomDirectives(executionSchema); - return graphql(executionSchema, query, input, context); + return graphql(executionSchema, query, input, context, variables); }; exports.testEqual = function({ @@ -51,6 +52,7 @@ exports.testEqual = function({ query, schema, input, + variables, passServer = false, expected, done, @@ -61,6 +63,7 @@ exports.testEqual = function({ query, schema, input, + variables, passServer, done, context,