1- from collections import defaultdict
2- from typing import Any , DefaultDict , Dict , List , Set
1+ from typing import Any , Dict , List , Set
32
43from ..language import (
54 DocumentNode ,
65 FragmentDefinitionNode ,
76 FragmentSpreadNode ,
87 OperationDefinitionNode ,
8+ SelectionSetNode ,
99 Visitor ,
1010 visit ,
1111)
1212
1313__all__ = ["separate_operations" ]
1414
1515
16- DepGraph = DefaultDict [str , Set [str ]]
16+ DepGraph = Dict [str , List [str ]]
1717
1818
1919def separate_operations (document_ast : DocumentNode ) -> Dict [str , DocumentNode ]:
@@ -23,19 +23,31 @@ def separate_operations(document_ast: DocumentNode) -> Dict[str, DocumentNode]:
2323 fragments and returns a collection of AST documents each of which contains a single
2424 operation as well the fragment definitions it refers to.
2525 """
26+ operations : List [OperationDefinitionNode ] = []
27+ dep_graph : DepGraph = {}
28+
2629 # Populate metadata and build a dependency graph.
27- visitor = SeparateOperations ()
28- visit (document_ast , visitor )
29- operations = visitor .operations
30- dep_graph = visitor .dep_graph
30+ for definition_node in document_ast .definitions :
31+ if isinstance (definition_node , OperationDefinitionNode ):
32+ operations .append (definition_node )
33+ elif isinstance (
34+ definition_node , FragmentDefinitionNode
35+ ): # pragma: no cover else
36+ dep_graph [definition_node .name .value ] = collect_dependencies (
37+ definition_node .selection_set
38+ )
3139
3240 # For each operation, produce a new synthesized AST which includes only what is
3341 # necessary for completing that operation.
34- separated_document_asts = {}
42+ separated_document_asts : Dict [ str , DocumentNode ] = {}
3543 for operation in operations :
36- operation_name = op_name (operation )
3744 dependencies : Set [str ] = set ()
38- collect_transitive_dependencies (dependencies , dep_graph , operation_name )
45+
46+ for fragment_name in collect_dependencies (operation .selection_set ):
47+ collect_transitive_dependencies (dependencies , dep_graph , fragment_name )
48+
49+ # Provides the empty string for anonymous operations.
50+ operation_name = operation .name .value if operation .name else ""
3951
4052 # The list of definition nodes to be included for this operation, sorted
4153 # to retain the same order as the original document.
@@ -54,36 +66,6 @@ def separate_operations(document_ast: DocumentNode) -> Dict[str, DocumentNode]:
5466 return separated_document_asts
5567
5668
57- class SeparateOperations (Visitor ):
58- operations : List [OperationDefinitionNode ]
59- dep_graph : DepGraph
60- from_name : str
61-
62- def __init__ (self ) -> None :
63- super ().__init__ ()
64- self .operations = []
65- self .dep_graph = defaultdict (set )
66-
67- def enter_operation_definition (
68- self , node : OperationDefinitionNode , * _args : Any
69- ) -> None :
70- self .from_name = op_name (node )
71- self .operations .append (node )
72-
73- def enter_fragment_definition (
74- self , node : FragmentDefinitionNode , * _args : Any
75- ) -> None :
76- self .from_name = node .name .value
77-
78- def enter_fragment_spread (self , node : FragmentSpreadNode , * _args : Any ) -> None :
79- self .dep_graph [self .from_name ].add (node .name .value )
80-
81-
82- def op_name (operation : OperationDefinitionNode ) -> str :
83- """Provide the empty string for anonymous operations."""
84- return operation .name .value if operation .name else ""
85-
86-
8769def collect_transitive_dependencies (
8870 collected : Set [str ], dep_graph : DepGraph , from_name : str
8971) -> None :
@@ -92,8 +74,28 @@ def collect_transitive_dependencies(
9274 From a dependency graph, collects a list of transitive dependencies by recursing
9375 through a dependency graph.
9476 """
95- immediate_deps = dep_graph [from_name ]
96- for to_name in immediate_deps :
97- if to_name not in collected :
98- collected .add (to_name )
99- collect_transitive_dependencies (collected , dep_graph , to_name )
77+ if from_name not in collected :
78+ collected .add (from_name )
79+
80+ immediate_deps = dep_graph .get (from_name )
81+ if immediate_deps is not None :
82+ for to_name in immediate_deps :
83+ collect_transitive_dependencies (collected , dep_graph , to_name )
84+
85+
86+ class DependencyCollector (Visitor ):
87+ dependencies : List [str ]
88+
89+ def __init__ (self ) -> None :
90+ super ().__init__ ()
91+ self .dependencies = []
92+ self .add_dependency = self .dependencies .append
93+
94+ def enter_fragment_spread (self , node : FragmentSpreadNode , * _args : Any ) -> None :
95+ self .add_dependency (node .name .value )
96+
97+
98+ def collect_dependencies (selection_set : SelectionSetNode ) -> List [str ]:
99+ collector = DependencyCollector ()
100+ visit (selection_set , collector )
101+ return collector .dependencies
0 commit comments