graphql 1.12.16 → 1.13.2

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.

Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -1
  3. data/lib/generators/graphql/install_generator.rb +9 -2
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/object_generator.rb +2 -1
  6. data/lib/generators/graphql/relay.rb +19 -11
  7. data/lib/generators/graphql/templates/schema.erb +14 -2
  8. data/lib/generators/graphql/type_generator.rb +0 -1
  9. data/lib/graphql/analysis/ast/field_usage.rb +3 -3
  10. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  11. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  12. data/lib/graphql/backtrace/table.rb +1 -1
  13. data/lib/graphql/base_type.rb +4 -2
  14. data/lib/graphql/boolean_type.rb +1 -1
  15. data/lib/graphql/dataloader/source.rb +50 -2
  16. data/lib/graphql/dataloader.rb +93 -37
  17. data/lib/graphql/define/instance_definable.rb +1 -1
  18. data/lib/graphql/deprecated_dsl.rb +11 -3
  19. data/lib/graphql/deprecation.rb +1 -5
  20. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  21. data/lib/graphql/directive/include_directive.rb +1 -1
  22. data/lib/graphql/directive/skip_directive.rb +1 -1
  23. data/lib/graphql/directive.rb +0 -4
  24. data/lib/graphql/enum_type.rb +5 -1
  25. data/lib/graphql/execution/errors.rb +1 -0
  26. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  27. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
  28. data/lib/graphql/execution/interpreter/runtime.rb +39 -23
  29. data/lib/graphql/execution/lookahead.rb +2 -2
  30. data/lib/graphql/execution/multiplex.rb +4 -1
  31. data/lib/graphql/float_type.rb +1 -1
  32. data/lib/graphql/id_type.rb +1 -1
  33. data/lib/graphql/int_type.rb +1 -1
  34. data/lib/graphql/integer_encoding_error.rb +18 -2
  35. data/lib/graphql/introspection/directive_type.rb +1 -1
  36. data/lib/graphql/introspection/entry_points.rb +2 -2
  37. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  38. data/lib/graphql/introspection/field_type.rb +2 -2
  39. data/lib/graphql/introspection/input_value_type.rb +10 -4
  40. data/lib/graphql/introspection/schema_type.rb +2 -2
  41. data/lib/graphql/introspection/type_type.rb +10 -10
  42. data/lib/graphql/language/block_string.rb +2 -6
  43. data/lib/graphql/language/document_from_schema_definition.rb +4 -2
  44. data/lib/graphql/language/lexer.rb +0 -3
  45. data/lib/graphql/language/lexer.rl +0 -4
  46. data/lib/graphql/language/nodes.rb +12 -2
  47. data/lib/graphql/language/parser.rb +442 -434
  48. data/lib/graphql/language/parser.y +5 -4
  49. data/lib/graphql/language/printer.rb +6 -1
  50. data/lib/graphql/language/sanitized_printer.rb +5 -5
  51. data/lib/graphql/language/token.rb +0 -4
  52. data/lib/graphql/name_validator.rb +0 -4
  53. data/lib/graphql/pagination/connections.rb +35 -16
  54. data/lib/graphql/query/arguments.rb +1 -1
  55. data/lib/graphql/query/arguments_cache.rb +1 -1
  56. data/lib/graphql/query/context.rb +15 -2
  57. data/lib/graphql/query/literal_input.rb +1 -1
  58. data/lib/graphql/query/null_context.rb +12 -7
  59. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  60. data/lib/graphql/query/validation_pipeline.rb +1 -1
  61. data/lib/graphql/query/variables.rb +5 -1
  62. data/lib/graphql/query.rb +4 -0
  63. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  64. data/lib/graphql/relay/global_id_resolve.rb +1 -1
  65. data/lib/graphql/relay/page_info.rb +1 -1
  66. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  67. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  68. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  69. data/lib/graphql/rubocop.rb +4 -0
  70. data/lib/graphql/schema/addition.rb +37 -28
  71. data/lib/graphql/schema/argument.rb +79 -34
  72. data/lib/graphql/schema/build_from_definition.rb +5 -5
  73. data/lib/graphql/schema/directive/feature.rb +1 -1
  74. data/lib/graphql/schema/directive/flagged.rb +2 -2
  75. data/lib/graphql/schema/directive/include.rb +1 -1
  76. data/lib/graphql/schema/directive/skip.rb +1 -1
  77. data/lib/graphql/schema/directive/transform.rb +1 -1
  78. data/lib/graphql/schema/directive.rb +7 -3
  79. data/lib/graphql/schema/enum.rb +60 -10
  80. data/lib/graphql/schema/enum_value.rb +6 -0
  81. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  82. data/lib/graphql/schema/field.rb +140 -42
  83. data/lib/graphql/schema/field_extension.rb +52 -2
  84. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  85. data/lib/graphql/schema/finder.rb +5 -5
  86. data/lib/graphql/schema/input_object.rb +13 -14
  87. data/lib/graphql/schema/interface.rb +11 -20
  88. data/lib/graphql/schema/introspection_system.rb +1 -1
  89. data/lib/graphql/schema/list.rb +3 -1
  90. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  91. data/lib/graphql/schema/member/build_type.rb +0 -4
  92. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  93. data/lib/graphql/schema/member/has_arguments.rb +145 -57
  94. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  95. data/lib/graphql/schema/member/has_fields.rb +76 -18
  96. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  97. data/lib/graphql/schema/member.rb +1 -0
  98. data/lib/graphql/schema/non_null.rb +3 -1
  99. data/lib/graphql/schema/object.rb +10 -75
  100. data/lib/graphql/schema/printer.rb +1 -1
  101. data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
  102. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  103. data/lib/graphql/schema/resolver.rb +49 -64
  104. data/lib/graphql/schema/scalar.rb +2 -0
  105. data/lib/graphql/schema/subscription.rb +17 -9
  106. data/lib/graphql/schema/traversal.rb +1 -1
  107. data/lib/graphql/schema/type_expression.rb +1 -1
  108. data/lib/graphql/schema/type_membership.rb +18 -4
  109. data/lib/graphql/schema/union.rb +8 -1
  110. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  111. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  112. data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
  113. data/lib/graphql/schema/validator/format_validator.rb +4 -5
  114. data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
  115. data/lib/graphql/schema/validator/length_validator.rb +5 -3
  116. data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
  117. data/lib/graphql/schema/validator.rb +33 -25
  118. data/lib/graphql/schema/warden.rb +116 -52
  119. data/lib/graphql/schema.rb +124 -27
  120. data/lib/graphql/static_validation/base_visitor.rb +8 -5
  121. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  122. data/lib/graphql/static_validation/error.rb +3 -1
  123. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  124. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  125. data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
  126. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  127. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  128. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
  129. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  130. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
  131. data/lib/graphql/static_validation/validation_context.rb +8 -2
  132. data/lib/graphql/static_validation/validator.rb +15 -12
  133. data/lib/graphql/string_encoding_error.rb +13 -3
  134. data/lib/graphql/string_type.rb +1 -1
  135. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +15 -5
  136. data/lib/graphql/subscriptions/event.rb +66 -13
  137. data/lib/graphql/subscriptions/serialize.rb +1 -1
  138. data/lib/graphql/subscriptions.rb +17 -19
  139. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  140. data/lib/graphql/types/int.rb +1 -1
  141. data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
  142. data/lib/graphql/types/relay/default_relay.rb +5 -1
  143. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  144. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  145. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  146. data/lib/graphql/types/string.rb +1 -1
  147. data/lib/graphql/unauthorized_error.rb +1 -1
  148. data/lib/graphql/version.rb +1 -1
  149. data/lib/graphql.rb +10 -32
  150. data/readme.md +1 -1
  151. metadata +13 -6
