1
- from collections import defaultdict
2
- from typing import Any , DefaultDict , Dict , List , Set
1
+ from typing import Any , Dict , List , Set
3
2
4
3
from ..language import (
5
4
DocumentNode ,
6
5
FragmentDefinitionNode ,
7
6
FragmentSpreadNode ,
8
7
OperationDefinitionNode ,
8
+ SelectionSetNode ,
9
9
Visitor ,
10
10
visit ,
11
11
)
12
12
13
13
__all__ = ["separate_operations" ]
14
14
15
15
16
- DepGraph = DefaultDict [str , Set [str ]]
16
+ DepGraph = Dict [str , List [str ]]
17
17
18
18
19
19
def separate_operations (document_ast : DocumentNode ) -> Dict [str , DocumentNode ]:
@@ -23,19 +23,31 @@ def separate_operations(document_ast: DocumentNode) -> Dict[str, DocumentNode]:
23
23
fragments and returns a collection of AST documents each of which contains a single
24
24
operation as well the fragment definitions it refers to.
25
25
"""
26
+ operations : List [OperationDefinitionNode ] = []
27
+ dep_graph : DepGraph = {}
28
+
26
29
# 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
+ )
31
39
32
40
# For each operation, produce a new synthesized AST which includes only what is
33
41
# necessary for completing that operation.
34
- separated_document_asts = {}
42
+ separated_document_asts : Dict [ str , DocumentNode ] = {}
35
43
for operation in operations :
36
- operation_name = op_name (operation )
37
44
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 ""
39
51
40
52
# The list of definition nodes to be included for this operation, sorted
41
53
# to retain the same order as the original document.
@@ -54,36 +66,6 @@ def separate_operations(document_ast: DocumentNode) -> Dict[str, DocumentNode]:
54
66
return separated_document_asts
55
67
56
68
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
-
87
69
def collect_transitive_dependencies (
88
70
collected : Set [str ], dep_graph : DepGraph , from_name : str
89
71
) -> None :
@@ -92,8 +74,28 @@ def collect_transitive_dependencies(
92
74
From a dependency graph, collects a list of transitive dependencies by recursing
93
75
through a dependency graph.
94
76
"""
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