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.

@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- class FieldUsage < Analyzer
6
- def initialize(query)
7
- super
8
- @used_fields = Set.new
9
- @used_deprecated_fields = Set.new
10
- @used_deprecated_arguments = Set.new
11
- @used_deprecated_enum_values = Set.new
12
- end
13
-
14
- def on_leave_field(node, parent, visitor)
15
- field_defn = visitor.field_definition
16
- field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
17
- @used_fields << field
18
- @used_deprecated_fields << field if field_defn.deprecation_reason
19
- arguments = visitor.query.arguments_for(node, field_defn)
20
- # If there was an error when preparing this argument object,
21
- # then this might be an error or something:
22
- if arguments.respond_to?(:argument_values)
23
- extract_deprecated_arguments(arguments.argument_values)
24
- end
25
- end
26
-
27
- def result
28
- {
29
- used_fields: @used_fields.to_a,
30
- used_deprecated_fields: @used_deprecated_fields.to_a,
31
- used_deprecated_arguments: @used_deprecated_arguments.to_a,
32
- used_deprecated_enum_values: @used_deprecated_enum_values.to_a,
33
- }
34
- end
35
-
36
- private
37
-
38
- def extract_deprecated_arguments(argument_values)
39
- argument_values.each_pair do |_argument_name, argument|
40
- if argument.definition.deprecation_reason
41
- @used_deprecated_arguments << argument.definition.path
42
- end
43
-
44
- arg_val = argument.value
45
-
46
- next if arg_val.nil?
47
-
48
- argument_type = argument.definition.type
49
- if argument_type.non_null?
50
- argument_type = argument_type.of_type
51
- end
52
-
53
- if argument_type.kind.input_object?
54
- extract_deprecated_arguments(argument.original_value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
55
- elsif argument_type.kind.enum?
56
- extract_deprecated_enum_value(argument_type, arg_val)
57
- elsif argument_type.list?
58
- inner_type = argument_type.unwrap
59
- case inner_type.kind
60
- when TypeKinds::INPUT_OBJECT
61
- argument.original_value.each do |value|
62
- extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
63
- end
64
- when TypeKinds::ENUM
65
- arg_val.each do |value|
66
- extract_deprecated_enum_value(inner_type, value)
67
- end
68
- else
69
- # Not a kind of input that we track
70
- end
71
- end
72
- end
73
- end
74
-
75
- def extract_deprecated_enum_value(enum_type, value)
76
- enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
77
- if enum_value&.deprecation_reason
78
- @used_deprecated_enum_values << enum_value.path
79
- end
80
- end
81
- end
82
- end
83
- end
84
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- # Used under the hood to implement complexity validation,
6
- # see {Schema#max_complexity} and {Query#max_complexity}
7
- class MaxQueryComplexity < QueryComplexity
8
- def result
9
- return if subject.max_complexity.nil?
10
-
11
- total_complexity = max_possible_complexity
12
-
13
- if total_complexity > subject.max_complexity
14
- GraphQL::AnalysisError.new("Query has complexity of #{total_complexity}, which exceeds max complexity of #{subject.max_complexity}")
15
- else
16
- nil
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- class MaxQueryDepth < QueryDepth
6
- def result
7
- configured_max_depth = if query
8
- query.max_depth
9
- else
10
- multiplex.schema.max_depth
11
- end
12
-
13
- if configured_max_depth && @max_depth > configured_max_depth
14
- GraphQL::AnalysisError.new("Query has depth of #{@max_depth}, which exceeds max depth of #{configured_max_depth}")
15
- else
16
- nil
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,185 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- # Calculate the complexity of a query, using {Field#complexity} values.
5
- module AST
6
- class QueryComplexity < Analyzer
7
- # State for the query complexity calculation:
8
- # - `complexities_on_type` holds complexity scores for each type
9
- def initialize(query)
10
- super
11
- @skip_introspection_fields = !query.schema.max_complexity_count_introspection_fields
12
- @complexities_on_type_by_query = {}
13
- end
14
-
15
- # Override this method to use the complexity result
16
- def result
17
- max_possible_complexity
18
- end
19
-
20
- # ScopedTypeComplexity models a tree of GraphQL types mapped to inner selections, ie:
21
- # Hash<GraphQL::BaseType, Hash<String, ScopedTypeComplexity>>
22
- class ScopedTypeComplexity < Hash
23
- # A proc for defaulting empty namespace requests as a new scope hash.
24
- DEFAULT_PROC = ->(h, k) { h[k] = {} }
25
-
26
- attr_reader :field_definition, :response_path, :query
27
-
28
- # @param parent_type [Class] The owner of `field_definition`
29
- # @param field_definition [GraphQL::Field, GraphQL::Schema::Field] Used for getting the `.complexity` configuration
30
- # @param query [GraphQL::Query] Used for `query.possible_types`
31
- # @param response_path [Array<String>] The path to the response key for the field
32
- # @return [Hash<GraphQL::BaseType, Hash<String, ScopedTypeComplexity>>]
33
- def initialize(parent_type, field_definition, query, response_path)
34
- super(&DEFAULT_PROC)
35
- @parent_type = parent_type
36
- @field_definition = field_definition
37
- @query = query
38
- @response_path = response_path
39
- @nodes = []
40
- end
41
-
42
- # @return [Array<GraphQL::Language::Nodes::Field>]
43
- attr_reader :nodes
44
-
45
- def own_complexity(child_complexity)
46
- @field_definition.calculate_complexity(query: @query, nodes: @nodes, child_complexity: child_complexity)
47
- end
48
- end
49
-
50
- def on_enter_field(node, parent, visitor)
51
- # We don't want to visit fragment definitions,
52
- # we'll visit them when we hit the spreads instead
53
- return if visitor.visiting_fragment_definition?
54
- return if visitor.skipping?
55
- return if @skip_introspection_fields && visitor.field_definition.introspection?
56
- parent_type = visitor.parent_type_definition
57
- field_key = node.alias || node.name
58
-
59
- # Find or create a complexity scope stack for this query.
60
- scopes_stack = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query, visitor.response_path)]
61
-
62
- # Find or create the complexity costing node for this field.
63
- scope = scopes_stack.last[parent_type][field_key] ||= ScopedTypeComplexity.new(parent_type, visitor.field_definition, visitor.query, visitor.response_path)
64
- scope.nodes.push(node)
65
- scopes_stack.push(scope)
66
- end
67
-
68
- def on_leave_field(node, parent, visitor)
69
- # We don't want to visit fragment definitions,
70
- # we'll visit them when we hit the spreads instead
71
- return if visitor.visiting_fragment_definition?
72
- return if visitor.skipping?
73
- return if @skip_introspection_fields && visitor.field_definition.introspection?
74
- scopes_stack = @complexities_on_type_by_query[visitor.query]
75
- scopes_stack.pop
76
- end
77
-
78
- private
79
-
80
- # @return [Integer]
81
- def max_possible_complexity
82
- @complexities_on_type_by_query.reduce(0) do |total, (query, scopes_stack)|
83
- total + merged_max_complexity_for_scopes(query, [scopes_stack.first])
84
- end
85
- end
86
-
87
- # @param query [GraphQL::Query] Used for `query.possible_types`
88
- # @param scopes [Array<ScopedTypeComplexity>] Array of scoped type complexities
89
- # @return [Integer]
90
- def merged_max_complexity_for_scopes(query, scopes)
91
- # Aggregate a set of all possible scope types encountered (scope keys).
92
- # Use a hash, but ignore the values; it's just a fast way to work with the keys.
93
- possible_scope_types = scopes.each_with_object({}) do |scope, memo|
94
- memo.merge!(scope)
95
- end
96
-
97
- # Expand abstract scope types into their concrete implementations;
98
- # overlapping abstracts coalesce through their intersecting types.
99
- possible_scope_types.keys.each do |possible_scope_type|
100
- next unless possible_scope_type.kind.abstract?
101
-
102
- query.possible_types(possible_scope_type).each do |impl_type|
103
- possible_scope_types[impl_type] ||= true
104
- end
105
- possible_scope_types.delete(possible_scope_type)
106
- end
107
-
108
- # Aggregate the lexical selections that may apply to each possible type,
109
- # and then return the maximum cost among possible typed selections.
110
- possible_scope_types.each_key.reduce(0) do |max, possible_scope_type|
111
- # Collect inner selections from all scopes that intersect with this possible type.
112
- all_inner_selections = scopes.each_with_object([]) do |scope, memo|
113
- scope.each do |scope_type, inner_selections|
114
- memo << inner_selections if types_intersect?(query, scope_type, possible_scope_type)
115
- end
116
- end
117
-
118
- # Find the maximum complexity for the scope type among possible lexical branches.
119
- complexity = merged_max_complexity(query, all_inner_selections)
120
- complexity > max ? complexity : max
121
- end
122
- end
123
-
124
- def types_intersect?(query, a, b)
125
- return true if a == b
126
-
127
- a_types = query.possible_types(a)
128
- query.possible_types(b).any? { |t| a_types.include?(t) }
129
- end
130
-
131
- # A hook which is called whenever a field's max complexity is calculated.
132
- # Override this method to capture individual field complexity details.
133
- #
134
- # @param scoped_type_complexity [ScopedTypeComplexity]
135
- # @param max_complexity [Numeric] Field's maximum complexity including child complexity
136
- # @param child_complexity [Numeric, nil] Field's child complexity
137
- def field_complexity(scoped_type_complexity, max_complexity:, child_complexity: nil)
138
- end
139
-
140
- # @param inner_selections [Array<Hash<String, ScopedTypeComplexity>>] Field selections for a scope
141
- # @return [Integer] Total complexity value for all these selections in the parent scope
142
- def merged_max_complexity(query, inner_selections)
143
- # Aggregate a set of all unique field selection keys across all scopes.
144
- # Use a hash, but ignore the values; it's just a fast way to work with the keys.
145
- unique_field_keys = inner_selections.each_with_object({}) do |inner_selection, memo|
146
- memo.merge!(inner_selection)
147
- end
148
-
149
- # Add up the total cost for each unique field name's coalesced selections
150
- unique_field_keys.each_key.reduce(0) do |total, field_key|
151
- composite_scopes = nil
152
- field_cost = 0
153
-
154
- # Collect composite selection scopes for further aggregation,
155
- # leaf selections report their costs directly.
156
- inner_selections.each do |inner_selection|
157
- child_scope = inner_selection[field_key]
158
- next unless child_scope
159
-
160
- # Empty child scopes are leaf nodes with zero child complexity.
161
- if child_scope.empty?
162
- field_cost = child_scope.own_complexity(0)
163
- field_complexity(child_scope, max_complexity: field_cost, child_complexity: nil)
164
- else
165
- composite_scopes ||= []
166
- composite_scopes << child_scope
167
- end
168
- end
169
-
170
- if composite_scopes
171
- child_complexity = merged_max_complexity_for_scopes(query, composite_scopes)
172
-
173
- # This is the last composite scope visited; assume it's representative (for backwards compatibility).
174
- # Note: it would be more correct to score each composite scope and use the maximum possibility.
175
- field_cost = composite_scopes.last.own_complexity(child_complexity)
176
- field_complexity(composite_scopes.last, max_complexity: field_cost, child_complexity: child_complexity)
177
- end
178
-
179
- total + field_cost
180
- end
181
- end
182
- end
183
- end
184
- end
185
- end
@@ -1,284 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- # Depth first traversal through a query AST, calling AST analyzers
6
- # along the way.
7
- #
8
- # The visitor is a special case of GraphQL::Language::StaticVisitor, visiting
9
- # only the selected operation, providing helpers for common use cases such
10
- # as skipped fields and visiting fragment spreads.
11
- #
12
- # @see {GraphQL::Analysis::AST::Analyzer} AST Analyzers for queries
13
- class Visitor < GraphQL::Language::StaticVisitor
14
- def initialize(query:, analyzers:)
15
- @analyzers = analyzers
16
- @path = []
17
- @object_types = []
18
- @directives = []
19
- @field_definitions = []
20
- @argument_definitions = []
21
- @directive_definitions = []
22
- @rescued_errors = []
23
- @query = query
24
- @schema = query.schema
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 && @schema.get_field(parent_type, node.name, @query.context)
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
- arg_type.get_argument(node.name, @query.context)
172
- else
173
- nil
174
- end
175
- elsif (directive_defn = @directive_definitions.last)
176
- directive_defn.get_argument(node.name, @query.context)
177
- elsif (field_defn = @field_definitions.last)
178
- field_defn.get_argument(node.name, @query.context)
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
- @query.warden.get_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
- @query.warden.get_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
284
- end
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
- require "graphql/analysis/ast/visitor"
3
- require "graphql/analysis/ast/analyzer"
4
- require "graphql/analysis/ast/field_usage"
5
- require "graphql/analysis/ast/query_complexity"
6
- require "graphql/analysis/ast/max_query_complexity"
7
- require "graphql/analysis/ast/query_depth"
8
- require "graphql/analysis/ast/max_query_depth"
9
- require "timeout"
10
-
11
- module GraphQL
12
- module Analysis
13
- module AST
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::AST::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::AST::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::AST::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
94
- end