From 04554312faf66933b8462082b20fc07e3833d129 Mon Sep 17 00:00:00 2001 From: Brian O'Neill Date: Tue, 3 Jan 2017 21:10:04 -0500 Subject: [PATCH 1/6] Export directive to support batch processing. --- graphql/execution/base.py | 14 +++++++++++--- graphql/execution/executor.py | 3 ++- graphql/execution/tests/test_directives.py | 7 +++++++ graphql/type/directives.py | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/graphql/execution/base.py b/graphql/execution/base.py index 7929cd5a..e46cf466 100644 --- a/graphql/execution/base.py +++ b/graphql/execution/base.py @@ -3,7 +3,7 @@ from ..language import ast from ..pyutils.default_ordered_dict import DefaultOrderedDict from ..type.definition import GraphQLInterfaceType, GraphQLUnionType -from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective +from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective, GraphQLExportDirective from ..type.introspection import (SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef) from ..utils.type_from_ast import type_from_ast @@ -103,11 +103,12 @@ class ExecutionResult(object): query, `errors` is null if no errors occurred, and is a non-empty array if an error occurred.""" - __slots__ = 'data', 'errors', 'invalid' + __slots__ = 'data', 'errors', 'invalid', 'variable_values' - def __init__(self, data=None, errors=None, invalid=False): + def __init__(self, data=None, errors=None, invalid=False, variable_values=None): self.data = data self.errors = errors + self.variable_values = variable_values if invalid: assert data is None @@ -171,6 +172,13 @@ def collect_fields(ctx, runtime_type, selection_set, fields, prev_fragment_names directives = selection.directives if isinstance(selection, ast.Field): + if directives: + for directive in directives: + if directive.name.value == GraphQLExportDirective.name: + variable = get_argument_values(GraphQLExportDirective.args, directive.arguments)['as'] + import pdb; pdb.set_trace() + ctx.variable_values.setdefault(variable, []).append(selection.name.value) + if not should_include_node(ctx, directives): continue diff --git a/graphql/execution/executor.py b/graphql/execution/executor.py index 7d08db32..9948bac7 100644 --- a/graphql/execution/executor.py +++ b/graphql/execution/executor.py @@ -64,7 +64,8 @@ def on_rejected(error): return None def on_resolve(data): - return ExecutionResult(data=data, errors=context.errors) + return ExecutionResult(data=data, errors=context.errors, + variable_values=context.variable_values) promise = Promise(executor).catch(on_rejected).then(on_resolve) if return_promise: diff --git a/graphql/execution/tests/test_directives.py b/graphql/execution/tests/test_directives.py index 059a8050..1935b064 100644 --- a/graphql/execution/tests/test_directives.py +++ b/graphql/execution/tests/test_directives.py @@ -35,6 +35,13 @@ def test_if_true_includes_scalar(): assert result.data == {'a': 'a', 'b': 'b'} +def test_export(): + result = execute_test_query('{ a, b @export(as: "ids") }') + assert not result.errors + assert result.data == {'a': 'a', 'b': 'b'} + assert result.variable_values == {'ids': 'b'} + + def test_if_false_omits_on_scalar(): result = execute_test_query('{ a, b @include(if: false) }') assert not result.errors diff --git a/graphql/type/directives.py b/graphql/type/directives.py index 06c920de..060e3e1d 100644 --- a/graphql/type/directives.py +++ b/graphql/type/directives.py @@ -103,6 +103,21 @@ def __init__(self, name, description=None, args=None, locations=None): ] ) +"""Used to export variable values as the result of a query (for batching).""" +GraphQLExportDirective = GraphQLDirective( + name='export', + description='Exports variable values from the results of the query.', + args={ + 'as': GraphQLArgument( + type=GraphQLNonNull(GraphQLString), + description='Variable name used for export.', + ), + }, + locations=[ + DirectiveLocation.FIELD + ] +) + """Constant string used for default reason for a deprecation.""" DEFAULT_DEPRECATION_REASON = 'No longer supported' From 3f932744414a7e8baf7d4d595e518c62ec4f53b7 Mon Sep 17 00:00:00 2001 From: Brian O'Neill Date: Tue, 3 Jan 2017 21:11:38 -0500 Subject: [PATCH 2/6] Passing test, removed debugging --- graphql/execution/base.py | 1 - graphql/execution/tests/test_directives.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/graphql/execution/base.py b/graphql/execution/base.py index e46cf466..3e7d3aff 100644 --- a/graphql/execution/base.py +++ b/graphql/execution/base.py @@ -176,7 +176,6 @@ def collect_fields(ctx, runtime_type, selection_set, fields, prev_fragment_names for directive in directives: if directive.name.value == GraphQLExportDirective.name: variable = get_argument_values(GraphQLExportDirective.args, directive.arguments)['as'] - import pdb; pdb.set_trace() ctx.variable_values.setdefault(variable, []).append(selection.name.value) if not should_include_node(ctx, directives): diff --git a/graphql/execution/tests/test_directives.py b/graphql/execution/tests/test_directives.py index 1935b064..06f781a5 100644 --- a/graphql/execution/tests/test_directives.py +++ b/graphql/execution/tests/test_directives.py @@ -39,7 +39,7 @@ def test_export(): result = execute_test_query('{ a, b @export(as: "ids") }') assert not result.errors assert result.data == {'a': 'a', 'b': 'b'} - assert result.variable_values == {'ids': 'b'} + assert result.variable_values == {'ids': ['b']} def test_if_false_omits_on_scalar(): From 023dbb2c0a0637ac301f915311c8f9d88444ce5d Mon Sep 17 00:00:00 2001 From: Brian O'Neill Date: Wed, 4 Jan 2017 13:10:03 -0500 Subject: [PATCH 3/6] append the value --- graphql/execution/base.py | 6 ------ graphql/execution/executor.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/graphql/execution/base.py b/graphql/execution/base.py index 3e7d3aff..28101680 100644 --- a/graphql/execution/base.py +++ b/graphql/execution/base.py @@ -172,12 +172,6 @@ def collect_fields(ctx, runtime_type, selection_set, fields, prev_fragment_names directives = selection.directives if isinstance(selection, ast.Field): - if directives: - for directive in directives: - if directive.name.value == GraphQLExportDirective.name: - variable = get_argument_values(GraphQLExportDirective.args, directive.arguments)['as'] - ctx.variable_values.setdefault(variable, []).append(selection.name.value) - if not should_include_node(ctx, directives): continue diff --git a/graphql/execution/executor.py b/graphql/execution/executor.py index 9948bac7..36522687 100644 --- a/graphql/execution/executor.py +++ b/graphql/execution/executor.py @@ -6,6 +6,7 @@ from six import string_types from promise import Promise, promise_for_dict, promisify, is_thenable +from ..type.directives import GraphQLExportDirective from ..error import GraphQLError, GraphQLLocatedError from ..pyutils.default_ordered_dict import DefaultOrderedDict from ..pyutils.ordereddict import OrderedDict @@ -125,10 +126,19 @@ def execute_fields(exe_context, parent_type, source_value, fields): for response_name, field_asts in fields.items(): result = resolve_field(exe_context, parent_type, source_value, field_asts) + directives = field_asts[0].directives + if directives: + for directive in directives: + if directive.name.value == GraphQLExportDirective.name: + import pdb; pdb.set_trace() + variable_name = directive.arguments[0].value.value + exe_context.variable_values.setdefault(variable_name, []).append(result) + if result is Undefined: continue final_results[response_name] = result + if is_promise(result): contains_promise = True From fbc07b952d9e165564dc9a9aa78e3ab130060e6c Mon Sep 17 00:00:00 2001 From: Brian O'Neill Date: Wed, 4 Jan 2017 13:14:42 -0500 Subject: [PATCH 4/6] s/pdb//g --- graphql/execution/executor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/graphql/execution/executor.py b/graphql/execution/executor.py index 36522687..dc246ab5 100644 --- a/graphql/execution/executor.py +++ b/graphql/execution/executor.py @@ -130,7 +130,6 @@ def execute_fields(exe_context, parent_type, source_value, fields): if directives: for directive in directives: if directive.name.value == GraphQLExportDirective.name: - import pdb; pdb.set_trace() variable_name = directive.arguments[0].value.value exe_context.variable_values.setdefault(variable_name, []).append(result) From c02716737ef2c75e5bab2018cbb220026ffe3f6c Mon Sep 17 00:00:00 2001 From: Brian O'Neill Date: Mon, 23 Jan 2017 15:50:00 -0500 Subject: [PATCH 5/6] Removing dep on Export, not needed here. --- graphql/execution/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql/execution/base.py b/graphql/execution/base.py index 28101680..fdbebd5b 100644 --- a/graphql/execution/base.py +++ b/graphql/execution/base.py @@ -3,7 +3,7 @@ from ..language import ast from ..pyutils.default_ordered_dict import DefaultOrderedDict from ..type.definition import GraphQLInterfaceType, GraphQLUnionType -from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective, GraphQLExportDirective +from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective, from ..type.introspection import (SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef) from ..utils.type_from_ast import type_from_ast From 206c51ed0f4dbd4b7c921e56da1c0338c13a2e1c Mon Sep 17 00:00:00 2001 From: Brian O'Neill Date: Mon, 24 Apr 2017 13:24:10 -0400 Subject: [PATCH 6/6] no prints --- graphql/execution/executor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/graphql/execution/executor.py b/graphql/execution/executor.py index c3734231..d4ab6d1d 100644 --- a/graphql/execution/executor.py +++ b/graphql/execution/executor.py @@ -68,7 +68,6 @@ def executor(resolve, reject): return resolve(execute_operation(context, context.operation, root_value)) def on_rejected(error): - print "ERROR! [{}]".format(error) context.errors.append(error) return None