graphql 1.10.7 → 1.10.12

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/object_generator.rb +50 -8
  3. data/lib/graphql.rb +4 -4
  4. data/lib/graphql/analysis/ast/query_complexity.rb +1 -1
  5. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  6. data/lib/graphql/execution/instrumentation.rb +1 -1
  7. data/lib/graphql/execution/interpreter.rb +2 -0
  8. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  9. data/lib/graphql/execution/interpreter/arguments.rb +36 -0
  10. data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -7
  11. data/lib/graphql/execution/interpreter/runtime.rb +38 -35
  12. data/lib/graphql/execution/lookahead.rb +3 -1
  13. data/lib/graphql/internal_representation/scope.rb +2 -2
  14. data/lib/graphql/internal_representation/visit.rb +2 -2
  15. data/lib/graphql/object_type.rb +1 -1
  16. data/lib/graphql/relay/base_connection.rb +0 -2
  17. data/lib/graphql/schema.rb +17 -11
  18. data/lib/graphql/schema/argument.rb +6 -0
  19. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  20. data/lib/graphql/schema/enum.rb +9 -1
  21. data/lib/graphql/schema/field.rb +30 -21
  22. data/lib/graphql/schema/input_object.rb +10 -9
  23. data/lib/graphql/schema/interface.rb +5 -0
  24. data/lib/graphql/schema/list.rb +2 -1
  25. data/lib/graphql/schema/loader.rb +3 -0
  26. data/lib/graphql/schema/member.rb +1 -0
  27. data/lib/graphql/schema/member/has_arguments.rb +33 -13
  28. data/lib/graphql/schema/member/has_fields.rb +1 -1
  29. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  30. data/lib/graphql/schema/object.rb +9 -2
  31. data/lib/graphql/schema/resolver.rb +1 -1
  32. data/lib/graphql/schema/union.rb +6 -0
  33. data/lib/graphql/schema/warden.rb +7 -1
  34. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
  35. data/lib/graphql/subscriptions/subscription_root.rb +10 -2
  36. data/lib/graphql/tracing.rb +5 -4
  37. data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
  38. data/lib/graphql/tracing/platform_tracing.rb +14 -0
  39. data/lib/graphql/tracing/scout_tracing.rb +11 -0
  40. data/lib/graphql/types/iso_8601_date.rb +2 -2
  41. data/lib/graphql/types/iso_8601_date_time.rb +19 -15
  42. data/lib/graphql/version.rb +1 -1
  43. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dffbc22cc92ab7e74cf26f0a6e916ae1f03014722e350318af8ee367c9eb6f40
4
- data.tar.gz: 8b98748a98c2a7b5d471542989245a16d148b44519c69aa51cec062649357b31
3
+ metadata.gz: 840c0d49f547bfb3a44878e0631df87f3b287179a077806169480a8d2820b30b
4
+ data.tar.gz: 498c331da5d7f3818054def18b1a964f1ff97699b79b7be1635eefbad0dcc946
5
5
  SHA512:
6
- metadata.gz: bf169d1fe41d60ad045e0faae46cfa263d4b5f2bffd56bdd527b940702e9a6e44996d7b8402a6a213a593e68adcd1b885b3ba8d7240b236db1383ae9d9073f6c
7
- data.tar.gz: 24293245691980f79e316977fe24a6dfc6025f25d7d46806d6e46a8cc44ce9d1f84a7217487fd147209d6bef4a4ffdb0d67645fb87506850e420683a745aa90a
6
+ metadata.gz: 0cc7bdbeff9a1c70f28c50ffa93740020173a969d34e9aa8872073f721555ebe1c67fa74e13d012d6c547bc4aa6515ab7dafe173dd75bfdea9fe0c4a2b256ac8
7
+ data.tar.gz: a466d22dd0e14f95b7273faedc68d3ff214e0f72411e491f454d037b1b74034a67b1eb6334cd567334cf5856c1b91e65a38024955426079c317c43eee47557c7
@@ -15,20 +15,62 @@ module Graphql
15
15
  desc "Create a GraphQL::ObjectType with the given name and fields"
