graphql 2.3.5 → 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/execution/interpreter/runtime.rb +6 -6
- data/lib/graphql/execution/interpreter.rb +1 -1
- data/lib/graphql/execution/lookahead.rb +10 -10
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +13 -3
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language/document_from_schema_definition.rb +19 -26
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +0 -1
- data/lib/graphql/query/context.rb +4 -0
- data/lib/graphql/query/null_context.rb +4 -0
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query.rb +26 -3
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +19 -5
- data/lib/graphql/schema/directive.rb +2 -0
- data/lib/graphql/schema/enum.rb +4 -4
- data/lib/graphql/schema/field.rb +13 -1
- data/lib/graphql/schema/has_single_input_argument.rb +2 -1
- data/lib/graphql/schema/input_object.rb +8 -7
- data/lib/graphql/schema/introspection_system.rb +2 -14
- data/lib/graphql/schema/member/has_arguments.rb +7 -6
- data/lib/graphql/schema/member/has_fields.rb +6 -4
- data/lib/graphql/schema/resolver.rb +5 -5
- data/lib/graphql/schema/subset.rb +397 -0
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/validator/all_validator.rb +60 -0
- data/lib/graphql/schema/validator.rb +2 -0
- data/lib/graphql/schema/warden.rb +88 -1
- data/lib/graphql/schema.rb +44 -15
- data/lib/graphql/static_validation/base_visitor.rb +6 -5
- data/lib/graphql/static_validation/literal_validator.rb +4 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +7 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +2 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +3 -3
- data/lib/graphql/testing/helpers.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +10 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +10 -0
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/version.rb +1 -1
- metadata +12 -13
- data/lib/graphql/analysis/ast/analyzer.rb +0 -91
- data/lib/graphql/analysis/ast/field_usage.rb +0 -84
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
- data/lib/graphql/analysis/ast/visitor.rb +0 -284
- data/lib/graphql/analysis/ast.rb +0 -94
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -0,0 +1,283 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Analysis
|
4
|
+
# Depth first traversal through a query AST, calling AST analyzers
|
5
|
+
# along the way.
|
6
|
+
#
|
7
|
+
# The visitor is a special case of GraphQL::Language::StaticVisitor, visiting
|
8
|
+
# only the selected operation, providing helpers for common use cases such
|
9
|
+
# as skipped fields and visiting fragment spreads.
|
10
|
+
#
|
11
|
+
# @see {GraphQL::Analysis::Analyzer} AST Analyzers for queries
|
12
|
+
class Visitor < GraphQL::Language::StaticVisitor
|
13
|
+
def initialize(query:, analyzers:)
|
14
|
+
@analyzers = analyzers
|
15
|
+
@path = []
|
16
|
+
@object_types = []
|
17
|
+
@directives = []
|
18
|
+
@field_definitions = []
|
19
|
+
@argument_definitions = []
|
20
|
+
@directive_definitions = []
|
21
|
+
@rescued_errors = []
|
22
|
+
@query = query
|
23
|
+
@schema = query.schema
|
24
|
+
@types = query.types
|
25
|
+
@response_path = []
|
26
|
+
@skip_stack = [false]
|
27
|
+
super(query.selected_operation)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [GraphQL::Query] the query being visited
|
31
|
+
attr_reader :query
|
32
|
+
|
33
|
+
# @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
|
34
|
+
attr_reader :object_types
|
35
|
+
|
36
|
+
# @return [Array<GraphQL::AnalysisError]
|
37
|
+
attr_reader :rescued_errors
|
38
|
+
|
39
|
+
def visit
|
40
|
+
return unless @document
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
# Visit Helpers
|
45
|
+
|
46
|
+
# @return [GraphQL::Execution::Interpreter::Arguments] Arguments for this node, merging default values, literal values and query variables
|
47
|
+
# @see {GraphQL::Query#arguments_for}
|
48
|
+
def arguments_for(ast_node, field_definition)
|
49
|
+
@query.arguments_for(ast_node, field_definition)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Boolean] If the visitor is currently inside a fragment definition
|
53
|
+
def visiting_fragment_definition?
|
54
|
+
@in_fragment_def
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Boolean] If the current node should be skipped because of a skip or include directive
|
58
|
+
def skipping?
|
59
|
+
@skipping
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Array<String>] The path to the response key for the current field
|
63
|
+
def response_path
|
64
|
+
@response_path.dup
|
65
|
+
end
|
66
|
+
|
67
|
+
# Visitor Hooks
|
68
|
+
[
|
69
|
+
:operation_definition, :fragment_definition,
|
70
|
+
:inline_fragment, :field, :directive, :argument, :fragment_spread
|
71
|
+
].each do |node_type|
|
72
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
73
|
+
def call_on_enter_#{node_type}(node, parent)
|
74
|
+
@analyzers.each do |a|
|
75
|
+
begin
|
76
|
+
a.on_enter_#{node_type}(node, parent, self)
|
77
|
+
rescue AnalysisError => err
|
78
|
+
@rescued_errors << err
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def call_on_leave_#{node_type}(node, parent)
|
84
|
+
@analyzers.each do |a|
|
85
|
+
begin
|
86
|
+
a.on_leave_#{node_type}(node, parent, self)
|
87
|
+
rescue AnalysisError => err
|
88
|
+
@rescued_errors << err
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
RUBY
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_operation_definition(node, parent)
|
97
|
+
object_type = @schema.root_type_for_operation(node.operation_type)
|
98
|
+
@object_types.push(object_type)
|
99
|
+
@path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
|
100
|
+
call_on_enter_operation_definition(node, parent)
|
101
|
+
super
|
102
|
+
call_on_leave_operation_definition(node, parent)
|
103
|
+
@object_types.pop
|
104
|
+
@path.pop
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_fragment_definition(node, parent)
|
108
|
+
on_fragment_with_type(node) do
|
109
|
+
@path.push("fragment #{node.name}")
|
110
|
+
@in_fragment_def = false
|
111
|
+
call_on_enter_fragment_definition(node, parent)
|
112
|
+
super
|
113
|
+
@in_fragment_def = false
|
114
|
+
call_on_leave_fragment_definition(node, parent)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def on_inline_fragment(node, parent)
|
119
|
+
on_fragment_with_type(node) do
|
120
|
+
@path.push("...#{node.type ? " on #{node.type.name}" : ""}")
|
121
|
+
@skipping = @skip_stack.last || skip?(node)
|
122
|
+
@skip_stack << @skipping
|
123
|
+
|
124
|
+
call_on_enter_inline_fragment(node, parent)
|
125
|
+
super
|
126
|
+
@skipping = @skip_stack.pop
|
127
|
+
call_on_leave_inline_fragment(node, parent)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def on_field(node, parent)
|
132
|
+
@response_path.push(node.alias || node.name)
|
133
|
+
parent_type = @object_types.last
|
134
|
+
# This could be nil if the previous field wasn't found:
|
135
|
+
field_definition = parent_type && @types.field(parent_type, node.name)
|
136
|
+
@field_definitions.push(field_definition)
|
137
|
+
if !field_definition.nil?
|
138
|
+
next_object_type = field_definition.type.unwrap
|
139
|
+
@object_types.push(next_object_type)
|
140
|
+
else
|
141
|
+
@object_types.push(nil)
|
142
|
+
end
|
143
|
+
@path.push(node.alias || node.name)
|
144
|
+
|
145
|
+
@skipping = @skip_stack.last || skip?(node)
|
146
|
+
@skip_stack << @skipping
|
147
|
+
|
148
|
+
call_on_enter_field(node, parent)
|
149
|
+
super
|
150
|
+
@skipping = @skip_stack.pop
|
151
|
+
call_on_leave_field(node, parent)
|
152
|
+
@response_path.pop
|
153
|
+
@field_definitions.pop
|
154
|
+
@object_types.pop
|
155
|
+
@path.pop
|
156
|
+
end
|
157
|
+
|
158
|
+
def on_directive(node, parent)
|
159
|
+
directive_defn = @schema.directives[node.name]
|
160
|
+
@directive_definitions.push(directive_defn)
|
161
|
+
call_on_enter_directive(node, parent)
|
162
|
+
super
|
163
|
+
call_on_leave_directive(node, parent)
|
164
|
+
@directive_definitions.pop
|
165
|
+
end
|
166
|
+
|
167
|
+
def on_argument(node, parent)
|
168
|
+
argument_defn = if (arg = @argument_definitions.last)
|
169
|
+
arg_type = arg.type.unwrap
|
170
|
+
if arg_type.kind.input_object?
|
171
|
+
@types.argument(arg_type, node.name)
|
172
|
+
else
|
173
|
+
nil
|
174
|
+
end
|
175
|
+
elsif (directive_defn = @directive_definitions.last)
|
176
|
+
@types.argument(directive_defn, node.name)
|
177
|
+
elsif (field_defn = @field_definitions.last)
|
178
|
+
@types.argument(field_defn, node.name)
|
179
|
+
else
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
|
183
|
+
@argument_definitions.push(argument_defn)
|
184
|
+
@path.push(node.name)
|
185
|
+
call_on_enter_argument(node, parent)
|
186
|
+
super
|
187
|
+
call_on_leave_argument(node, parent)
|
188
|
+
@argument_definitions.pop
|
189
|
+
@path.pop
|
190
|
+
end
|
191
|
+
|
192
|
+
def on_fragment_spread(node, parent)
|
193
|
+
@path.push("... #{node.name}")
|
194
|
+
@skipping = @skip_stack.last || skip?(node)
|
195
|
+
@skip_stack << @skipping
|
196
|
+
|
197
|
+
call_on_enter_fragment_spread(node, parent)
|
198
|
+
enter_fragment_spread_inline(node)
|
199
|
+
super
|
200
|
+
@skipping = @skip_stack.pop
|
201
|
+
leave_fragment_spread_inline(node)
|
202
|
+
call_on_leave_fragment_spread(node, parent)
|
203
|
+
@path.pop
|
204
|
+
end
|
205
|
+
|
206
|
+
# @return [GraphQL::BaseType] The current object type
|
207
|
+
def type_definition
|
208
|
+
@object_types.last
|
209
|
+
end
|
210
|
+
|
211
|
+
# @return [GraphQL::BaseType] The type which the current type came from
|
212
|
+
def parent_type_definition
|
213
|
+
@object_types[-2]
|
214
|
+
end
|
215
|
+
|
216
|
+
# @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
|
217
|
+
def field_definition
|
218
|
+
@field_definitions.last
|
219
|
+
end
|
220
|
+
|
221
|
+
# @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
|
222
|
+
def previous_field_definition
|
223
|
+
@field_definitions[-2]
|
224
|
+
end
|
225
|
+
|
226
|
+
# @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
|
227
|
+
def directive_definition
|
228
|
+
@directive_definitions.last
|
229
|
+
end
|
230
|
+
|
231
|
+
# @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
|
232
|
+
def argument_definition
|
233
|
+
@argument_definitions.last
|
234
|
+
end
|
235
|
+
|
236
|
+
# @return [GraphQL::Argument, nil] The previous GraphQL argument
|
237
|
+
def previous_argument_definition
|
238
|
+
@argument_definitions[-2]
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
# Visit a fragment spread inline instead of visiting the definition
|
244
|
+
# by itself.
|
245
|
+
def enter_fragment_spread_inline(fragment_spread)
|
246
|
+
fragment_def = query.fragments[fragment_spread.name]
|
247
|
+
|
248
|
+
object_type = if fragment_def.type
|
249
|
+
@types.type(fragment_def.type.name)
|
250
|
+
else
|
251
|
+
object_types.last
|
252
|
+
end
|
253
|
+
|
254
|
+
object_types << object_type
|
255
|
+
|
256
|
+
on_fragment_definition_children(fragment_def)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Visit a fragment spread inline instead of visiting the definition
|
260
|
+
# by itself.
|
261
|
+
def leave_fragment_spread_inline(_fragment_spread)
|
262
|
+
object_types.pop
|
263
|
+
end
|
264
|
+
|
265
|
+
def skip?(ast_node)
|
266
|
+
dir = ast_node.directives
|
267
|
+
dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
|
268
|
+
end
|
269
|
+
|
270
|
+
def on_fragment_with_type(node)
|
271
|
+
object_type = if node.type
|
272
|
+
@types.type(node.type.name)
|
273
|
+
else
|
274
|
+
@object_types.last
|
275
|
+
end
|
276
|
+
@object_types.push(object_type)
|
277
|
+
yield(node)
|
278
|
+
@object_types.pop
|
279
|
+
@path.pop
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
data/lib/graphql/analysis.rb
CHANGED
@@ -1,2 +1,93 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "graphql/analysis/
|
2
|
+
require "graphql/analysis/visitor"
|
3
|
+
require "graphql/analysis/analyzer"
|
4
|
+
require "graphql/analysis/field_usage"
|
5
|
+
require "graphql/analysis/query_complexity"
|
6
|
+
require "graphql/analysis/max_query_complexity"
|
7
|
+
require "graphql/analysis/query_depth"
|
8
|
+
require "graphql/analysis/max_query_depth"
|
9
|
+
require "timeout"
|
10
|
+
|
11
|
+
module GraphQL
|
12
|
+
module Analysis
|
13
|
+
AST = self
|
14
|
+
module_function
|
15
|
+
# Analyze a multiplex, and all queries within.
|
16
|
+
# Multiplex analyzers are ran for all queries, keeping state.
|
17
|
+
# Query analyzers are ran per query, without carrying state between queries.
|
18
|
+
#
|
19
|
+
# @param multiplex [GraphQL::Execution::Multiplex]
|
20
|
+
# @param analyzers [Array<GraphQL::Analysis::Analyzer>]
|
21
|
+
# @return [Array<Any>] Results from multiplex analyzers
|
22
|
+
def analyze_multiplex(multiplex, analyzers)
|
23
|
+
multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) }
|
24
|
+
|
25
|
+
multiplex.current_trace.analyze_multiplex(multiplex: multiplex) do
|
26
|
+
query_results = multiplex.queries.map do |query|
|
27
|
+
if query.valid?
|
28
|
+
analyze_query(
|
29
|
+
query,
|
30
|
+
query.analyzers,
|
31
|
+
multiplex_analyzers: multiplex_analyzers
|
32
|
+
)
|
33
|
+
else
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
multiplex_results = multiplex_analyzers.map(&:result)
|
39
|
+
multiplex_errors = analysis_errors(multiplex_results)
|
40
|
+
|
41
|
+
multiplex.queries.each_with_index do |query, idx|
|
42
|
+
query.analysis_errors = multiplex_errors + analysis_errors(query_results[idx])
|
43
|
+
end
|
44
|
+
multiplex_results
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param query [GraphQL::Query]
|
49
|
+
# @param analyzers [Array<GraphQL::Analysis::Analyzer>]
|
50
|
+
# @return [Array<Any>] Results from those analyzers
|
51
|
+
def analyze_query(query, analyzers, multiplex_analyzers: [])
|
52
|
+
query.current_trace.analyze_query(query: query) do
|
53
|
+
query_analyzers = analyzers
|
54
|
+
.map { |analyzer| analyzer.new(query) }
|
55
|
+
.tap { _1.select!(&:analyze?) }
|
56
|
+
|
57
|
+
analyzers_to_run = query_analyzers + multiplex_analyzers
|
58
|
+
if analyzers_to_run.any?
|
59
|
+
|
60
|
+
analyzers_to_run.select!(&:visit?)
|
61
|
+
if analyzers_to_run.any?
|
62
|
+
visitor = GraphQL::Analysis::Visitor.new(
|
63
|
+
query: query,
|
64
|
+
analyzers: analyzers_to_run
|
65
|
+
)
|
66
|
+
|
67
|
+
# `nil` or `0` causes no timeout
|
68
|
+
Timeout::timeout(query.validate_timeout_remaining) do
|
69
|
+
visitor.visit
|
70
|
+
end
|
71
|
+
|
72
|
+
if visitor.rescued_errors.any?
|
73
|
+
return visitor.rescued_errors
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
query_analyzers.map(&:result)
|
78
|
+
else
|
79
|
+
[]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
rescue Timeout::Error
|
83
|
+
[GraphQL::AnalysisError.new("Timeout on validation of query")]
|
84
|
+
rescue GraphQL::UnauthorizedError
|
85
|
+
# This error was raised during analysis and will be returned the client before execution
|
86
|
+
[]
|
87
|
+
end
|
88
|
+
|
89
|
+
def analysis_errors(results)
|
90
|
+
results.flatten.tap { _1.select! { |r| r.is_a?(GraphQL::AnalysisError) } }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -21,6 +21,7 @@ module GraphQL
|
|
21
21
|
manager = spawn_fiber do
|
22
22
|
while first_pass || job_fibers.any?
|
23
23
|
first_pass = false
|
24
|
+
fiber_vars = get_fiber_variables
|
24
25
|
|
25
26
|
while (f = (job_fibers.shift || spawn_job_fiber))
|
26
27
|
if f.alive?
|
@@ -34,6 +35,7 @@ module GraphQL
|
|
34
35
|
next_job_fibers.clear
|
35
36
|
|
36
37
|
Sync do |root_task|
|
38
|
+
set_fiber_variables(fiber_vars)
|
37
39
|
while source_tasks.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
|
38
40
|
while (task = source_tasks.shift || spawn_source_task(root_task, sources_condition))
|
39
41
|
if task.alive?
|
@@ -160,9 +160,9 @@ module GraphQL
|
|
160
160
|
case node
|
161
161
|
when GraphQL::Language::Nodes::InlineFragment
|
162
162
|
if node.type
|
163
|
-
type_defn =
|
163
|
+
type_defn = query.types.type(node.type.name)
|
164
164
|
|
165
|
-
if query.
|
165
|
+
if query.types.possible_types(type_defn).include?(owner_type)
|
166
166
|
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
167
167
|
if !result.equal?(next_selections)
|
168
168
|
selections_to_run = result
|
@@ -177,8 +177,8 @@ module GraphQL
|
|
177
177
|
end
|
178
178
|
when GraphQL::Language::Nodes::FragmentSpread
|
179
179
|
fragment_def = query.fragments[node.name]
|
180
|
-
type_defn = query.
|
181
|
-
if query.
|
180
|
+
type_defn = query.types.type(fragment_def.type.name)
|
181
|
+
if query.types.possible_types(type_defn).include?(owner_type)
|
182
182
|
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
183
183
|
if !result.equal?(next_selections)
|
184
184
|
selections_to_run = result
|
@@ -245,7 +245,7 @@ module GraphQL
|
|
245
245
|
end
|
246
246
|
field_name = ast_node.name
|
247
247
|
owner_type = selections_result.graphql_result_type
|
248
|
-
field_defn = query.
|
248
|
+
field_defn = query.types.field(owner_type, field_name)
|
249
249
|
|
250
250
|
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
251
251
|
runtime_state = get_current_runtime_state
|
@@ -579,7 +579,7 @@ module GraphQL
|
|
579
579
|
resolved_value = value
|
580
580
|
end
|
581
581
|
|
582
|
-
possible_types = query.possible_types(current_type)
|
582
|
+
possible_types = query.types.possible_types(current_type)
|
583
583
|
if !possible_types.include?(resolved_type)
|
584
584
|
parent_type = field.owner_type
|
585
585
|
err_class = current_type::UnresolvedTypeError
|
@@ -40,7 +40,7 @@ module GraphQL
|
|
40
40
|
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
41
41
|
multiplex_analyzers = schema.multiplex_analyzers
|
42
42
|
if multiplex.max_complexity
|
43
|
-
multiplex_analyzers += [GraphQL::Analysis::
|
43
|
+
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
|
44
44
|
end
|
45
45
|
|
46
46
|
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
@@ -108,16 +108,16 @@ module GraphQL
|
|
108
108
|
def selection(field_name, selected_type: @selected_type, arguments: nil)
|
109
109
|
next_field_defn = case field_name
|
110
110
|
when String
|
111
|
-
@query.
|
111
|
+
@query.types.field(selected_type, field_name)
|
112
112
|
when Symbol
|
113
113
|
# Try to avoid the `.to_s` below, if possible
|
114
114
|
all_fields = if selected_type.kind.fields?
|
115
|
-
@query.
|
115
|
+
@query.types.fields(selected_type)
|
116
116
|
else
|
117
117
|
# Handle unions by checking possible
|
118
|
-
@query.
|
118
|
+
@query.types
|
119
119
|
.possible_types(selected_type)
|
120
|
-
.map { |t| @query.
|
120
|
+
.map { |t| @query.types.fields(t) }
|
121
121
|
.tap(&:flatten!)
|
122
122
|
end
|
123
123
|
|
@@ -128,7 +128,7 @@ module GraphQL
|
|
128
128
|
# Symbol#name is only present on 3.0+
|
129
129
|
sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
|
130
130
|
guessed_name = Schema::Member::BuildType.camelize(sym_s)
|
131
|
-
@query.
|
131
|
+
@query.types.field(selected_type, guessed_name)
|
132
132
|
end
|
133
133
|
end
|
134
134
|
lookahead_for_selection(next_field_defn, selected_type, arguments)
|
@@ -144,7 +144,7 @@ module GraphQL
|
|
144
144
|
alias_node = lookup_alias_node(ast_nodes, alias_name)
|
145
145
|
return NULL_LOOKAHEAD unless alias_node
|
146
146
|
|
147
|
-
next_field_defn = @query.
|
147
|
+
next_field_defn = @query.types.field(selected_type, alias_node.name)
|
148
148
|
|
149
149
|
alias_arguments = @query.arguments_for(alias_node, next_field_defn)
|
150
150
|
if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments)
|
@@ -183,7 +183,7 @@ module GraphQL
|
|
183
183
|
|
184
184
|
subselections_by_type.each do |type, ast_nodes_by_response_key|
|
185
185
|
ast_nodes_by_response_key.each do |response_key, ast_nodes|
|
186
|
-
field_defn = @query.
|
186
|
+
field_defn = @query.types.field(type, ast_nodes.first.name)
|
187
187
|
lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
|
188
188
|
subselections.push(lookahead)
|
189
189
|
end
|
@@ -266,7 +266,7 @@ module GraphQL
|
|
266
266
|
elsif arguments.nil? || arguments.empty?
|
267
267
|
selections_on_type[response_key] = [ast_selection]
|
268
268
|
else
|
269
|
-
field_defn = @query.
|
269
|
+
field_defn = @query.types.field(selected_type, ast_selection.name)
|
270
270
|
if arguments_match?(arguments, field_defn, ast_selection)
|
271
271
|
selections_on_type[response_key] = [ast_selection]
|
272
272
|
end
|
@@ -276,14 +276,14 @@ module GraphQL
|
|
276
276
|
subselections_on_type = selections_on_type
|
277
277
|
if (t = ast_selection.type)
|
278
278
|
# Assuming this is valid, that `t` will be found.
|
279
|
-
on_type = @query.
|
279
|
+
on_type = @query.types.type(t.name)
|
280
280
|
subselections_on_type = subselections_by_type[on_type] ||= {}
|
281
281
|
end
|
282
282
|
find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
|
283
283
|
when GraphQL::Language::Nodes::FragmentSpread
|
284
284
|
frag_defn = lookup_fragment(ast_selection)
|
285
285
|
# Again, assuming a valid AST
|
286
|
-
on_type = @query.
|
286
|
+
on_type = @query.types.type(frag_defn.type.name)
|
287
287
|
subselections_on_type = subselections_by_type[on_type] ||= {}
|
288
288
|
find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
|
289
289
|
else
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
22
22
|
field :is_repeatable, Boolean, method: :repeatable?
|
23
23
|
|
24
24
|
def args(include_deprecated:)
|
25
|
-
args = @context.
|
25
|
+
args = @context.types.arguments(@object)
|
26
26
|
args = args.reject(&:deprecation_reason) unless include_deprecated
|
27
27
|
args
|
28
28
|
end
|
@@ -15,8 +15,8 @@ module GraphQL
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def __type(name:)
|
18
|
-
if context.
|
19
|
-
context.
|
18
|
+
if context.types.reachable_type?(name)
|
19
|
+
context.types.type(name)
|
20
20
|
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
|
21
21
|
type
|
22
22
|
else
|
@@ -20,7 +20,8 @@ module GraphQL
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def types
|
23
|
-
|
23
|
+
query_types = context.types.all_types
|
24
|
+
types = query_types + context.schema.extra_types
|
24
25
|
types.sort_by!(&:graphql_name)
|
25
26
|
types
|
26
27
|
end
|
@@ -38,13 +39,22 @@ module GraphQL
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def directives
|
41
|
-
@context.
|
42
|
+
@context.types.directives
|
42
43
|
end
|
43
44
|
|
44
45
|
private
|
45
46
|
|
46
47
|
def permitted_root_type(op_type)
|
47
|
-
|
48
|
+
case op_type
|
49
|
+
when "query"
|
50
|
+
@context.types.query_root
|
51
|
+
when "mutation"
|
52
|
+
@context.types.mutation_root
|
53
|
+
when "subcription"
|
54
|
+
@context.types.subscription_root
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
48
58
|
end
|
49
59
|
end
|
50
60
|
end
|
@@ -52,7 +52,7 @@ module GraphQL
|
|
52
52
|
if !@object.kind.enum?
|
53
53
|
nil
|
54
54
|
else
|
55
|
-
enum_values = @context.
|
55
|
+
enum_values = @context.types.enum_values(@object)
|
56
56
|
|
57
57
|
if !include_deprecated
|
58
58
|
enum_values = enum_values.select {|f| !f.deprecation_reason }
|
@@ -64,7 +64,7 @@ module GraphQL
|
|
64
64
|
|
65
65
|
def interfaces
|
66
66
|
if @object.kind.object? || @object.kind.interface?
|
67
|
-
@context.
|
67
|
+
@context.types.interfaces(@object).sort_by(&:graphql_name)
|
68
68
|
else
|
69
69
|
nil
|
70
70
|
end
|
@@ -72,7 +72,7 @@ module GraphQL
|
|
72
72
|
|
73
73
|
def input_fields(include_deprecated:)
|
74
74
|
if @object.kind.input_object?
|
75
|
-
args = @context.
|
75
|
+
args = @context.types.arguments(@object)
|
76
76
|
args = args.reject(&:deprecation_reason) unless include_deprecated
|
77
77
|
args
|
78
78
|
else
|
@@ -82,7 +82,7 @@ module GraphQL
|
|
82
82
|
|
83
83
|
def possible_types
|
84
84
|
if @object.kind.abstract?
|
85
|
-
@context.
|
85
|
+
@context.types.possible_types(@object).sort_by(&:graphql_name)
|
86
86
|
else
|
87
87
|
nil
|
88
88
|
end
|
@@ -92,7 +92,7 @@ module GraphQL
|
|
92
92
|
if !@object.kind.fields?
|
93
93
|
nil
|
94
94
|
else
|
95
|
-
fields = @context.
|
95
|
+
fields = @context.types.fields(@object)
|
96
96
|
if !include_deprecated
|
97
97
|
fields = fields.select {|f| !f.deprecation_reason }
|
98
98
|
end
|