graphql 1.10.6 → 1.10.11

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 (46) 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 +47 -38
  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/language/document_from_schema_definition.rb +42 -23
  16. data/lib/graphql/object_type.rb +1 -1
  17. data/lib/graphql/relay/base_connection.rb +0 -2
  18. data/lib/graphql/schema.rb +28 -13
  19. data/lib/graphql/schema/argument.rb +6 -0
  20. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  21. data/lib/graphql/schema/enum.rb +9 -1
  22. data/lib/graphql/schema/field.rb +30 -21
  23. data/lib/graphql/schema/input_object.rb +9 -6
  24. data/lib/graphql/schema/interface.rb +5 -0
  25. data/lib/graphql/schema/list.rb +7 -1
  26. data/lib/graphql/schema/loader.rb +110 -103
  27. data/lib/graphql/schema/member.rb +1 -0
  28. data/lib/graphql/schema/member/has_arguments.rb +33 -13
  29. data/lib/graphql/schema/member/has_fields.rb +1 -1
  30. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  31. data/lib/graphql/schema/non_null.rb +5 -0
  32. data/lib/graphql/schema/object.rb +10 -3
  33. data/lib/graphql/schema/printer.rb +0 -14
  34. data/lib/graphql/schema/resolver.rb +1 -1
  35. data/lib/graphql/schema/union.rb +6 -0
  36. data/lib/graphql/schema/warden.rb +7 -1
  37. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
  38. data/lib/graphql/subscriptions/subscription_root.rb +10 -2
  39. data/lib/graphql/tracing.rb +5 -4
  40. data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
  41. data/lib/graphql/tracing/platform_tracing.rb +14 -0
  42. data/lib/graphql/tracing/scout_tracing.rb +11 -0
  43. data/lib/graphql/types/iso_8601_date.rb +3 -3
  44. data/lib/graphql/types/iso_8601_date_time.rb +18 -8
  45. data/lib/graphql/version.rb +1 -1
  46. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b006fe34754dbc2e3ee7ef459e05bdf325755538e013efd96c9eef3990af9a7
4
- data.tar.gz: b655b349e59ea3a34d6ed71fb3870af1cc91afc75bf7147cb0d9b436b78a97c8
3
+ metadata.gz: 647d1d9e043e1e409e56f3d38f7139f2462f2590d5f32b93627dffbb46b0dcdf
4
+ data.tar.gz: 1cd86cbb6e6452f5300eadaf51a8e6404a6a5a5cdfcffc883ba76190d4960c75
5
5
  SHA512:
6
- metadata.gz: 9a07c35f18dfdb53924fc9ff8466c3255c7235f855d5ea7380738e4cde90c47c6cd12aaa505a27ee1c3ec3bee84a8a82ed9ec113cbbd362ef152a8b1ec6cdf38
7
- data.tar.gz: 23e61762fd23870cb651334aa84a9e83bb9dda486b114ea8bb2b937eeb77705bac200255b321983caf4f81d6efd0b231a992b618563b86d6446b68585ec429bc
6
+ metadata.gz: 99484e3df661ee34b882bf8e999cbb134574f24d04eb4ed591ecad8f4098ef44b91ba72ae7e856115297cadce8ac13b605df30de5c9b5fd41ba8fbfd5581ac1f
7
+ data.tar.gz: dfc446058e5abbc0575bbc5ad9e07ec7b2f626ff8b0da73f8bb358efb31c6e35776a9950f07b01dc79032aa8178cf0b9d082b454dab4ce3ecb1d06c051aaecf5
@@ -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"
@@ -347,9 +357,15 @@ module GraphQL
347
357
  end
348
358
  end
349
359
  end
350
- rescue NoMethodError
351
- # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
352
- raise ListResultFailedError.new(value: value, field: field, path: path)
360
+ rescue NoMethodError => err
361
+ # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
362
+ if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
363
+ # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
364
+ raise ListResultFailedError.new(value: value, field: field, path: path)
365
+ else
366
+ # This was some other NoMethodError -- let it bubble to reveal the real error.
367
+ raise
368
+ end
353
369
  end
