graphql 2.3.5 → 2.3.6
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 +282 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/execution/interpreter.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/schema/argument.rb +19 -5
- data/lib/graphql/schema/directive.rb +2 -0
- data/lib/graphql/schema/field.rb +8 -0
- data/lib/graphql/schema/resolver.rb +1 -0
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +10 -11
- 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
@@ -0,0 +1,282 @@
|
|
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
|
+
@response_path = []
|
25
|
+
@skip_stack = [false]
|
26
|
+
super(query.selected_operation)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [GraphQL::Query] the query being visited
|
30
|
+
attr_reader :query
|
31
|
+
|
32
|
+
# @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
|
33
|
+
attr_reader :object_types
|
34
|
+
|
35
|
+
# @return [Array<GraphQL::AnalysisError]
|
36
|
+
attr_reader :rescued_errors
|
37
|
+
|
38
|
+
def visit
|
39
|
+
return unless @document
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
# Visit Helpers
|
44
|
+
|
45
|
+
# @return [GraphQL::Execution::Interpreter::Arguments] Arguments for this node, merging default values, literal values and query variables
|
46
|
+
# @see {GraphQL::Query#arguments_for}
|
47
|
+
def arguments_for(ast_node, field_definition)
|
48
|
+
@query.arguments_for(ast_node, field_definition)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Boolean] If the visitor is currently inside a fragment definition
|
52
|
+
def visiting_fragment_definition?
|
53
|
+
@in_fragment_def
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Boolean] If the current node should be skipped because of a skip or include directive
|
57
|
+
def skipping?
|
58
|
+
@skipping
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [Array<String>] The path to the response key for the current field
|
62
|
+
def response_path
|
63
|
+
@response_path.dup
|
64
|
+
end
|
65
|
+
|
66
|
+
# Visitor Hooks
|
67
|
+
[
|
68
|
+
:operation_definition, :fragment_definition,
|
69
|
+
:inline_fragment, :field, :directive, :argument, :fragment_spread
|
70
|
+
].each do |node_type|
|
71
|
+
module_eval <<-RUBY, __FILE__, __LINE__
|
72
|
+
def call_on_enter_#{node_type}(node, parent)
|
73
|
+
@analyzers.each do |a|
|
74
|
+
begin
|
75
|
+
a.on_enter_#{node_type}(node, parent, self)
|
76
|
+
rescue AnalysisError => err
|
77
|
+
@rescued_errors << err
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def call_on_leave_#{node_type}(node, parent)
|
83
|
+
@analyzers.each do |a|
|
84
|
+
begin
|
85
|
+
a.on_leave_#{node_type}(node, parent, self)
|
86
|
+
rescue AnalysisError => err
|
87
|
+
@rescued_errors << err
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
RUBY
|
93
|
+
end
|
94
|
+
|
95
|
+
def on_operation_definition(node, parent)
|
96
|
+
object_type = @schema.root_type_for_operation(node.operation_type)
|
97
|
+
@object_types.push(object_type)
|
98
|
+
@path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
|
99
|
+
call_on_enter_operation_definition(node, parent)
|
100
|
+
super
|
101
|
+
call_on_leave_operation_definition(node, parent)
|
102
|
+
@object_types.pop
|
103
|
+
@path.pop
|
104
|
+
end
|
105
|
+
|
106
|
+
def on_fragment_definition(node, parent)
|
107
|
+
on_fragment_with_type(node) do
|
108
|
+
@path.push("fragment #{node.name}")
|
109
|
+
@in_fragment_def = false
|
110
|
+
call_on_enter_fragment_definition(node, parent)
|
111
|
+
super
|
112
|
+
@in_fragment_def = false
|
113
|
+
call_on_leave_fragment_definition(node, parent)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def on_inline_fragment(node, parent)
|
118
|
+
on_fragment_with_type(node) do
|
119
|
+
@path.push("...#{node.type ? " on #{node.type.name}" : ""}")
|
120
|
+
@skipping = @skip_stack.last || skip?(node)
|
121
|
+
@skip_stack << @skipping
|
122
|
+
|
123
|
+
call_on_enter_inline_fragment(node, parent)
|
124
|
+
super
|
125
|
+
@skipping = @skip_stack.pop
|
126
|
+
call_on_leave_inline_fragment(node, parent)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def on_field(node, parent)
|
131
|
+
@response_path.push(node.alias || node.name)
|
132
|
+
parent_type = @object_types.last
|
133
|
+
# This could be nil if the previous field wasn't found:
|
134
|
+
field_definition = parent_type && @schema.get_field(parent_type, node.name, @query.context)
|
135
|
+
@field_definitions.push(field_definition)
|
136
|
+
if !field_definition.nil?
|
137
|
+
next_object_type = field_definition.type.unwrap
|
138
|
+
@object_types.push(next_object_type)
|
139
|
+
else
|
140
|
+
@object_types.push(nil)
|
141
|
+
end
|
142
|
+
@path.push(node.alias || node.name)
|
143
|
+
|
144
|
+
@skipping = @skip_stack.last || skip?(node)
|
145
|
+
@skip_stack << @skipping
|
146
|
+
|
147
|
+
call_on_enter_field(node, parent)
|
148
|
+
super
|
149
|
+
@skipping = @skip_stack.pop
|
150
|
+
call_on_leave_field(node, parent)
|
151
|
+
@response_path.pop
|
152
|
+
@field_definitions.pop
|
153
|
+
@object_types.pop
|
154
|
+
@path.pop
|
155
|
+
end
|
156
|
+
|
157
|
+
def on_directive(node, parent)
|
158
|
+
directive_defn = @schema.directives[node.name]
|
159
|
+
@directive_definitions.push(directive_defn)
|
160
|
+
call_on_enter_directive(node, parent)
|
161
|
+
super
|
162
|
+
call_on_leave_directive(node, parent)
|
163
|
+
@directive_definitions.pop
|
164
|
+
end
|
165
|
+
|
166
|
+
def on_argument(node, parent)
|
167
|
+
argument_defn = if (arg = @argument_definitions.last)
|
168
|
+
arg_type = arg.type.unwrap
|
169
|
+
if arg_type.kind.input_object?
|
170
|
+
arg_type.get_argument(node.name, @query.context)
|
171
|
+
else
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
elsif (directive_defn = @directive_definitions.last)
|
175
|
+
directive_defn.get_argument(node.name, @query.context)
|
176
|
+
elsif (field_defn = @field_definitions.last)
|
177
|
+
field_defn.get_argument(node.name, @query.context)
|
178
|
+
else
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
|
182
|
+
@argument_definitions.push(argument_defn)
|
183
|
+
@path.push(node.name)
|
184
|
+
call_on_enter_argument(node, parent)
|
185
|
+
super
|
186
|
+
call_on_leave_argument(node, parent)
|
187
|
+
@argument_definitions.pop
|
188
|
+
@path.pop
|
189
|
+
end
|
190
|
+
|
191
|
+
def on_fragment_spread(node, parent)
|
192
|
+
@path.push("... #{node.name}")
|
193
|
+
@skipping = @skip_stack.last || skip?(node)
|
194
|
+
@skip_stack << @skipping
|
195
|
+
|
196
|
+
call_on_enter_fragment_spread(node, parent)
|
197
|
+
enter_fragment_spread_inline(node)
|
198
|
+
super
|
199
|
+
@skipping = @skip_stack.pop
|
200
|
+
leave_fragment_spread_inline(node)
|
201
|
+
call_on_leave_fragment_spread(node, parent)
|
202
|
+
@path.pop
|
203
|
+
end
|
204
|
+
|
205
|
+
# @return [GraphQL::BaseType] The current object type
|
206
|
+
def type_definition
|
207
|
+
@object_types.last
|
208
|
+
end
|
209
|
+
|
210
|
+
# @return [GraphQL::BaseType] The type which the current type came from
|
211
|
+
def parent_type_definition
|
212
|
+
@object_types[-2]
|
213
|
+
end
|
214
|
+
|
215
|
+
# @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
|
216
|
+
def field_definition
|
217
|
+
@field_definitions.last
|
218
|
+
end
|
219
|
+
|
220
|
+
# @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
|
221
|
+
def previous_field_definition
|
222
|
+
@field_definitions[-2]
|
223
|
+
end
|
224
|
+
|
225
|
+
# @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
|
226
|
+
def directive_definition
|
227
|
+
@directive_definitions.last
|
228
|
+
end
|
229
|
+
|
230
|
+
# @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
|
231
|
+
def argument_definition
|
232
|
+
@argument_definitions.last
|
233
|
+
end
|
234
|
+
|
235
|
+
# @return [GraphQL::Argument, nil] The previous GraphQL argument
|
236
|
+
def previous_argument_definition
|
237
|
+
@argument_definitions[-2]
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
# Visit a fragment spread inline instead of visiting the definition
|
243
|
+
# by itself.
|
244
|
+
def enter_fragment_spread_inline(fragment_spread)
|
245
|
+
fragment_def = query.fragments[fragment_spread.name]
|
246
|
+
|
247
|
+
object_type = if fragment_def.type
|
248
|
+
@query.warden.get_type(fragment_def.type.name)
|
249
|
+
else
|
250
|
+
object_types.last
|
251
|
+
end
|
252
|
+
|
253
|
+
object_types << object_type
|
254
|
+
|
255
|
+
on_fragment_definition_children(fragment_def)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Visit a fragment spread inline instead of visiting the definition
|
259
|
+
# by itself.
|
260
|
+
def leave_fragment_spread_inline(_fragment_spread)
|
261
|
+
object_types.pop
|
262
|
+
end
|
263
|
+
|
264
|
+
def skip?(ast_node)
|
265
|
+
dir = ast_node.directives
|
266
|
+
dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
|
267
|
+
end
|
268
|
+
|
269
|
+
def on_fragment_with_type(node)
|
270
|
+
object_type = if node.type
|
271
|
+
@query.warden.get_type(node.type.name)
|
272
|
+
else
|
273
|
+
@object_types.last
|
274
|
+
end
|
275
|
+
@object_types.push(object_type)
|
276
|
+
yield(node)
|
277
|
+
@object_types.pop
|
278
|
+
@path.pop
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
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?
|
@@ -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)
|
@@ -100,10 +100,10 @@ module GraphQL
|
|
100
100
|
# Depending on the analysis engine, we must use different analyzers
|
101
101
|
# remove this once everything has switched over to AST analyzers
|
102
102
|
if max_depth
|
103
|
-
qa << GraphQL::Analysis::
|
103
|
+
qa << GraphQL::Analysis::MaxQueryDepth
|
104
104
|
end
|
105
105
|
if max_complexity
|
106
|
-
qa << GraphQL::Analysis::
|
106
|
+
qa << GraphQL::Analysis::MaxQueryComplexity
|
107
107
|
end
|
108
108
|
qa
|
109
109
|
else
|
@@ -312,10 +312,15 @@ module GraphQL
|
|
312
312
|
context.query.after_lazy(custom_loaded_value) do |custom_value|
|
313
313
|
if loads
|
314
314
|
if type.list?
|
315
|
-
loaded_values =
|
316
|
-
|
317
|
-
|
318
|
-
|
315
|
+
loaded_values = []
|
316
|
+
context.dataloader.run_isolated do
|
317
|
+
custom_value.each_with_index.map { |custom_val, idx|
|
318
|
+
id = coerced_value[idx]
|
319
|
+
context.dataloader.append_job do
|
320
|
+
loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val)
|
321
|
+
end
|
322
|
+
}
|
323
|
+
end
|
319
324
|
context.schema.after_any_lazies(loaded_values, &:itself)
|
320
325
|
else
|
321
326
|
load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
|
@@ -326,7 +331,16 @@ module GraphQL
|
|
326
331
|
end
|
327
332
|
elsif loads
|
328
333
|
if type.list?
|
329
|
-
loaded_values =
|
334
|
+
loaded_values = []
|
335
|
+
# We want to run these list items all together,
|
336
|
+
# but we also need to wait for the result so we can return it :S
|
337
|
+
context.dataloader.run_isolated do
|
338
|
+
coerced_value.each_with_index { |val, idx|
|
339
|
+
context.dataloader.append_job do
|
340
|
+
loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context)
|
341
|
+
end
|
342
|
+
}
|
343
|
+
end
|
330
344
|
context.schema.after_any_lazies(loaded_values, &:itself)
|
331
345
|
else
|
332
346
|
load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
|
@@ -188,6 +188,8 @@ module GraphQL
|
|
188
188
|
assert_has_location(SCALAR)
|
189
189
|
elsif @owner < GraphQL::Schema
|
190
190
|
assert_has_location(SCHEMA)
|
191
|
+
elsif @owner < GraphQL::Schema::Resolver
|
192
|
+
assert_has_location(FIELD_DEFINITION)
|
191
193
|
else
|
192
194
|
raise "Unexpected directive owner class: #{@owner}"
|
193
195
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -25,6 +25,7 @@ module GraphQL
|
|
25
25
|
extend GraphQL::Schema::Member::HasValidators
|
26
26
|
include Schema::Member::HasPath
|
27
27
|
extend Schema::Member::HasPath
|
28
|
+
extend Schema::Member::HasDirectives
|
28
29
|
|
29
30
|
# @param object [Object] The application object that this field is being resolved on
|
30
31
|
# @param context [GraphQL::Query::Context]
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
# Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
|
10
10
|
# @api private
|
11
11
|
# @see Subscriptions#broadcastable? for a public API
|
12
|
-
class BroadcastAnalyzer < GraphQL::Analysis::
|
12
|
+
class BroadcastAnalyzer < GraphQL::Analysis::Analyzer
|
13
13
|
def initialize(subject)
|
14
14
|
super
|
15
15
|
@default_broadcastable = subject.schema.subscriptions.default_broadcastable
|
@@ -235,7 +235,7 @@ module GraphQL
|
|
235
235
|
if !query.valid?
|
236
236
|
raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
|
237
237
|
end
|
238
|
-
GraphQL::Analysis
|
238
|
+
GraphQL::Analysis.analyze_query(query, @schema.query_analyzers)
|
239
239
|
query.context.namespace(:subscriptions)[:subscription_broadcastable]
|
240
240
|
end
|
241
241
|
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.
|
4
|
+
version: 2.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -306,14 +306,13 @@ files:
|
|
306
306
|
- lib/generators/graphql/union_generator.rb
|
307
307
|
- lib/graphql.rb
|
308
308
|
- lib/graphql/analysis.rb
|
309
|
-
- lib/graphql/analysis/
|
310
|
-
- lib/graphql/analysis/
|
311
|
-
- lib/graphql/analysis/
|
312
|
-
- lib/graphql/analysis/
|
313
|
-
- lib/graphql/analysis/
|
314
|
-
- lib/graphql/analysis/
|
315
|
-
- lib/graphql/analysis/
|
316
|
-
- lib/graphql/analysis/ast/visitor.rb
|
309
|
+
- lib/graphql/analysis/analyzer.rb
|
310
|
+
- lib/graphql/analysis/field_usage.rb
|
311
|
+
- lib/graphql/analysis/max_query_complexity.rb
|
312
|
+
- lib/graphql/analysis/max_query_depth.rb
|
313
|
+
- lib/graphql/analysis/query_complexity.rb
|
314
|
+
- lib/graphql/analysis/query_depth.rb
|
315
|
+
- lib/graphql/analysis/visitor.rb
|
317
316
|
- lib/graphql/analysis_error.rb
|
318
317
|
- lib/graphql/backtrace.rb
|
319
318
|
- lib/graphql/backtrace/inspect_result.rb
|
@@ -645,7 +644,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
645
644
|
- !ruby/object:Gem::Version
|
646
645
|
version: '0'
|
647
646
|
requirements: []
|
648
|
-
rubygems_version: 3.5.
|
647
|
+
rubygems_version: 3.5.12
|
649
648
|
signing_key:
|
650
649
|
specification_version: 4
|
651
650
|
summary: A GraphQL language and runtime for Ruby
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Analysis
|
4
|
-
module AST
|
5
|
-
# Query analyzer for query ASTs. Query analyzers respond to visitor style methods
|
6
|
-
# but are prefixed by `enter` and `leave`.
|
7
|
-
#
|
8
|
-
# When an analyzer is initialized with a Multiplex, you can always get the current query from
|
9
|
-
# `visitor.query` in the visit methods.
|
10
|
-
#
|
11
|
-
# @param [GraphQL::Query, GraphQL::Execution::Multiplex] The query or multiplex to analyze
|
12
|
-
class Analyzer
|
13
|
-
def initialize(subject)
|
14
|
-
@subject = subject
|
15
|
-
|
16
|
-
if subject.is_a?(GraphQL::Query)
|
17
|
-
@query = subject
|
18
|
-
@multiplex = nil
|
19
|
-
else
|
20
|
-
@multiplex = subject
|
21
|
-
@query = nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Analyzer hook to decide at analysis time whether a query should
|
26
|
-
# be analyzed or not.
|
27
|
-
# @return [Boolean] If the query should be analyzed or not
|
28
|
-
def analyze?
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
# Analyzer hook to decide at analysis time whether analysis
|
33
|
-
# requires a visitor pass; can be disabled for precomputed results.
|
34
|
-
# @return [Boolean] If analysis requires visitation or not
|
35
|
-
def visit?
|
36
|
-
true
|
37
|
-
end
|
38
|
-
|
39
|
-
# The result for this analyzer. Returning {GraphQL::AnalysisError} results
|
40
|
-
# in a query error.
|
41
|
-
# @return [Any] The analyzer result
|
42
|
-
def result
|
43
|
-
raise GraphQL::RequiredImplementationMissingError
|
44
|
-
end
|
45
|
-
|
46
|
-
class << self
|
47
|
-
private
|
48
|
-
|
49
|
-
def build_visitor_hooks(member_name)
|
50
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
51
|
-
def on_enter_#{member_name}(node, parent, visitor)
|
52
|
-
end
|
53
|
-
|
54
|
-
def on_leave_#{member_name}(node, parent, visitor)
|
55
|
-
end
|
56
|
-
EOS
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
build_visitor_hooks :argument
|
61
|
-
build_visitor_hooks :directive
|
62
|
-
build_visitor_hooks :document
|
63
|
-
build_visitor_hooks :enum
|
64
|
-
build_visitor_hooks :field
|
65
|
-
build_visitor_hooks :fragment_spread
|
66
|
-
build_visitor_hooks :inline_fragment
|
67
|
-
build_visitor_hooks :input_object
|
68
|
-
build_visitor_hooks :list_type
|
69
|
-
build_visitor_hooks :non_null_type
|
70
|
-
build_visitor_hooks :null_value
|
71
|
-
build_visitor_hooks :operation_definition
|
72
|
-
build_visitor_hooks :type_name
|
73
|
-
build_visitor_hooks :variable_definition
|
74
|
-
build_visitor_hooks :variable_identifier
|
75
|
-
build_visitor_hooks :abstract_node
|
76
|
-
|
77
|
-
protected
|
78
|
-
|
79
|
-
# @return [GraphQL::Query, GraphQL::Execution::Multiplex] Whatever this analyzer is analyzing
|
80
|
-
attr_reader :subject
|
81
|
-
|
82
|
-
# @return [GraphQL::Query, nil] `nil` if this analyzer is visiting a multiplex
|
83
|
-
# (When this is `nil`, use `visitor.query` inside visit methods to get the current query)
|
84
|
-
attr_reader :query
|
85
|
-
|
86
|
-
# @return [GraphQL::Execution::Multiplex, nil] `nil` if this analyzer is visiting a query
|
87
|
-
attr_reader :multiplex
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|