90
90
get_vertex_field_type , invert_dict , is_tagged_parameter , is_vertex_field_name ,
91
91
strip_non_null_from_type , validate_output_name , validate_safe_string
92
92
)
93
- from .metadata import LocationInfo , QueryMetadataTable , RecurseInfo , TagInfo
93
+ from .metadata import LocationInfo , OutputInfo , QueryMetadataTable , RecurseInfo , TagInfo
94
94
95
95
96
96
# LocationStackEntry contains the following:
@@ -298,7 +298,7 @@ def _compile_property_ast(schema, current_schema_type, ast, location,
298
298
if output_directive :
299
299
# Schema validation has ensured that the fields below exist.
300
300
output_name = output_directive .arguments [0 ].value .value
301
- if output_name in context ['outputs' ] :
301
+ if context ['metadata' ]. get_output_info ( output_name ) :
302
302
raise GraphQLCompilationError (u'Cannot reuse output name: '
303
303
u'{}, {}' .format (output_name , context ))
304
304
validate_safe_string (output_name )
@@ -312,12 +312,12 @@ def _compile_property_ast(schema, current_schema_type, ast, location,
312
312
if location .field != COUNT_META_FIELD_NAME :
313
313
graphql_type = GraphQLList (graphql_type )
314
314
315
- context [ 'outputs' ][ output_name ] = {
316
- ' location' : location ,
317
- 'optional' : is_in_optional_scope ( context ) ,
318
- 'type' : graphql_type ,
319
- 'fold' : context . get ( 'fold' , None ),
320
- }
315
+ output_info = OutputInfo (
316
+ location = location ,
317
+ type = graphql_type ,
318
+ optional = is_in_optional_scope ( context ) ,
319
+ )
320
+ context [ 'metadata' ]. record_output_info ( output_name , output_info )
321
321
322
322
323
323
def _get_recurse_directive_depth (field_name , field_directives ):
@@ -524,7 +524,7 @@ def _compile_vertex_ast(schema, current_schema_type, ast,
524
524
if edge_traversal_is_folded :
525
525
has_count_filter = has_fold_count_filter (context )
526
526
_validate_fold_has_outputs_or_count_filter (
527
- get_context_fold_info (context ), has_count_filter , context [ 'outputs' ] )
527
+ get_context_fold_info (context ), has_count_filter , query_metadata_table )
528
528
basic_blocks .append (blocks .Unfold ())
529
529
unmark_context_fold_scope (context )
530
530
if has_count_filter :
@@ -560,7 +560,19 @@ def _compile_vertex_ast(schema, current_schema_type, ast,
560
560
return basic_blocks
561
561
562
562
563
- def _validate_fold_has_outputs_or_count_filter (fold_scope_location , fold_has_count_filter , outputs ):
563
+ def _are_locations_in_same_fold (first_location , second_location ):
564
+ """Returns True if locations are contained in the same fold scope."""
565
+ return (
566
+ isinstance (first_location , FoldScopeLocation ) and
567
+ isinstance (second_location , FoldScopeLocation ) and
568
+ first_location .base_location == second_location .base_location and
569
+ first_location .get_first_folded_edge () == second_location .get_first_folded_edge ()
570
+ )
571
+
572
+
573
+ def _validate_fold_has_outputs_or_count_filter (
574
+ fold_scope_location , fold_has_count_filter , query_metadata_table
575
+ ):
564
576
"""Ensure the @fold scope has at least one output, or filters on the size of the fold."""
565
577
# This function makes sure that the @fold scope has an effect.
566
578
# Folds either output data, or filter the data enclosing the fold based on the size of the fold.
@@ -570,8 +582,8 @@ def _validate_fold_has_outputs_or_count_filter(fold_scope_location, fold_has_cou
570
582
571
583
# At least one output in the outputs list must point to the fold_scope_location,
572
584
# or the scope corresponding to fold_scope_location had no @outputs and is illegal.
573
- for output in six . itervalues ( outputs ) :
574
- if output [ 'fold' ] == fold_scope_location :
585
+ for _ , output_info in query_metadata_table . outputs :
586
+ if _are_locations_in_same_fold ( output_info . location , fold_scope_location ) :
575
587
return True
576
588
577
589
raise GraphQLCompilationError (u'Found a @fold scope that has no effect on the query. '
@@ -798,13 +810,6 @@ def _compile_root_ast_to_ir(schema, ast, type_equivalence_hints=None):
798
810
# query processing, but apply to the global query scope and should be appended to the
799
811
# IR blocks only after the GlobalOperationsStart block has been emitted.
800
812
'global_filters' : [],
801
- # 'outputs' is a dict mapping each output name to another dict which contains
802
- # - location: Location where to output from
803
- # - optional: boolean representing whether the output was defined within an @optional scope
804
- # - type: GraphQLType of the output
805
- # - fold: FoldScopeLocation object if the current output was defined within a fold scope,
806
- # and None otherwise
807
- 'outputs' : dict (),
808
813
# 'inputs' is a dict mapping input parameter names to their respective expected GraphQL
809
814
# types, as automatically inferred by inspecting the query structure
810
815
'inputs' : dict (),
@@ -853,11 +858,10 @@ def _compile_root_ast_to_ir(schema, ast, type_equivalence_hints=None):
853
858
basic_blocks .extend (context ['global_filters' ])
854
859
855
860
# Based on the outputs context data, add an output step and construct the output metadata.
856
- outputs_context = context ['outputs' ]
857
- basic_blocks .append (_compile_output_step (outputs_context ))
861
+ basic_blocks .append (_compile_output_step (query_metadata_table ))
858
862
output_metadata = {
859
- name : OutputMetadata (type = value [ ' type' ] , optional = value [ ' optional' ] )
860
- for name , value in six . iteritems ( outputs_context )
863
+ name : OutputMetadata (type = info . type , optional = info . optional )
864
+ for name , info in query_metadata_table . outputs
861
865
}
862
866
863
867
return IrAndMetadata (
@@ -867,34 +871,35 @@ def _compile_root_ast_to_ir(schema, ast, type_equivalence_hints=None):
867
871
query_metadata_table = context ['metadata' ])
868
872
869
873
870
- def _compile_output_step (outputs ):
874
+ def _compile_output_step (query_metadata_table ):
871
875
"""Construct the final ConstructResult basic block that defines the output format of the query.
872
876
873
877
Args:
874
- outputs: dict, output name (string) -> output data dict, specifying the location
875
- from where to get the data, and whether the data is optional (and therefore
876
- may be missing); missing optional data is replaced with 'null'
878
+ query_metadata_table: QueryMetadataTable object, part of which specifies the location from
879
+ where to get the output, and whether the output is optional (and
880
+ therefore may be missing); missing optional data is replaced with
881
+ 'null'
877
882
878
883
Returns:
879
884
a ConstructResult basic block that constructs appropriate outputs for the query
880
885
"""
881
- if not outputs :
886
+ if next ( query_metadata_table . outputs , None ) is None :
882
887
raise GraphQLCompilationError (u'No fields were selected for output! Please mark at least '
883
888
u'one field with the @output directive.' )
884
889
885
890
output_fields = {}
886
- for output_name , output_context in six . iteritems ( outputs ) :
887
- location = output_context [ ' location' ]
888
- optional = output_context [ ' optional' ]
889
- graphql_type = output_context [ ' type' ]
891
+ for output_name , output_info in query_metadata_table . outputs :
892
+ location = output_info . location
893
+ optional = output_info . optional
894
+ graphql_type = output_info . type
890
895
891
896
expression = None
892
897
existence_check = None
893
898
# pylint: disable=redefined-variable-type
894
899
if isinstance (location , FoldScopeLocation ):
895
900
if optional :
896
901
raise AssertionError (u'Unreachable state reached, optional in fold: '
897
- u'{}' .format (output_context ))
902
+ u'{}' .format (output_info ))
898
903
899
904
if location .field == COUNT_META_FIELD_NAME :
900
905
expression = expressions .FoldCountContextField (location )
0 commit comments