graphql 1.10.7 → 1.10.12

Sign up to get free protection for your applications and to get access to all the features.
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