16
16
  source_root File.expand_path('../templates', __FILE__)
17
17
 
18
- argument :fields,
19
- type: :array,
20
- default: [],
21
- banner: "name:type name:type ...",
22
- desc: "Fields for this object (type may be expressed as Ruby or GraphQL)"
18
+ argument :custom_fields,
19
+ type: :array,
20
+ default: [],
21
+ banner: "name:type name:type ...",
22
+ desc: "Fields for this object (type may be expressed as Ruby or GraphQL)"
23
23
 
24
24
  class_option :node,
25
- type: :boolean,
26
- default: false,
27
- desc: "Include the Relay Node interface"
25
+ type: :boolean,
26
+ default: false,
27
+ desc: "Include the Relay Node interface"
28
28
 
29
29
  def create_type_file
30
30
  template "object.erb", "#{options[:directory]}/types/#{type_file_name}.rb"
31
31
  end
32
+
33
+ def fields
34
+ columns = []
35
+ columns += klass.columns.map { |c| generate_column_string(c) } if class_exists?
36
+ columns + custom_fields
37
+ end
38
+
39
+ def self.normalize_type_expression(type_expression, mode:, null: true)
40
+ case type_expression
41
+ when "Text"
42
+ ["String", null]
43
+ when "DateTime", "Datetime"
44
+ ["GraphQL::Types::ISO8601DateTime", null]
45
+ when "Date"
46
+ ["GraphQL::Types::ISO8601Date", null]
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def generate_column_string(column)
55
+ name = column.name
56
+ required = column.null ? "" : "!"
57
+ type = column_type_string(column)
58
+ "#{name}:#{required}#{type}"
59
+ end
60
+
61
+ def column_type_string(column)
62
+ column.name == "id" ? "ID" : column.type.to_s.camelize
63
+ end
64
+
65
+ def class_exists?
66
+ klass.is_a?(Class) && klass.ancestors.include?(ActiveRecord::Base)
67
+ rescue NameError
68
+ return false
69
+ end
70
+
71
+ def klass
72
+ @klass ||= Module.const_get(type_name.camelize)
73
+ end
32
74
  end
33
75
  end
34
76
  end
@@ -89,12 +89,15 @@ require "graphql/name_validator"
89
89
  require "graphql/language"
90
90
  require "graphql/analysis"
91
91
  require "graphql/tracing"
92
- require "graphql/execution"
93
92
  require "graphql/dig"
93
+ require "graphql/execution"
94
94
  require "graphql/schema"
95
95
  require "graphql/query"
96
96
  require "graphql/directive"
97
97
  require "graphql/execution"
98
+ require "graphql/runtime_type_error"
99
+ require "graphql/unresolved_type_error"
100
+ require "graphql/invalid_null_error"
98
101
  require "graphql/types"
99
102
  require "graphql/relay"
100
103
  require "graphql/boolean_type"
@@ -109,10 +112,7 @@ require "graphql/introspection"
109
112
 
110
113
  require "graphql/analysis_error"
111
114
  require "graphql/coercion_error"
112
- require "graphql/runtime_type_error"
113
- require "graphql/invalid_null_error"
114
115
  require "graphql/invalid_name_error"
115
- require "graphql/unresolved_type_error"
116
116
  require "graphql/integer_encoding_error"
117
117
  require "graphql/string_encoding_error"
118
118
  require "graphql/internal_representation"
@@ -54,7 +54,7 @@ module GraphQL
54
54
  case defined_complexity
55
55
  when Proc
56
56
  arguments = @query.arguments_for(@node, @field_definition)
57
- defined_complexity.call(@query.context, arguments, child_complexity)
57
+ defined_complexity.call(@query.context, arguments.keyword_arguments, child_complexity)
58
58
  when Numeric
59
59
  defined_complexity + child_complexity
60
60
  else
@@ -43,8 +43,8 @@ module GraphQL
43
43
  @storage = storage
44
44
  end
45
45
 
46
- def each
47
- @storage.each { |i| yield(i) }
46
+ def each(&block)
47
+ @storage.each(&block)
48
48
  end
