graphql 1.13.12 → 1.13.19

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90fcd1d4c840be80d47ed66bad6c4cdd4c16a4981b249b57e7e126a3ff5d5d6d
4
- data.tar.gz: 6d9f87843cd626dff3a9f46158fce7f1be22c3f1aa4131c8b2390b82f93edb52
3
+ metadata.gz: c21327429549138738debab2d522197133548eb4b275a5f67086154e08469149
4
+ data.tar.gz: ec61107f4f45aaf1de25c6674d7fcf3b8e19b1245fdbf2a1bd1b0f7f1dd9b0aa
5
5
  SHA512:
6
- metadata.gz: 355972194b55f12d4bc43d610249c94ce6d73c574f12688863b01bf60ad4f2a9ad093ee19c76403f4b84371583ea5c236a3d7cfda72e3564a222dd9f12c1d0ea
7
- data.tar.gz: 5ebd795ac0884a46f413e4ede96165d3b2be2489266c36fbc06d90f0d940e69a2afa86c86417757716dc47ddc9c3bdbbe475c7f1547951c5cbe843a0ed7d45e5
6
+ metadata.gz: 5b56c3aaa86b6d758b53d49a7906de483375c54f40db9126b96d791f41c7bba6407d9b59205a4d47215cef1391cd5ebbc03d5eb76825747cbfc1d0f6baf1f0ef
7
+ data.tar.gz: 7f7cb3b2e4fa8c26b355ea4adf01803ab32e2d97036475469071e2cf67d06431ab0486f0daf8ab28433deb95bb8642ea0a2b52739abe6d0ad06bb22b4fdcb2cc
@@ -400,6 +400,7 @@ module GraphQL
400
400
  raise "Invariant: no field for #{owner_type}.#{field_name}"
401
401
  end
402
402
  end
403
+
403
404
  return_type = field_defn.type
404
405
 
405
406
  next_path = path.dup
@@ -425,18 +426,17 @@ module GraphQL
425
426
  total_args_count = field_defn.arguments(context).size
426
427
  if total_args_count == 0
427
428
  resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
428
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
429
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
429
430
  else
430
431
  # TODO remove all arguments(...) usages?
431
432
  @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
432
- evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
433
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
433
434
  end
434
435
  end
435
436
  end
436
437
 
437
- def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
438
+ def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type) # rubocop:disable Metrics/ParameterLists
438
439
  context.scoped_context = scoped_context
439
- return_type = field_defn.type
440
440
  after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
441
441
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
442
442
  continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
@@ -11,8 +11,8 @@ module GraphQL
11
11
  "to the executor."
12
12
  field :name, String, null: false, method: :graphql_name
13
13
  field :description, String
14
- field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false
15
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
14
+ field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false, scope: false
15
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
16
16
  argument :include_deprecated, Boolean, required: false, default_value: false
17
17
  end
18
18
  field :on_operation, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_operation?
@@ -7,7 +7,7 @@ module GraphQL
7
7
  "a name, potentially a list of arguments, and a return type."
8
8
  field :name, String, null: false
9
9
  field :description, String
10
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
10
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
11
11
  argument :include_deprecated, Boolean, required: false, default_value: false
12
12
  end
13
13
  field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false
@@ -8,11 +8,11 @@ module GraphQL
8
8
  "available types and directives on the server, as well as the entry points for "\
9
9
  "query, mutation, and subscription operations."
10
10
 
11
- field :types, [GraphQL::Schema::LateBoundType.new("__Type")], "A list of all types supported by this server.", null: false
11
+ field :types, [GraphQL::Schema::LateBoundType.new("__Type")], "A list of all types supported by this server.", null: false, scope: false
12
12
  field :query_type, GraphQL::Schema::LateBoundType.new("__Type"), "The type that query operations will be rooted at.", null: false
13
13
  field :mutation_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server supports mutation, the type that mutation operations will be rooted at."
