graphql 2.4.3 → 2.4.13

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 (141) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +2 -1
  3. data/lib/graphql/analysis/visitor.rb +38 -41
  4. data/lib/graphql/analysis.rb +15 -12
  5. data/lib/graphql/autoload.rb +38 -0
  6. data/lib/graphql/backtrace/table.rb +95 -55
  7. data/lib/graphql/backtrace.rb +1 -19
  8. data/lib/graphql/current.rb +6 -1
  9. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  10. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  11. data/lib/graphql/dashboard/statics/dashboard.css +3 -0
  12. data/lib/graphql/dashboard/statics/dashboard.js +78 -0
  13. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  14. data/lib/graphql/dashboard/statics/icon.png +0 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  16. data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +63 -0
  17. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +60 -0
  18. data/lib/graphql/dashboard.rb +142 -0
  19. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  20. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  21. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  22. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  23. data/lib/graphql/dataloader/source.rb +3 -3
  24. data/lib/graphql/dataloader.rb +43 -14
  25. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  26. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
  27. data/lib/graphql/execution/interpreter/runtime.rb +67 -40
  28. data/lib/graphql/execution/interpreter.rb +16 -6
  29. data/lib/graphql/execution/multiplex.rb +0 -4
  30. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  31. data/lib/graphql/invalid_name_error.rb +1 -1
  32. data/lib/graphql/invalid_null_error.rb +5 -15
  33. data/lib/graphql/language/cache.rb +13 -0
  34. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  35. data/lib/graphql/language/lexer.rb +11 -4
  36. data/lib/graphql/language/nodes.rb +3 -0
  37. data/lib/graphql/language/parser.rb +2 -2
  38. data/lib/graphql/language/printer.rb +8 -8
  39. data/lib/graphql/language/static_visitor.rb +37 -33
  40. data/lib/graphql/language/visitor.rb +59 -55
  41. data/lib/graphql/pagination/connection.rb +1 -1
  42. data/lib/graphql/query/context/scoped_context.rb +1 -1
  43. data/lib/graphql/query/context.rb +6 -5
  44. data/lib/graphql/query/variable_validation_error.rb +1 -1
  45. data/lib/graphql/query.rb +20 -22
  46. data/lib/graphql/railtie.rb +7 -0
  47. data/lib/graphql/schema/addition.rb +1 -1
  48. data/lib/graphql/schema/argument.rb +3 -5
  49. data/lib/graphql/schema/build_from_definition.rb +8 -7
  50. data/lib/graphql/schema/directive/flagged.rb +1 -1
  51. data/lib/graphql/schema/directive.rb +2 -2
  52. data/lib/graphql/schema/enum.rb +36 -1
  53. data/lib/graphql/schema/enum_value.rb +1 -1
  54. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  55. data/lib/graphql/schema/field.rb +12 -12
  56. data/lib/graphql/schema/field_extension.rb +1 -1
  57. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  58. data/lib/graphql/schema/input_object.rb +70 -34
  59. data/lib/graphql/schema/interface.rb +3 -2
  60. data/lib/graphql/schema/loader.rb +1 -1
  61. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  62. data/lib/graphql/schema/member/has_dataloader.rb +60 -0
  63. data/lib/graphql/schema/member/has_directives.rb +4 -4
  64. data/lib/graphql/schema/member/has_fields.rb +19 -1
  65. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  66. data/lib/graphql/schema/member/has_validators.rb +1 -1
  67. data/lib/graphql/schema/member/scoped.rb +1 -1
  68. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  69. data/lib/graphql/schema/member.rb +1 -0
  70. data/lib/graphql/schema/object.rb +25 -8
  71. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  72. data/lib/graphql/schema/resolver.rb +11 -10
  73. data/lib/graphql/schema/subscription.rb +52 -6
  74. data/lib/graphql/schema/union.rb +1 -1
  75. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  76. data/lib/graphql/schema/validator.rb +1 -1
  77. data/lib/graphql/schema/visibility/migration.rb +1 -0
  78. data/lib/graphql/schema/visibility/profile.rb +69 -237
  79. data/lib/graphql/schema/visibility/visit.rb +190 -0
  80. data/lib/graphql/schema/visibility.rb +169 -28
  81. data/lib/graphql/schema/warden.rb +18 -5
  82. data/lib/graphql/schema.rb +90 -43
  83. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  84. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  85. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  86. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  87. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  88. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  89. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  90. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  91. data/lib/graphql/static_validation/validation_context.rb +1 -0
  92. data/lib/graphql/static_validation/validator.rb +6 -1
  93. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  94. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  95. data/lib/graphql/subscriptions/event.rb +12 -1
  96. data/lib/graphql/subscriptions/serialize.rb +1 -1
  97. data/lib/graphql/subscriptions.rb +1 -1
  98. data/lib/graphql/testing/helpers.rb +2 -2
  99. data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -3
  100. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  101. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  102. data/lib/graphql/tracing/appoptics_tracing.rb +2 -0
  103. data/lib/graphql/tracing/appsignal_trace.rb +12 -0
  104. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  105. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  106. data/lib/graphql/tracing/data_dog_trace.rb +11 -0
  107. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  108. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  109. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  110. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  111. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  112. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  113. data/lib/graphql/tracing/new_relic_trace.rb +164 -41
  114. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  115. data/lib/graphql/tracing/notifications_trace.rb +4 -0
  116. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  117. data/lib/graphql/tracing/null_trace.rb +9 -0
  118. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  119. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  120. data/lib/graphql/tracing/perfetto_trace.rb +737 -0
  121. data/lib/graphql/tracing/platform_trace.rb +5 -0
  122. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  123. data/lib/graphql/tracing/prometheus_trace.rb +31 -0
  124. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  125. data/lib/graphql/tracing/scout_trace.rb +11 -0
  126. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  127. data/lib/graphql/tracing/sentry_trace.rb +11 -0
  128. data/lib/graphql/tracing/statsd_trace.rb +15 -0
  129. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  130. data/lib/graphql/tracing/trace.rb +128 -1
  131. data/lib/graphql/tracing.rb +30 -30
  132. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  133. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  134. data/lib/graphql/types.rb +18 -11
  135. data/lib/graphql/version.rb +1 -1
  136. data/lib/graphql.rb +55 -47
  137. metadata +152 -10
  138. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  139. data/lib/graphql/backtrace/trace.rb +0 -93
  140. data/lib/graphql/backtrace/tracer.rb +0 -80
  141. data/lib/graphql/schema/null_mask.rb +0 -11