49
49
  end
50
50
 
@@ -77,7 +77,7 @@ module GraphQL
77
77
  end
78
78
 
79
79
  def call_after_hooks(instrumenters, object, after_hook_name, ex)
80
- instrumenters.reverse.each do |instrumenter|
80
+ instrumenters.reverse_each do |instrumenter|
81
81
  begin
82
82
  instrumenter.public_send(after_hook_name, object)
83
83
  rescue => e
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/execution/interpreter/argument_value"
3
+ require "graphql/execution/interpreter/arguments"
2
4
  require "graphql/execution/interpreter/arguments_cache"
3
5
  require "graphql/execution/interpreter/execution_errors"
4
6
  require "graphql/execution/interpreter/hash_response"
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Execution
5
+ class Interpreter
6
+ # A container for metadata regarding arguments present in a GraphQL query.
7
+ # @see Interpreter::Arguments#argument_values for a hash of these objects.
8
+ class ArgumentValue
9
+ def initialize(definition:, value:, default_used:)
10
+ @definition = definition
11
+ @value = value
12
+ @default_used = default_used
13
+ end
14
+
15
+ # @return [Object] The Ruby-ready value for this Argument
16
+ attr_reader :value
17
+
18
+ # @return [GraphQL::Schema::Argument] The definition instance for this argument
19
+ attr_reader :definition
20
+
21
+ # @return [Boolean] `true` if the schema-defined `default_value:` was applied in this case. (No client-provided value was present.)
22
+ def default_used?
23
+ @default_used
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Execution
5
+ class Interpreter
6
+ # A wrapper for argument hashes in GraphQL queries.
7
+ #
8
+ # @see GraphQL::Query#arguments_for to get access to these objects.
9
+ class Arguments
10
+ extend Forwardable
11
+ include GraphQL::Dig
12
+
13
+ # The Ruby-style arguments hash, ready for a resolver.
14
+ # This hash is the one used at runtime.
15
+ #
16
+ # @return [Hash<Symbol, Object>]
17
+ attr_reader :keyword_arguments
18
+
19
+ def initialize(keyword_arguments:, argument_values:)
20
+ @keyword_arguments = keyword_arguments
21
+ @argument_values = argument_values
22
+ end
23
+
24
+ # @return [Hash{Symbol => ArgumentValue}]
25
+ attr_reader :argument_values
26
+
27
+ def_delegators :@keyword_arguments, :key?, :[], :keys, :each, :values
28
+ def_delegators :@argument_values, :each_value
29
+
30
+ def inspect
31
+ "#<#{self.class} @keyword_arguments=#{keyword_arguments.inspect}>"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -14,13 +14,9 @@ module GraphQL
14
14
  # Then call into the schema to coerce those incoming values
15
15
  args = arg_owner.coerce_arguments(parent_object, args_hash, query.context)
16
16
 
17
- h3[parent_object] = if args.is_a?(GraphQL::Execution::Lazy)
18
- args.then { |resolved_args|
19
- # when this promise is resolved, update the cache with the resolved value
20
- h3[parent_object] = resolved_args
21
- }
22
- else
23
- args
17
+ h3[parent_object] = @query.schema.after_lazy(args) do |resolved_args|
18
+ # when this promise is resolved, update the cache with the resolved value
19
+ h3[parent_object] = resolved_args
24
20
  end
25
21
  end
26
22
  end
@@ -174,9 +174,14 @@ module GraphQL
174
174
  next
175
175
  end
176
176
 
177
- after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |kwarg_arguments|
178
- # It might turn out that making arguments for every field is slow.
179
- # If we have to cache them, we'll need a more subtle approach here.
177
+ after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |resolved_arguments|
178
+ if resolved_arguments.is_a? GraphQL::ExecutionError
179
+ continue_value(next_path, resolved_arguments, field_defn, return_type.non_null?, ast_node)
180
+ next
181
+ end
182
+
183
+ kwarg_arguments = resolved_arguments.keyword_arguments
184
+
180
185
  field_defn.extras.each do |extra|
