graphql 2.4.3 → 2.5.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.
Files changed (166) 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 +118 -55
  7. data/lib/graphql/backtrace.rb +1 -19
  8. data/lib/graphql/current.rb +6 -1
  9. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  10. data/lib/graphql/dashboard/installable.rb +22 -0
  11. data/lib/graphql/dashboard/limiters.rb +93 -0
  12. data/lib/graphql/dashboard/operation_store.rb +199 -0
  13. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  14. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  15. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  16. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  17. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  18. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  19. data/lib/graphql/dashboard/statics/icon.png +0 -0
  20. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  24. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  25. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  26. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  27. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  28. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  29. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  30. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  31. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  36. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  37. data/lib/graphql/dashboard.rb +158 -0
  38. data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
  39. data/lib/graphql/dataloader/active_record_source.rb +26 -0
  40. data/lib/graphql/dataloader/async_dataloader.rb +21 -9
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/source.rb +3 -3
  43. data/lib/graphql/dataloader.rb +43 -14
  44. data/lib/graphql/execution/interpreter/resolve.rb +3 -3
  45. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
  46. data/lib/graphql/execution/interpreter/runtime.rb +94 -51
  47. data/lib/graphql/execution/interpreter.rb +16 -7
  48. data/lib/graphql/execution/multiplex.rb +1 -5
  49. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  50. data/lib/graphql/invalid_name_error.rb +1 -1
  51. data/lib/graphql/invalid_null_error.rb +5 -15
  52. data/lib/graphql/language/cache.rb +13 -0
  53. data/lib/graphql/language/document_from_schema_definition.rb +8 -7
  54. data/lib/graphql/language/lexer.rb +11 -4
  55. data/lib/graphql/language/nodes.rb +3 -0
  56. data/lib/graphql/language/parser.rb +15 -8
  57. data/lib/graphql/language/printer.rb +8 -8
  58. data/lib/graphql/language/static_visitor.rb +37 -33
  59. data/lib/graphql/language/visitor.rb +59 -55
  60. data/lib/graphql/pagination/connection.rb +1 -1
  61. data/lib/graphql/query/context/scoped_context.rb +1 -1
  62. data/lib/graphql/query/context.rb +6 -5
  63. data/lib/graphql/query/variable_validation_error.rb +1 -1
  64. data/lib/graphql/query.rb +19 -23
  65. data/lib/graphql/railtie.rb +7 -0
  66. data/lib/graphql/schema/addition.rb +1 -1
  67. data/lib/graphql/schema/argument.rb +7 -8
  68. data/lib/graphql/schema/build_from_definition.rb +99 -53
  69. data/lib/graphql/schema/directive/flagged.rb +3 -1
  70. data/lib/graphql/schema/directive.rb +2 -2
  71. data/lib/graphql/schema/enum.rb +36 -1
  72. data/lib/graphql/schema/enum_value.rb +1 -1
  73. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  74. data/lib/graphql/schema/field.rb +27 -13
  75. data/lib/graphql/schema/field_extension.rb +1 -1
  76. data/lib/graphql/schema/has_single_input_argument.rb +3 -1
  77. data/lib/graphql/schema/input_object.rb +77 -40
  78. data/lib/graphql/schema/interface.rb +3 -2
  79. data/lib/graphql/schema/loader.rb +1 -1
  80. data/lib/graphql/schema/member/has_arguments.rb +25 -17
  81. data/lib/graphql/schema/member/has_dataloader.rb +60 -0
  82. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  83. data/lib/graphql/schema/member/has_directives.rb +4 -4
  84. data/lib/graphql/schema/member/has_fields.rb +19 -1
  85. data/lib/graphql/schema/member/has_interfaces.rb +5 -5
  86. data/lib/graphql/schema/member/has_validators.rb +1 -1
  87. data/lib/graphql/schema/member/scoped.rb +1 -1
  88. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  89. data/lib/graphql/schema/member.rb +1 -0
  90. data/lib/graphql/schema/object.rb +25 -8
  91. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  92. data/lib/graphql/schema/resolver.rb +12 -10
  93. data/lib/graphql/schema/subscription.rb +52 -6
  94. data/lib/graphql/schema/union.rb +1 -1
  95. data/lib/graphql/schema/validator/required_validator.rb +23 -6
  96. data/lib/graphql/schema/validator.rb +1 -1
  97. data/lib/graphql/schema/visibility/migration.rb +1 -0
  98. data/lib/graphql/schema/visibility/profile.rb +95 -243
  99. data/lib/graphql/schema/visibility/visit.rb +190 -0
  100. data/lib/graphql/schema/visibility.rb +169 -28
  101. data/lib/graphql/schema/warden.rb +18 -5
  102. data/lib/graphql/schema.rb +93 -44
  103. data/lib/graphql/static_validation/all_rules.rb +1 -1
  104. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  105. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  106. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  107. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  108. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  109. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  110. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  111. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  112. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  113. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  114. data/lib/graphql/static_validation/validation_context.rb +1 -0
  115. data/lib/graphql/static_validation/validator.rb +6 -1
  116. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
  117. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  118. data/lib/graphql/subscriptions/event.rb +12 -1
  119. data/lib/graphql/subscriptions/serialize.rb +1 -1
  120. data/lib/graphql/subscriptions.rb +1 -1
  121. data/lib/graphql/testing/helpers.rb +7 -4
  122. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  123. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  124. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  125. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  126. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  127. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  128. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  129. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  130. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  131. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  132. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  133. data/lib/graphql/tracing/detailed_trace.rb +93 -0
  134. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  135. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  136. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  137. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  138. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  139. data/lib/graphql/tracing/notifications_trace.rb +182 -34
  140. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  141. data/lib/graphql/tracing/null_trace.rb +9 -0
  142. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  143. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  144. data/lib/graphql/tracing/perfetto_trace.rb +734 -0
  145. data/lib/graphql/tracing/platform_trace.rb +5 -0
  146. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  147. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  148. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  149. data/lib/graphql/tracing/scout_trace.rb +32 -55
  150. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  151. data/lib/graphql/tracing/sentry_trace.rb +62 -94
  152. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  153. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  154. data/lib/graphql/tracing/trace.rb +111 -1
  155. data/lib/graphql/tracing.rb +31 -30
  156. data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
  157. data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
  158. data/lib/graphql/types.rb +18 -11
  159. data/lib/graphql/version.rb +1 -1
  160. data/lib/graphql.rb +55 -47
  161. metadata +146 -11
  162. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  163. data/lib/graphql/backtrace/trace.rb +0 -93
  164. data/lib/graphql/backtrace/tracer.rb +0 -80
  165. data/lib/graphql/schema/null_mask.rb +0 -11
  166. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -41,10 +41,24 @@ module GraphQL