@@ -42,8 +42,8 @@ module GraphQL
42
42
  end
43
43
 
44
44
  def directives
45
- if @resolver_class && (r_dirs = @resolver_class.directives).any?
46
- if (own_dirs = super).any?
45
+ if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
46
+ if !(own_dirs = super).empty?
47
47
  own_dirs + r_dirs
48
48
  else
49
49
  r_dirs
@@ -81,7 +81,7 @@ module GraphQL
81
81
  end
82
82
 
83
83
  def inspect
84
- "#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
84
+ "#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
85
85
  end
86
86
 
87
87
  alias :mutation :resolver
@@ -255,7 +255,7 @@ module GraphQL
255
255
 
256
256
  @underscored_name = -Member::BuildType.underscore(name_s)
257
257
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
258
-
258
+ NameValidator.validate!(@name)
259
259
  @description = description
260
260
  @comment = comment
261
261
  @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
@@ -335,15 +335,15 @@ module GraphQL
335
335
  @call_after_define = false
336
336
  set_pagination_extensions(connection_extension: connection_extension)
337
337
  # Do this last so we have as much context as possible when initializing them:
338
- if extensions.any?
338
+ if !extensions.empty?
339
339
  self.extensions(extensions)
340
340
  end
341
341
 
342
- if resolver_class && resolver_class.extensions.any?
342
+ if resolver_class && !resolver_class.extensions.empty?
343
343
  self.extensions(resolver_class.extensions)
344
344
  end
345
345
 
346
- if directives.any?
346
+ if !directives.empty?
347
347
  directives.each do |(dir_class, options)|
348
348
  self.directive(dir_class, **options)
349
349
  end
@@ -369,7 +369,7 @@ module GraphQL
369
369
  if @definition_block.arity == 1
370
370
  @definition_block.call(self)
371
371
  else
372
- instance_eval(&@definition_block)
372
+ instance_exec(self, &@definition_block)
373
373
  end
374
374
  self.extensions.each(&:after_define_apply)
375
375
  @call_after_define = true
