graphql 1.5.3 → 1.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/define/assign_enum_value.rb +1 -1
- data/lib/graphql/execution/directive_checks.rb +5 -5
- data/lib/graphql/internal_representation.rb +1 -0
- data/lib/graphql/internal_representation/node.rb +117 -16
- data/lib/graphql/internal_representation/rewrite.rb +39 -94
- data/lib/graphql/internal_representation/scope.rb +88 -0
- data/lib/graphql/introspection/schema_field.rb +5 -10
- data/lib/graphql/introspection/type_by_name_field.rb +8 -13
- data/lib/graphql/introspection/typename_field.rb +5 -10
- data/lib/graphql/query.rb +24 -155
- data/lib/graphql/query/arguments_cache.rb +25 -0
- data/lib/graphql/query/validation_pipeline.rb +114 -0
- data/lib/graphql/query/variables.rb +18 -14
- data/lib/graphql/schema.rb +4 -3
- data/lib/graphql/schema/mask.rb +55 -0
- data/lib/graphql/schema/possible_types.rb +2 -2
- data/lib/graphql/schema/type_expression.rb +19 -4
- data/lib/graphql/schema/warden.rb +1 -3
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +3 -2
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +4 -2
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -20
- data/lib/graphql/static_validation/validation_context.rb +6 -18
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/enum_type_spec.rb +12 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +26 -5
- data/spec/graphql/query_spec.rb +23 -3
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +12 -0
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +14 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eacb69bcf39a028dc32debd2a0f266a6f920fd3d
|
4
|
+
data.tar.gz: 921436c519b6b6b8aff3e4dc03845f8d6108a275
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8e1d965b82383e8cee0423fa06b7f20ea5fde13bedb8e9cd2316686c7f7e72dd409a55667b81ea9f0e502906bee45e7957cbdd79c44e96618a405bebf81fc98
|
7
|
+
data.tar.gz: 56c92576bee82ce626d332df1070079579c02007ad78718831301de4c815a6d61c57a17464e71498d9e6a4c51412e80de50e2090aca40fed8266b282b6c12a58
|
@@ -5,7 +5,7 @@ module GraphQL
|
|
5
5
|
module AssignEnumValue
|
6
6
|
def self.call(enum_type, name, desc = nil, deprecation_reason: nil, value: name, &block)
|
7
7
|
enum_value = GraphQL::EnumType::EnumValue.define(
|
8
|
-
name: name,
|
8
|
+
name: name.to_s,
|
9
9
|
description: desc,
|
10
10
|
deprecation_reason: deprecation_reason,
|
11
11
|
value: value,
|
@@ -11,18 +11,18 @@ module GraphQL
|
|
11
11
|
module_function
|
12
12
|
|
13
13
|
# @return [Boolean] Should this node be included in the query?
|
14
|
-
def include?(
|
15
|
-
|
16
|
-
name =
|
14
|
+
def include?(directive_ast_nodes, query)
|
15
|
+
directive_ast_nodes.each do |directive_ast_node|
|
16
|
+
name = directive_ast_node.name
|
17
17
|
directive_defn = query.schema.directives[name]
|
18
18
|
case name
|
19
19
|
when SKIP
|
20
|
-
args = query.arguments_for(
|
20
|
+
args = query.arguments_for(directive_ast_node, directive_defn)
|
21
21
|
if args['if'] == true
|
22
22
|
return false
|
23
23
|
end
|
24
24
|
when INCLUDE
|
25
|
-
args = query.arguments_for(
|
25
|
+
args = query.arguments_for(directive_ast_node, directive_defn)
|
26
26
|
if args['if'] == false
|
27
27
|
return false
|
28
28
|
end
|
@@ -8,42 +8,92 @@ module GraphQL
|
|
8
8
|
# @return [GraphQL::ObjectType]
|
9
9
|
attr_reader :owner_type
|
10
10
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
11
|
+
# Each key is a {GraphQL::ObjectType} which this selection _may_ be made on.
|
12
|
+
# The values for that key are selections which apply to that type.
|
13
|
+
#
|
14
|
+
# This value is derived from {#scoped_children} after the rewrite is finished.
|
15
|
+
# @return [Hash<GraphQL::ObjectType, Hash<String => Node>>]
|
16
|
+
def typed_children
|
17
|
+
@typed_childen ||= begin
|
18
|
+
new_tc = Hash.new { |h, k| h[k] = {} }
|
19
|
+
if @scoped_children.any?
|
20
|
+
all_object_types = Set.new
|
21
|
+
scoped_children.each_key { |t| all_object_types.merge(@query.possible_types(t)) }
|
22
|
+
all_object_types.each do |t|
|
23
|
+
new_tc[t] = get_typed_children(t)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
new_tc
|
27
|
+
end
|
17
28
|
end
|
18
29
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
30
|
+
# These children correspond closely to scopes in the AST.
|
31
|
+
# Keys _may_ be abstract types. They're assumed to be read-only after rewrite is finished
|
32
|
+
# because {#typed_children} is derived from them.
|
33
|
+
#
|
34
|
+
# Using {#scoped_children} during the rewrite step reduces the overhead of reifying
|
35
|
+
# abstract types because they're only reified _after_ the rewrite.
|
36
|
+
# @return [Hash<GraphQL::BaseType, Hash<String => Node>>]
|
37
|
+
attr_reader :scoped_children
|
38
|
+
|
39
|
+
# @return [Array<Language::Nodes::AbstractNode>] AST nodes which are represented by this node
|
40
|
+
attr_reader :ast_nodes
|
41
|
+
|
42
|
+
# @return [Array<GraphQL::Field>] Field definitions for this node (there should only be one!)
|
43
|
+
attr_reader :definitions
|
23
44
|
|
24
45
|
# @return [GraphQL::BaseType]
|
25
46
|
attr_reader :return_type
|
26
47
|
|
48
|
+
# @return [InternalRepresentation::Node, nil]
|
49
|
+
attr_reader :parent
|
50
|
+
|
27
51
|
def initialize(
|
28
|
-
name:, owner_type:, query:, return_type:,
|
29
|
-
ast_nodes:
|
30
|
-
definitions:
|
52
|
+
name:, owner_type:, query:, return_type:, parent:,
|
53
|
+
ast_nodes: [],
|
54
|
+
definitions: []
|
31
55
|
)
|
32
56
|
@name = name
|
33
57
|
@query = query
|
34
58
|
@owner_type = owner_type
|
35
|
-
@
|
59
|
+
@parent = parent
|
60
|
+
@typed_children = nil
|
61
|
+
@scoped_children = Hash.new { |h1, k1| h1[k1] = {} }
|
36
62
|
@ast_nodes = ast_nodes
|
37
63
|
@definitions = definitions
|
38
64
|
@return_type = return_type
|
39
65
|
end
|
40
66
|
|
67
|
+
def initialize_copy(other_node)
|
68
|
+
super
|
69
|
+
# Bust some caches:
|
70
|
+
@typed_children = nil
|
71
|
+
@definition = nil
|
72
|
+
@definition_name = nil
|
73
|
+
@ast_node = nil
|
74
|
+
# Shallow-copy some state:
|
75
|
+
@scoped_children = other_node.scoped_children.dup
|
76
|
+
@ast_nodes = other_node.ast_nodes.dup
|
77
|
+
@definitions = other_node.definitions.dup
|
78
|
+
end
|
79
|
+
|
80
|
+
def ==(other)
|
81
|
+
other.is_a?(self.class) &&
|
82
|
+
other.name == name &&
|
83
|
+
other.parent == parent &&
|
84
|
+
other.return_type == return_type &&
|
85
|
+
other.owner_type == owner_type &&
|
86
|
+
other.scoped_children == scoped_children &&
|
87
|
+
other.definitions == definitions &&
|
88
|
+
other.ast_nodes == ast_nodes
|
89
|
+
end
|
90
|
+
|
41
91
|
def definition_name
|
42
92
|
@definition_name ||= definition.name
|
43
93
|
end
|
44
94
|
|
45
95
|
def definition
|
46
|
-
@definition ||= definitions.first
|
96
|
+
@definition ||= @query.get_field(@owner_type, @definitions.first.name)
|
47
97
|
end
|
48
98
|
|
49
99
|
def ast_node
|
@@ -51,7 +101,58 @@ module GraphQL
|
|
51
101
|
end
|
52
102
|
|
53
103
|
def inspect
|
54
|
-
|
104
|
+
all_children_names = scoped_children.values.map(&:keys).flatten.uniq.join(", ")
|
105
|
+
all_locations = ast_nodes.map {|n| "#{n.line}:#{n.col}" }.join(", ")
|
106
|
+
"#<Node #{@owner_type}.#{@name} -> #{@return_type} {#{all_children_names}} @ [#{all_locations}] #{object_id}>"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Merge selections from `new_parent` into `self`.
|
110
|
+
# Selections are merged in place, not copied.
|
111
|
+
def deep_merge_node(new_parent, merge_self: true)
|
112
|
+
if merge_self
|
113
|
+
@ast_nodes.concat(new_parent.ast_nodes)
|
114
|
+
@definitions.concat(new_parent.definitions)
|
115
|
+
end
|
116
|
+
new_parent.scoped_children.each do |obj_type, new_fields|
|
117
|
+
prev_fields = @scoped_children[obj_type]
|
118
|
+
new_fields.each do |name, new_node|
|
119
|
+
prev_node = prev_fields[name]
|
120
|
+
if prev_node
|
121
|
+
prev_node.deep_merge_node(new_node)
|
122
|
+
else
|
123
|
+
prev_fields[name] = new_node
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
protected
|
130
|
+
|
131
|
+
attr_writer :owner_type, :parent
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# Get applicable children from {#scoped_children}
|
136
|
+
# @param obj_type [GraphQL::ObjectType]
|
137
|
+
# @return [Hash<String => Node>]
|
138
|
+
def get_typed_children(obj_type)
|
139
|
+
new_tc = {}
|
140
|
+
@scoped_children.each do |scope_type, scope_nodes|
|
141
|
+
if GraphQL::Execution::Typecast.subtype?(scope_type, obj_type)
|
142
|
+
scope_nodes.each do |name, new_node|
|
143
|
+
prev_node = new_tc[name]
|
144
|
+
if prev_node
|
145
|
+
prev_node.deep_merge_node(new_node)
|
146
|
+
else
|
147
|
+
copied_node = new_node.dup
|
148
|
+
copied_node.owner_type = obj_type
|
149
|
+
copied_node.parent = self
|
150
|
+
new_tc[name] = copied_node
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
new_tc
|
55
156
|
end
|
56
157
|
end
|
57
158
|
end
|
@@ -34,17 +34,17 @@ module GraphQL
|
|
34
34
|
# Array<Set<InternalRepresentation::Node>>
|
35
35
|
# The current point of the irep_tree during visitation
|
36
36
|
nodes_stack = []
|
37
|
-
# Array<
|
38
|
-
|
39
|
-
|
37
|
+
# Array<Scope>
|
38
|
+
scopes_stack = []
|
39
|
+
|
40
40
|
fragment_definitions = {}
|
41
41
|
skip_nodes = Set.new
|
42
42
|
|
43
|
-
visit_op = VisitDefinition.new(context, @operations, nodes_stack,
|
43
|
+
visit_op = VisitDefinition.new(context, @operations, nodes_stack, scopes_stack)
|
44
44
|
visitor[Nodes::OperationDefinition].enter << visit_op.method(:enter)
|
45
45
|
visitor[Nodes::OperationDefinition].leave << visit_op.method(:leave)
|
46
46
|
|
47
|
-
visit_frag = VisitDefinition.new(context, fragment_definitions, nodes_stack,
|
47
|
+
visit_frag = VisitDefinition.new(context, fragment_definitions, nodes_stack, scopes_stack)
|
48
48
|
visitor[Nodes::FragmentDefinition].enter << visit_frag.method(:enter)
|
49
49
|
visitor[Nodes::FragmentDefinition].leave << visit_frag.method(:leave)
|
50
50
|
|
@@ -52,28 +52,18 @@ module GraphQL
|
|
52
52
|
# Inline fragments provide two things to the rewritten tree:
|
53
53
|
# - They _may_ narrow the scope by their type condition
|
54
54
|
# - They _may_ apply their directives to their children
|
55
|
-
|
56
55
|
if skip?(ast_node, query)
|
57
56
|
skip_nodes.add(ast_node)
|
58
57
|
end
|
59
58
|
|
60
59
|
if skip_nodes.none?
|
61
|
-
|
62
|
-
prev_scope = scope_stack.last
|
63
|
-
each_type(query, context.type_definition) do |obj_type|
|
64
|
-
# What this fragment can apply to is also determined by
|
65
|
-
# the scope around it (it can't widen the scope)
|
66
|
-
if prev_scope.include?(obj_type)
|
67
|
-
next_scope.add(obj_type)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
scope_stack.push(next_scope)
|
60
|
+
scopes_stack.push(scopes_stack.last.enter(context.type_definition))
|
71
61
|
end
|
72
62
|
}
|
73
63
|
|
74
64
|
visitor[Nodes::InlineFragment].leave << ->(ast_node, ast_parent) {
|
75
65
|
if skip_nodes.none?
|
76
|
-
|
66
|
+
scopes_stack.pop
|
77
67
|
end
|
78
68
|
|
79
69
|
if skip_nodes.include?(ast_node)
|
@@ -90,42 +80,39 @@ module GraphQL
|
|
90
80
|
node_name = ast_node.alias || ast_node.name
|
91
81
|
parent_nodes = nodes_stack.last
|
92
82
|
next_nodes = []
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
# It's a non-existent field
|
102
|
-
else
|
103
|
-
field_return_type = field_defn.type.unwrap
|
104
|
-
each_type(query, field_return_type) do |obj_type|
|
105
|
-
next_scope.add(obj_type)
|
106
|
-
end
|
83
|
+
|
84
|
+
field_defn = context.field_definition
|
85
|
+
if field_defn.nil?
|
86
|
+
# It's a non-existent field
|
87
|
+
new_scope = nil
|
88
|
+
else
|
89
|
+
field_return_type = field_defn.type.unwrap
|
90
|
+
scopes_stack.last.each do |scope_type|
|
107
91
|
parent_nodes.each do |parent_node|
|
108
|
-
node = parent_node.
|
92
|
+
node = parent_node.scoped_children[scope_type][node_name] ||= Node.new(
|
93
|
+
parent: parent_node,
|
109
94
|
name: node_name,
|
110
|
-
owner_type:
|
95
|
+
owner_type: scope_type,
|
111
96
|
query: query,
|
112
97
|
return_type: field_return_type,
|
113
98
|
)
|
114
|
-
node.ast_nodes
|
115
|
-
node.definitions
|
99
|
+
node.ast_nodes << ast_node
|
100
|
+
node.definitions << field_defn
|
116
101
|
next_nodes << node
|
117
102
|
end
|
118
103
|
end
|
104
|
+
new_scope = Scope.new(query, field_return_type)
|
119
105
|
end
|
106
|
+
|
120
107
|
nodes_stack.push(next_nodes)
|
121
|
-
|
108
|
+
scopes_stack.push(new_scope)
|
122
109
|
end
|
123
110
|
}
|
124
111
|
|
125
112
|
visitor[Nodes::Field].leave << ->(ast_node, ast_parent) {
|
126
113
|
if skip_nodes.none?
|
127
114
|
nodes_stack.pop
|
128
|
-
|
115
|
+
scopes_stack.pop
|
129
116
|
end
|
130
117
|
|
131
118
|
if skip_nodes.include?(ast_node)
|
@@ -147,97 +134,55 @@ module GraphQL
|
|
147
134
|
# can be shared between its usages.
|
148
135
|
context.on_dependency_resolve do |defn_ast_node, spread_ast_nodes, frag_ast_node|
|
149
136
|
frag_name = frag_ast_node.name
|
150
|
-
|
151
|
-
parent_nodes = spread_parents[spread_ast_node]
|
152
|
-
parent_nodes.each do |parent_node|
|
153
|
-
fragment_node = fragment_definitions[frag_name]
|
154
|
-
if fragment_node
|
155
|
-
deep_merge_selections(query, parent_node, fragment_node)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
137
|
+
fragment_node = fragment_definitions[frag_name]
|
161
138
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
new_fields.each do |name, new_node|
|
169
|
-
prev_node = prev_fields[name]
|
170
|
-
if prev_node
|
171
|
-
prev_node.ast_nodes.merge(new_node.ast_nodes)
|
172
|
-
prev_node.definitions.merge(new_node.definitions)
|
173
|
-
deep_merge_selections(query, prev_node, new_node)
|
174
|
-
else
|
175
|
-
prev_fields[name] = new_node
|
139
|
+
if fragment_node
|
140
|
+
spread_ast_nodes.each do |spread_ast_node|
|
141
|
+
parent_nodes = spread_parents[spread_ast_node]
|
142
|
+
parent_nodes.each do |parent_node|
|
143
|
+
parent_node.deep_merge_node(fragment_node, merge_self: false)
|
144
|
+
end
|
176
145
|
end
|
177
146
|
end
|
178
147
|
end
|
179
148
|
end
|
180
149
|
|
181
|
-
# @see {.each_type}
|
182
|
-
def each_type(query, owner_type, &block)
|
183
|
-
self.class.each_type(query, owner_type, &block)
|
184
|
-
end
|
185
|
-
|
186
|
-
# Call the block for each of `owner_type`'s possible types
|
187
|
-
def self.each_type(query, owner_type)
|
188
|
-
case owner_type
|
189
|
-
when GraphQL::ObjectType, GraphQL::ScalarType, GraphQL::EnumType
|
190
|
-
yield(owner_type)
|
191
|
-
when GraphQL::UnionType, GraphQL::InterfaceType
|
192
|
-
query.possible_types(owner_type).each(&Proc.new)
|
193
|
-
when GraphQL::InputObjectType, nil
|
194
|
-
# this is an error, don't give 'em nothin
|
195
|
-
else
|
196
|
-
raise "Unexpected owner type: #{owner_type.inspect}"
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
150
|
def skip?(ast_node, query)
|
201
151
|
dir = ast_node.directives
|
202
152
|
dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
|
203
153
|
end
|
204
154
|
|
205
155
|
class VisitDefinition
|
206
|
-
def initialize(context, definitions, nodes_stack,
|
156
|
+
def initialize(context, definitions, nodes_stack, scopes_stack)
|
207
157
|
@context = context
|
208
158
|
@query = context.query
|
209
159
|
@definitions = definitions
|
210
160
|
@nodes_stack = nodes_stack
|
211
|
-
@
|
161
|
+
@scopes_stack = scopes_stack
|
212
162
|
end
|
213
163
|
|
214
164
|
def enter(ast_node, ast_parent)
|
215
165
|
# Either QueryType or the fragment type condition
|
216
166
|
owner_type = @context.type_definition && @context.type_definition.unwrap
|
217
|
-
next_nodes = []
|
218
|
-
next_scope = Set.new
|
219
167
|
defn_name = ast_node.name
|
220
|
-
Rewrite.each_type(@query, owner_type) do |obj_type|
|
221
|
-
next_scope.add(obj_type)
|
222
|
-
end
|
223
168
|
|
224
169
|
node = Node.new(
|
170
|
+
parent: nil,
|
225
171
|
name: defn_name,
|
226
172
|
owner_type: owner_type,
|
227
173
|
query: @query,
|
228
|
-
ast_nodes:
|
174
|
+
ast_nodes: [ast_node],
|
229
175
|
return_type: owner_type,
|
230
176
|
)
|
231
|
-
@definitions[defn_name] = node
|
232
|
-
next_nodes << node
|
233
177
|
|
234
|
-
@
|
235
|
-
@
|
178
|
+
@definitions[defn_name] = node
|
179
|
+
@scopes_stack.push(Scope.new(@query, owner_type))
|
180
|
+
@nodes_stack.push([node])
|
236
181
|
end
|
237
182
|
|
238
183
|
def leave(ast_node, ast_parent)
|
239
184
|
@nodes_stack.pop
|
240
|
-
@
|
185
|
+
@scopes_stack.pop
|
241
186
|
end
|
242
187
|
end
|
243
188
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module InternalRepresentation
|
4
|
+
# At a point in the AST, selections may apply to one or more types.
|
5
|
+
# {Scope} represents those types which selections may apply to.
|
6
|
+
#
|
7
|
+
# Scopes can be defined by:
|
8
|
+
#
|
9
|
+
# - A single concrete or abstract type
|
10
|
+
# - An array of types
|
11
|
+
# - `nil`
|
12
|
+
#
|
13
|
+
# The AST may be scoped to an array of types when two abstractly-typed
|
14
|
+
# fragments occur in inside one another.
|
15
|
+
class Scope
|
16
|
+
NO_TYPES = [].freeze
|
17
|
+
|
18
|
+
# @param query [GraphQL::Query]
|
19
|
+
# @param type_defn [GraphQL::BaseType, Array<GraphQL::BaseType>, nil]
|
20
|
+
def initialize(query, type_defn)
|
21
|
+
@query = query
|
22
|
+
@type = type_defn
|
23
|
+
@abstract_type = false
|
24
|
+
@types = case type_defn
|
25
|
+
when Array
|
26
|
+
type_defn
|
27
|
+
when GraphQL::BaseType
|
28
|
+
@abstract_type = true
|
29
|
+
nil
|
30
|
+
when nil
|
31
|
+
NO_TYPES
|
32
|
+
else
|
33
|
+
raise "Unexpected scope type: #{type_defn}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# From a starting point of `self`, create a new scope by condition `other_type_defn`.
|
38
|
+
# @param other_type_defn [GraphQL::BaseType, nil]
|
39
|
+
# @return [Scope]
|
40
|
+
def enter(other_type_defn)
|
41
|
+
case other_type_defn
|
42
|
+
when nil
|
43
|
+
# The type wasn't found, who cares
|
44
|
+
Scope.new(@query, nil)
|
45
|
+
when @type
|
46
|
+
# The condition is the same as current, so reuse self
|
47
|
+
self
|
48
|
+
when GraphQL::UnionType, GraphQL::InterfaceType
|
49
|
+
# Make a new scope of the intersection between the previous & next conditions
|
50
|
+
new_types = @query.possible_types(other_type_defn) & concrete_types
|
51
|
+
Scope.new(@query, new_types)
|
52
|
+
when GraphQL::BaseType
|
53
|
+
# If this type is valid within the current scope,
|
54
|
+
# return a new scope of _exactly_ this type.
|
55
|
+
# Otherwise, this type is out-of-scope so the scope is null.
|
56
|
+
if concrete_types.include?(other_type_defn)
|
57
|
+
Scope.new(@query, other_type_defn)
|
58
|
+
else
|
59
|
+
Scope.new(@query, nil)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
raise "Unexpected scope: #{other_type_defn.inspect}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Call the block for each type in `self`.
|
67
|
+
# This uses the simplest possible expression of `self`,
|
68
|
+
# so if this scope is defined by an abstract type, it gets yielded.
|
69
|
+
def each
|
70
|
+
if @abstract_type
|
71
|
+
yield(@type)
|
72
|
+
else
|
73
|
+
@types.each { |t| yield(t) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def concrete_types
|
80
|
+
@concrete_types ||= if @abstract_type
|
81
|
+
@query.possible_types(@type)
|
82
|
+
else
|
83
|
+
@types
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|