14
14
  field :subscription_type, GraphQL::Schema::LateBoundType.new("__Type"), "If this server support subscription, the type that subscription operations will be rooted at."
15
- field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false
15
+ field :directives, [GraphQL::Schema::LateBoundType.new("__Directive")], "A list of all directives supported by this server.", null: false, scope: false
16
16
  field :description, String, resolver_method: :schema_description
17
17
 
18
18
  def schema_description
@@ -14,15 +14,15 @@ module GraphQL
14
14
  field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false
15
15
  field :name, String, method: :graphql_name
16
16
  field :description, String
17
- field :fields, [GraphQL::Schema::LateBoundType.new("__Field")] do
17
+ field :fields, [GraphQL::Schema::LateBoundType.new("__Field")], scope: false do
18
18
  argument :include_deprecated, Boolean, required: false, default_value: false
19
19
  end
20
- field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")]
21
- field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")]
22
- field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")] do
20
+ field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
21
+ field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
22
+ field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")], scope: false do
23
23
  argument :include_deprecated, Boolean, required: false, default_value: false
24
24
  end
25
- field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")] do
25
+ field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], scope: false do
26
26
  argument :include_deprecated, Boolean, required: false, default_value: false
27
27
  end
28
28
  field :of_type, GraphQL::Schema::LateBoundType.new("__Type")
@@ -4,6 +4,12 @@ module GraphQL
4
4
  class InputValidationResult
5
5
  attr_accessor :problems
6
6
 
7
+ def self.from_problem(explanation, path = nil, extensions: nil, message: nil)
8
+ result = self.new
9
+ result.add_problem(explanation, path, extensions: extensions, message: message)
10
+ result
11
+ end
12
+
7
13
  def initialize(valid: true, problems: nil)
8
14
  @valid = valid
9
15
  @problems = problems
@@ -38,6 +44,9 @@ module GraphQL
38
44
  # It could have been explicitly set on inner_result (if it had no problems)
39
45
  @valid = false
40
46
  end
47
+
48
+ VALID = self.new
49
+ VALID.freeze
41
50
  end
42
51
  end
43
52
  end
@@ -16,10 +16,9 @@ module GraphQL
16
16
  class ValidationPipeline
17
17
  attr_reader :max_depth, :max_complexity
18
18
 