@@ -482,7 +482,7 @@ module GraphQL
482
482
  if new_extras.nil?
483
483
  # Read the value
484
484
  field_extras = @extras
485
- if @resolver_class && @resolver_class.extras.any?
485
+ if @resolver_class && !@resolver_class.extras.empty?
486
486
  field_extras + @resolver_class.extras
487
487
  else
488
488
  field_extras
@@ -732,7 +732,7 @@ module GraphQL
732
732
  method_to_call = resolver_method
733
733
  method_receiver = obj
734
734
  # Call the method with kwargs, if there are any
735
- if ruby_kwargs.any?
735
+ if !ruby_kwargs.empty?
736
736
  obj.public_send(resolver_method, **ruby_kwargs)
737
737
  else
738
738
  obj.public_send(resolver_method)
@@ -752,7 +752,7 @@ module GraphQL
752
752
  elsif inner_object.respond_to?(@method_sym)
753
753
  method_to_call = @method_sym
754
754
  method_receiver = obj.object
755
- if ruby_kwargs.any?
755
+ if !ruby_kwargs.empty?
756
756
  inner_object.public_send(@method_sym, **ruby_kwargs)
757
757
  else
758
758
  inner_object.public_send(@method_sym)
@@ -839,7 +839,7 @@ module GraphQL
839
839
  unsatisfied_ruby_kwargs.clear
840
840
  end
841
841
 
842
- if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
842
+ if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
843
843
  raise FieldImplementationFailed.new, <<-ERR
844
844
  Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
845
845
 
@@ -104,7 +104,7 @@ module GraphQL
104
104
  end
105
105
  end
106
106
  end
107
- if (extras = self.class.extras).any?
107
+ if !(extras = self.class.extras).empty?
108
108
  @added_extras = extras - field.extras
109
109
  field.extras(@added_extras)
110
110
  else
@@ -32,7 +32,7 @@ module GraphQL
32
32
  input_kwargs = {}
33
33
  end
34
34
 
35
- if input_kwargs.any?
35
+ if !input_kwargs.empty?
36
36
  super(**input_kwargs)
37
37
  else
38
38
  super()
@@ -136,6 +136,8 @@ module GraphQL
136
136
  super || "Autogenerated input type of #{self.mutation.graphql_name}"
137
137
  end
138
138
  end
139
+ # For compatibility, in case no arguments are defined:
140
+ has_no_arguments(true)
139
141
  mutation(mutation_class)
140
142
  # these might be inherited:
141
143
  mutation_args.each do |arg|
@@ -10,6 +10,14 @@ module GraphQL
10
10
 
11
11
  include GraphQL::Dig
12
12
 
13
+ # Raised when an InputObject doesn't have any arguments defined and hasn't explicitly opted out of this requirement
14
+ class ArgumentsAreRequiredError < GraphQL::Error
15
+ def initialize(input_object_type)
16
+ message = "Input Object types must have arguments, but #{input_object_type.graphql_name} doesn't have any. Define an argument for this type, remove it from your schema, or add `has_no_arguments(true)` to its definition."
17
+ super(message)
18
+ end
19
+ end
20
+
13
21
  # @return [GraphQL::Query::Context] The context for this query
14
22
  attr_reader :context
15
23
  # @return [GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance
@@ -45,6 +53,16 @@ module GraphQL
45
53
  to_h
46
54
  end
47
55
 
56
+ def deconstruct_keys(keys = nil)
57
+ if keys.nil?
58
+ @ruby_style_hash
59
+ else
60
+ new_h = {}
61
+ keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
62
+ new_h
63
+ end
64
+ end
65
+
48
66
  def prepare
49
67
  if @context
50
68
  object = @context[:current_object]
@@ -56,33 +74,6 @@ module GraphQL
56
74
  end
57
75
  end
58
76
 
59
- def self.authorized?(obj, value, ctx)
60
- # Authorize each argument (but this doesn't apply if `prepare` is implemented):
61
- if value.respond_to?(:key?)
62
- ctx.types.arguments(self).each do |input_obj_arg|
63
- if value.key?(input_obj_arg.keyword) &&
64
- !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
65
- return false
66
- end
67
- end
68
- end
69
- # It didn't early-return false:
70
- true
71
- end
72
-
73
- def self.one_of
74
- if !one_of?
75
- if all_argument_definitions.any? { |arg| arg.type.non_null? }
76
- raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
77
- end
78
- directive(GraphQL::Schema::Directive::OneOf)
79
- end
80
- end
81
-
82
- def self.one_of?
83
- false # Re-defined when `OneOf` is added
84
- end
85
-
86
77
  def unwrap_value(value)