181
186
  case extra
182
187
  when :ast_node
@@ -194,6 +199,8 @@ module GraphQL
194
199
  ast_nodes: field_ast_nodes,
195
200
  field: field_defn,
196
201
  )
202
+ when :argument_details
203
+ kwarg_arguments[:argument_details] = resolved_arguments
197
204
  else
198
205
  kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
199
206
  end
@@ -247,7 +254,7 @@ module GraphQL
247
254
  def continue_value(path, value, field, is_non_null, ast_node)
248
255
  if value.nil?
249
256
  if is_non_null
250
- err = GraphQL::InvalidNullError.new(field.owner, field, value)
257
+ err = field.owner::InvalidNullError.new(field.owner, field, value)
251
258
  write_invalid_null_in_response(path, err)
252
259
  else
253
260
  write_in_response(path, nil)
@@ -297,18 +304,21 @@ module GraphQL
297
304
  write_in_response(path, r)
298
305
  r
299
306
  when "UNION", "INTERFACE"
300
- resolved_type_or_lazy = resolve_type(type, value, path)
307
+ resolved_type_or_lazy, resolved_value = resolve_type(type, value, path)
308
+ resolved_value ||= value
309
+
301
310
  after_lazy(resolved_type_or_lazy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
302
311
  possible_types = query.possible_types(type)
303
312
 
304
313
  if !possible_types.include?(resolved_type)
305
314
  parent_type = field.owner
306
- type_error = GraphQL::UnresolvedTypeError.new(value, field, parent_type, resolved_type, possible_types)
315
+ err_class = type::UnresolvedTypeError
316
+ type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
307
317
  schema.type_error(type_error, context)
308
318
  write_in_response(path, nil)
309
319
  nil
310
320
  else
311
- continue_field(path, value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
321
+ continue_field(path, resolved_value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
312
322
  end
313
323
  end
314
324
  when "OBJECT"
@@ -369,11 +379,12 @@ module GraphQL
369
379
  end
370
380
  end
371
381
 
372
- def resolve_with_directives(object, ast_node)
373
- run_directive(object, ast_node, 0) { yield }
382
+ def resolve_with_directives(object, ast_node, &block)
383
+ return yield if ast_node.directives.empty?
384
+ run_directive(object, ast_node, 0, &block)
374
385
  end
375
386
 
376
- def run_directive(object, ast_node, idx)
387
+ def run_directive(object, ast_node, idx, &block)
377
388
  dir_node = ast_node.directives[idx]
378
389
  if !dir_node
379
390
  yield
@@ -382,9 +393,9 @@ module GraphQL
382
393
  if !dir_defn.is_a?(Class)
383
394
  dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
384
395
  end
385
- dir_args = arguments(nil, dir_defn, dir_node)
396
+ dir_args = arguments(nil, dir_defn, dir_node).keyword_arguments
386
397
  dir_defn.resolve(object, dir_args, context) do
387
- run_directive(object, ast_node, idx + 1) { yield }
398
+ run_directive(object, ast_node, idx + 1, &block)
388
399
  end
389
400
  end
390
401
  end
@@ -393,7 +404,7 @@ module GraphQL
393
404
  def directives_include?(node, graphql_object, parent_type)
394
405
  node.directives.each do |dir_node|
395
406
  dir_defn = schema.directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
396
- args = arguments(graphql_object, dir_defn, dir_node)
407
+ args = arguments(graphql_object, dir_defn, dir_node).keyword_arguments
397
408
  if !dir_defn.include?(graphql_object, args, context)
398
409
  return false
399
410
  end
@@ -407,7 +418,7 @@ module GraphQL
407
418
  # @param eager [Boolean] Set to `true` for mutation root fields only
408
419
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
409
420
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
410
- def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true)
421
+ def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true, &block)
411
422
  @interpreter_context[:current_object] = owner_object
412
423
  @interpreter_context[:current_arguments] = arguments
413
424
  @interpreter_context[:current_path] = path
@@ -432,11 +443,9 @@ module GraphQL
432
443
  end
433
444
  end
434
445
  rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
435
- yield(err)
436
- end
437
- after_lazy(inner_obj, owner: owner, field: field, path: path, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager) do |really_inner_obj|
438
- yield(really_inner_obj)
446
+ err
439
447
  end
448
+ after_lazy(inner_obj, owner: owner, field: field, path: path, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager, trace: trace, &block)
440
449
  end
441
450
 
442
451
  if eager
@@ -451,7 +460,13 @@ module GraphQL
451
460
  end
452
461
 
453
462
  def arguments(graphql_object, arg_owner, ast_node)
454
- query.arguments_for(ast_node, arg_owner, parent_object: graphql_object)
463
+ # Don't cache arguments if field extras are requested since extras mutate the argument data structure
464
+ if arg_owner.arguments_statically_coercible? && (!arg_owner.is_a?(GraphQL::Schema::Field) || arg_owner.extras.empty?)
465
+ query.arguments_for(ast_node, arg_owner)
466
+ else
467
+ # The arguments must be prepared in the context of the given object
468
+ query.arguments_for(ast_node, arg_owner, parent_object: graphql_object)
469
+ end
455
470
  end
456
471
 
457
472
  def write_invalid_null_in_response(path, invalid_null_error)
@@ -491,23 +506,11 @@ module GraphQL
491
506
  # at previous parts of the response.
492
507
  # This hash matches the response
493
508
  def type_at(path)
494
- t = @types_at_paths
495
- path.each do |part|
496
- t = t[part] || (raise("Invariant: #{part.inspect} not found in #{t}"))
497
- end
498
- t = t[:__type]
499
- t
509
+ @types_at_paths.fetch(path)
500
510
  end
501
511
 
502
512
  def set_type_at_path(path, type)
503
- types = @types_at_paths
504
- path.each do |part|
505
- types = types[part] ||= {}
506
- end
507
- # Use this magic key so that the hash contains:
508
- # - string keys for nested fields
509
- # - :__type for the object type of a selection
510
- types[:__type] ||= type
513
+ @types_at_paths[path] = type
511
514
  nil
512
515
  end
513
516
 
@@ -537,7 +540,7 @@ module GraphQL
537
540
 
538
541
  def resolve_type(type, value, path)
539
542
  trace_payload = { context: context, type: type, object: value, path: path }
540
- resolved_type = query.trace("resolve_type", trace_payload) do
543
+ resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
541
544
  query.resolve_type(type, value)
542
545
  end
543
546
 
@@ -548,7 +551,7 @@ module GraphQL
548
551
  end
549
552
  end
550
553
  else
551
- resolved_type
554
+ [resolved_type, resolved_value]
552
555
  end
553
556
  end
554
557
 
@@ -55,7 +55,9 @@ module GraphQL
55
55
  @arguments
56
56
  else
57
57
  @arguments = if @field
58
- @query.arguments_for(@ast_nodes.first, @field)
58
+ @query.schema.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
59
+ args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
60
+ end
59
61
  else
60
62
  nil
61
63
  end
@@ -66,11 +66,11 @@ module GraphQL
66
66
  # Call the block for each type in `self`.
67
67
  # This uses the simplest possible expression of `self`,
68
68
  # so if this scope is defined by an abstract type, it gets yielded.
69
- def each
69
+ def each(&block)
70
70
  if @abstract_type
71
71
  yield(@type)
72
72
  else
73
- @types.each { |t| yield(t) }
73
+ @types.each(&block)
74
74
  end
75
75
  end
76
76
 
@@ -23,11 +23,11 @@ module GraphQL
23
23
 
24
24
  # Traverse a node in a rewritten query tree,
25
25
  # visiting the node itself and each of its typed children.
26
- def each_node(node)
26
+ def each_node(node, &block)
27
27
  yield(node)
28
28
  node.typed_children.each do |obj_type, children|
29
29
  children.each do |name, node|
30
- each_node(node) { |n| yield(n) }
30
+ each_node(node, &block)
31
31
  end
32
32
  end
33
33
  end