graphql 2.2.5 → 2.2.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast.rb +7 -1
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/execution/interpreter/runtime.rb +9 -0
- data/lib/graphql/execution/interpreter.rb +90 -150
- data/lib/graphql/introspection/entry_points.rb +9 -3
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +1 -2
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +11 -1
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/pagination/array_connection.rb +3 -3
- data/lib/graphql/pagination/relation_connection.rb +3 -3
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/field.rb +31 -30
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/resolver.rb +9 -5
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +49 -7
- data/lib/graphql/static_validation/literal_validator.rb +1 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/testing/helpers.rb +8 -4
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +2 -0
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/sentry_trace.rb +95 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -2
- metadata +24 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ed6a9339a1c67dca4307a9753eeb5c25d8ff6cd32fa156de65e852ddb21f5e9
|
4
|
+
data.tar.gz: 0ed35f51512eba4f60fa1c16d80d267f800c516d174c024d8beaa68fbcca3d33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 159a9c932168794b7d4446ed0b207269c6f4ca05c45542c7632816a8bb0449b024f4879be95d8b762b0eb5e8969a31ce28915ec53853d4f67559d952908e59e2
|
7
|
+
data.tar.gz: f153e397dd3b83504ca97ff44ac3f479228d7d18df5bf24879c8e8a8113e54d61ba26b8b9c74516bc05d47cb7febfcd507b12e6f3d42988b8dad9b5e3a7fa211
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
@used_fields = Set.new
|
9
9
|
@used_deprecated_fields = Set.new
|
10
10
|
@used_deprecated_arguments = Set.new
|
11
|
+
@used_deprecated_enum_values = Set.new
|
11
12
|
end
|
12
13
|
|
13
14
|
def on_leave_field(node, parent, visitor)
|
@@ -15,7 +16,7 @@ module GraphQL
|
|
15
16
|
field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
|
16
17
|
@used_fields << field
|
17
18
|
@used_deprecated_fields << field if field_defn.deprecation_reason
|
18
|
-
arguments = visitor.query.arguments_for(node,
|
19
|
+
arguments = visitor.query.arguments_for(node, field_defn)
|
19
20
|
# If there was an error when preparing this argument object,
|
20
21
|
# then this might be an error or something:
|
21
22
|
if arguments.respond_to?(:argument_values)
|
@@ -28,6 +29,7 @@ module GraphQL
|
|
28
29
|
used_fields: @used_fields.to_a,
|
29
30
|
used_deprecated_fields: @used_deprecated_fields.to_a,
|
30
31
|
used_deprecated_arguments: @used_deprecated_arguments.to_a,
|
32
|
+
used_deprecated_enum_values: @used_deprecated_enum_values.to_a,
|
31
33
|
}
|
32
34
|
end
|
33
35
|
|
@@ -41,16 +43,39 @@ module GraphQL
|
|
41
43
|
|
42
44
|
next if argument.value.nil?
|
43
45
|
|
44
|
-
|
46
|
+
argument_type = argument.definition.type
|
47
|
+
if argument_type.non_null?
|
48
|
+
argument_type = argument_type.of_type
|
49
|
+
end
|
50
|
+
|
51
|
+
if argument_type.kind.input_object?
|
45
52
|
extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
46
|
-
elsif
|
47
|
-
argument
|
48
|
-
|
49
|
-
|
50
|
-
|
53
|
+
elsif argument_type.kind.enum?
|
54
|
+
extract_deprecated_enum_value(argument_type, argument.value)
|
55
|
+
elsif argument_type.list?
|
56
|
+
inner_type = argument_type.unwrap
|
57
|
+
case inner_type.kind
|
58
|
+
when TypeKinds::INPUT_OBJECT
|
59
|
+
argument.value.each do |value|
|
60
|
+
extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
61
|
+
end
|
62
|
+
when TypeKinds::ENUM
|
63
|
+
argument.value.each do |value|
|
64
|
+
extract_deprecated_enum_value(inner_type, value)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
# Not a kind of input that we track
|
68
|
+
end
|
51
69
|
end
|
52
70
|
end
|
53
71
|
end
|
72
|
+
|
73
|
+
def extract_deprecated_enum_value(enum_type, value)
|
74
|
+
enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
|
75
|
+
if enum_value&.deprecation_reason
|
76
|
+
@used_deprecated_enum_values << enum_value.path
|
77
|
+
end
|
78
|
+
end
|
54
79
|
end
|
55
80
|
end
|
56
81
|
end
|
data/lib/graphql/analysis/ast.rb
CHANGED
@@ -6,6 +6,7 @@ require "graphql/analysis/ast/query_complexity"
|
|
6
6
|
require "graphql/analysis/ast/max_query_complexity"
|
7
7
|
require "graphql/analysis/ast/query_depth"
|
8
8
|
require "graphql/analysis/ast/max_query_depth"
|
9
|
+
require "timeout"
|
9
10
|
|
10
11
|
module GraphQL
|
11
12
|
module Analysis
|
@@ -63,7 +64,10 @@ module GraphQL
|
|
63
64
|
analyzers: analyzers_to_run
|
64
65
|
)
|
65
66
|
|
66
|
-
|
67
|
+
# `nil` or `0` causes no timeout
|
68
|
+
Timeout::timeout(query.validate_timeout_remaining) do
|
69
|
+
visitor.visit
|
70
|
+
end
|
67
71
|
|
68
72
|
if visitor.rescued_errors.any?
|
69
73
|
return visitor.rescued_errors
|
@@ -75,6 +79,8 @@ module GraphQL
|
|
75
79
|
[]
|
76
80
|
end
|
77
81
|
end
|
82
|
+
rescue Timeout::Error
|
83
|
+
[GraphQL::AnalysisError.new("Timeout on validation of query")]
|
78
84
|
end
|
79
85
|
|
80
86
|
def analysis_errors(results)
|
@@ -1,13 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
|
-
class CoercionError < GraphQL::
|
4
|
-
# @return [Hash] Optional custom data for error objects which will be added
|
5
|
-
# under the `extensions` key.
|
6
|
-
attr_accessor :extensions
|
7
|
-
|
8
|
-
def initialize(message, extensions: nil)
|
9
|
-
@extensions = extensions
|
10
|
-
super(message)
|
11
|
-
end
|
3
|
+
class CoercionError < GraphQL::ExecutionError
|
12
4
|
end
|
13
5
|
end
|
@@ -14,6 +14,11 @@ module GraphQL
|
|
14
14
|
def load
|
15
15
|
@source.load(@key)
|
16
16
|
end
|
17
|
+
|
18
|
+
def load_with_deprecation_warning
|
19
|
+
warn("Returning `.request(...)` from GraphQL::Dataloader is deprecated, use `.load(...)` instead. (See usage of #{@source} with #{@key.inspect}).")
|
20
|
+
load
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -352,6 +352,15 @@ module GraphQL
|
|
352
352
|
end
|
353
353
|
|
354
354
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
355
|
+
if directives.any?
|
356
|
+
# This might be executed in a different context; reset this info
|
357
|
+
runtime_state = get_current_runtime_state
|
358
|
+
runtime_state.current_field = field_defn
|
359
|
+
runtime_state.current_object = object
|
360
|
+
runtime_state.current_arguments = resolved_arguments
|
361
|
+
runtime_state.current_result_name = result_name
|
362
|
+
runtime_state.current_result = selection_result
|
363
|
+
end
|
355
364
|
# Actually call the field resolver and capture the result
|
356
365
|
app_result = begin
|
357
366
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
@@ -37,173 +37,113 @@ module GraphQL
|
|
37
37
|
multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
|
38
38
|
schema = multiplex.schema
|
39
39
|
queries = multiplex.queries
|
40
|
-
query_instrumenters = schema.instrumenters[:query]
|
41
|
-
multiplex_instrumenters = schema.instrumenters[:multiplex]
|
42
40
|
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
41
|
+
multiplex_analyzers = schema.multiplex_analyzers
|
42
|
+
if multiplex.max_complexity
|
43
|
+
multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
47
|
+
begin
|
48
|
+
# Since this is basically the batching context,
|
49
|
+
# share it for a whole multiplex
|
50
|
+
multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy.new
|
51
|
+
# Do as much eager evaluation of the query as possible
|
52
|
+
results = []
|
53
|
+
queries.each_with_index do |query, idx|
|
54
|
+
if query.subscription? && !query.subscription_update?
|
55
|
+
query.context.namespace(:subscriptions)[:events] = []
|
52
56
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
begin
|
68
|
-
# Although queries in a multiplex _share_ an Interpreter instance,
|
69
|
-
# they also have another item of state, which is private to that query
|
70
|
-
# in particular, assign it here:
|
71
|
-
runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
|
72
|
-
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
73
|
-
|
74
|
-
query.current_trace.execute_query(query: query) do
|
75
|
-
runtime.run_eager
|
76
|
-
end
|
77
|
-
rescue GraphQL::ExecutionError => err
|
78
|
-
query.context.errors << err
|
79
|
-
NO_OPERATION
|
80
|
-
end
|
57
|
+
multiplex.dataloader.append_job {
|
58
|
+
operation = query.selected_operation
|
59
|
+
result = if operation.nil? || !query.valid? || query.context.errors.any?
|
60
|
+
NO_OPERATION
|
61
|
+
else
|
62
|
+
begin
|
63
|
+
# Although queries in a multiplex _share_ an Interpreter instance,
|
64
|
+
# they also have another item of state, which is private to that query
|
65
|
+
# in particular, assign it here:
|
66
|
+
runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
|
67
|
+
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
68
|
+
|
69
|
+
query.current_trace.execute_query(query: query) do
|
70
|
+
runtime.run_eager
|
81
71
|
end
|
82
|
-
|
83
|
-
|
72
|
+
rescue GraphQL::ExecutionError => err
|
73
|
+
query.context.errors << err
|
74
|
+
NO_OPERATION
|
75
|
+
end
|
84
76
|
end
|
77
|
+
results[idx] = result
|
78
|
+
}
|
79
|
+
end
|
85
80
|
|
86
|
-
|
81
|
+
multiplex.dataloader.run
|
87
82
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
# Then, find all errors and assign the result to the query object
|
105
|
-
results.each_with_index do |data_result, idx|
|
106
|
-
query = queries[idx]
|
107
|
-
# Assign the result so that it can be accessed in instrumentation
|
108
|
-
query.result_values = if data_result.equal?(NO_OPERATION)
|
109
|
-
if !query.valid? || query.context.errors.any?
|
110
|
-
# A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
|
111
|
-
{ "errors" => query.static_errors.map(&:to_h) }
|
112
|
-
else
|
113
|
-
data_result
|
114
|
-
end
|
115
|
-
else
|
116
|
-
result = {
|
117
|
-
"data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
|
118
|
-
}
|
83
|
+
# Then, work through lazy results in a breadth-first way
|
84
|
+
multiplex.dataloader.append_job {
|
85
|
+
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
86
|
+
queries = multiplex ? multiplex.queries : [query]
|
87
|
+
final_values = queries.map do |query|
|
88
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
89
|
+
# it might not be present if the query has an error
|
90
|
+
runtime ? runtime.final_result : nil
|
91
|
+
end
|
92
|
+
final_values.compact!
|
93
|
+
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
94
|
+
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
95
|
+
end
|
96
|
+
}
|
97
|
+
multiplex.dataloader.run
|
119
98
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
99
|
+
# Then, find all errors and assign the result to the query object
|
100
|
+
results.each_with_index do |data_result, idx|
|
101
|
+
query = queries[idx]
|
102
|
+
if (events = query.context.namespace(:subscriptions)[:events]) && events.any?
|
103
|
+
schema.subscriptions.write_subscription(query, events)
|
104
|
+
end
|
105
|
+
# Assign the result so that it can be accessed in instrumentation
|
106
|
+
query.result_values = if data_result.equal?(NO_OPERATION)
|
107
|
+
if !query.valid? || query.context.errors.any?
|
108
|
+
# A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
|
109
|
+
{ "errors" => query.static_errors.map(&:to_h) }
|
110
|
+
else
|
111
|
+
data_result
|
112
|
+
end
|
113
|
+
else
|
114
|
+
result = {}
|
124
115
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
|
129
|
-
end
|
130
|
-
# Get the Query::Result, not the Hash
|
131
|
-
results[idx] = query.result
|
116
|
+
if query.context.errors.any?
|
117
|
+
error_result = query.context.errors.map(&:to_h)
|
118
|
+
result["errors"] = error_result
|
132
119
|
end
|
133
120
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
ensure
|
141
|
-
queries.map { |query|
|
142
|
-
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
143
|
-
if runtime
|
144
|
-
runtime.delete_all_interpreter_context
|
145
|
-
end
|
146
|
-
}
|
121
|
+
result["data"] = query.context.namespace(:interpreter_runtime)[:runtime].final_result
|
122
|
+
|
123
|
+
result
|
124
|
+
end
|
125
|
+
if query.context.namespace?(:__query_result_extensions__)
|
126
|
+
query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
|
147
127
|
end
|
128
|
+
# Get the Query::Result, not the Hash
|
129
|
+
results[idx] = query.result
|
148
130
|
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
131
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
yield
|
132
|
+
results
|
133
|
+
rescue Exception
|
134
|
+
# TODO rescue at a higher level so it will catch errors in analysis, too
|
135
|
+
# Assign values here so that the query's `@executed` becomes true
|
136
|
+
queries.map { |q| q.result_values ||= {} }
|
137
|
+
raise
|
138
|
+
ensure
|
139
|
+
queries.map { |query|
|
140
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
141
|
+
if runtime
|
142
|
+
runtime.delete_all_interpreter_context
|
143
|
+
end
|
166
144
|
}
|
167
|
-
}
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
# Call each before hook, and if they all succeed, yield.
|
172
|
-
# If they don't all succeed, call after_ for each one that succeeded.
|
173
|
-
def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
|
174
|
-
begin
|
175
|
-
successful = []
|
176
|
-
instrumenters.each do |instrumenter|
|
177
|
-
instrumenter.public_send(before_hook_name, object)
|
178
|
-
successful << instrumenter
|
179
|
-
end
|
180
|
-
|
181
|
-
# if any before hooks raise an exception, quit calling before hooks,
|
182
|
-
# but call the after hooks on anything that succeeded but also
|
183
|
-
# raise the exception that came from the before hook.
|
184
|
-
rescue GraphQL::ExecutionError => err
|
185
|
-
object.context.errors << err
|
186
|
-
rescue => e
|
187
|
-
raise call_after_hooks(successful, object, after_hook_name, e)
|
188
|
-
end
|
189
|
-
|
190
|
-
begin
|
191
|
-
yield # Call the user code
|
192
|
-
ensure
|
193
|
-
ex = call_after_hooks(successful, object, after_hook_name, nil)
|
194
|
-
raise ex if ex
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
def call_after_hooks(instrumenters, object, after_hook_name, ex)
|
199
|
-
instrumenters.reverse_each do |instrumenter|
|
200
|
-
begin
|
201
|
-
instrumenter.public_send(after_hook_name, object)
|
202
|
-
rescue => e
|
203
|
-
ex = e
|
204
145
|
end
|
205
146
|
end
|
206
|
-
ex
|
207
147
|
end
|
208
148
|
end
|
209
149
|
|
@@ -9,13 +9,19 @@ module GraphQL
|
|
9
9
|
|
10
10
|
def __schema
|
11
11
|
# Apply wrapping manually since this field isn't wrapped by instrumentation
|
12
|
-
schema =
|
12
|
+
schema = context.schema
|
13
13
|
schema_type = schema.introspection_system.types["__Schema"]
|
14
|
-
schema_type.wrap(schema,
|
14
|
+
schema_type.wrap(schema, context)
|
15
15
|
end
|
16
16
|
|
17
17
|
def __type(name:)
|
18
|
-
context.warden.reachable_type?(name)
|
18
|
+
if context.warden.reachable_type?(name)
|
19
|
+
context.warden.get_type(name)
|
20
|
+
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
|
21
|
+
type
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
@@ -266,8 +266,7 @@ module GraphQL
|
|
266
266
|
end
|
267
267
|
definitions = build_directive_nodes(dirs_to_build)
|
268
268
|
|
269
|
-
type_nodes = build_type_definition_nodes(warden.reachable_types)
|
270
|
-
|
269
|
+
type_nodes = build_type_definition_nodes(warden.reachable_types + schema.extra_types)
|
271
270
|
if @include_one_of
|
272
271
|
# This may have been set to true when iterating over all types
|
273
272
|
definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
|
@@ -512,7 +512,7 @@ module GraphQL
|
|
512
512
|
# An operation-level query variable
|
513
513
|
class VariableDefinition < AbstractNode
|
514
514
|
scalar_methods :name, :type, :default_value
|
515
|
-
children_methods
|
515
|
+
children_methods(directives: Directive)
|
516
516
|
# @!attribute default_value
|
517
517
|
# @return [String, Integer, Float, Boolean, Array, NullValue] A Ruby value to use if no other value is provided
|
518
518
|
|
@@ -121,7 +121,17 @@ module GraphQL
|
|
121
121
|
value
|
122
122
|
end
|
123
123
|
|
124
|
-
|
124
|
+
directives = parse_directives
|
125
|
+
|
126
|
+
defs << Nodes::VariableDefinition.new(
|
127
|
+
pos: loc,
|
128
|
+
name: var_name,
|
129
|
+
type: var_type,
|
130
|
+
default_value: default_value,
|
131
|
+
directives: directives,
|
132
|
+
filename: @filename,
|
133
|
+
source_string: @graphql_str
|
134
|
+
)
|
125
135
|
end
|
126
136
|
expect_token(:RPAREN)
|
127
137
|
defs
|
@@ -208,6 +208,10 @@ module GraphQL
|
|
208
208
|
print_string(" = ")
|
209
209
|
print_node(variable_definition.default_value)
|
210
210
|
end
|
211
|
+
variable_definition.directives.each do |dir|
|
212
|
+
print_string(" ")
|
213
|
+
print_directive(dir)
|
214
|
+
end
|
211
215
|
end
|
212
216
|
|
213
217
|
def print_variable_identifier(variable_identifier)
|
@@ -35,10 +35,10 @@ module GraphQL
|
|
35
35
|
def load_nodes
|
36
36
|
@nodes ||= begin
|
37
37
|
sliced_nodes = if before && after
|
38
|
-
end_idx = index_from_cursor(before)-
|
38
|
+
end_idx = index_from_cursor(before) - 2
|
39
39
|
end_idx < 0 ? [] : items[index_from_cursor(after)..end_idx] || []
|
40
40
|
elsif before
|
41
|
-
end_idx = index_from_cursor(before)-2
|
41
|
+
end_idx = index_from_cursor(before) - 2
|
42
42
|
end_idx < 0 ? [] : items[0..end_idx] || []
|
43
43
|
elsif after
|
44
44
|
items[index_from_cursor(after)..-1] || []
|
@@ -56,7 +56,7 @@ module GraphQL
|
|
56
56
|
false
|
57
57
|
end
|
58
58
|
|
59
|
-
@has_next_page = if first
|
59
|
+
@has_next_page = if first_value && first
|
60
60
|
# There are more items after these items
|
61
61
|
sliced_nodes.count > first
|
62
62
|
elsif before
|
@@ -29,14 +29,14 @@ module GraphQL
|
|
29
29
|
|
30
30
|
def has_next_page
|
31
31
|
if @has_next_page.nil?
|
32
|
-
@has_next_page = if
|
33
|
-
true
|
34
|
-
elsif first
|
32
|
+
@has_next_page = if first && first_value
|
35
33
|
if @nodes && @nodes.count < first
|
36
34
|
false
|
37
35
|
else
|
38
36
|
relation_larger_than(sliced_nodes, @sliced_nodes_offset, first)
|
39
37
|
end
|
38
|
+
elsif before_offset && before_offset > 0
|
39
|
+
true
|
40
40
|
else
|
41
41
|
false
|
42
42
|
end
|
@@ -14,7 +14,7 @@ module GraphQL
|
|
14
14
|
#
|
15
15
|
# @api private
|
16
16
|
class ValidationPipeline
|
17
|
-
attr_reader :max_depth, :max_complexity
|
17
|
+
attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
|
18
18
|
|
19
19
|
def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
|
20
20
|
@validation_errors = []
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
validator = @query.static_validator || @schema.static_validator
|
72
72
|
validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
|
73
73
|
@validation_errors.concat(validation_result[:errors])
|
74
|
-
|
74
|
+
@validate_timeout_remaining = validation_result[:remaining_timeout]
|
75
75
|
if @validation_errors.empty?
|
76
76
|
@validation_errors.concat(@query.variables.errors)
|
77
77
|
end
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
26
26
|
# - Then, fall back to the default value from the query string
|
27
27
|
# If it's still nil, raise an error if it's required.
|
28
28
|
variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
|
29
|
-
if variable_type.nil?
|
29
|
+
if variable_type.nil? || !variable_type.unwrap.kind.input?
|
30
30
|
# Pass -- it will get handled by a validator
|
31
31
|
else
|
32
32
|
variable_name = ast_variable.name
|
@@ -80,12 +80,12 @@ module GraphQL
|
|
80
80
|
else
|
81
81
|
val
|
82
82
|
end
|
83
|
-
end
|
83
|
+
end
|
84
84
|
|
85
85
|
def add_max_errors_reached_message
|
86
86
|
message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
|
87
87
|
validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
|
88
|
-
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
88
|
+
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
data/lib/graphql/query.rb
CHANGED
@@ -317,7 +317,7 @@ module GraphQL
|
|
317
317
|
end
|
318
318
|
|
319
319
|
def_delegators :validation_pipeline, :validation_errors,
|
320
|
-
:analyzers, :ast_analyzers, :max_depth, :max_complexity
|
320
|
+
:analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
|
321
321
|
|
322
322
|
attr_accessor :analysis_errors
|
323
323
|
def valid?
|
@@ -1,18 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'graphql/schema/base_64_bp'
|
4
|
-
|
2
|
+
require "base64"
|
5
3
|
module GraphQL
|
6
4
|
class Schema
|
7
5
|
# @api private
|
8
6
|
module Base64Encoder
|
9
7
|
def self.encode(unencoded_text, nonce: false)
|
10
|
-
|
8
|
+
Base64.urlsafe_encode64(unencoded_text, padding: false)
|
11
9
|
end
|
12
10
|
|
13
11
|
def self.decode(encoded_text, nonce: false)
|
14
12
|
# urlsafe_decode64 is for forward compatibility
|
15
|
-
|
13
|
+
Base64.urlsafe_decode64(encoded_text)
|
16
14
|
rescue ArgumentError
|
17
15
|
raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
|
18
16
|
end
|