19
- def initialize(query:, validate:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
19
+ def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
20
20
  @validation_errors = []
21
21
  @internal_representation = nil
22
- @validate = validate
23
22
  @parse_error = parse_error
24
23
  @operation_name_error = operation_name_error
25
24
  @query = query
@@ -72,7 +71,7 @@ module GraphQL
72
71
  elsif @operation_name_error
73
72
  @validation_errors << @operation_name_error
74
73
  else
75
- validation_result = @schema.static_validator.validate(@query, validate: @validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
74
+ validation_result = @schema.static_validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
76
75
  @validation_errors.concat(validation_result[:errors])
77
76
  @internal_representation = validation_result[:irep]
78
77
 
@@ -4,11 +4,11 @@ module GraphQL
4
4
  class VariableValidationError < GraphQL::ExecutionError
5
5
  attr_accessor :value, :validation_result
6
6
 
7
- def initialize(variable_ast, type, value, validation_result)
7
+ def initialize(variable_ast, type, value, validation_result, msg: nil)
8
8
  @value = value
9
9
  @validation_result = validation_result
10
10
 
11
- msg = "Variable $#{variable_ast.name} of type #{type.to_type_signature} was provided invalid value"
11
+ msg ||= "Variable $#{variable_ast.name} of type #{type.to_type_signature} was provided invalid value"
12
12
 
13
13
  if problem_fields.any?
14
14
  msg += " for #{problem_fields.join(", ")}"
@@ -17,6 +17,10 @@ module GraphQL
17
17
  @provided_variables = GraphQL::Argument.deep_stringify(provided_variables)
18
18
  @errors = []
19
19
  @storage = ast_variables.each_with_object({}) do |ast_variable, memo|
20
+ if schema.validate_max_errors && schema.validate_max_errors <= @errors.count
21
+ add_max_errors_reached_message
22
+ break
23
+ end
20
24
  # Find the right value for this variable:
21
25
  # - First, use the value provided at runtime
22
26
  # - Then, fall back to the default value from the query string
@@ -29,8 +33,9 @@ module GraphQL
29
33
  default_value = ast_variable.default_value
30
34
  provided_value = @provided_variables[variable_name]
31
35
  value_was_provided = @provided_variables.key?(variable_name)
36
+ max_errors = schema.validate_max_errors - @errors.count if schema.validate_max_errors
32
37
  begin
33
- validation_result = variable_type.validate_input(provided_value, ctx)
38
+ validation_result = variable_type.validate_input(provided_value, ctx, max_errors: max_errors)
34
39
  if validation_result.valid?
35
40
  if value_was_provided
36
41
  # Add the variable if a value was provided
@@ -61,8 +66,7 @@ module GraphQL
61
66
  # like InputValidationResults generated by validate_non_null_input but unfortunately we don't
62
67
  # have this information available in the coerce_input call chain. Note this path is the path
63
68
  # that appears under errors.extensions.problems.path and NOT the result path under errors.path.
64
- validation_result = GraphQL::Query::InputValidationResult.new
65
- validation_result.add_problem(ex.message)
69
+ validation_result = GraphQL::Query::InputValidationResult.from_problem(ex.message)
66
70
  end
67
71
 
68
72
  if !validation_result.valid?
@@ -73,6 +77,29 @@ module GraphQL
73
77
  end
74
78
 
75
79
  def_delegators :@storage, :length, :key?, :[], :fetch, :to_h
80
+
81
+ private
82
+
83
+ def deep_stringify(val)
84
+ case val
85
+ when Array
86
+ val.map { |v| deep_stringify(v) }
87
+ when Hash
88
+ new_val = {}
89
+ val.each do |k, v|
90
+ new_val[k.to_s] = deep_stringify(v)
91
+ end
92
+ new_val
93
+ else
94
+ val
95
+ end
96
+ end
97
+
98
+ def add_max_errors_reached_message
99
+ message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
100
+ validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
101
+ errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
102
+ end
76
103
  end
77
104
  end
78
105
  end
data/lib/graphql/query.rb CHANGED
@@ -434,7 +434,6 @@ module GraphQL
434
434
 
435
435
  @validation_pipeline = GraphQL::Query::ValidationPipeline.new(
436
436
  query: self,
437
- validate: @validate,
438
437
  parse_error: parse_error,
439
438
  operation_name_error: operation_name_error,
440
439
  max_depth: @max_depth,
@@ -22,7 +22,7 @@ module GraphQL
22
22
  # but downcase the first letter.
23
23
  def default_graphql_name
24
24
  @default_graphql_name ||= begin
25
- camelized_name = super
25
+ camelized_name = super.dup
26
26
  camelized_name[0] = camelized_name[0].downcase
27
27
  camelized_name
28
28
  end
@@ -139,9 +139,8 @@ module GraphQL
139
139
  GraphQL::TypeKinds::ENUM
140
140
  end
141
141
 
142
- def validate_non_null_input(value_name, ctx)
142
+ def validate_non_null_input(value_name, ctx, max_errors: nil)
143
143
  result = GraphQL::Query::InputValidationResult.new
144
-
145
144
  allowed_values = ctx.warden.enum_values(self)
146
145
  matching_value = allowed_values.find { |v| v.graphql_name == value_name }
147
146
 
@@ -176,6 +176,8 @@ module GraphQL
176
176
 
177
177
  # @return Boolean
178
178
  attr_reader :relay_node_field
179
+ # @return Boolean
180
+ attr_reader :relay_nodes_field
179
181
 
180
182
  # @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
181
183
  def method_conflict_warning?
@@ -227,6 +229,7 @@ module GraphQL
227
229
  end
228
230
  @original_name = name
229
231
  name_s = -name.to_s
232
+
230
233
  @underscored_name = -Member::BuildType.underscore(name_s)
231
234
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
232
235
  @description = description
@@ -256,6 +259,10 @@ module GraphQL
256
259
  # TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
257
260
  method_name = method || hash_key || name_s
258
261
  @dig_keys = dig
262
+ if hash_key
263
+ @hash_key = hash_key
264
+ end
265
+
259
266
  resolver_method ||= name_s.to_sym
260
267
 
261
268
  @method_str = -method_name.to_s
@@ -471,7 +478,13 @@ module GraphQL
471
478
  case defined_complexity
472
479
  when Proc
473
480
  arguments = query.arguments_for(nodes.first, self)
474
- defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
481
+ if arguments.is_a?(GraphQL::ExecutionError)
482
+ return child_complexity
483
+ elsif arguments.respond_to?(:keyword_arguments)
484
+ arguments = arguments.keyword_arguments
485
+ end
486
+
487
+ defined_complexity.call(query.context, arguments, child_complexity)
475
488
  when Numeric
476
489
  defined_complexity + child_complexity
477
490
  else
@@ -638,27 +651,29 @@ module GraphQL
638
651
  arg_values = args
639
652
  using_arg_values = false
640
653
  end
641
- # Faster than `.any?`
642
- arguments(context).each_value do |arg|
643
- arg_key = arg.keyword
644
- if arg_values.key?(arg_key)
645
- arg_value = arg_values[arg_key]
646
- if using_arg_values
647
- if arg_value.default_used?
648
- # pass -- no auth required for default used
649
- next
650
- else
651
- application_arg_value = arg_value.value
652
- if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
653
- application_arg_value.keyword_arguments
654
+ if args.size > 0
655
+ args = context.warden.arguments(self)
656
+ args.each do |arg|
657
+ arg_key = arg.keyword
658
+ if arg_values.key?(arg_key)
659
+ arg_value = arg_values[arg_key]
660
+ if using_arg_values
661
+ if arg_value.default_used?
662
+ # pass -- no auth required for default used
663
+ next
664
+ else
665
+ application_arg_value = arg_value.value
666
+ if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
667
+ application_arg_value.keyword_arguments
668
+ end
654
669
  end
670
+ else
671
+ application_arg_value = arg_value
655
672
  end
656
- else
657
- application_arg_value = arg_value
658
- end
659
673
 
660
- if !arg.authorized?(object, application_arg_value, context)
661
- return false
674
+ if !arg.authorized?(object, application_arg_value, context)
675
+ return false
676
+ end
662
677
  end
663
678
  end
664
679
  end
@@ -814,7 +829,7 @@ module GraphQL
814
829
  # Find a way to resolve this field, checking:
815
830
  #
816
831
  # - A method on the type instance;
817
- # - Hash keys, if the wrapped object is a hash;
832
+ # - Hash keys, if the wrapped object is a hash or responds to `#[]`
818
833
  # - A method on the wrapped object;
819
834
  # - Or, raise not implemented.
820
835
  #
@@ -836,6 +851,8 @@ module GraphQL
836
851
  else
837
852
  inner_object[@method_str]
838
853
  end
854
+ elsif defined?(@hash_key) && obj.object.respond_to?(:[])
855
+ obj.object[@hash_key]
839
856
  elsif obj.object.respond_to?(@method_sym)
840
857
  method_to_call = @method_sym
841
858
  method_receiver = obj.object
@@ -173,9 +173,8 @@ module GraphQL
173
173
  # @api private
174
174
  INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
175
175
 
176
- def validate_non_null_input(input, ctx)
176
+ def validate_non_null_input(input, ctx, max_errors: nil)
177
177
  result = GraphQL::Query::InputValidationResult.new
178
-
179
178
  warden = ctx.warden
180
179
 
181
180
  if input.is_a?(Array)
@@ -51,15 +51,24 @@ module GraphQL
51
51
  end
52
52
  end
53
53
 
54
- def validate_non_null_input(value, ctx)
54
+ def validate_non_null_input(value, ctx, max_errors: nil)
55
55
  result = GraphQL::Query::InputValidationResult.new
56
56
  ensure_array(value).each_with_index do |item, index|
57
57
  item_result = of_type.validate_input(item, ctx)
58
- if !item_result.valid?
58
+ unless item_result.valid?
59
+ if max_errors
60
+ if max_errors == 0
61
+ add_max_errros_reached_message(result)
62
+ break
63
+ end
64
+
65
+ max_errors -= 1
66
+ end
67
+
59
68
  result.merge_result!(index, item_result)
60
69
  end
61
70
  end
62
- result
71
+ result.valid? ? nil : result
63
72
  end
64
73
 
65
74
  private
@@ -72,6 +81,12 @@ module GraphQL
72
81
  [value]
73
82
  end
74
83
  end
84
+
85
+ def add_max_errros_reached_message(result)
86
+ message = "Too many errors processing list variable, max validation error limit reached. Execution aborted"
87
+ item_result = GraphQL::Query::InputValidationResult.from_problem(message)
88
+ result.merge_result!(nil, item_result)
89
+ end
75
90
  end
76
91
  end
77
92
  end
@@ -108,7 +108,7 @@ module GraphQL
108
108
  @default_graphql_name ||= begin
109
109
  raise GraphQL::RequiredImplementationMissingError, 'Anonymous class should declare a `graphql_name`' if name.nil?
110
110
 
111
- name.split("::").last.sub(/Type\Z/, "")
111
+ -name.split("::").last.sub(/Type\Z/, "")
112
112
  end
113
113
  end
114
114
 
@@ -167,7 +167,7 @@ module GraphQL
167
167
  # @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
168
168
  def coerce_arguments(parent_object, values, context, &block)
169
169
  # Cache this hash to avoid re-merging it
170
- arg_defns = self.arguments(context)
170
+ arg_defns = context.warden.arguments(self)
171
171
  total_args_count = arg_defns.size
172
172
 
173
173
  finished_args = nil
@@ -181,7 +181,7 @@ module GraphQL
181
181
  argument_values = {}
182
182
  resolved_args_count = 0
183
183
  raised_error = false
184
- arg_defns.each do |arg_name, arg_defn|
184
+ arg_defns.each do |arg_defn|
185
185
  context.dataloader.append_job do
186
186
  begin
187
187
  arg_defn.coerce_into_values(parent_object, values, context, argument_values)
@@ -134,7 +134,7 @@ module GraphQL
134
134
  if type.respond_to?(:kind) && type.kind.interface?
135
135
  implements_this_interface = false
136
136
  implementation_is_visible = false
137
- interface_type_memberships.each do |tm|
137
+ warden.interface_type_memberships(self, context).each do |tm|
138
138
  if tm.abstract_type == type
139
139
  implements_this_interface ||= true
140
140
  if warden.visible_type_membership?(tm, context)
@@ -57,7 +57,17 @@ module GraphQL
57
57
  end
58
58
 
59
59
  def interface_type_memberships
60
- own_interface_type_memberships + ((self.is_a?(Class) && superclass.respond_to?(:interface_type_memberships)) ? superclass.interface_type_memberships : [])
60
+ own_tms = own_interface_type_memberships
61
+ if (self.is_a?(Class) && superclass.respond_to?(:interface_type_memberships))
62
+ inherited_tms = superclass.interface_type_memberships
63
+ if inherited_tms.size > 0
64
+ own_tms + inherited_tms
65
+ else
66
+ own_tms
67
+ end
68
+ else
69
+ own_tms
70
+ end
61
71
  end
62
72
 
63
73
  # param context [Query::Context] If omitted, skip filtering.
@@ -8,11 +8,11 @@ module GraphQL
8
8
  validate_input(val, ctx).valid?
9
9
  end
10
10
 
11
- def validate_input(val, ctx)
11
+ def validate_input(val, ctx, max_errors: nil)
12
12
  if val.nil?
13
13
  GraphQL::Query::InputValidationResult.new
14
14
  else
15
- validate_non_null_input(val, ctx)
15
+ validate_non_null_input(val, ctx, max_errors: max_errors) || Query::InputValidationResult::VALID
16
16
  end
17
17
  end
18
18
 
@@ -37,13 +37,13 @@ module GraphQL
37
37
  "#<#{self.class.name} @of_type=#{@of_type.inspect}>"
38
38
  end
39
39
 
40
- def validate_input(value, ctx)
40
+ def validate_input(value, ctx, max_errors: nil)
41
41
  if value.nil?
42
42
  result = GraphQL::Query::InputValidationResult.new
43
43
  result.add_problem("Expected value to not be null")
44
44
  result
45
45
  else
46
- of_type.validate_input(value, ctx)
46
+ of_type.validate_input(value, ctx, max_errors: max_errors)
47
47
  end
48
48
  end
49
49
 
@@ -55,7 +55,7 @@ module GraphQL
55
55
  @default_scalar ||= false
56
56
  end
57
57
 
58
- def validate_non_null_input(value, ctx)
58
+ def validate_non_null_input(value, ctx, max_errors: nil)
59
59
  result = Query::InputValidationResult.new
60
60
  coerced_result = begin
61
61
  ctx.query.with_error_handling do
@@ -78,6 +78,8 @@ module GraphQL
78
78
  def visible_type?(type, ctx); type.visible?(ctx); end
79
79
  def visible_enum_value?(ev, ctx); ev.visible?(ctx); end
80
80
  def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
81
+ def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
82
+ def arguments(owner, ctx); owner.arguments(ctx); end
81
83
  end
82
84
  end
83
85
 
@@ -172,8 +174,8 @@ module GraphQL
172
174
 
173
175
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
174
176
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
175
- def arguments(argument_owner)
176
- @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a) } }
177
+ def arguments(argument_owner, ctx = nil)
178
+ @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a, @context) } }
177
179
  @visible_arguments[argument_owner]