@@ -5,10 +5,6 @@ require "graphql/schema/field/scope_extension"
5
5
  module GraphQL
6
6
  class Schema
7
7
  class Field
8
- if !String.method_defined?(:-@)
9
- using GraphQL::StringDedupBackport
10
- end
11
-
12
8
  include GraphQL::Schema::Member::CachedGraphQLDefinition
13
9
  include GraphQL::Schema::Member::AcceptsDefinition
14
10
  include GraphQL::Schema::Member::HasArguments
@@ -61,7 +57,7 @@ module GraphQL
61
57
  end
62
58
 
63
59
  def inspect
64
- "#<#{self.class} #{path}#{arguments.any? ? "(...)" : ""}: #{type.to_type_signature}>"
60
+ "#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
65
61
  end
66
62
 
67
63
  alias :mutation :resolver
@@ -122,6 +118,9 @@ module GraphQL
122
118
  else
123
119
  kwargs[:type] = type
124
120
  end
121
+ if type.is_a?(Class) && type < GraphQL::Schema::Mutation
122
+ raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
123
+ end
125
124
  end
126
125
  new(**kwargs, &block)
127
126
  end
@@ -209,7 +208,7 @@ module GraphQL
209
208
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
210
209
  # @param validates [Array<Hash>] Configurations for validating this field