87
78
  case value
88
79
  when Array
@@ -121,6 +112,33 @@ module GraphQL
121
112
  end
122
113
 
123
114
  class << self
115
+ def authorized?(obj, value, ctx)
116
+ # Authorize each argument (but this doesn't apply if `prepare` is implemented):
117
+ if value.respond_to?(:key?)
118
+ ctx.types.arguments(self).each do |input_obj_arg|
119
+ if value.key?(input_obj_arg.keyword) &&
120
+ !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
121
+ return false
122
+ end
123
+ end
124
+ end
125
+ # It didn't early-return false:
126
+ true
127
+ end
128
+
129
+ def one_of
130
+ if !one_of?
131
+ if all_argument_definitions.any? { |arg| arg.type.non_null? }
132
+ raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
133
+ end
134
+ directive(GraphQL::Schema::Directive::OneOf)
135
+ end
136
+ end
137
+
138
+ def one_of?
139
+ false # Re-defined when `OneOf` is added
140
+ end
141
+
124
142
  def argument(*args, **kwargs, &block)
125
143
  argument_defn = super(*args, **kwargs, &block)
126
144
  if one_of?
@@ -132,14 +150,8 @@ module GraphQL
132
150
  end
133
151
  end
134
152
  # Add a method access
135
- method_name = argument_defn.keyword
136
153
  suppress_redefinition_warning do
137
- class_eval <<-RUBY, __FILE__, __LINE__
138
- def #{method_name}
139
- self[#{method_name.inspect}]
140
- end
141
- alias_method :#{method_name}, :#{method_name}
142
- RUBY
154
+ define_accessor_method(argument_defn.keyword)
143
155
  end
144
156
  argument_defn
145
157
  end
@@ -246,6 +258,25 @@ module GraphQL
246
258
  result
247
259
  end
248
260
 
261
+ # @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments.
262
+ # @return [void]
263
+ def has_no_arguments(new_has_no_arguments)
264
+ @has_no_arguments = new_has_no_arguments
265
+ nil
266
+ end
267
+
268
+ # @return [Boolean] `true` if `has_no_arguments(true)` was configued
269
+ def has_no_arguments?
270
+ @has_no_arguments
271
+ end
272
+
273
+ def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
274
+ if require_defined_arguments && !has_no_arguments? && !any_arguments?
275
+ warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
276
+ end
277
+ super(context, false)
278
+ end
279
+
249
280
  private
250
281
 
251
282
  # Suppress redefinition warning for objectId arguments
@@ -256,6 +287,11 @@ module GraphQL
256
287
  ensure
257
288
  $VERBOSE = verbose
258
289
  end
290
+
291
+ def define_accessor_method(method_name)
292
+ define_method(method_name) { self[method_name] }
293
+ alias_method(method_name, method_name)
294
+ end
259
295
  end
260
296
 
261
297
  private
@@ -13,6 +13,7 @@ module GraphQL
13
13
  include GraphQL::Schema::Member::Scoped
14
14
  include GraphQL::Schema::Member::HasAstNode
15
15
  include GraphQL::Schema::Member::HasUnresolvedTypeError
16
+ include GraphQL::Schema::Member::HasDataloader
16
17
  include GraphQL::Schema::Member::HasDirectives
17
18
  include GraphQL::Schema::Member::HasInterfaces
18
19
 
@@ -29,7 +30,7 @@ module GraphQL
29
30
  const_set(:DefinitionMethods, defn_methods_module)
30
31
  extend(self::DefinitionMethods)
31
32
  end
32
- self::DefinitionMethods.module_eval(&block)
33
+ self::DefinitionMethods.module_exec(&block)
33
34
  end
34
35
 
35
36
  # @see {Schema::Warden} hides interfaces without visible implementations
@@ -90,7 +91,7 @@ module GraphQL
90
91
  # @param types [Class, Module]
91
92
  # @return [Array<Module, Class>] Implementers of this interface, if they're registered
92
93
  def orphan_types(*types)
93
- if types.any?
94
+ if !types.empty?
94
95
  @orphan_types ||= []
95
96
  @orphan_types.concat(types)
96
97
  else
@@ -187,7 +187,7 @@ module GraphQL
187
187
  camelize: false,
188
188
  connection_extension: nil,
189
189
  ) do
