Skip to content

Commit c108fcd

Browse files
committed
recursive directive
1 parent 47a62f5 commit c108fcd

File tree

4 files changed

+71
-8
lines changed

4 files changed

+71
-8
lines changed

graphql/execution/executor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ def execute(
107107
if executor is None:
108108
executor = SyncExecutor()
109109

110+
# operation_name, document_ast
111+
110112
exe_context = ExecutionContext(
111113
schema,
112114
document_ast,

graphql/execution/utils.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
# -*- coding: utf-8 -*-
22
import logging
33
from traceback import format_exception
4+
from copy import deepcopy
45

56
from ..error import GraphQLError
67
from ..language import ast
78
from ..pyutils.default_ordered_dict import DefaultOrderedDict
89
from ..type.definition import GraphQLInterfaceType, GraphQLUnionType
9-
from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective
10+
from ..type.directives import (
11+
GraphQLIncludeDirective,
12+
GraphQLSkipDirective,
13+
GraphQLRecursionDirective,
14+
)
1015
from ..type.introspection import (
1116
SchemaMetaFieldDef,
1217
TypeMetaFieldDef,
@@ -237,7 +242,8 @@ def collect_fields(
237242
directives = selection.directives
238243

239244
if isinstance(selection, ast.Field):
240-
if not should_include_node(ctx, directives):
245+
validate = validate_directives(ctx, directives, selection)
246+
if isinstance(validate, bool) and not validate:
241247
continue
242248

243249
name = get_field_entry_key(selection)
@@ -316,6 +322,65 @@ def should_include_node(ctx, directives):
316322
return True
317323

318324

325+
def validate_directives(ctx, directives, selection):
326+
for directive in directives:
327+
if directive.name.value in (GraphQLSkipDirective.name, GraphQLIncludeDirective.name):
328+
# @skip, @include checking directive
329+
return should_include_node(ctx, directive)
330+
elif directive.name.value == GraphQLRecursionDirective.name:
331+
build_recursive_selection_set(ctx, directive, selection)
332+
333+
334+
def relay_node_check(selection, frame=['edges', 'node']):
335+
""" Check it if relay structure is presented
336+
modules {
337+
edges {
338+
node {
339+
uid # place new recursive query here
340+
}
341+
}
342+
}
343+
"""
344+
if frame:
345+
relay_frame = frame.pop(0)
346+
else:
347+
return True
348+
for selection in selection.selection_set.selections:
349+
if selection.name.value == relay_frame:
350+
return relay_node_check(selection, frame)
351+
return False
352+
353+
def insert_recursive_selection(selection, depth, frame=[]):
354+
def insert_in_frame(selection, paste_selection, frame=frame):
355+
if frame:
356+
relay_frame = frame.pop(0)
357+
else:
358+
# remove directive
359+
selection.directives = []
360+
paste_selection.directives = []
361+
# return inner selection
362+
returnable_selection_set = selection.selection_set
363+
# insert in depth
364+
returnable_selection_set.selections.append(paste_selection)
365+
return returnable_selection_set
366+
for selection in selection.selection_set.selections:
367+
if selection.name.value == relay_frame:
368+
return insert_in_frame(selection, paste_selection, frame)
369+
# remove_directive(selection)
370+
for counter in range(int(depth)):
371+
copy_selection = deepcopy(selection)
372+
copy_frame = deepcopy(frame)
373+
selection = insert_in_frame(selection, copy_selection, frame)
374+
375+
376+
def build_recursive_selection_set(ctx, directive, selection):
377+
depth_size = directive.arguments[0].value.value
378+
is_relay = relay_node_check(selection)
379+
if is_relay:
380+
insert_recursive_selection(selection, depth_size, ['edges', 'node'])
381+
else:
382+
insert_recursive_selection(selection, depth_size)
383+
319384
def does_fragment_condition_match(
320385
ctx, # type: ExecutionContext
321386
fragment, # type: Union[FragmentDefinition, InlineFragment]

graphql/language/parser.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,19 +252,15 @@ def parse_document(parser):
252252

253253
if skip(parser, TokenKind.EOF):
254254
break
255-
print('Final definiations and location', definitions, loc(parser, start))
256255
return ast.Document(definitions=definitions, loc=loc(parser, start))
257256

258257

259258
def parse_definition(parser):
260259
# type: (Parser) -> Any
261-
print('parse_definition', )
262260
if peek(parser, TokenKind.BRACE_L):
263-
print('parse_definition: BRACE_L', parser.lexer, parser.token )
264261
return parse_operation_definition(parser)
265262

266263
if peek(parser, TokenKind.NAME):
267-
print('parse_definition: NAME', parser.lexer, parser.token)
268264

269265
name = parser.token.value
270266

graphql/type/schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections import Iterable
22

33
from .definition import GraphQLObjectType
4-
from .directives import GraphQLDirective, specified_directives
4+
from .directives import GraphQLDirective, specified_directives, GraphQLRecursionDirective
55
from .introspection import IntrospectionSchema
66
from .typemap import GraphQLTypeMap
77

@@ -122,7 +122,7 @@ def get_type(self, name):
122122

123123
def get_directives(self):
124124
# type: () -> List[GraphQLDirective]
125-
return self._directives
125+
return self._directives + [GraphQLRecursionDirective, ]
126126

127127
def get_directive(self, name):
128128
# type: (str) -> Optional[GraphQLDirective]

0 commit comments

Comments
 (0)