211
210
  # @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
212
- def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
211
+ def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
213
212
  if name.nil?
214
213
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
215
214
  end
@@ -217,9 +216,6 @@ module GraphQL
217
216
  if type.nil?
218
217
  raise ArgumentError, "missing second `type` argument or keyword `type:`"
219
218
  end
220
- if null.nil?
221
- raise ArgumentError, "missing keyword argument null:"
222
- end
223
219
  end
224
220
  if (field || function || resolve) && extras.any?
225
221
  raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
@@ -278,10 +274,15 @@ module GraphQL
278
274
  @legacy_edge_class = legacy_edge_class
279
275
 
280
276
  arguments.each do |name, arg|
281
- if arg.is_a?(Hash)
277
+ case arg
278
+ when Hash
282
279
  argument(name: name, **arg)
283
- else
280
+ when GraphQL::Schema::Argument
284
281
  add_argument(arg)
282
+ when Array
283
+ arg.each { |a| add_argument(a) }
284
+ else
285
+ raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
285
286
  end
286
287
  end
287
288
 
@@ -289,6 +290,7 @@ module GraphQL
289
290
  @subscription_scope = subscription_scope
290
291
 
291
292
  @extensions = EMPTY_ARRAY
293
+ @call_after_define = false
292
294
  # This should run before connection extension,
293
295
  # but should it run after the definition block?
294
296
  if scoped?
@@ -321,6 +323,9 @@ module GraphQL
321
323
  instance_eval(&definition_block)
322
324
  end
323
325
  end
326
+
327
+ self.extensions.each(&:after_define_apply)
328
+ @call_after_define = true
324
329
  end
325
330
 
326
331
  # If true, subscription updates with this field can be shared between viewers
@@ -353,27 +358,20 @@ module GraphQL
353
358
  # @example adding an extension with options
354
359
  # extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
355
360
  #
356
- # @param extensions [Array<Class, Hash<Class => Object>>] Add extensions to this field. For hash elements, only the first key/value is used.
361
+ # @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
357
362
  # @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
358
363
  def extensions(new_extensions = nil)
359
- if new_extensions.nil?
360
- # Read the value
361
- @extensions
362
- else
363
- if @extensions.frozen?
364
- @extensions = @extensions.dup
365
- end
366
- new_extensions.each do |extension|
367
- if extension.is_a?(Hash)
368
- extension = extension.to_a[0]
369
- extension_class, options = *extension
370
- @extensions << extension_class.new(field: self, options: options)
364
+ if new_extensions
365
+ new_extensions.each do |extension_config|
366
+ if extension_config.is_a?(Hash)
367
+ extension_class, options = *extension_config.to_a[0]
368
+ self.extension(extension_class, options)
371
369
  else
372
- extension_class = extension
373
- @extensions << extension_class.new(field: self, options: nil)
370
+ self.extension(extension_config)
374
371
  end
375
372
  end
376
373
  end
374
+ @extensions
377
375
  end
378
376
 
379
377
  # Add `extension` to this field, initialized with `options` if provided.
@@ -384,10 +382,19 @@ module GraphQL
384
382
  # @example adding an extension with options
385
383
  # extension(MyExtensionClass, filter: true)
386
384
  #
