graphql 2.6.14 → 2.6.15
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/graphql_controller.erb +7 -1
- data/lib/graphql/analysis/max_query_complexity.rb +2 -1
- data/lib/graphql/analysis/max_query_depth.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +2 -1
- data/lib/graphql/analysis/query_depth.rb +1 -1
- data/lib/graphql/analysis/visitor.rb +14 -2
- data/lib/graphql/analysis.rb +4 -5
- data/lib/graphql/analysis_error.rb +1 -1
- data/lib/graphql/coercion_error.rb +1 -1
- data/lib/graphql/current.rb +5 -1
- data/lib/graphql/dashboard.rb +1 -1
- data/lib/graphql/date_encoding_error.rb +3 -1
- data/lib/graphql/duration_encoding_error.rb +3 -1
- data/lib/graphql/execution/directive_checks.rb +2 -1
- data/lib/graphql/execution/errors.rb +18 -7
- data/lib/graphql/execution/field_resolve_step.rb +3 -3
- data/lib/graphql/execution/input_values.rb +160 -120
- data/lib/graphql/execution/interpreter/runtime.rb +9 -5
- data/lib/graphql/execution/lookahead.rb +19 -8
- data/lib/graphql/execution/next.rb +1 -0
- data/lib/graphql/execution/runner.rb +19 -12
- data/lib/graphql/execution/selections_step.rb +1 -1
- data/lib/graphql/execution/variable_values.rb +248 -0
- data/lib/graphql/float_decoding_error.rb +3 -1
- data/lib/graphql/integer_decoding_error.rb +3 -1
- data/lib/graphql/introspection/base_object.rb +1 -1
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +3 -1
- data/lib/graphql/introspection/dynamic_fields.rb +3 -1
- data/lib/graphql/introspection/enum_value_type.rb +3 -1
- data/lib/graphql/introspection/field_type.rb +5 -1
- data/lib/graphql/introspection/type_kind_enum.rb +1 -1
- data/lib/graphql/introspection.rb +2 -1
- data/lib/graphql/language/comment.rb +2 -1
- data/lib/graphql/language/generation.rb +2 -1
- data/lib/graphql/language/lexer.rb +29 -21
- data/lib/graphql/language/parser.rb +10 -7
- data/lib/graphql/language.rb +1 -1
- data/lib/graphql/name_validator.rb +2 -1
- data/lib/graphql/pagination.rb +1 -1
- data/lib/graphql/query/fingerprint.rb +2 -1
- data/lib/graphql/query/partial.rb +4 -0
- data/lib/graphql/query.rb +8 -12
- data/lib/graphql/railtie.rb +1 -1
- data/lib/graphql/relay.rb +1 -1
- data/lib/graphql/rubocop.rb +1 -1
- data/lib/graphql/runtime_error.rb +1 -1
- data/lib/graphql/runtime_type_error.rb +1 -1
- data/lib/graphql/schema/always_visible.rb +3 -1
- data/lib/graphql/schema/base_64_encoder.rb +3 -1
- data/lib/graphql/schema/built_in_types.rb +2 -2
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/directive/include.rb +2 -1
- data/lib/graphql/schema/directive/skip.rb +2 -1
- data/lib/graphql/schema/directive/specified_by.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +2 -1
- data/lib/graphql/schema/field.rb +3 -0
- data/lib/graphql/schema/find_inherited_value.rb +5 -1
- data/lib/graphql/schema/member/graphql_type_names.rb +1 -1
- data/lib/graphql/schema/member/has_directives.rb +20 -22
- data/lib/graphql/schema/member/has_path.rb +2 -1
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +2 -1
- data/lib/graphql/schema/member.rb +3 -2
- data/lib/graphql/schema/mutation.rb +6 -1
- data/lib/graphql/schema/object.rb +1 -1
- data/lib/graphql/schema/resolver.rb +2 -2
- data/lib/graphql/schema/type_expression.rb +3 -1
- data/lib/graphql/schema/validator/all_validator.rb +6 -3
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +125 -53
- data/lib/graphql/static_validation/definition_dependencies.rb +14 -2
- data/lib/graphql/static_validation/interpreter_visitor.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +4 -1
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +8 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -1
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +2 -1
- data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +4 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +6 -3
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +2 -1
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +3 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +3 -2
- data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +4 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +4 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +4 -1
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +9 -2
- data/lib/graphql/static_validation/rules/query_root_exists.rb +3 -2
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +4 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +10 -4
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +8 -6
- data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +4 -1
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +5 -4
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +2 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +2 -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 +49 -14
- data/lib/graphql/static_validation/validation_timeout_error.rb +4 -1
- data/lib/graphql/static_validation.rb +1 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +14 -13
- data/lib/graphql/testing.rb +1 -1
- data/lib/graphql/tracing/legacy_trace.rb +1 -1
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/types/big_int.rb +4 -1
- data/lib/graphql/types/boolean.rb +3 -1
- data/lib/graphql/types/id.rb +3 -1
- data/lib/graphql/types/iso_8601_date.rb +1 -0
- data/lib/graphql/types/iso_8601_date_time.rb +6 -2
- data/lib/graphql/types/iso_8601_duration.rb +3 -0
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/types/relay/base_edge.rb +1 -1
- data/lib/graphql/types/relay/node.rb +1 -1
- data/lib/graphql/types/relay/page_info.rb +1 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -1
- data/lib/graphql/types/relay.rb +1 -1
- data/lib/graphql/types/string.rb +1 -0
- data/lib/graphql/types.rb +1 -1
- data/lib/graphql/version.rb +2 -2
- metadata +3 -4
- data/lib/graphql/query/variables.rb +0 -99
- data/lib/graphql/static_validation/literal_validator.rb +0 -157
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Execution
|
|
5
|
+
# Validates and coerces a query's provided variables in a single pass.
|
|
6
|
+
#
|
|
7
|
+
# Reading methods (`#[]`, `#fetch`, `#to_h`, ...) return the values _as provided_ by the client
|
|
8
|
+
# (or the default value from the query string), so that existing callers keep working.
|
|
9
|
+
# Coerced values are available from `#coerced_value` for the runtime.
|
|
10
|
+
class VariableValues
|
|
11
|
+
extend Forwardable
|
|
12
|
+
|
|
13
|
+
# **Returns**
|
|
14
|
+
#
|
|
15
|
+
# - `Array<GraphQL::Query::VariableValidationError>` — Any errors encountered when coercing the provided variables
|
|
16
|
+
attr_reader :errors
|
|
17
|
+
|
|
18
|
+
attr_reader :context
|
|
19
|
+
|
|
20
|
+
def initialize(query, ast_variables, provided_variables)
|
|
21
|
+
@query = query
|
|
22
|
+
@context = query.context
|
|
23
|
+
@types = query.types
|
|
24
|
+
@schema = query.schema
|
|
25
|
+
@provided_variables = deep_stringify(provided_variables)
|
|
26
|
+
@errors = []
|
|
27
|
+
@storage = {}
|
|
28
|
+
@coerced_values = {}
|
|
29
|
+
build_values(ast_variables)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def_delegators :@storage, :length, :key?, :[], :fetch, :to_h, :each
|
|
33
|
+
|
|
34
|
+
# **Returns**
|
|
35
|
+
#
|
|
36
|
+
# - `Object` — The coerced value for the variable named `name`, or `nil` if it wasn't given
|
|
37
|
+
def coerced_value(name) # :nodoc:
|
|
38
|
+
@coerced_values[name]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
UNEXPECTED_NULL_MESSAGE = "Expected value to not be null"
|
|
44
|
+
INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object."
|
|
45
|
+
|
|
46
|
+
def build_values(ast_variables)
|
|
47
|
+
max_errors = @schema.validate_max_errors
|
|
48
|
+
ast_variables.each do |ast_variable|
|
|
49
|
+
if max_errors && max_errors <= @errors.size
|
|
50
|
+
add_max_errors_reached_message
|
|
51
|
+
break
|
|
52
|
+
end
|
|
53
|
+
variable_type = GraphQL::Schema::TypeExpression.build_type(@types, ast_variable.type)
|
|
54
|
+
if variable_type.nil? || !variable_type.unwrap.kind.input?
|
|
55
|
+
# Pass -- it will get handled by a validator
|
|
56
|
+
next
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
variable_name = ast_variable.name
|
|
60
|
+
default_value = ast_variable.default_value
|
|
61
|
+
value_was_provided = @provided_variables.key?(variable_name)
|
|
62
|
+
|
|
63
|
+
if value_was_provided || default_value.nil?
|
|
64
|
+
provided_value = @provided_variables[variable_name]
|
|
65
|
+
validation_result = GraphQL::Query::InputValidationResult.new
|
|
66
|
+
remaining_errors = max_errors ? max_errors - @errors.size : nil
|
|
67
|
+
path = []
|
|
68
|
+
coerced_value = begin
|
|
69
|
+
coerce_variable_value(provided_value, variable_type, path, validation_result, remaining_errors)
|
|
70
|
+
rescue GraphQL::ExecutionError => ex
|
|
71
|
+
# TODO: This should really include the path to the problematic node in the variable value
|
|
72
|
+
validation_result = GraphQL::Query::InputValidationResult.from_problem(ex.message)
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
if !validation_result.valid?
|
|
76
|
+
@errors << GraphQL::Query::VariableValidationError.new(ast_variable, variable_type, provided_value, validation_result)
|
|
77
|
+
elsif value_was_provided
|
|
78
|
+
@storage[variable_name] = provided_value
|
|
79
|
+
@coerced_values[variable_name] = coerced_value
|
|
80
|
+
end
|
|
81
|
+
elsif default_value.is_a?(Language::Nodes::NullValue)
|
|
82
|
+
@storage[variable_name] = nil
|
|
83
|
+
@coerced_values[variable_name] = nil
|
|
84
|
+
else
|
|
85
|
+
@storage[variable_name] = default_value
|
|
86
|
+
@coerced_values[variable_name] = @query.input_values.value_from_ast(default_value, variable_type)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def coerce_variable_value(value, type, path, result, max_errors)
|
|
92
|
+
if type.non_null?
|
|
93
|
+
if value.nil?
|
|
94
|
+
result.add_problem(UNEXPECTED_NULL_MESSAGE, path.dup)
|
|
95
|
+
return nil
|
|
96
|
+
end
|
|
97
|
+
type = type.of_type
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if value.nil?
|
|
101
|
+
nil
|
|
102
|
+
elsif type.list?
|
|
103
|
+
coerce_list_value(value, type.of_type, path, result, max_errors)
|
|
104
|
+
elsif type.kind.input_object?
|
|
105
|
+
coerce_input_object_value(value, type, path, result)
|
|
106
|
+
elsif type.kind.enum?
|
|
107
|
+
coerce_enum_value(value, type, path, result)
|
|
108
|
+
elsif type.kind.scalar?
|
|
109
|
+
coerce_scalar_value(value, type, path, result)
|
|
110
|
+
else
|
|
111
|
+
raise GraphQL::Error, "Unexpected input type: #{type.graphql_name}."
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def coerce_list_value(value, item_type, path, result, max_errors)
|
|
116
|
+
items = value.is_a?(Array) ? value : [value]
|
|
117
|
+
coerced_items = Array.new(items.size)
|
|
118
|
+
invalid_items = 0
|
|
119
|
+
items.each_with_index do |item, idx|
|
|
120
|
+
item_result = GraphQL::Query::InputValidationResult.new
|
|
121
|
+
path.push(idx)
|
|
122
|
+
coerced_items[idx] = coerce_variable_value(item, item_type, path, item_result, nil)
|
|
123
|
+
path.pop
|
|
124
|
+
if !item_result.valid?
|
|
125
|
+
if max_errors
|
|
126
|
+
if invalid_items == max_errors
|
|
127
|
+
result.add_problem("Too many errors processing list variable, max validation error limit reached. Execution aborted", [*path, nil])
|
|
128
|
+
break
|
|
129
|
+
end
|
|
130
|
+
invalid_items += 1
|
|
131
|
+
end
|
|
132
|
+
item_result.problems.each do |problem|
|
|
133
|
+
result.add_problem(problem["explanation"], problem["path"], message: problem["message"], extensions: problem["extensions"])
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
coerced_items.freeze
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def coerce_input_object_value(value, type, path, result)
|
|
141
|
+
if value.is_a?(Array) || !(value.respond_to?(:to_h) || value.respond_to?(:to_unsafe_h))
|
|
142
|
+
result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(value) }, path.dup)
|
|
143
|
+
return nil
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
coerced_obj = {}
|
|
147
|
+
value.each do |argument_name, argument_value|
|
|
148
|
+
path.push(argument_name)
|
|
149
|
+
argument = @types.argument(type, argument_name)
|
|
150
|
+
if argument.nil?
|
|
151
|
+
result.add_problem("Field is not defined on #{type.graphql_name}", path.dup)
|
|
152
|
+
else
|
|
153
|
+
if argument_value.nil? && argument.replace_null_with_default?
|
|
154
|
+
coerced_obj[argument.keyword] = argument.default_value
|
|
155
|
+
else
|
|
156
|
+
coerced_obj[argument.keyword] = coerce_variable_value(argument_value, argument.type, path, result, nil)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
path.pop
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
@types.arguments(type).each do |argument|
|
|
163
|
+
if !value.key?(argument.graphql_name)
|
|
164
|
+
if argument.default_value?
|
|
165
|
+
coerced_obj[argument.keyword] = argument.default_value
|
|
166
|
+
elsif argument.type.non_null?
|
|
167
|
+
path.push(argument.graphql_name)
|
|
168
|
+
result.add_problem(UNEXPECTED_NULL_MESSAGE, path.dup)
|
|
169
|
+
path.pop
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if type.one_of?
|
|
175
|
+
if value.size == 1
|
|
176
|
+
value.each do |name, v|
|
|
177
|
+
if v.nil?
|
|
178
|
+
result.add_problem("'#{type.graphql_name}' requires exactly one argument, but '#{name}' was `null`.", path.dup)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
else
|
|
182
|
+
result.add_problem("'#{type.graphql_name}' requires exactly one argument, but #{value.size} were provided.", path.dup)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
coerced_obj
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def coerce_enum_value(value, type, path, result)
|
|
190
|
+
allowed_values = @types.enum_values(type)
|
|
191
|
+
matching_value = allowed_values.find { |v| v.graphql_name == value }
|
|
192
|
+
if matching_value.nil?
|
|
193
|
+
result.add_problem("Expected #{GraphQL::Language.serialize(value)} to be one of: #{allowed_values.map(&:graphql_name).join(', ')}", path.dup)
|
|
194
|
+
nil
|
|
195
|
+
else
|
|
196
|
+
begin
|
|
197
|
+
type.coerce_input(value, @context)
|
|
198
|
+
rescue GraphQL::UnauthorizedEnumValueError => enum_err
|
|
199
|
+
@schema.unauthorized_object(enum_err)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
rescue GraphQL::ExecutionError => err
|
|
203
|
+
result.add_problem(err.message, path.dup)
|
|
204
|
+
nil
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def coerce_scalar_value(value, type, path, result)
|
|
208
|
+
coerced_value = begin
|
|
209
|
+
type.coerce_input(value, @context)
|
|
210
|
+
rescue GraphQL::CoercionError => err
|
|
211
|
+
result.add_problem(err.message, path.dup, message: err.message, extensions: err.extensions)
|
|
212
|
+
return nil
|
|
213
|
+
rescue GraphQL::ExecutionError => err
|
|
214
|
+
result.add_problem(err.message, path.dup)
|
|
215
|
+
return nil
|
|
216
|
+
rescue StandardError => err
|
|
217
|
+
@query.handle_or_reraise(err)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
if coerced_value.nil?
|
|
221
|
+
result.add_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{type.graphql_name}", path.dup)
|
|
222
|
+
end
|
|
223
|
+
coerced_value
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def add_max_errors_reached_message
|
|
227
|
+
message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
|
|
228
|
+
validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
|
|
229
|
+
@errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def deep_stringify(val)
|
|
233
|
+
case val
|
|
234
|
+
when Array
|
|
235
|
+
val.map { |v| deep_stringify(v) }
|
|
236
|
+
when Hash
|
|
237
|
+
new_val = {}
|
|
238
|
+
val.each do |k, v|
|
|
239
|
+
new_val[k.to_s] = deep_stringify(v)
|
|
240
|
+
end
|
|
241
|
+
new_val
|
|
242
|
+
else
|
|
243
|
+
val
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
# This error is raised when `Types::Float` is given a non-finite input value.
|
|
5
5
|
class FloatDecodingError < GraphQL::RuntimeTypeError
|
|
6
6
|
# The value which couldn't be decoded
|
|
7
|
+
#: Float
|
|
7
8
|
attr_reader :float_value
|
|
8
9
|
|
|
10
|
+
#: (Float value) -> void
|
|
9
11
|
def initialize(value)
|
|
10
12
|
@float_value = value
|
|
11
13
|
super("Float is not finite: #{value.inspect}.")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
# This error is raised when `Types::Int` is given an input value outside of 32-bit integer range.
|
|
5
5
|
#
|
|
@@ -8,8 +8,10 @@ module GraphQL
|
|
|
8
8
|
# See [GraphQL::Types::Int](rdoc-ref:GraphQL::Types::Int) which raises this error
|
|
9
9
|
class IntegerDecodingError < GraphQL::RuntimeTypeError
|
|
10
10
|
# The value which couldn't be decoded
|
|
11
|
+
#: Integer
|
|
11
12
|
attr_reader :integer_value
|
|
12
13
|
|
|
14
|
+
#: (Integer value) -> void
|
|
13
15
|
def initialize(value)
|
|
14
16
|
@integer_value = value
|
|
15
17
|
super("Integer out of bounds: #{value}. \nConsider using GraphQL::Types::BigInt instead.")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Introspection
|
|
5
5
|
class DirectiveType < Introspection::BaseObject
|
|
@@ -22,12 +22,14 @@ module GraphQL
|
|
|
22
22
|
|
|
23
23
|
field :is_repeatable, Boolean, method: :repeatable?
|
|
24
24
|
|
|
25
|
+
#: (singleton(GraphQL::Schema::Directive) object, GraphQL::Query::Context context, include_deprecated: bool) -> Array[GraphQL::Schema::Argument]
|
|
25
26
|
def self.resolve_args(object, context, include_deprecated:)
|
|
26
27
|
args = context.types.arguments(object)
|
|
27
28
|
args = args.reject(&:deprecation_reason) unless include_deprecated
|
|
28
29
|
args
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
#: (include_deprecated: bool) -> Array[GraphQL::Schema::Argument]
|
|
31
33
|
def args(include_deprecated:)
|
|
32
34
|
self.class.resolve_args(object, context, include_deprecated: include_deprecated)
|
|
33
35
|
end
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Introspection
|
|
5
5
|
class DynamicFields < Introspection::BaseObject
|
|
6
6
|
field :__typename, String, "The name of this type", null: false, dynamic_introspection: true, resolve_each: true
|
|
7
7
|
|
|
8
|
+
#: -> ::String
|
|
8
9
|
def __typename
|
|
9
10
|
self.class.__typename(object, context)
|
|
10
11
|
end
|
|
11
12
|
|
|
13
|
+
#: (untyped object, GraphQL::Query::Context context) -> ::String
|
|
12
14
|
def self.__typename(object, context)
|
|
13
15
|
object.class.graphql_name
|
|
14
16
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Introspection
|
|
5
5
|
class EnumValueType < Introspection::BaseObject
|
|
@@ -12,10 +12,12 @@ module GraphQL
|
|
|
12
12
|
field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
|
|
13
13
|
field :deprecation_reason, String
|
|
14
14
|
|
|
15
|
+
#: (GraphQL::Schema::EnumValue object, GraphQL::Query::Context context) -> bool
|
|
15
16
|
def self.resolve_is_deprecated(object, context)
|
|
16
17
|
!!object.deprecation_reason
|
|
17
18
|
end
|
|
18
19
|
|
|
20
|
+
#: -> bool
|
|
19
21
|
def is_deprecated
|
|
20
22
|
self.class.resolve_is_deprecated(object, context)
|
|
21
23
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Introspection
|
|
5
5
|
class FieldType < Introspection::BaseObject
|
|
@@ -15,20 +15,24 @@ module GraphQL
|
|
|
15
15
|
field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
|
|
16
16
|
field :deprecation_reason, String
|
|
17
17
|
|
|
18
|
+
#: (GraphQL::Schema::Field object, GraphQL::Query::Context _context) -> bool
|
|
18
19
|
def self.resolve_is_deprecated(object, _context)
|
|
19
20
|
!!object.deprecation_reason
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
#: -> bool
|
|
22
24
|
def is_deprecated
|
|
23
25
|
self.class.resolve_is_deprecated(object, context)
|
|
24
26
|
end
|
|
25
27
|
|
|
28
|
+
#: (GraphQL::Schema::Field object, GraphQL::Query::Context context, include_deprecated: bool) -> Array[GraphQL::Schema::Argument]
|
|
26
29
|
def self.resolve_args(object, context, include_deprecated:)
|
|
27
30
|
args = context.types.arguments(object)
|
|
28
31
|
args = args.reject(&:deprecation_reason) unless include_deprecated
|
|
29
32
|
args
|
|
30
33
|
end
|
|
31
34
|
|
|
35
|
+
#: (include_deprecated: bool) -> Array[GraphQL::Schema::Argument]
|
|
32
36
|
def args(include_deprecated:)
|
|
33
37
|
self.class.resolve_args(object, context, include_deprecated: include_deprecated)
|
|
34
38
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Introspection
|
|
5
|
+
#: (?include_deprecated_args: bool, ?include_schema_description: bool, ?include_is_repeatable: bool, ?include_specified_by_url: bool, ?include_is_one_of: bool) -> String
|
|
5
6
|
def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
|
6
7
|
# The introspection query to end all introspection queries, copied from
|
|
7
8
|
# https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Language
|
|
5
5
|
# Exposes [.generate](rdoc-ref:.generate), which turns AST nodes back into query strings.
|
|
@@ -30,6 +30,7 @@ module GraphQL
|
|
|
30
30
|
#
|
|
31
31
|
# :call-seq:
|
|
32
32
|
# generate(GraphQL::Language::Nodes::AbstractNode node, String indent:, GraphQL::Language::Printer printer:) -> String
|
|
33
|
+
#: (GraphQL::Language::Nodes::AbstractNode node, ?indent: String, ?printer: GraphQL::Language::Printer) -> String
|
|
33
34
|
def generate(node, indent: "", printer: GraphQL::Language::Printer.new)
|
|
34
35
|
printer.print(node, indent: indent)
|
|
35
36
|
end
|
|
@@ -32,17 +32,16 @@ module GraphQL
|
|
|
32
32
|
attr_reader :pos, :tokens_count
|
|
33
33
|
|
|
34
34
|
def advance
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
next
|
|
35
|
+
@scanner.skip(IGNORE_REGEXP)
|
|
36
|
+
# A failed skip clears matched_size, which debug_token_value relies on for UNKNOWN_CHAR.
|
|
37
|
+
while @scanner.skip("#")
|
|
38
|
+
@scanner.skip(COMMENT_BODY_REGEXP)
|
|
39
|
+
@tokens_count += 1
|
|
40
|
+
if @tokens_count > @max_tokens
|
|
41
|
+
@pos = @scanner.pos
|
|
42
|
+
raise_parse_error("This query is too large to execute.")
|
|
44
43
|
end
|
|
45
|
-
|
|
44
|
+
@scanner.skip(IGNORE_REGEXP)
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
if @scanner.eos?
|
|
@@ -50,10 +49,10 @@ module GraphQL
|
|
|
50
49
|
return false
|
|
51
50
|
end
|
|
52
51
|
@tokens_count += 1
|
|
52
|
+
@pos = @scanner.pos
|
|
53
53
|
if @tokens_count > @max_tokens
|
|
54
54
|
raise_parse_error("This query is too large to execute.")
|
|
55
55
|
end
|
|
56
|
-
@pos = @scanner.pos
|
|
57
56
|
next_byte = @string.getbyte(@pos)
|
|
58
57
|
next_byte_is_for = FIRST_BYTES[next_byte]
|
|
59
58
|
case next_byte_is_for
|
|
@@ -82,11 +81,12 @@ module GraphQL
|
|
|
82
81
|
@scanner.skip(IDENTIFIER_REGEXP)
|
|
83
82
|
:IDENTIFIER
|
|
84
83
|
when ByteFor::NUMBER
|
|
85
|
-
if
|
|
86
|
-
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
if @scanner.skip(NUMERIC_REGEXP)
|
|
85
|
+
peek_byte = @string.getbyte(@scanner.pos)
|
|
86
|
+
if peek_byte == 46 || (peek_byte && peek_byte >= 48 && peek_byte <= 57)
|
|
87
|
+
raise_parse_error("Invalid number: unexpected character #{peek_byte.chr.inspect}")
|
|
88
|
+
end
|
|
89
|
+
if peek_byte && GraphQL.reject_numbers_followed_by_names
|
|
90
90
|
next_first_byte = FIRST_BYTES[peek_byte]
|
|
91
91
|
if next_first_byte == ByteFor::NAME || next_first_byte == ByteFor::IDENTIFIER
|
|
92
92
|
number_part = token_value
|
|
@@ -152,7 +152,9 @@ module GraphQL
|
|
|
152
152
|
"\\t" => "\t",
|
|
153
153
|
}
|
|
154
154
|
UTF_8 = /\\u(?:([\da-f]{4})|\{([\da-f]+)\})(?:\\u([\da-f]{4}))?/i
|
|
155
|
-
|
|
155
|
+
# Surrogate pairs are combined by `UTF_8` during replacement; validating each escape alone keeps this unambiguous, so it can't backtrack exponentially.
|
|
156
|
+
VALID_UTF_8 = /\\u(?:[\da-f]{4}|\{[\da-f]+\})/i
|
|
157
|
+
VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{VALID_UTF_8})*\z/o
|
|
156
158
|
ESCAPED = /(?:#{ESCAPES}|#{UTF_8})/o
|
|
157
159
|
|
|
158
160
|
def string_value
|
|
@@ -183,13 +185,20 @@ module GraphQL
|
|
|
183
185
|
end
|
|
184
186
|
|
|
185
187
|
def line_number
|
|
186
|
-
@scanner.string.byteslice(0, @pos).b
|
|
188
|
+
line_prefix = @scanner.string.byteslice(0, @pos).b
|
|
189
|
+
line_number = line_prefix.count("\r") + line_prefix.count("\n") + 1
|
|
190
|
+
offset = 0 #: Integer?
|
|
191
|
+
while (offset = line_prefix.index("\r\n", offset))
|
|
192
|
+
line_number -= 1
|
|
193
|
+
offset += 2
|
|
194
|
+
end
|
|
195
|
+
line_number
|
|
187
196
|
end
|
|
188
197
|
|
|
189
198
|
def column_number
|
|
190
199
|
line_prefix = @scanner.string.byteslice(0, @pos)
|
|
191
200
|
line_prefix = line_prefix.b unless line_prefix.valid_encoding?
|
|
192
|
-
newline_index = line_prefix.rindex(
|
|
201
|
+
newline_index = line_prefix.rindex(/[\r\n]/)
|
|
193
202
|
newline_index ? line_prefix.length - newline_index : line_prefix.length + 1
|
|
194
203
|
end
|
|
195
204
|
|
|
@@ -198,12 +207,11 @@ module GraphQL
|
|
|
198
207
|
end
|
|
199
208
|
|
|
200
209
|
IGNORE_REGEXP = /[, \r\n\t\uFEFF]+/
|
|
201
|
-
|
|
210
|
+
COMMENT_BODY_REGEXP = /[^\n\r]*/
|
|
202
211
|
IDENTIFIER_REGEXP = /[_A-Za-z][_0-9A-Za-z]*/
|
|
203
212
|
INT_REGEXP = /-?(?:[0]|[1-9][0-9]*)/
|
|
204
213
|
FLOAT_DECIMAL_REGEXP = /[.][0-9]+/
|
|
205
214
|
FLOAT_EXP_REGEXP = /[eE][+-]?[0-9]+/
|
|
206
|
-
# TODO: FLOAT_EXP_REGEXP should not be allowed to follow INT_REGEXP, integers are not allowed to have exponent parts.
|
|
207
215
|
NUMERIC_REGEXP = /#{INT_REGEXP}(#{FLOAT_DECIMAL_REGEXP}#{FLOAT_EXP_REGEXP}|#{FLOAT_DECIMAL_REGEXP}|#{FLOAT_EXP_REGEXP})?/
|
|
208
216
|
|
|
209
217
|
KEYWORDS = [
|
|
@@ -47,7 +47,7 @@ module GraphQL
|
|
|
47
47
|
document
|
|
48
48
|
end
|
|
49
49
|
rescue SystemStackError
|
|
50
|
-
raise GraphQL::ParseError.new("This query is too large to execute.", nil, nil, @
|
|
50
|
+
raise GraphQL::ParseError.new("This query is too large to execute.", nil, nil, @graphql_str, filename: @filename)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -89,8 +89,10 @@ module GraphQL
|
|
|
89
89
|
la = []
|
|
90
90
|
idx = 0 #: Integer?
|
|
91
91
|
while idx
|
|
92
|
-
idx = graphql_str.index(
|
|
92
|
+
idx = graphql_str.index(/[\r\n]/, idx)
|
|
93
93
|
if idx
|
|
94
|
+
# Record the final byte so CRLF counts as one line break.
|
|
95
|
+
idx += 1 if graphql_str.getbyte(idx) == 13 && graphql_str.getbyte(idx + 1) == 10
|
|
94
96
|
la << idx
|
|
95
97
|
idx += 1
|
|
96
98
|
end
|
|
@@ -172,7 +174,7 @@ module GraphQL
|
|
|
172
174
|
var_type = self.type || raise_parse_error("Missing type definition for variable: $#{var_name}")
|
|
173
175
|
default_value = if at?(:EQUALS)
|
|
174
176
|
advance_token
|
|
175
|
-
value
|
|
177
|
+
value(const: true)
|
|
176
178
|
end
|
|
177
179
|
|
|
178
180
|
directives = parse_directives #: untyped
|
|
@@ -491,7 +493,7 @@ module GraphQL
|
|
|
491
493
|
type = self.type
|
|
492
494
|
default_value = if at?(:EQUALS)
|
|
493
495
|
advance_token
|
|
494
|
-
value
|
|
496
|
+
value(const: true)
|
|
495
497
|
else
|
|
496
498
|
nil
|
|
497
499
|
end
|
|
@@ -715,7 +717,7 @@ module GraphQL
|
|
|
715
717
|
token_value
|
|
716
718
|
end
|
|
717
719
|
|
|
718
|
-
def value
|
|
720
|
+
def value(const: false)
|
|
719
721
|
case token_name
|
|
720
722
|
when :INT
|
|
721
723
|
expect_token_value(:INT).to_i
|
|
@@ -738,7 +740,7 @@ module GraphQL
|
|
|
738
740
|
advance_token
|
|
739
741
|
list = []
|
|
740
742
|
while !at?(:RBRACKET)
|
|
741
|
-
list << value
|
|
743
|
+
list << value(const: const)
|
|
742
744
|
end
|
|
743
745
|
expect_token(:RBRACKET)
|
|
744
746
|
list
|
|
@@ -750,11 +752,12 @@ module GraphQL
|
|
|
750
752
|
loc = pos #: untyped
|
|
751
753
|
n = parse_name
|
|
752
754
|
expect_token(:COLON)
|
|
753
|
-
args << Argument.new(pos: loc, name: n, value: value, filename: @filename, source: self)
|
|
755
|
+
args << Argument.new(pos: loc, name: n, value: value(const: const), filename: @filename, source: self)
|
|
754
756
|
end
|
|
755
757
|
expect_token(:RCURLY)
|
|
756
758
|
InputObject.new(pos: start, arguments: args, filename: @filename, source: self)
|
|
757
759
|
when :VAR_SIGN
|
|
760
|
+
raise_parse_error("Unexpected variable in constant value") if const
|
|
758
761
|
loc = pos
|
|
759
762
|
advance_token
|
|
760
763
|
VariableIdentifier.new(pos: loc, name: parse_name, filename: @filename, source: self)
|
data/lib/graphql/language.rb
CHANGED
|
@@ -59,7 +59,7 @@ module GraphQL
|
|
|
59
59
|
if scanner.skip('"""')
|
|
60
60
|
inside_triple_quoted_string = !inside_triple_quoted_string
|
|
61
61
|
new_query_str && (new_query_str << scanner.matched)
|
|
62
|
-
elsif scanner.skip(/(?:\\"|[^"\n\r])+/m)
|
|
62
|
+
elsif scanner.skip(inside_single_quoted_string ? /(?>\\[\\"]|[^"\n\r])+/ : /(?:\\"|[^"\n\r])+/m)
|
|
63
63
|
new_query_str && (new_query_str << scanner.matched)
|
|
64
64
|
elsif scanner.skip('"')
|
|
65
65
|
new_query_str && (new_query_str << '"')
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
class NameValidator
|
|
5
5
|
VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/
|
|
6
6
|
|
|
7
|
+
#: (String | Symbol name) -> void
|
|
7
8
|
def self.validate!(name)
|
|
8
9
|
name = name.is_a?(String) ? name : name.to_s
|
|
9
10
|
raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless name.match?(VALID_NAME_REGEX)
|
data/lib/graphql/pagination.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
|
|
4
4
|
require 'digest/sha2'
|
|
5
5
|
|
|
@@ -20,6 +20,7 @@ module GraphQL
|
|
|
20
20
|
#
|
|
21
21
|
# :call-seq:
|
|
22
22
|
# generate(input_str) -> String
|
|
23
|
+
#: (String input_str) -> String
|
|
23
24
|
def self.generate(input_str)
|
|
24
25
|
# Implemented to be:
|
|
25
26
|
# - Short (and uniform) length
|