178
180
  end
179
181
 
@@ -231,6 +233,13 @@ module GraphQL
231
233
  visible?(type_membership)
232
234
  end
233
235
 
236
+ def interface_type_memberships(obj_type, _ctx = nil)
237
+ @type_memberships ||= read_through do |obj_t|
238
+ obj_t.interface_type_memberships
239
+ end
240
+ @type_memberships[obj_type]
241
+ end
242
+
234
243
  private
235
244
 
236
245
  def visible_and_reachable_type?(type_defn)
@@ -17,15 +17,21 @@ module GraphQL
17
17
  def platform_trace(platform_key, key, data)
18
18
  tracer.trace(platform_key, service: service_name) do |span|
19
19
  span.span_type = 'custom'
20
+ if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
21
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
22
+ span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, key)
23
+ end
20
24
 
21
25
  if key == 'execute_multiplex'
22
26
  operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
23
- span.resource = if operations.empty?
27
+
28
+ resource = if operations.empty?
24
29
  first_query = data[:multiplex].queries.first
25
30
  fallback_transaction_name(first_query && first_query.context)
26
31
  else
27
32
  operations
28
33
  end
34
+ span.resource = resource if resource
29
35
 
30
36
  # For top span of query, set the analytics sample rate tag, if available.
31
37
  if analytics_enabled?