387
- # @param extension [Class] subclass of {Schema::Fieldextension}
388
- # @param options [Object] if provided, given as `options:` when initializing `extension`.
389
- def extension(extension, options = nil)
390
- extensions([{extension => options}])
385
+ # @param extension_class [Class] subclass of {Schema::FieldExtension}
386
+ # @param options [Hash] if provided, given as `options:` when initializing `extension`.
387
+ # @return [void]
388
+ def extension(extension_class, options = nil)
389
+ extension_inst = extension_class.new(field: self, options: options)
390
+ if @extensions.frozen?
391
+ @extensions = @extensions.dup
392
+ end
393
+ if @call_after_define
394
+ extension_inst.after_define_apply
395
+ end
396
+ @extensions << extension_inst
397
+ nil
391
398
  end
392
399
 
393
400
  # Read extras (as symbols) from this field,
@@ -408,6 +415,62 @@ module GraphQL
408
415
  end
409
416
  end
410
417
 
418
+ def calculate_complexity(query:, nodes:, child_complexity:)
419
+ if respond_to?(:complexity_for)
420
+ lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
421
+ complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead)
422
+ elsif connection?
423
+ arguments = query.arguments_for(nodes.first, self)
424
+ max_possible_page_size = nil
425
+ if arguments[:first]
426
+ max_possible_page_size = arguments[:first]
427
+ end
428
+ if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
429
+ max_possible_page_size = arguments[:last]
430
+ end
431
+
432
+ if max_possible_page_size.nil?
433
+ max_possible_page_size = max_page_size || query.schema.default_max_page_size
434
+ end
435
+
436
+ if max_possible_page_size.nil?
437
+ raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
438
+ else
439
+ metadata_complexity = 0
440
+ lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
441
+
442
+ if (page_info_lookahead = lookahead.selection(:page_info)).selected?
443
+ metadata_complexity += 1 # pageInfo
444
+ metadata_complexity += page_info_lookahead.selections.size # subfields
445
+ end
446
+
447
+ if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
448
+ metadata_complexity += 1
449
+ end
450
+
451
+ nodes_edges_complexity = 0
452
+ nodes_edges_complexity += 1 if lookahead.selects?(:edges)
453
+ nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
454
+
455
+ # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
456
+ items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
457
+ # Add 1 for _this_ field
458
+ 1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
459
+ end
460
+ else
461
+ defined_complexity = complexity
462
+ case defined_complexity
463
+ when Proc
464
+ arguments = query.arguments_for(nodes.first, self)
465
+ defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
466
+ when Numeric
467
+ defined_complexity + child_complexity
468
+ else
469
+ raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
470
+ end
471
+ end
472
+ end
473
+
411
474
  def complexity(new_complexity = nil)
412
475
  case new_complexity
413
476
  when Proc
@@ -436,6 +499,8 @@ module GraphQL
436
499
  # @return [Integer, nil] Applied to connections if {#has_max_page_size?}
437
500
  attr_reader :max_page_size
438
501
 
502
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
503
+
439
504
  # @return [GraphQL::Field]
440
505
  def to_graphql
441
506
  field_defn = if @field
@@ -490,9 +555,9 @@ module GraphQL
490
555
  field_defn.subscription_scope = @subscription_scope
491
556
  field_defn.ast_node = ast_node
492
557
 
493
- arguments.each do |name, defn|
494
- arg_graphql = defn.to_graphql
495
- field_defn.arguments[arg_graphql.name] = arg_graphql
558
+ all_argument_definitions.each do |defn|
559
+ arg_graphql = defn.deprecated_to_graphql
560
+ field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
496
561
  end
497
562
 
498
563
  # Support a passed-in proc, one way or another
@@ -510,6 +575,7 @@ module GraphQL
510
575
  field_defn
511
576
  end
512
577
 
578
+ class MissingReturnTypeError < GraphQL::Error; end
513
579
  attr_writer :type
514
580
 
515
581
  def type
@@ -517,14 +583,21 @@ module GraphQL
517
583
  Member::BuildType.parse_type(@function.type, null: false)
518
584
  elsif @field
519
585
  Member::BuildType.parse_type(@field.type, null: false)
586
+ elsif @return_type_expr.nil?
587
+ # Not enough info to determine type
588
+ message = "Can't determine the return type for #{self.path}"
589
+ if @resolver_class
590
+ message += " (it has `resolver: #{@resolver_class}`, consider configuration a `type ...` for that class)"
591
+ end
592
+ raise MissingReturnTypeError, message
520
593
  else