190
- if field_hash["args"].any?
190
+ if !field_hash["args"].empty?
191
191
  loader.build_arguments(self, field_hash["args"], type_resolver)
192
192
  end
193
193
  end
@@ -76,8 +76,8 @@ module GraphQL
76
76
  end
77
77
 
78
78
  # @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
79
- def arguments(context = GraphQL::Query::NullContext.instance)
80
- if own_arguments.any?
79
+ def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
80
+ if !own_arguments.empty?
81
81
  own_arguments_that_apply = {}
82
82
  own_arguments.each do |name, args_entry|
83
83
  if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
@@ -90,7 +90,7 @@ module GraphQL
90
90
  end
91
91
 
92
92
  def any_arguments?
93
- own_arguments.any?
93
+ !own_arguments.empty?
94
94
  end
95
95
 
96
96
  module ClassConfigured
@@ -100,12 +100,12 @@ module GraphQL
100
100
  end
101
101
 
102
102
  module InheritedArguments
103
- def arguments(context = GraphQL::Query::NullContext.instance)
104
- own_arguments = super
105
- inherited_arguments = superclass.arguments(context)
103
+ def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
104
+ own_arguments = super(context, require_defined_arguments)
105
+ inherited_arguments = superclass.arguments(context, false)
106
106
 
107
- if own_arguments.any?
108
- if inherited_arguments.any?
107
+ if !own_arguments.empty?
108
+ if !inherited_arguments.empty?
109
109
  # Local definitions override inherited ones
110
110
  inherited_arguments.merge(own_arguments)
111
111
  else
@@ -149,12 +149,12 @@ module GraphQL
149
149
  end
150
150
 
151
151
  module FieldConfigured
152
- def arguments(context = GraphQL::Query::NullContext.instance)
152
+ def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
153
153
  own_arguments = super
154
154
  if @resolver_class
155
155
  inherited_arguments = @resolver_class.field_arguments(context)
156
- if own_arguments.any?
157
- if inherited_arguments.any?
156
+ if !own_arguments.empty?
157
+ if !inherited_arguments.empty?
158
158
  inherited_arguments.merge(own_arguments)
159
159
  else
160
160
  own_arguments
@@ -198,7 +198,7 @@ module GraphQL
198
198
  end
199
199
 
200
200
  def all_argument_definitions
201
- if own_arguments.any?
201
+ if !own_arguments.empty?
202
202
  all_defns = own_arguments.values
203
203
  all_defns.flatten!
204
204
  all_defns
@@ -359,7 +359,8 @@ module GraphQL
359
359
  if application_object.nil?
360
360
  nil
361
361
  else
362
- maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
362
+ arg_loads_type = argument.loads
363
+ maybe_lazy_resolve_type = context.schema.resolve_type(arg_loads_type, application_object, context)
363
364
  context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
364
365
  if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
365
366
  application_object_type, application_object = resolve_type_result
@@ -368,10 +369,17 @@ module GraphQL
368
369
  # application_object is already assigned
369
370
  end
370
371
 
371
- if !(
372
- context.types.possible_types(argument.loads).include?(application_object_type) ||
373
- context.types.loadable?(argument.loads, context)
374
- )
372
+ passes_possible_types_check = if context.types.loadable?(arg_loads_type, context)
373
+ if arg_loads_type.kind.union?
374
+ # This union is used in `loads:` but not otherwise visible to this query
375
+ context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
376
+ else
377
+ true
378
+ end
379
+ else
380
+ context.types.possible_types(arg_loads_type).include?(application_object_type)
381
+ end
382
+ if !passes_possible_types_check
375
383
  err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
376
384
  application_object = load_application_object_failed(err)