@@ -39,6 +45,8 @@ module GraphQL
39
45
  span.set_tag(:query_string, data[:query].query_string)
40
46
  end
41
47
 
48
+ prepare_span(key, data, span)
49
+
42
50
  yield
43
51
  end
44
52
  end
@@ -47,8 +55,17 @@ module GraphQL
47
55
  options.fetch(:service, 'ruby-graphql')
48
56
  end
49
57
 
58
+ # Implement this method in a subclass to apply custom tags to datadog spans
59
+ # @param key [String] The event being traced
60
+ # @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
61
+ # @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
62
+ def prepare_span(key, data, span)
63
+ end
64
+
50
65
  def tracer
51
- options.fetch(:tracer, Datadog.tracer)
66
+ default_tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
67
+
68
+ options.fetch(:tracer, default_tracer)
52
69
  end
53
70
 
54
71
  def analytics_available?
@@ -10,6 +10,10 @@ module GraphQL
10
10
  class PlatformTracing
11
11
  class << self
12
12
  attr_accessor :platform_keys
13
+
14
+ def inherited(child_class)
15
+ child_class.platform_keys = self.platform_keys
16
+ end
13
17
  end
14
18
 
15
19
  def initialize(options = {})
@@ -32,6 +36,7 @@ module GraphQL
32
36
  trace_field = true # implemented with instrumenter