521
594
  Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
522
595
  end
523
- rescue GraphQL::Schema::InvalidDocumentError => err
596
+ rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
524
597
  # Let this propagate up
525
598
  raise err
526
599
  rescue StandardError => err
527
- raise ArgumentError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
600
+ raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
528
601
  end
529
602
 
530
603
  def visible?(context)
@@ -545,13 +618,39 @@ module GraphQL
545
618
 
546
619
  def authorized?(object, args, context)
547
620
  if @resolver_class
548
- # The resolver will check itself during `resolve()`
621
+ # The resolver _instance_ will check itself during `resolve()`
549
622
  @resolver_class.authorized?(object, context)
550
623
  else
624
+ if (arg_values = context[:current_arguments])
625
+ # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
626
+ using_arg_values = true
627
+ arg_values = arg_values.argument_values
628
+ else
629
+ arg_values = args
630
+ using_arg_values = false
631
+ end
551
632
  # Faster than `.any?`
552
- arguments.each_value do |arg|
553
- if args.key?(arg.keyword) && !arg.authorized?(object, args[arg.keyword], context)
554
- return false
633
+ arguments(context).each_value do |arg|
634
+ arg_key = arg.keyword
635
+ if arg_values.key?(arg_key)
636
+ arg_value = arg_values[arg_key]
637
+ if using_arg_values
638
+ if arg_value.default_used?
639
+ # pass -- no auth required for default used
640
+ next
641
+ else
642
+ application_arg_value = arg_value.value
643
+ if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
644
+ application_arg_value.keyword_arguments
645
+ end
646
+ end
647
+ else
648
+ application_arg_value = arg_value
649
+ end
650
+
651
+ if !arg.authorized?(object, application_arg_value, context)
652
+ return false
653
+ end
555
654
  end
556
655
  end
557
656
  true
@@ -608,8 +707,7 @@ module GraphQL
608
707
  if is_authorized
609
708
  public_send_field(object, args, ctx)
610
709
  else
611
- err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
612
- ctx.schema.unauthorized_field(err)
710
+ raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
613
711
  end
614
712
  end
615
713
  rescue GraphQL::UnauthorizedFieldError => err
@@ -649,7 +747,7 @@ module GraphQL
649
747
  ruby_kwargs = graphql_args.to_kwargs
650
748
  maybe_lazies = []
651
749
  # Apply any `prepare` methods. Not great code organization, can this go somewhere better?
652
- arguments.each do |name, arg_defn|
750
+ arguments(field_ctx).each do |name, arg_defn|
653
751
  ruby_kwargs_key = arg_defn.keyword
654
752
 
655
753
  if ruby_kwargs.key?(ruby_kwargs_key)
@@ -15,15 +15,40 @@ module GraphQL
15
15
  # @return [Object]
16
16
  attr_reader :options
17
17
 
18
+ # @return [Array<Symbol>, nil] `default_argument`s added, if any were added (otherwise, `nil`)
19
+ attr_reader :added_default_arguments
20
+
18
21
  # Called when the extension is mounted with `extension(name, options)`.
19
- # The instance is frozen to avoid improper use of state during execution.
22
+ # The instance will be frozen to avoid improper use of state during execution.
20
23
  # @param field [GraphQL::Schema::Field] The field where this extension was mounted
21
24
  # @param options [Object] The second argument to `extension`, or `{}` if nothing was passed.
22
25
  def initialize(field:, options:)
23
26
  @field = field
24
27
  @options = options || {}
28
+ @added_default_arguments = nil
25
29
  apply
26
- freeze
30
+ end
31
+
32
+ class << self
33
+ # @return [Array(Array, Hash), nil] A list of default argument configs, or `nil` if there aren't any
34
+ def default_argument_configurations
35
+ args = superclass.respond_to?(:default_argument_configurations) ? superclass.default_argument_configurations : nil
36
+ if @own_default_argument_configurations
37
+ if args
38
+ args.concat(@own_default_argument_configurations)
39
+ else
40
+ args = @own_default_argument_configurations.dup
41
+ end
42
+ end
43
+ args
44
+ end
45
+
46
+ # @see Argument#initialize
47
+ # @see HasArguments#argument
48
+ def default_argument(*argument_args, **argument_kwargs)
49
+ configs = @own_default_argument_configurations ||= []
50
+ configs << [argument_args, argument_kwargs]
51
+ end
27
52
  end
