|
1 | 1 | # -*- coding: utf-8 -*-
|
2 | 2 | import logging
|
3 | 3 | from traceback import format_exception
|
| 4 | +from copy import deepcopy |
4 | 5 |
|
5 | 6 | from ..error import GraphQLError
|
6 | 7 | from ..language import ast
|
7 | 8 | from ..pyutils.default_ordered_dict import DefaultOrderedDict
|
8 | 9 | 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 | +) |
10 | 15 | from ..type.introspection import (
|
11 | 16 | SchemaMetaFieldDef,
|
12 | 17 | TypeMetaFieldDef,
|
@@ -237,7 +242,8 @@ def collect_fields(
|
237 | 242 | directives = selection.directives
|
238 | 243 |
|
239 | 244 | 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: |
241 | 247 | continue
|
242 | 248 |
|
243 | 249 | name = get_field_entry_key(selection)
|
@@ -316,6 +322,65 @@ def should_include_node(ctx, directives):
|
316 | 322 | return True
|
317 | 323 |
|
318 | 324 |
|
| 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 | + |
319 | 384 | def does_fragment_condition_match(
|
320 | 385 | ctx, # type: ExecutionContext
|
321 | 386 | fragment, # type: Union[FragmentDefinition, InlineFragment]
|
|
0 commit comments