41
41
  end
42
42
  end
43
43
 
44
+ # @return [String, nil]
45
+ def deprecation_reason
46
+ super || @resolver_class&.deprecation_reason
47
+ end
48
+
44
49
  def directives
45
- if @resolver_class && (r_dirs = @resolver_class.directives).any?
46
- if (own_dirs = super).any?
47
- own_dirs + r_dirs
50
+ if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
51
+ if !(own_dirs = super).empty?
52
+ new_dirs = own_dirs.dup
53
+ r_dirs.each do |r_dir|
54
+ if r_dir.class.repeatable? ||
55
+ ( (r_dir_name = r_dir.graphql_name) &&
56
+ (!new_dirs.any? { |d| d.graphql_name == r_dir_name })
57
+ )
58
+ new_dirs << r_dir
59
+ end
60
+ end
61
+ new_dirs
48
62
  else
49
63
  r_dirs
50
64
  end
@@ -81,7 +95,7 @@ module GraphQL
81
95
  end
82
96
 
83
97
  def inspect
84
- "#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
98
+ "#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
85
99
  end
86
100
 
87
101
  alias :mutation :resolver
@@ -255,7 +269,7 @@ module GraphQL
255
269
 
256
270
  @underscored_name = -Member::BuildType.underscore(name_s)