28
53
 
29
54
  # Called when this extension is attached to a field.
@@ -32,6 +57,31 @@ module GraphQL
32
57
  def apply
33
58
  end
34
59
 
60
+ # Called after the field's definition block has been executed.
61
+ # (Any arguments from the block are present on `field`)
62
+ # @return [void]
63
+ def after_define
64
+ end
65
+
66
+ # @api private
67
+ def after_define_apply
68
+ after_define
69
+ if (configs = self.class.default_argument_configurations)
70
+ existing_keywords = field.all_argument_definitions.map(&:keyword)
71
+ existing_keywords.uniq!
72
+ @added_default_arguments = []
73
+ configs.each do |config|
74
+ argument_args, argument_kwargs = config
75
+ arg_name = argument_args[0]
76
+ if !existing_keywords.include?(arg_name)
77
+ @added_default_arguments << arg_name
78
+ field.argument(*argument_args, **argument_kwargs)
79
+ end
80
+ end
81
+ end
82
+ freeze
83
+ end
84
+
35
85
  # Called before resolving {#field}. It should either:
36
86
  #
37
87
  # - `yield` values to continue execution; OR
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Schema
3
4
  module FindInheritedValue
@@ -38,7 +38,7 @@ module GraphQL
38
38
 
39
39
  find_in_directive(directive, path: path)
40
40
  else
41
- type = schema.get_type(type_or_directive)
41
+ type = schema.get_type(type_or_directive) # rubocop:disable Development/ContextIsPassedCop -- build-time
42
42
 
43
43
  if type.nil?
44
44
  raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
@@ -56,7 +56,7 @@ module GraphQL
56
56
 
57
57
  def find_in_directive(directive, path:)
58
58
  argument_name = path.shift
59
- argument = directive.arguments[argument_name]
59
+ argument = directive.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
60
60
 
61
61
  if argument.nil?
62
62
  raise MemberNotFoundError, "Could not find argument `#{argument_name}` on directive #{directive}."
@@ -102,7 +102,7 @@ module GraphQL
102
102
 
103
103
  def find_in_field(field, path:)
104
104
  argument_name = path.shift
105
- argument = field.arguments[argument_name]
105
+ argument = field.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
106
106
 
107
107
  if argument.nil?
108
108
  raise MemberNotFoundError, "Could not find argument `#{argument_name}` on field `#{field.name}`."
@@ -119,7 +119,7 @@ module GraphQL
119
119
 
120
120
  def find_in_input_object(input_object, path:)
121
121
  field_name = path.shift