377
385
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ module HasDataloader
7
+ # @return [GraphQL::Dataloader] The dataloader for the currently-running query
8
+ def dataloader
9
+ context.dataloader
10
+ end
11
+
12
+ # A shortcut method for loading a key from a source.
13
+ # Identical to `dataloader.with(source_class, *source_args).load(load_key)`
14
+ # @param source_class [Class<GraphQL::Dataloader::Source>]
15
+ # @param source_args [Array<Object>] Any extra parameters defined in `source_class`'s `initialize` method
16
+ # @param load_key [Object] The key to look up using `def fetch`
17
+ def dataload(source_class, *source_args, load_key)
18
+ dataloader.with(source_class, *source_args).load(load_key)
19
+ end
20
+
21
+ # Find an object with ActiveRecord via {Dataloader::ActiveRecordSource}.
22
+ # @param model [Class<ActiveRecord::Base>]
23
+ # @param find_by_value [Object] Usually an `id`, might be another value if `find_by:` is also provided
24
+ # @param find_by [Symbol, String] A column name to look the record up by. (Defaults to the model's primary key.)
25
+ # @return [ActiveRecord::Base, nil]
26
+ # @example Finding a record by ID
27
+ # dataload_record(Post, 5) # Like `Post.find(5)`, but dataloaded
28
+ # @example Finding a record by another attribute
29
+ # dataload_record(User, "matz", find_by: :handle) # Like `User.find_by(handle: "matz")`, but dataloaded
30
+ def dataload_record(model, find_by_value, find_by: nil)
31
+ source = if find_by
32
+ dataloader.with(Dataloader::ActiveRecordSource, model, find_by: find_by)
33
+ else
34
+ dataloader.with(Dataloader::ActiveRecordSource, model)
35
+ end
36
+
37
+ source.load(find_by_value)
38
+ end
39
+
40
+ # Look up an associated record using a Rails association.
41
+ # @param association_name [Symbol] A `belongs_to` or `has_one` association. (If a `has_many` association is named here, it will be selected without pagination.)
42
+ # @param record [ActiveRecord::Base] The object that the association belongs to.
43
+ # @param scope [ActiveRecord::Relation] A scope to look up the associated record in
44
+ # @return [ActiveRecord::Base, nil] The associated record, if there is one
45
+ # @example Looking up a belongs_to on the current object
46
+ # dataload_association(:parent) # Equivalent to `object.parent`, but dataloaded
47
+ # @example Looking up an associated record on some other object
48
+ # dataload_association(:post, comment) # Equivalent to `comment.post`, but dataloaded
49
+ def dataload_association(record = object, association_name, scope: nil)
50
+ source = if scope
51
+ dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope)
52
+ else
53
+ dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name)
54
+ end
55
+ source.load(record)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -6,7 +6,7 @@ module GraphQL
6
6
  module HasDirectives
7
7
  def self.extended(child_cls)
8
8
  super
9
- child_cls.module_eval { self.own_directives = nil }
9
+ child_cls.module_exec { self.own_directives = nil }
10
10
  end
11
11
 
12
12
  def inherited(child_cls)
@@ -55,14 +55,14 @@ module GraphQL
55
55
  else
56
56
  GraphQL::EmptyObjects::EMPTY_ARRAY
57
57
  end
58
- if inherited_directives.any? && directives
58
+ if !inherited_directives.empty? && directives
59
59
  dirs = []
60
60
  merge_directives(dirs, inherited_directives)
61
61
  merge_directives(dirs, directives)
62
62
  dirs
63
63
  elsif directives
64
64
  directives
65
- elsif inherited_directives.any?
65
+ elsif !inherited_directives.empty?
66
66
  inherited_directives
67
67
  else
68
68
  GraphQL::EmptyObjects::EMPTY_ARRAY
@@ -71,7 +71,7 @@ module GraphQL
71
71
  dirs = nil
72
72
  schema_member.ancestors.reverse_each do |ancestor|
73
73
  if ancestor.respond_to?(:own_directives) &&
74
- (anc_dirs = ancestor.own_directives).any?
74
+ !(anc_dirs = ancestor.own_directives).empty?
75
75
  dirs ||= []
76
76
  merge_directives(dirs, anc_dirs)
77
77
  end
@@ -79,6 +79,18 @@ module GraphQL
79
79
  end
80
80
  end
81
81
 
82
+ # @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields.
83
+ # @return [void]
84
+ def has_no_fields(new_has_no_fields)
85
+ @has_no_fields = new_has_no_fields
86
+ nil
87
+ end
88
+
89
+ # @return [Boolean] `true` if `has_no_fields(true)` was configued
90
+ def has_no_fields?
91
+ @has_no_fields
92
+ end
93
+
82
94
  # @return [Hash<String => GraphQL::Schema::Field, Array<GraphQL::Schema::Field>>] Fields defined on this class _specifically_, not parent classes