257
271
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
258
-
272
+ NameValidator.validate!(@name)
259
273
  @description = description
260
274
  @comment = comment
261
275
  @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
@@ -335,15 +349,15 @@ module GraphQL
335
349
  @call_after_define = false
336
350
  set_pagination_extensions(connection_extension: connection_extension)
337
351
  # Do this last so we have as much context as possible when initializing them:
338
- if extensions.any?
352
+ if !extensions.empty?
339
353
  self.extensions(extensions)
340
354
  end
341
355
 
342
- if resolver_class && resolver_class.extensions.any?
356
+ if resolver_class && !resolver_class.extensions.empty?
343
357
  self.extensions(resolver_class.extensions)
344
358
  end
345
359
 
346
- if directives.any?
360
+ if !directives.empty?
347
361
  directives.each do |(dir_class, options)|
348
362
  self.directive(dir_class, **options)
349
363
  end
@@ -369,7 +383,7 @@ module GraphQL
369
383
  if @definition_block.arity == 1
370
384
  @definition_block.call(self)
371
385
  else
372
- instance_eval(&@definition_block)
386
+ instance_exec(self, &@definition_block)
373
387
  end
374
388
  self.extensions.each(&:after_define_apply)
375
389
  @call_after_define = true
@@ -482,7 +496,7 @@ module GraphQL
482
496
  if new_extras.nil?
483
497
  # Read the value
484
498
  field_extras = @extras
485
- if @resolver_class && @resolver_class.extras.any?
499
+ if @resolver_class && !@resolver_class.extras.empty?
486
500
  field_extras + @resolver_class.extras
487
501
  else
488
502
  field_extras
@@ -732,7 +746,7 @@ module GraphQL
732
746
  method_to_call = resolver_method
733
747
  method_receiver = obj
734
748
  # Call the method with kwargs, if there are any
735
- if ruby_kwargs.any?
749
+ if !ruby_kwargs.empty?
736
750
  obj.public_send(resolver_method, **ruby_kwargs)
737
751
  else
738
752
  obj.public_send(resolver_method)
@@ -752,7 +766,7 @@ module GraphQL
752
766
  elsif inner_object.respond_to?(@method_sym)
753
767
  method_to_call = @method_sym
754
768
  method_receiver = obj.object
755
- if ruby_kwargs.any?
769
+ if !ruby_kwargs.empty?
756
770
  inner_object.public_send(@method_sym, **ruby_kwargs)
757
771
  else
758
772
  inner_object.public_send(@method_sym)
@@ -839,7 +853,7 @@ module GraphQL
839
853
  unsatisfied_ruby_kwargs.clear
840
854
  end
841
855
 
842
- if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
856
+ if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
843
857
  raise FieldImplementationFailed.new, <<-ERR
844
858
  Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
845
859
 
@@ -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
@@ -30,7 +38,7 @@ module GraphQL
30
38
  # Weirdly, procs are applied during coercion, but not methods.
31
39
  # Probably because these methods require a `self`.
32
40
  if arg_defn.prepare.is_a?(Symbol) || context.nil?
33
- prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
41
+ prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key], context: context)
34
42
  overwrite_argument(ruby_kwargs_key, prepared_value)
35
43
  end
36
44
  end
@@ -45,42 +53,18 @@ module GraphQL
45
53
  to_h
46
54
  end
47
55
 
48
- def prepare
49
- if @context
50
- object = @context[:current_object]
51
- # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
52
- Schema::Validator.validate!(self.class.validators, object, @context, @ruby_style_hash, as: self.class)
53
- self
56
+ def deconstruct_keys(keys = nil)
57
+ if keys.nil?
58
+ @ruby_style_hash
54
59
  else
