graphql 1.12.16 → 1.12.20
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/generators/graphql/relay.rb +19 -11
- data/lib/generators/graphql/templates/schema.erb +13 -1
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/dataloader/source.rb +50 -2
- data/lib/graphql/dataloader.rb +39 -16
- data/lib/graphql/define/instance_definable.rb +1 -1
- data/lib/graphql/deprecated_dsl.rb +11 -3
- data/lib/graphql/deprecation.rb +1 -5
- data/lib/graphql/execution/interpreter/runtime.rb +10 -6
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/input_value_type.rb +6 -0
- data/lib/graphql/pagination/connections.rb +35 -16
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query.rb +4 -0
- data/lib/graphql/schema/argument.rb +71 -28
- data/lib/graphql/schema/field.rb +14 -4
- data/lib/graphql/schema/input_object.rb +5 -9
- data/lib/graphql/schema/member/has_arguments.rb +90 -44
- data/lib/graphql/schema/resolver.rb +24 -59
- data/lib/graphql/schema/subscription.rb +6 -8
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/format_validator.rb +2 -1
- data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/length_validator.rb +5 -3
- data/lib/graphql/schema/validator/numericality_validator.rb +12 -2
- data/lib/graphql/schema/validator.rb +36 -25
- data/lib/graphql/schema.rb +18 -5
- data/lib/graphql/static_validation/base_visitor.rb +3 -0
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +40 -21
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +8 -2
- data/lib/graphql/static_validation/validator.rb +15 -12
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -1
- data/lib/graphql/subscriptions/event.rb +47 -2
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- metadata +5 -3
@@ -7,7 +7,6 @@ module GraphQL
|
|
7
7
|
# @return [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>]
|
8
8
|
attr_reader :validated
|
9
9
|
|
10
|
-
# TODO should this implement `if:` and `unless:` ?
|
11
10
|
# @param validated [GraphQL::Schema::Argument, GraphQL::Schema::Field, GraphQL::Schema::Resolver, Class<GraphQL::Schema::InputObject>] The argument or argument owner this validator is attached to
|
12
11
|
# @param allow_blank [Boolean] if `true`, then objects that respond to `.blank?` and return true for `.blank?` will skip this validation
|
13
12
|
# @param allow_null [Boolean] if `true`, then incoming `null`s will skip this validation
|
@@ -25,26 +24,6 @@ module GraphQL
|
|
25
24
|
raise GraphQL::RequiredImplementationMissingError, "Validator classes should implement #validate"
|
26
25
|
end
|
27
26
|
|
28
|
-
# This is called by the validation system and eventually calls {#validate}.
|
29
|
-
# @api private
|
30
|
-
def apply(object, context, value)
|
31
|
-
if value.nil?
|
32
|
-
if @allow_null
|
33
|
-
nil # skip this
|
34
|
-
else
|
35
|
-
"%{validated} can't be null"
|
36
|
-
end
|
37
|
-
elsif value.respond_to?(:blank?) && value.blank?
|
38
|
-
if @allow_blank
|
39
|
-
nil # skip this
|
40
|
-
else
|
41
|
-
"%{validated} can't be blank"
|
42
|
-
end
|
43
|
-
else
|
44
|
-
validate(object, context, value)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
27
|
# This is like `String#%`, but it supports the case that only some of `string`'s
|
49
28
|
# values are present in `substitutions`
|
50
29
|
def partial_format(string, substitutions)
|
@@ -55,6 +34,12 @@ module GraphQL
|
|
55
34
|
string
|
56
35
|
end
|
57
36
|
|
37
|
+
# @return [Boolean] `true` if `value` is `nil` and this validator has `allow_null: true` or if value is `.blank?` and this validator has `allow_blank: true`
|
38
|
+
def permitted_empty_value?(value)
|
39
|
+
(value.nil? && @allow_null) ||
|
40
|
+
(@allow_blank && value.respond_to?(:blank?) && value.blank?)
|
41
|
+
end
|
42
|
+
|
58
43
|
# @param schema_member [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class<GraphQL::Schema::InputObject>]
|
59
44
|
# @param validates_hash [Hash{Symbol => Hash}, Hash{Class => Hash} nil] A configuration passed as `validates:`
|
60
45
|
# @return [Array<Validator>]
|
@@ -62,6 +47,24 @@ module GraphQL
|
|
62
47
|
if validates_hash.nil? || validates_hash.empty?
|
63
48
|
EMPTY_ARRAY
|
64
49
|
else
|
50
|
+
validates_hash = validates_hash.dup
|
51
|
+
allow_null = validates_hash.delete(:allow_null)
|
52
|
+
allow_blank = validates_hash.delete(:allow_blank)
|
53
|
+
|
54
|
+
# This could be {...}.compact on Ruby 2.4+
|
55
|
+
default_options = {}
|
56
|
+
if !allow_null.nil?
|
57
|
+
default_options[:allow_null] = allow_null
|
58
|
+
end
|
59
|
+
if !allow_blank.nil?
|
60
|
+
default_options[:allow_blank] = allow_blank
|
61
|
+
end
|
62
|
+
|
63
|
+
# allow_nil or allow_blank are the _only_ validations:
|
64
|
+
if validates_hash.empty?
|
65
|
+
validates_hash = default_options
|
66
|
+
end
|
67
|
+
|
65
68
|
validates_hash.map do |validator_name, options|
|
66
69
|
validator_class = case validator_name
|
67
70
|
when Class
|
@@ -69,7 +72,11 @@ module GraphQL
|
|
69
72
|
else
|
70
73
|
all_validators[validator_name] || raise(ArgumentError, "unknown validation: #{validator_name.inspect}")
|
71
74
|
end
|
72
|
-
|
75
|
+
if options.is_a?(Hash)
|
76
|
+
validator_class.new(validated: schema_member, **(default_options.merge(options)))
|
77
|
+
else
|
78
|
+
validator_class.new(options, validated: schema_member, **default_options)
|
79
|
+
end
|
73
80
|
end
|
74
81
|
end
|
75
82
|
end
|
@@ -122,10 +129,10 @@ module GraphQL
|
|
122
129
|
|
123
130
|
validators.each do |validator|
|
124
131
|
validated = as || validator.validated
|
125
|
-
errors = validator.
|
132
|
+
errors = validator.validate(object, context, value)
|
126
133
|
if errors &&
|
127
|
-
|
128
|
-
|
134
|
+
(errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
|
135
|
+
(errors.is_a?(String))
|
129
136
|
if all_errors.frozen? # It's empty
|
130
137
|
all_errors = []
|
131
138
|
end
|
@@ -161,3 +168,7 @@ require "graphql/schema/validator/exclusion_validator"
|
|
161
168
|
GraphQL::Schema::Validator.install(:exclusion, GraphQL::Schema::Validator::ExclusionValidator)
|
162
169
|
require "graphql/schema/validator/required_validator"
|
163
170
|
GraphQL::Schema::Validator.install(:required, GraphQL::Schema::Validator::RequiredValidator)
|
171
|
+
require "graphql/schema/validator/allow_null_validator"
|
172
|
+
GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator)
|
173
|
+
require "graphql/schema/validator/allow_blank_validator"
|
174
|
+
GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator)
|
data/lib/graphql/schema.rb
CHANGED
@@ -161,7 +161,7 @@ module GraphQL
|
|
161
161
|
|
162
162
|
accepts_definitions \
|
163
163
|
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
|
164
|
-
:validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
|
164
|
+
:validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
|
165
165
|
:orphan_types, :resolve_type, :type_error, :parse_error,
|
166
166
|
:error_bubbling,
|
167
167
|
:raise_definition_error,
|
@@ -200,7 +200,7 @@ module GraphQL
|
|
200
200
|
attr_accessor \
|
201
201
|
:query, :mutation, :subscription,
|
202
202
|
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
|
203
|
-
:validate_timeout, :max_depth, :max_complexity, :default_max_page_size,
|
203
|
+
:validate_timeout, :validate_max_errors, :max_depth, :max_complexity, :default_max_page_size,
|
204
204
|
:orphan_types, :directives,
|
205
205
|
:query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
|
206
206
|
:cursor_encoder,
|
@@ -552,7 +552,7 @@ module GraphQL
|
|
552
552
|
end
|
553
553
|
end
|
554
554
|
|
555
|
-
# @see [GraphQL::Schema::Warden]
|
555
|
+
# @see [GraphQL::Schema::Warden] Restricted access to root types
|
556
556
|
# @return [GraphQL::ObjectType, nil]
|
557
557
|
def root_type_for_operation(operation)
|
558
558
|
case operation
|
@@ -934,6 +934,7 @@ module GraphQL
|
|
934
934
|
schema_defn.mutation = mutation && mutation.graphql_definition
|
935
935
|
schema_defn.subscription = subscription && subscription.graphql_definition
|
936
936
|
schema_defn.validate_timeout = validate_timeout
|
937
|
+
schema_defn.validate_max_errors = validate_max_errors
|
937
938
|
schema_defn.max_complexity = max_complexity
|
938
939
|
schema_defn.error_bubbling = error_bubbling
|
939
940
|
schema_defn.max_depth = max_depth
|
@@ -1073,7 +1074,7 @@ module GraphQL
|
|
1073
1074
|
end
|
1074
1075
|
end
|
1075
1076
|
|
1076
|
-
# @see [GraphQL::Schema::Warden]
|
1077
|
+
# @see [GraphQL::Schema::Warden] Restricted access to root types
|
1077
1078
|
# @return [GraphQL::ObjectType, nil]
|
1078
1079
|
def root_type_for_operation(operation)
|
1079
1080
|
case operation
|
@@ -1290,10 +1291,22 @@ module GraphQL
|
|
1290
1291
|
validator_opts = { schema: self }
|
1291
1292
|
rules && (validator_opts[:rules] = rules)
|
1292
1293
|
validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
|
1293
|
-
res = validator.validate(query, timeout: validate_timeout)
|
1294
|
+
res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
|
1294
1295
|
res[:errors]
|
1295
1296
|
end
|
1296
1297
|
|
1298
|
+
attr_writer :validate_max_errors
|
1299
|
+
|
1300
|
+
def validate_max_errors(new_validate_max_errors = nil)
|
1301
|
+
if new_validate_max_errors
|
1302
|
+
@validate_max_errors = new_validate_max_errors
|
1303
|
+
elsif defined?(@validate_max_errors)
|
1304
|
+
@validate_max_errors
|
1305
|
+
else
|
1306
|
+
find_inherited_value(:validate_max_errors)
|
1307
|
+
end
|
1308
|
+
end
|
1309
|
+
|
1297
1310
|
attr_writer :max_complexity
|
1298
1311
|
|
1299
1312
|
def max_complexity(max_complexity = nil)
|
@@ -10,6 +10,7 @@ module GraphQL
|
|
10
10
|
#
|
11
11
|
# Original Algorithm: https://github.com/graphql/graphql-js/blob/master/src/validation/rules/OverlappingFieldsCanBeMerged.js
|
12
12
|
NO_ARGS = {}.freeze
|
13
|
+
|
13
14
|
Field = Struct.new(:node, :definition, :owner_type, :parents)
|
14
15
|
FragmentSpread = Struct.new(:name, :parents)
|
15
16
|
|
@@ -17,20 +18,43 @@ module GraphQL
|
|
17
18
|
super
|
18
19
|
@visited_fragments = {}
|
19
20
|
@compared_fragments = {}
|
21
|
+
@conflict_count = 0
|
20
22
|
end
|
21
23
|
|
22
24
|
def on_operation_definition(node, _parent)
|
23
|
-
conflicts_within_selection_set(node, type_definition)
|
25
|
+
setting_errors { conflicts_within_selection_set(node, type_definition) }
|
24
26
|
super
|
25
27
|
end
|
26
28
|
|
27
29
|
def on_field(node, _parent)
|
28
|
-
conflicts_within_selection_set(node, type_definition)
|
30
|
+
setting_errors { conflicts_within_selection_set(node, type_definition) }
|
29
31
|
super
|
30
32
|
end
|
31
33
|
|
32
34
|
private
|
33
35
|
|
36
|
+
def field_conflicts
|
37
|
+
@field_conflicts ||= Hash.new do |errors, field|
|
38
|
+
errors[field] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: :field, field_name: field)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def arg_conflicts
|
43
|
+
@arg_conflicts ||= Hash.new do |errors, field|
|
44
|
+
errors[field] = GraphQL::StaticValidation::FieldsWillMergeError.new(kind: :argument, field_name: field)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def setting_errors
|
49
|
+
@field_conflicts = nil
|
50
|
+
@arg_conflicts = nil
|
51
|
+
|
52
|
+
yield
|
53
|
+
|
54
|
+
field_conflicts.each_value { |error| add_error(error) }
|
55
|
+
arg_conflicts.each_value { |error| add_error(error) }
|
56
|
+
end
|
57
|
+
|
34
58
|
def conflicts_within_selection_set(node, parent_type)
|
35
59
|
return if parent_type.nil?
|
36
60
|
|
@@ -183,6 +207,8 @@ module GraphQL
|
|
183
207
|
end
|
184
208
|
|
185
209
|
def find_conflict(response_key, field1, field2, mutually_exclusive: false)
|
210
|
+
return if @conflict_count >= context.max_errors
|
211
|
+
|
186
212
|
node1 = field1.node
|
187
213
|
node2 = field2.node
|
188
214
|
|
@@ -191,28 +217,21 @@ module GraphQL
|
|
191
217
|
|
192
218
|
if !are_mutually_exclusive
|
193
219
|
if node1.name != node2.name
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
field_name: response_key,
|
201
|
-
conflicts: errored_nodes
|
202
|
-
)
|
220
|
+
conflict = field_conflicts[response_key]
|
221
|
+
|
222
|
+
conflict.add_conflict(node1, node1.name)
|
223
|
+
conflict.add_conflict(node2, node2.name)
|
224
|
+
|
225
|
+
@conflict_count += 1
|
203
226
|
end
|
204
227
|
|
205
228
|
if !same_arguments?(node1, node2)
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
path: [],
|
213
|
-
field_name: response_key,
|
214
|
-
conflicts: conflicts
|
215
|
-
)
|
229
|
+
conflict = arg_conflicts[response_key]
|
230
|
+
|
231
|
+
conflict.add_conflict(node1, GraphQL::Language.serialize(serialize_field_args(node1)))
|
232
|
+
conflict.add_conflict(node2, GraphQL::Language.serialize(serialize_field_args(node2)))
|
233
|
+
|
234
|
+
@conflict_count += 1
|
216
235
|
end
|
217
236
|
end
|
218
237
|
|
@@ -3,12 +3,33 @@ module GraphQL
|
|
3
3
|
module StaticValidation
|
4
4
|
class FieldsWillMergeError < StaticValidation::Error
|
5
5
|
attr_reader :field_name
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :kind
|
7
|
+
|
8
|
+
def initialize(kind:, field_name:)
|
9
|
+
super(nil)
|
7
10
|
|
8
|
-
def initialize(message, path: nil, nodes: [], field_name:, conflicts:)
|
9
|
-
super(message, path: path, nodes: nodes)
|
10
11
|
@field_name = field_name
|
11
|
-
@
|
12
|
+
@kind = kind
|
13
|
+
@conflicts = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def message
|
17
|
+
"Field '#{field_name}' has #{kind == :argument ? 'an' : 'a'} #{kind} conflict: #{conflicts}?"
|
18
|
+
end
|
19
|
+
|
20
|
+
def path
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
def conflicts
|
25
|
+
@conflicts.join(' or ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_conflict(node, conflict_str)
|
29
|
+
return if nodes.include?(node)
|
30
|
+
|
31
|
+
@nodes << node
|
32
|
+
@conflicts << conflict_str
|
12
33
|
end
|
13
34
|
|
14
35
|
# A hash representation of this Message
|
@@ -7,12 +7,12 @@ module GraphQL
|
|
7
7
|
dependency_map = context.dependencies
|
8
8
|
dependency_map.cyclical_definitions.each do |defn|
|
9
9
|
if defn.node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
10
|
-
|
10
|
+
add_error(GraphQL::StaticValidation::FragmentsAreFiniteError.new(
|
11
11
|
"Fragment #{defn.name} contains an infinite loop",
|
12
12
|
nodes: defn.node,
|
13
13
|
path: defn.path,
|
14
14
|
name: defn.name
|
15
|
-
)
|
15
|
+
))
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -15,14 +15,16 @@ module GraphQL
|
|
15
15
|
extend Forwardable
|
16
16
|
|
17
17
|
attr_reader :query, :errors, :visitor,
|
18
|
-
:on_dependency_resolve_handlers
|
18
|
+
:on_dependency_resolve_handlers,
|
19
|
+
:max_errors
|
19
20
|
|
20
21
|
def_delegators :@query, :schema, :document, :fragments, :operations, :warden
|
21
22
|
|
22
|
-
def initialize(query, visitor_class)
|
23
|
+
def initialize(query, visitor_class, max_errors)
|
23
24
|
@query = query
|
24
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
25
26
|
@errors = []
|
27
|
+
@max_errors = max_errors || Float::INFINITY
|
26
28
|
@on_dependency_resolve_handlers = []
|
27
29
|
@visitor = visitor_class.new(document, self)
|
28
30
|
end
|
@@ -38,6 +40,10 @@ module GraphQL
|
|
38
40
|
def validate_literal(ast_value, type)
|
39
41
|
@literal_validator.validate(ast_value, type)
|
40
42
|
end
|
43
|
+
|
44
|
+
def too_many_errors?
|
45
|
+
@errors.length >= @max_errors
|
46
|
+
end
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
@@ -24,8 +24,9 @@ module GraphQL
|
|
24
24
|
# @param query [GraphQL::Query]
|
25
25
|
# @param validate [Boolean]
|
26
26
|
# @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
|
27
|
+
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
27
28
|
# @return [Array<Hash>]
|
28
|
-
def validate(query, validate: true, timeout: nil)
|
29
|
+
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
29
30
|
query.trace("validate", { validate: validate, query: query }) do
|
30
31
|
can_skip_rewrite = query.context.interpreter? && query.schema.using_ast_analysis? && query.schema.is_a?(Class)
|
31
32
|
errors = if validate == false && can_skip_rewrite
|
@@ -34,25 +35,27 @@ module GraphQL
|
|
34
35
|
rules_to_use = validate ? @rules : []
|
35
36
|
visitor_class = BaseVisitor.including_rules(rules_to_use, rewrite: !can_skip_rewrite)
|
36
37
|
|
37
|
-
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
|
38
|
+
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class, max_errors)
|
38
39
|
|
39
40
|
begin
|
40
41
|
# CAUTION: Usage of the timeout module makes the assumption that validation rules are stateless Ruby code that requires no cleanup if process was interrupted. This means no blocking IO calls, native gems, locks, or `rescue` clauses that must be reached.
|
41
42
|
# A timeout value of 0 or nil will execute the block without any timeout.
|
42
43
|
Timeout::timeout(timeout) do
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
legacy_rules
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
catch(:too_many_validation_errors) do
|
45
|
+
# Attach legacy-style rules.
|
46
|
+
# Only loop through rules if it has legacy-style rules
|
47
|
+
unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
|
48
|
+
legacy_rules.each do |rule_class_or_module|
|
49
|
+
if rule_class_or_module.method_defined?(:validate)
|
50
|
+
GraphQL::Deprecation.warn "Legacy validator rules will be removed from GraphQL-Ruby 2.0, use a module instead (see the built-in rules: https://github.com/rmosolgo/graphql-ruby/tree/master/lib/graphql/static_validation/rules)"
|
51
|
+
GraphQL::Deprecation.warn " -> Legacy validator: #{rule_class_or_module}"
|
52
|
+
rule_class_or_module.new.validate(context)
|
53
|
+
end
|
51
54
|
end
|
52
55
|
end
|
53
|
-
end
|
54
56
|
|
55
|
-
|
57
|
+
context.visitor.visit
|
58
|
+
end
|
56
59
|
end
|
57
60
|
rescue Timeout::Error
|
58
61
|
handle_timeout(query, context)
|
@@ -1,10 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
class StringEncodingError < GraphQL::RuntimeTypeError
|
4
|
-
attr_reader :string
|
5
|
-
def initialize(str)
|
4
|
+
attr_reader :string, :field, :path
|
5
|
+
def initialize(str, context:)
|
6
6
|
@string = str
|
7
|
-
|
7
|
+
@field = context[:current_field]
|
8
|
+
@path = context[:current_path]
|
9
|
+
message = "String #{str.inspect} was encoded as #{str.encoding}".dup
|
10
|
+
if @path
|
11
|
+
message << " @ #{@path.join(".")}"
|
12
|
+
end
|
13
|
+
if @field
|
14
|
+
message << " (#{@field.path})"
|
15
|
+
end
|
16
|
+
message << ". GraphQL requires an encoding compatible with UTF-8."
|
17
|
+
super(message)
|
8
18
|
end
|
9
19
|
end
|
10
20
|
end
|
@@ -127,7 +127,13 @@ module GraphQL
|
|
127
127
|
# It will receive notifications when events come in
|
128
128
|
# and re-evaluate the query locally.
|
129
129
|
def write_subscription(query, events)
|
130
|
-
channel = query.context
|
130
|
+
unless (channel = query.context[:channel])
|
131
|
+
raise GraphQL::Error, "This GraphQL Subscription client does not support the transport protocol expected"\
|
132
|
+
"by the backend Subscription Server implementation (graphql-ruby ActionCableSubscriptions in this case)."\
|
133
|
+
"Some official client implementation including Apollo (https://graphql-ruby.org/javascript_client/apollo_subscriptions.html), "\
|
134
|
+
"Relay Modern (https://graphql-ruby.org/javascript_client/relay_subscriptions.html#actioncable)."\
|
135
|
+
"GraphiQL via `graphiql-rails` may not work out of box (#1051)."
|
136
|
+
end
|
131
137
|
subscription_id = query.context[:subscription_id] ||= build_id
|
132
138
|
stream = stream_subscription_name(subscription_id)
|
133
139
|
channel.stream_from(stream)
|
@@ -53,7 +53,38 @@ module GraphQL
|
|
53
53
|
|
54
54
|
class << self
|
55
55
|
private
|
56
|
+
|
57
|
+
# This method does not support cyclic references in the Hash,
|
58
|
+
# nor does it support Hashes whose keys are not sortable
|
59
|
+
# with respect to their peers ( cases where a <=> b might throw an error )
|
60
|
+
def deep_sort_hash_keys(hash_to_sort)
|
61
|
+
raise ArgumentError.new("Argument must be a Hash") unless hash_to_sort.is_a?(Hash)
|
62
|
+
hash_to_sort.keys.sort.map do |k|
|
63
|
+
if hash_to_sort[k].is_a?(Hash)
|
64
|
+
[k, deep_sort_hash_keys(hash_to_sort[k])]
|
65
|
+
elsif hash_to_sort[k].is_a?(Array)
|
66
|
+
[k, deep_sort_array_hashes(hash_to_sort[k])]
|
67
|
+
else
|
68
|
+
[k, hash_to_sort[k]]
|
69
|
+
end
|
70
|
+
end.to_h
|
71
|
+
end
|
72
|
+
|
73
|
+
def deep_sort_array_hashes(array_to_inspect)
|
74
|
+
raise ArgumentError.new("Argument must be an Array") unless array_to_inspect.is_a?(Array)
|
75
|
+
array_to_inspect.map do |v|
|
76
|
+
if v.is_a?(Hash)
|
77
|
+
deep_sort_hash_keys(v)
|
78
|
+
elsif v.is_a?(Array)
|
79
|
+
deep_sort_array_hashes(v)
|
80
|
+
else
|
81
|
+
v
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
56
86
|
def stringify_args(arg_owner, args)
|
87
|
+
arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
|
57
88
|
case args
|
58
89
|
when Hash
|
59
90
|
next_args = {}
|
@@ -68,8 +99,22 @@ module GraphQL
|
|
68
99
|
normalized_arg_name = arg_name
|
69
100
|
arg_defn = get_arg_definition(arg_owner, normalized_arg_name)
|
70
101
|
end
|
71
|
-
|
72
|
-
|
102
|
+
arg_base_type = arg_defn.type.unwrap
|
103
|
+
# In the case where the value being emitted is seen as a "JSON"
|
104
|
+
# type, treat the value as one atomic unit of serialization
|
105
|
+
is_json_definition = arg_base_type && arg_base_type <= GraphQL::Types::JSON
|
106
|
+
if is_json_definition
|
107
|
+
sorted_value = if v.is_a?(Hash)
|
108
|
+
deep_sort_hash_keys(v)
|
109
|
+
elsif v.is_a?(Array)
|
110
|
+
deep_sort_array_hashes(v)
|
111
|
+
else
|
112
|
+
v
|
113
|
+
end
|
114
|
+
next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value
|
115
|
+
else
|
116
|
+
next_args[normalized_arg_name] = stringify_args(arg_base_type, v)
|
117
|
+
end
|
73
118
|
end
|
74
119
|
# Make sure they're deeply sorted
|
75
120
|
next_args.sort.to_h
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
SYMBOL_KEY = "__sym__"
|
10
10
|
SYMBOL_KEYS_KEY = "__sym_keys__"
|
11
11
|
TIMESTAMP_KEY = "__timestamp__"
|
12
|
-
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%
|
12
|
+
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%z" # eg '2020-01-01 23:59:59.123456789+05:00'
|
13
13
|
OPEN_STRUCT_KEY = "__ostruct__"
|
14
14
|
|
15
15
|
module_function
|
@@ -14,7 +14,22 @@ module GraphQL
|
|
14
14
|
"execute_query_lazy" => "execute.graphql",
|
15
15
|
}
|
16
16
|
|
17
|
+
# @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
|
18
|
+
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
|
19
|
+
# It can also be specified per-query with `context[:set_appsignal_action_name]`.
|
20
|
+
def initialize(options = {})
|
21
|
+
@set_action_name = options.fetch(:set_action_name, false)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
17
25
|
def platform_trace(platform_key, key, data)
|
26
|
+
if key == "execute_query"
|
27
|
+
set_this_txn_name = data[:query].context[:set_appsignal_action_name]
|
28
|
+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name)
|
29
|
+
Appsignal::Transaction.current.set_action(transaction_name(data[:query]))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
18
33
|
Appsignal.instrument(platform_key) do
|
19
34
|
yield
|
20
35
|
end
|
data/lib/graphql/types/int.rb
CHANGED
data/lib/graphql/types/string.rb
CHANGED
@@ -12,7 +12,7 @@ module GraphQL
|
|
12
12
|
attr_reader :type
|
13
13
|
|
14
14
|
# @return [GraphQL::Query::Context] the context for the current query
|
15
|
-
|
15
|
+
attr_accessor :context
|
16
16
|
|
17
17
|
def initialize(message = nil, object: nil, type: nil, context: nil)
|
18
18
|
if message.nil? && object.nil? && type.nil?
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -44,6 +44,6 @@ I also sell [GraphQL::Pro](https://graphql.pro) which provides several features
|
|
44
44
|
|
45
45
|
## Getting Involved
|
46
46
|
|
47
|
-
- __Say hi & ask questions__ in the
|
47
|
+
- __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9) or [on Twitter](https://twitter.com/rmosolgo)!
|
48
48
|
- __Report bugs__ by posting a description, full stack trace, and all relevant code in a [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues).
|
49
49
|
- __Start hacking__ with the [Development guide](https://graphql-ruby.org/development).
|
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: 1.12.
|
4
|
+
version: 1.12.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -544,6 +544,8 @@ files:
|
|
544
544
|
- lib/graphql/schema/unique_within_type.rb
|
545
545
|
- lib/graphql/schema/validation.rb
|
546
546
|
- lib/graphql/schema/validator.rb
|
547
|
+
- lib/graphql/schema/validator/allow_blank_validator.rb
|
548
|
+
- lib/graphql/schema/validator/allow_null_validator.rb
|
547
549
|
- lib/graphql/schema/validator/exclusion_validator.rb
|
548
550
|
- lib/graphql/schema/validator/format_validator.rb
|
549
551
|
- lib/graphql/schema/validator/inclusion_validator.rb
|
@@ -700,7 +702,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
700
702
|
- !ruby/object:Gem::Version
|
701
703
|
version: '0'
|
702
704
|
requirements: []
|
703
|
-
rubygems_version: 3.2.
|
705
|
+
rubygems_version: 3.2.22
|
704
706
|
signing_key:
|
705
707
|
specification_version: 4
|
706
708
|
summary: A GraphQL language and runtime for Ruby
|