122
- input_field = input_object.arguments[field_name]
122
+ input_field = input_object.get_argument(field_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
123
123
 
124
124
  if input_field.nil?
125
125
  raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
@@ -136,7 +136,7 @@ module GraphQL
136
136
 
137
137
  def find_in_enum_type(enum_type, path:)
138
138
  value_name = path.shift
139
- enum_value = enum_type.values[value_name]
139
+ enum_value = enum_type.enum_values.find { |v| v.graphql_name == value_name } # rubocop:disable Development/ContextIsPassedCop -- build-time, not runtime
140
140
 
141
141
  if enum_value.nil?
142
142
  raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
@@ -31,7 +31,7 @@ module GraphQL
31
31
  end
32
32
  # Apply prepares, not great to have it duplicated here.
33
33
  maybe_lazies = []
34
- self.class.arguments.each_value do |arg_defn|
34
+ self.class.arguments(context).each_value do |arg_defn|
35
35
  ruby_kwargs_key = arg_defn.keyword
36
36
 
37
37
  if @ruby_style_hash.key?(ruby_kwargs_key)
@@ -40,11 +40,7 @@ module GraphQL
40
40
  # With the interpreter, it's done during `coerce_arguments`
41
41
  if loads && !arg_defn.from_resolver? && !context.interpreter?
42
42
  value = @ruby_style_hash[ruby_kwargs_key]
43
- loaded_value = if arg_defn.type.list?
44
- value.map { |val| load_application_object(arg_defn, loads, val, context) }
45
- else
46
- load_application_object(arg_defn, loads, value, context)
47
- end
43
+ loaded_value = arg_defn.load_and_authorize_value(self, value, context)
48
44
  maybe_lazies << context.schema.after_lazy(loaded_value) do |loaded_value|
49
45
  overwrite_argument(ruby_kwargs_key, loaded_value)
50
46
  end
@@ -71,11 +67,11 @@ module GraphQL
71
67
  end
72
68
 
73
69
  def prepare
74
- if context
75
- context.schema.after_any_lazies(@maybe_lazies) do
76
- object = context[:current_object]
70
+ if @context
71
+ @context.schema.after_any_lazies(@maybe_lazies) do
72
+ object = @context[:current_object]
77
73
  # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
78
- Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
74
+ Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
79
75
  self
80
76
  end
81
77
  else
@@ -133,8 +129,11 @@ module GraphQL
133
129
  self[#{method_name.inspect}]
134
130
  end
135
131
  RUBY
132
+ argument_defn
136
133
  end
137
134
 
135
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
136
+
138
137
  def to_graphql
139
138
  type_defn = GraphQL::InputObjectType.new
140
139
  type_defn.name = graphql_name
@@ -142,8 +141,8 @@ module GraphQL
142
141
  type_defn.metadata[:type_class] = self
143
142
  type_defn.mutation = mutation
144
143
  type_defn.ast_node = ast_node
145
- arguments.each do |name, arg|
146
- type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
144
+ all_argument_definitions.each do |arg|
145
+ type_defn.arguments[arg.graphql_definition(silence_deprecation_warning: true).name] = arg.graphql_definition(silence_deprecation_warning: true) # rubocop:disable Development/ContextIsPassedCop -- legacy-related
147
146
  end
148
147
  # Make a reference to a classic-style Arguments class
149
148
  self.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(type_defn)
@@ -176,7 +175,7 @@ module GraphQL
176
175
  end
177
176
 
178
177
  # Inject missing required arguments
179
- missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
178
+ missing_required_inputs = self.arguments(ctx).reduce({}) do |m, (argument_name, argument)|
180
179
  if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
181
180
  m[argument_name] = nil
182
181
  end
@@ -227,7 +226,7 @@ module GraphQL
227
226
 
228
227
  result = {}
229
228
 
230
- arguments.each do |input_key, input_field_defn|
229
+ arguments(ctx).each do |input_key, input_field_defn|
231
230
  input_value = value[input_key]
232
231
  if value.key?(input_key)
233
232
  result[input_key] = if input_value.nil?
@@ -16,6 +16,7 @@ module GraphQL
16
16
  include GraphQL::Schema::Member::HasAstNode
17
17
  include GraphQL::Schema::Member::HasUnresolvedTypeError
18
18
  include GraphQL::Schema::Member::HasDirectives
19
+ include GraphQL::Schema::Member::HasInterfaces
19
20
 
20
21
  # Methods defined in this block will be:
21
22
  # - Added as class methods to this interface
@@ -57,9 +58,10 @@ module GraphQL
57
58
  child_class.extend(Schema::Interface::DefinitionMethods)
58
59
 
59
60
  child_class.type_membership_class(self.type_membership_class)
60
- child_class.own_interfaces << self
61
- child_class.interfaces.reverse_each do |interface_defn|
62
- child_class.extend(interface_defn::DefinitionMethods)
61
+ child_class.ancestors.reverse_each do |ancestor|
62
+ if ancestor.const_defined?(:DefinitionMethods)
63
+ child_class.extend(ancestor::DefinitionMethods)
64
+ end
63
65
  end
64
66
 
65
67
  # Use an instance variable to tell whether it's been included previously or not;
@@ -73,16 +75,13 @@ module GraphQL
73
75
  end
74
76
  child_class.introspection(introspection)
75
77
  child_class.description(description)
76
- if overridden_graphql_name
77
- child_class.graphql_name(overridden_graphql_name)
78
- end
79
78
  # If interfaces are mixed into each other, only define this class once
80
79
  if !child_class.const_defined?(:UnresolvedTypeError, false)
81
80
  add_unresolved_type_error(child_class)
82
81
  end
83
82
  elsif child_class < GraphQL::Schema::Object
84
83
  # This is being included into an object type, make sure it's using `implements(...)`
85
- backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
84
+ backtrace_line = caller(0, 10).find { |line| line.include?("schema/member/has_interfaces.rb") && line.include?("in `implements'")}
86
85
  if !backtrace_line
87
86
  raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
88
87
  end
@@ -101,6 +100,8 @@ module GraphQL
101
100
  end
102
101
  end
103
102
 
103
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
104
+
104
105
  def to_graphql
105
106
  type_defn = GraphQL::InterfaceType.new
106
107
  type_defn.name = graphql_name
@@ -108,9 +109,9 @@ module GraphQL
108
109
  type_defn.orphan_types = orphan_types
109
110
  type_defn.type_membership_class = self.type_membership_class
110
111
  type_defn.ast_node = ast_node
111
- fields.each do |field_name, field_inst|
112
- field_defn = field_inst.graphql_definition
113
- type_defn.fields[field_defn.name] = field_defn
112
+ fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
113
+ field_defn = field_inst.graphql_definition(silence_deprecation_warning: true)
114
+ type_defn.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
114
115
  end
115
116
  type_defn.metadata[:type_class] = self
116
117
  if respond_to?(:resolve_type)
@@ -122,16 +123,6 @@ module GraphQL
122
123
  def kind
123
124
  GraphQL::TypeKinds::INTERFACE
124
125
  end
125
-
126
- protected
127
-
128
- def own_interfaces
129
- @own_interfaces ||= []
130
- end
131
-
132
- def interfaces
133
- own_interfaces + (own_interfaces.map { |i| i.own_interfaces }).flatten
134
- end
135
126
  end
136
127
 
137
128
  # Extend this _after_ `DefinitionMethods` is defined, so it will be used
@@ -107,7 +107,7 @@ module GraphQL
107
107
  dup_type_class(const)
108
108
  else
109
109
  # Use `.to_graphql` to get a freshly-made version, not shared between schemas
110
- const.to_graphql
110
+ const.deprecated_to_graphql
111
111
  end
112
112
  rescue NameError
113
113
  # Dup the built-in so that the cached fields aren't shared
@@ -8,8 +8,10 @@ module GraphQL
8
8
  class List < GraphQL::Schema::Wrapper
9
9
  include Schema::Member::ValidatesInput
10
10
 
11
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
12
+
11
13
  def to_graphql
12
- @of_type.graphql_definition.to_list_type
14
+ @of_type.graphql_definition(silence_deprecation_warning: true).to_list_type
13
15
  end
14
16
 
15
17
  # @return [GraphQL::TypeKinds::LIST]
@@ -85,8 +85,15 @@ module GraphQL
85
85
  define_method(name) do |*args|
86
86
  if args.any?
87
87
  instance_variable_set(ivar_name, args)
88
+ else
89
+ if (v = instance_variable_get(ivar_name))
90
+ v
91
+ elsif (ancestor = ancestors.find { |i| i.respond_to?(name) && i != self })
92
+ ancestor.public_send(name)
93
+ else
94
+ nil
95
+ end
88
96
  end
89
- instance_variable_get(ivar_name) || ((int = interfaces.first { |i| i.respond_to?()}) && int.public_send(name))
90
97
  end
91
98
  end
92
99
  end
@@ -116,8 +123,13 @@ module GraphQL
116
123
  end
117
124
 
118
125
  module ToGraphQLExtension
119
- def to_graphql
120
- defn = super
126
+ def to_graphql(*args, **kwargs)
127
+
128
+ defn = if args.empty? && kwargs.empty?
129
+ super()
130
+ else
131
+ super
132
+ end
121
133
  accepts_definition_methods.each do |method_name|
122
134
  value = public_send(method_name)
123
135
  if !value.nil?
@@ -4,10 +4,6 @@ module GraphQL
4
4
  class Member
5
5
  # @api private
6
6
  module BuildType
7
- if !String.method_defined?(:match?)
8
- using GraphQL::StringMatchBackport
9
- end
10
-
11
7
  LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
12
8
 
13
9
  module_function