33
37
  else
34
38
  field = data[:field]
39
+ # HERE
35
40
  return_type = field.type.unwrap
36
41
  trace_field = if return_type.kind.scalar? || return_type.kind.enum?
37
42
  (field.trace.nil? && @trace_scalars) || field.trace
@@ -41,7 +46,7 @@ module GraphQL
41
46
 
42
47
  platform_key = if trace_field
43
48
  context = data.fetch(:query).context
44
- cached_platform_key(context, field) { platform_field_key(data[:owner], field) }
49
+ cached_platform_key(context, field, :field) { platform_field_key(data[:owner], field) }
45
50
  else
46
51
  nil
47
52
  end
@@ -57,14 +62,14 @@ module GraphQL
57
62
  when "authorized", "authorized_lazy"
58
63
  type = data.fetch(:type)
59
64
  context = data.fetch(:context)
60
- platform_key = cached_platform_key(context, type) { platform_authorized_key(type) }
65
+ platform_key = cached_platform_key(context, type, :authorized) { platform_authorized_key(type) }
61
66
  platform_trace(platform_key, key, data) do
62
67
  yield
63
68
  end
64
69
  when "resolve_type", "resolve_type_lazy"
65
70
  type = data.fetch(:type)
66
71
  context = data.fetch(:context)