83
95
  def own_fields
84
96
  @own_fields ||= {}
@@ -155,9 +167,11 @@ module GraphQL
155
167
  warden = Warden.from_context(context)
156
168
  # Local overrides take precedence over inherited fields
157
169
  visible_fields = {}
170
+ had_any_fields_at_all = false
158
171
  for ancestor in ancestors
159
172
  if ancestor.respond_to?(:own_fields) && visible_interface_implementation?(ancestor, context, warden)
160
173
  ancestor.own_fields.each do |field_name, fields_entry|
174
+ had_any_fields_at_all = true
161
175
  # Choose the most local definition that passes `.visible?` --
162
176
  # stop checking for fields by name once one has been found.
163
177
  if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
@@ -166,6 +180,9 @@ module GraphQL
166
180
  end
167
181
  end
168
182
  end
183
+ if !had_any_fields_at_all && !has_no_fields?
184
+ warn(GraphQL::Schema::Object::FieldsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
185
+ end
169
186
  visible_fields
170
187
  end
171
188
  end
@@ -185,9 +202,10 @@ module GraphQL
185
202
 
186
203
  def inherited(subclass)
187
204
  super
188
- subclass.class_eval do
205
+ subclass.class_exec do
189
206
  @own_fields ||= nil
190
207
  @field_class ||= nil
208
+ @has_no_fields ||= false
191
209
  end
192
210
  end
193
211
 
@@ -24,7 +24,7 @@ module GraphQL
24
24
  implements(next_interface)
25
25
  end
26
26
  elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
27
- if options.any?
27
+ if !options.empty?
28
28
  raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
29
29
  end
30
30
  new_memberships << int
@@ -73,13 +73,13 @@ module GraphQL
73
73
  def interfaces(context = GraphQL::Query::NullContext.instance)
74
74
  visible_interfaces = super
75
75
  inherited_interfaces = superclass.interfaces(context)
76
- if visible_interfaces.any?
77
- if inherited_interfaces.any?
76
+ if !visible_interfaces.empty?
77
+ if !inherited_interfaces.empty?
78
78
  visible_interfaces.concat(inherited_interfaces)
79
79
  visible_interfaces.uniq!
80
80
  end
81
81
  visible_interfaces
82
- elsif inherited_interfaces.any?
82
+ elsif !inherited_interfaces.empty?
83
83
  inherited_interfaces
84
84
  else
85
85
  EmptyObjects::EMPTY_ARRAY
@@ -133,7 +133,7 @@ module GraphQL
133
133
 
134
134
  def inherited(subclass)
135
135
  super
136
- subclass.class_eval do
136
+ subclass.class_exec do
137
137
  @own_interface_type_memberships ||= nil
138
138
  end
139
139
  end
@@ -32,7 +32,7 @@ module GraphQL
32
32
 
33
33
  def validators
34
34
  inherited_validators = superclass.validators
35
- if inherited_validators.any?
35
+ if !inherited_validators.empty?
36
36
  if @own_validators.nil?
37
37
  inherited_validators
38
38
  else
@@ -30,7 +30,7 @@ module GraphQL
30
30
 
31
31
  def inherited(subclass)
32
32
  super
33
- subclass.class_eval do
33
+ subclass.class_exec do
34
34
  @reauthorize_scoped_objects = nil
35
35
  end
36
36
  end
@@ -42,7 +42,7 @@ module GraphQL
42
42
  private
43
43
 
44
44
  def inherited(subclass)
45
- subclass.class_eval do
45
+ subclass.class_exec do
46
46
  @to_non_null_type ||= nil
47
47
  @to_list_type ||= nil
48
48
  end
@@ -2,6 +2,7 @@
2
2
  require 'graphql/schema/member/base_dsl_methods'
3
3
  require 'graphql/schema/member/graphql_type_names'
4
4
  require 'graphql/schema/member/has_ast_node'
5
+ require 'graphql/schema/member/has_dataloader'
5
6
  require 'graphql/schema/member/has_directives'
6
7
  require 'graphql/schema/member/has_deprecation_reason'
7
8
  require 'graphql/schema/member/has_interfaces'