354
370
 
355
371
  response_list
@@ -363,11 +379,12 @@ module GraphQL
363
379
  end
364
380
  end
365
381
 
366
- def resolve_with_directives(object, ast_node)
367
- 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)
368
385
  end
369
386
 
370
- def run_directive(object, ast_node, idx)
387
+ def run_directive(object, ast_node, idx, &block)
371
388
  dir_node = ast_node.directives[idx]
372
389
  if !dir_node
373
390
  yield
@@ -376,9 +393,9 @@ module GraphQL
376
393
  if !dir_defn.is_a?(Class)
377
394
  dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
378
395
  end
379
- dir_args = arguments(nil, dir_defn, dir_node)
396
+ dir_args = arguments(nil, dir_defn, dir_node).keyword_arguments
380
397
  dir_defn.resolve(object, dir_args, context) do
381
- run_directive(object, ast_node, idx + 1) { yield }
398
+ run_directive(object, ast_node, idx + 1, &block)
382
399
  end
383
400
  end
384
401
  end
@@ -387,7 +404,7 @@ module GraphQL
387
404
  def directives_include?(node, graphql_object, parent_type)
388
405
  node.directives.each do |dir_node|
389
406
  dir_defn = schema.directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
390
- args = arguments(graphql_object, dir_defn, dir_node)
407
+ args = arguments(graphql_object, dir_defn, dir_node).keyword_arguments
391
408
  if !dir_defn.include?(graphql_object, args, context)
392
409
  return false
393
410
  end
@@ -401,7 +418,7 @@ module GraphQL
401
418
  # @param eager [Boolean] Set to `true` for mutation root fields only
402
419
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
403
420
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
404
- 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)
405
422
  @interpreter_context[:current_object] = owner_object
406
423
  @interpreter_context[:current_arguments] = arguments
407
424
  @interpreter_context[:current_path] = path
@@ -426,11 +443,9 @@ module GraphQL
426
443
  end
427
444
  end
428
445
  rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
429
- yield(err)
430
- end
431
- 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|
432
- yield(really_inner_obj)
446
+ err
433
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)
434
449
  end
435
450
 
436
451
  if eager
@@ -445,7 +460,13 @@ module GraphQL
445
460
  end
446
461
 
447
462
  def arguments(graphql_object, arg_owner, ast_node)
448
- 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
449
470
  end
450
471
 
451
472
  def write_invalid_null_in_response(path, invalid_null_error)
@@ -485,23 +506,11 @@ module GraphQL
485
506
  # at previous parts of the response.
486
507
  # This hash matches the response
487
508
  def type_at(path)
488
- t = @types_at_paths
489
- path.each do |part|
490
- t = t[part] || (raise("Invariant: #{part.inspect} not found in #{t}"))
491
- end
492
- t = t[:__type]
493
- t
509
+ @types_at_paths.fetch(path)
494
510
  end
495
511
 
496
512
  def set_type_at_path(path, type)
497
- types = @types_at_paths
498
- path.each do |part|
499
- types = types[part] ||= {}
500
- end
501
- # Use this magic key so that the hash contains:
502
- # - string keys for nested fields
503
- # - :__type for the object type of a selection
504
- types[:__type] ||= type
513
+ @types_at_paths[path] = type
505
514
  nil
506
515
  end
507
516
 
@@ -531,7 +540,7 @@ module GraphQL
531
540
 
532
541
  def resolve_type(type, value, path)
533
542
  trace_payload = { context: context, type: type, object: value, path: path }
534
- resolved_type = query.trace("resolve_type", trace_payload) do
543
+ resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
535
544
  query.resolve_type(type, value)
536
545
  end
537
546
 
@@ -542,7 +551,7 @@ module GraphQL
542
551
  end
543
552
  end
544
553
  else
545
- resolved_type
554
+ [resolved_type, resolved_value]
546
555
  end
547
556
  end
548
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