67
- platform_key = cached_platform_key(context, type) { platform_resolve_type_key(type) }
72
+ platform_key = cached_platform_key(context, type, :resolve_type) { platform_resolve_type_key(type) }
68
73
  platform_trace(platform_key, key, data) do
69
74
  yield
70
75
  end
@@ -135,7 +140,7 @@ module GraphQL
135
140
  # If the key isn't present, the given block is called and the result is cached for `key`.
136
141
  #
137
142
  # @return [String]
138
- def cached_platform_key(ctx, key)
143
+ def cached_platform_key(ctx, key, trace_phase)
139
144
  cache = ctx.namespace(self.class)[:platform_key_cache] ||= {}
140
145
  cache.fetch(key) { cache[key] = yield }
141
146
  end
@@ -7,7 +7,7 @@ module GraphQL
7
7
 
8
8
  def self.coerce_result(value, ctx)
9
9
  str = value.to_s
10
- if str.encoding == Encoding::UTF_8
10
+ if str.ascii_only? || str.encoding == Encoding::UTF_8
11
11
  str
12
12
  elsif str.frozen?
13
13
  str.encode(Encoding::UTF_8)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.13.12"
3
+ VERSION = "1.13.19"
4
4
  end
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.13.12
4
+ version: 1.13.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-14 00:00:00.000000000 Z
11
+ date: 2023-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -722,7 +722,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
722
722
  - !ruby/object:Gem::Version
723
723
  version: '0'
724
724
  requirements: []
725
- rubygems_version: 3.2.32
725
+ rubygems_version: 3.3.3
726
726
  signing_key:
727
727
  specification_version: 4
728
728
  summary: A GraphQL language and runtime for Ruby