55
- self
56
- end
57
- end
58
-
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
60
+ new_h = {}
61
+ keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
62
+ new_h
68
63
  end
69
- # It didn't early-return false:
70
- true
71
64
  end
72
65
 
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
66
+ def prepare
67
+ self
84
68
  end
85
69
 
86
70
  def unwrap_value(value)
@@ -120,7 +104,42 @@ module GraphQL
120
104
  @ruby_style_hash.dup
121
105
  end
122
106
 
107
+ # @api private
108
+ def validate_for(context)
109
+ object = context[:current_object]
110
+ # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
111
+ Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
112
+ nil
113
+ end
114
+
123
115
  class << self
116
+ def authorized?(obj, value, ctx)
117
+ # Authorize each argument (but this doesn't apply if `prepare` is implemented):
118
+ if value.respond_to?(:key?)
119
+ ctx.types.arguments(self).each do |input_obj_arg|
120
+ if value.key?(input_obj_arg.keyword) &&
121
+ !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
122
+ return false
123
+ end
124
+ end
125
+ end
126
+ # It didn't early-return false:
127
+ true
128
+ end
129
+
130
+ def one_of
131
+ if !one_of?
132
+ if all_argument_definitions.any? { |arg| arg.type.non_null? }
133
+ raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
134
+ end
135
+ directive(GraphQL::Schema::Directive::OneOf)
136
+ end
137
+ end
138
+
139
+ def one_of?
140
+ false # Re-defined when `OneOf` is added
141
+ end
142
+
124
143
  def argument(*args, **kwargs, &block)
125
144
  argument_defn = super(*args, **kwargs, &block)
126
145
  if one_of?
@@ -132,14 +151,8 @@ module GraphQL
132
151
  end
133
152
  end
134
153
  # Add a method access
135
- method_name = argument_defn.keyword
136
154
  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
155
+ define_accessor_method(argument_defn.keyword)
143
156
  end
144
157
  argument_defn
145
158
  end
@@ -246,6 +259,25 @@ module GraphQL
246
259
  result
247
260
  end
248
261
 
262
+ # @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments.
263
+ # @return [void]
264
+ def has_no_arguments(new_has_no_arguments)
265
+ @has_no_arguments = new_has_no_arguments
266
+ nil
267
+ end
268
+
269
+ # @return [Boolean] `true` if `has_no_arguments(true)` was configued
270
+ def has_no_arguments?
271
+ @has_no_arguments
272
+ end
273
+
274
+ def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
275
+ if require_defined_arguments && !has_no_arguments? && !any_arguments?
276
+ warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
277
+ end
278
+ super(context, false)
279
+ end
280
+
249
281
  private
250
282
 
251
283
  # Suppress redefinition warning for objectId arguments
@@ -256,6 +288,11 @@ module GraphQL
256
288
  ensure
257
289
  $VERBOSE = verbose
258
290
  end
291
+
292
+ def define_accessor_method(method_name)
293
+ define_method(method_name) { self[method_name] }
294
+ alias_method(method_name, method_name)
295
+ end
259
296
  end
260
297
 
261
298
  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
@@ -18,6 +18,21 @@ module GraphQL
18
18
  directive(GraphQL::Schema::Directive::Deprecated, reason: text)
19
19
  end
20
20
  end
21
+
22
+ def self.extended(child_class)
23
+ super
24
+ child_class.extend(ClassMethods)
25
+ end
26
+
27
+ module ClassMethods
28
+ def deprecation_reason(new_reason = NOT_CONFIGURED)
29
+ if NOT_CONFIGURED.equal?(new_reason)
30
+ super()
31
+ else
32
+ self.deprecation_reason = new_reason
33
+ end
34
+ end
35
+ end
21
36
  end
22
37
  end
23
38
  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