graphql 2.0.30 → 2.3.6

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 (157) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  7. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  8. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  9. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  10. data/lib/generators/graphql/templates/base_field.erb +2 -0
  11. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  12. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  13. data/lib/generators/graphql/templates/base_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  15. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  16. data/lib/generators/graphql/templates/base_union.erb +2 -0
  17. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  18. data/lib/generators/graphql/templates/loader.erb +2 -0
  19. data/lib/generators/graphql/templates/mutation.erb +2 -0
  20. data/lib/generators/graphql/templates/node_type.erb +2 -0
  21. data/lib/generators/graphql/templates/query_type.erb +2 -0
  22. data/lib/generators/graphql/templates/schema.erb +5 -0
  23. data/lib/graphql/analysis/analyzer.rb +89 -0
  24. data/lib/graphql/analysis/field_usage.rb +82 -0
  25. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  26. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  27. data/lib/graphql/analysis/query_complexity.rb +183 -0
  28. data/lib/graphql/analysis/query_depth.rb +58 -0
  29. data/lib/graphql/analysis/visitor.rb +282 -0
  30. data/lib/graphql/analysis.rb +92 -1
  31. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  32. data/lib/graphql/backtrace/trace.rb +12 -15
  33. data/lib/graphql/coercion_error.rb +1 -9
  34. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  35. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  36. data/lib/graphql/dataloader/request.rb +5 -0
  37. data/lib/graphql/dataloader/source.rb +11 -3
  38. data/lib/graphql/dataloader.rb +112 -142
  39. data/lib/graphql/duration_encoding_error.rb +16 -0
  40. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  41. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  42. data/lib/graphql/execution/interpreter/runtime.rb +163 -365
  43. data/lib/graphql/execution/interpreter.rb +92 -158
  44. data/lib/graphql/execution/lookahead.rb +88 -21
  45. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  46. data/lib/graphql/introspection/entry_points.rb +11 -5
  47. data/lib/graphql/introspection/schema_type.rb +3 -1
  48. data/lib/graphql/language/block_string.rb +34 -18
  49. data/lib/graphql/language/definition_slice.rb +1 -1
  50. data/lib/graphql/language/document_from_schema_definition.rb +38 -38
  51. data/lib/graphql/language/lexer.rb +305 -193
  52. data/lib/graphql/language/nodes.rb +113 -66
  53. data/lib/graphql/language/parser.rb +787 -1986
  54. data/lib/graphql/language/printer.rb +303 -146
  55. data/lib/graphql/language/sanitized_printer.rb +20 -22
  56. data/lib/graphql/language/static_visitor.rb +167 -0
  57. data/lib/graphql/language/visitor.rb +20 -81
  58. data/lib/graphql/language.rb +61 -0
  59. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  60. data/lib/graphql/pagination/array_connection.rb +6 -6
  61. data/lib/graphql/pagination/connection.rb +28 -1
  62. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  63. data/lib/graphql/query/context/scoped_context.rb +101 -0
  64. data/lib/graphql/query/context.rb +66 -131
  65. data/lib/graphql/query/null_context.rb +4 -11
  66. data/lib/graphql/query/validation_pipeline.rb +4 -4
  67. data/lib/graphql/query/variables.rb +3 -3
  68. data/lib/graphql/query.rb +17 -26
  69. data/lib/graphql/railtie.rb +9 -6
  70. data/lib/graphql/rake_task.rb +3 -12
  71. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  72. data/lib/graphql/schema/addition.rb +21 -11
  73. data/lib/graphql/schema/argument.rb +43 -8
  74. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  75. data/lib/graphql/schema/build_from_definition.rb +9 -12
  76. data/lib/graphql/schema/directive/one_of.rb +12 -0
  77. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  78. data/lib/graphql/schema/directive.rb +3 -1
  79. data/lib/graphql/schema/enum.rb +3 -3
  80. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  81. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  82. data/lib/graphql/schema/field.rb +49 -35
  83. data/lib/graphql/schema/has_single_input_argument.rb +157 -0
  84. data/lib/graphql/schema/input_object.rb +4 -4
  85. data/lib/graphql/schema/interface.rb +10 -10
  86. data/lib/graphql/schema/introspection_system.rb +4 -2
  87. data/lib/graphql/schema/late_bound_type.rb +4 -0
  88. data/lib/graphql/schema/list.rb +2 -2
  89. data/lib/graphql/schema/loader.rb +2 -3
  90. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  91. data/lib/graphql/schema/member/has_arguments.rb +63 -73
  92. data/lib/graphql/schema/member/has_directives.rb +1 -1
  93. data/lib/graphql/schema/member/has_fields.rb +8 -5
  94. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  95. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  96. data/lib/graphql/schema/member/scoped.rb +19 -0
  97. data/lib/graphql/schema/member/type_system_helpers.rb +1 -2
  98. data/lib/graphql/schema/member/validates_input.rb +3 -3
  99. data/lib/graphql/schema/mutation.rb +7 -0
  100. data/lib/graphql/schema/object.rb +8 -0
  101. data/lib/graphql/schema/printer.rb +8 -7
  102. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  103. data/lib/graphql/schema/resolver.rb +27 -13
  104. data/lib/graphql/schema/scalar.rb +3 -3
  105. data/lib/graphql/schema/subscription.rb +11 -4
  106. data/lib/graphql/schema/union.rb +1 -1
  107. data/lib/graphql/schema/unique_within_type.rb +1 -1
  108. data/lib/graphql/schema/warden.rb +96 -95
  109. data/lib/graphql/schema.rb +323 -102
  110. data/lib/graphql/static_validation/all_rules.rb +1 -1
  111. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  112. data/lib/graphql/static_validation/literal_validator.rb +2 -3
  113. data/lib/graphql/static_validation/rules/fields_will_merge.rb +2 -2
  114. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  115. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
  116. data/lib/graphql/static_validation/validation_context.rb +5 -5
  117. data/lib/graphql/static_validation/validator.rb +3 -0
  118. data/lib/graphql/static_validation.rb +0 -1
  119. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
  120. data/lib/graphql/subscriptions/broadcast_analyzer.rb +1 -1
  121. data/lib/graphql/subscriptions/event.rb +8 -2
  122. data/lib/graphql/subscriptions/serialize.rb +2 -0
  123. data/lib/graphql/subscriptions.rb +15 -13
  124. data/lib/graphql/testing/helpers.rb +151 -0
  125. data/lib/graphql/testing.rb +2 -0
  126. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  127. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  128. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  129. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  130. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  131. data/lib/graphql/tracing/prometheus_trace.rb +9 -9
  132. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  133. data/lib/graphql/tracing/trace.rb +1 -0
  134. data/lib/graphql/tracing.rb +3 -1
  135. data/lib/graphql/type_kinds.rb +1 -1
  136. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  137. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  138. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  139. data/lib/graphql/types.rb +1 -0
  140. data/lib/graphql/version.rb +1 -1
  141. data/lib/graphql.rb +13 -13
  142. data/readme.md +12 -2
  143. metadata +33 -26
  144. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  145. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  146. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  147. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  148. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  149. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  150. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  151. data/lib/graphql/analysis/ast.rb +0 -81
  152. data/lib/graphql/deprecation.rb +0 -9
  153. data/lib/graphql/filter.rb +0 -59
  154. data/lib/graphql/language/parser.y +0 -560
  155. data/lib/graphql/schema/base_64_bp.rb +0 -26
  156. data/lib/graphql/static_validation/type_stack.rb +0 -216
  157. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "logger"
2
3
  require "graphql/schema/addition"
3
4
  require "graphql/schema/always_visible"
4
5
  require "graphql/schema/base_64_encoder"
@@ -37,10 +38,12 @@ require "graphql/schema/directive/skip"
37
38
  require "graphql/schema/directive/feature"
38
39
  require "graphql/schema/directive/flagged"
39
40
  require "graphql/schema/directive/transform"
41
+ require "graphql/schema/directive/specified_by"
40
42
  require "graphql/schema/type_membership"
41
43
 
42
44
  require "graphql/schema/resolver"
43
45
  require "graphql/schema/mutation"
46
+ require "graphql/schema/has_single_input_argument"
44
47
  require "graphql/schema/relay_classic_mutation"
45
48
  require "graphql/schema/subscription"
46
49
 
@@ -60,10 +63,6 @@ module GraphQL
60
63
  # Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
61
64
  # (These configurations can be overridden by specific calls to {Schema#execute})
62
65
  #
63
- # Schemas can specify how queries should be executed against them.
64
- # `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
65
- # each apply to corresponding root types.
66
- #
67
66
  # @example defining a schema
68
67
  # class MySchema < GraphQL::Schema
69
68
  # query QueryType
@@ -144,53 +143,105 @@ module GraphQL
144
143
  @subscriptions = new_implementation
145
144
  end
146
145
 
146
+ # @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
147
+ def default_trace_mode(new_mode = nil)
148
+ if new_mode
149
+ @default_trace_mode = new_mode
150
+ elsif defined?(@default_trace_mode)
151
+ @default_trace_mode
152
+ elsif superclass.respond_to?(:default_trace_mode)
153
+ superclass.default_trace_mode
154
+ else
155
+ :default
156
+ end
157
+ end
158
+
147
159
  def trace_class(new_class = nil)
148
160
  if new_class
161
+ # If any modules were already added for `:default`,
162
+ # re-apply them here
163
+ mods = trace_modules_for(:default)
164
+ mods.each { |mod| new_class.include(mod) }
149
165
  trace_mode(:default, new_class)
150
166
  backtrace_class = Class.new(new_class)
151
167
  backtrace_class.include(GraphQL::Backtrace::Trace)
152
168
  trace_mode(:default_backtrace, backtrace_class)
153
169
  end
154
- trace_class_for(:default)
170
+ trace_class_for(:default, build: true)
155
171
  end
156
172
 
157
173
  # @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
158
- def trace_class_for(mode)
159
- @trace_modes ||= {}
160
- @trace_modes[mode] ||= begin
161
- case mode
162
- when :default
163
- superclass_base_class = if superclass.respond_to?(:trace_class_for)
164
- superclass.trace_class_for(mode)
165
- else
166
- GraphQL::Tracing::Trace
167
- end
168
- Class.new(superclass_base_class)
169
- when :default_backtrace
170
- schema_base_class = trace_class_for(:default)
171
- Class.new(schema_base_class) do
172
- include(GraphQL::Backtrace::Trace)
173
- end
174
- else
175
- mods = trace_modules_for(mode)
176
- Class.new(trace_class_for(:default)) do
177
- mods.any? && include(*mods)
178
- end
179
- end
174
+ def trace_class_for(mode, build: false)
175
+ if (trace_class = own_trace_modes[mode])
176
+ trace_class
177
+ elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false))
178
+ trace_class
179
+ elsif build
180
+ own_trace_modes[mode] = build_trace_mode(mode)
181
+ else
182
+ nil
180
183
  end
181
184
  end
182
185
 
183
186
  # Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
184
- # `:default` is used when no `trace_mode: ...` is requested.
187
+ # {default_trace_mode} is used when no `trace_mode: ...` is requested.
188
+ #
189
+ # When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
190
+ # unless `trace_mode` is explicitly given. (This class will not receive any default trace modules.)
191
+ #
192
+ # Subclasses of the schema will use `trace_class` as a base class for this mode and those
193
+ # subclass also will _not_ receive default tracing modules.
194
+ #
185
195
  # @param mode_name [Symbol]
186
196
  # @param trace_class [Class] subclass of GraphQL::Tracing::Trace
187
197
  # @return void
188
198
  def trace_mode(mode_name, trace_class)
189
- @trace_modes ||= {}
190
- @trace_modes[mode_name] = trace_class
199
+ own_trace_modes[mode_name] = trace_class
191
200
  nil
192
201
  end
193
202
 
203
+ def own_trace_modes
204
+ @own_trace_modes ||= {}
205
+ end
206
+
207
+ module DefaultTraceClass
208
+ end
209
+
210
+ private_constant :DefaultTraceClass
211
+
212
+ def build_trace_mode(mode)
213
+ case mode
214
+ when :default
215
+ # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
216
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
217
+ Class.new(base_class) do
218
+ include DefaultTraceClass
219
+ end
220
+ when :default_backtrace
221
+ schema_base_class = trace_class_for(:default, build: true)
222
+ Class.new(schema_base_class) do
223
+ include(GraphQL::Backtrace::Trace)
224
+ end
225
+ else
226
+ # First, see if the superclass has a custom-defined class for this.
227
+ # Then, if it doesn't, use this class's default trace
228
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true)
229
+ # Prepare the default trace class if it hasn't been initialized yet
230
+ base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
231
+ mods = trace_modules_for(mode)
232
+ if base_class < DefaultTraceClass
233
+ mods = trace_modules_for(:default) + mods
234
+ end
235
+ # Copy the existing default options into this mode's options
236
+ default_options = trace_options_for(:default)
237
+ add_trace_options_for(mode, default_options)
238
+
239
+ Class.new(base_class) do
240
+ mods.any? && include(*mods)
241
+ end
242
+ end
243
+ end
244
+
194
245
  def own_trace_modules
195
246
  @own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
196
247
  end
@@ -222,7 +273,7 @@ module GraphQL
222
273
  # @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
223
274
  # @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
224
275
  # @return [Hash] GraphQL result
225
- def as_json(only: nil, except: nil, context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
276
+ def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
226
277
  introspection_query = Introspection.query(
227
278
  include_deprecated_args: include_deprecated_args,
228
279
  include_schema_description: include_schema_description,
@@ -231,16 +282,14 @@ module GraphQL
231
282
  include_specified_by_url: include_specified_by_url,
232
283
  )
233
284
 
234
- execute(introspection_query, only: only, except: except, context: context).to_h
285
+ execute(introspection_query, context: context).to_h
235
286
  end
236
287
 
237
288
  # Return the GraphQL IDL for the schema
238
289
  # @param context [Hash]
239
- # @param only [<#call(member, ctx)>]
240
- # @param except [<#call(member, ctx)>]
241
290
  # @return [String]
242
- def to_definition(only: nil, except: nil, context: {})
243
- GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
291
+ def to_definition(context: {})
292
+ GraphQL::Schema::Printer.print_schema(self, context: context)
244
293
  end
245
294
 
246
295
  # Return the GraphQL::Language::Document IDL AST for the schema
@@ -268,20 +317,6 @@ module GraphQL
268
317
  @find_cache[path] ||= @finder.find(path)
269
318
  end
270
319
 
271
- def default_filter
272
- GraphQL::Filter.new(except: default_mask)
273
- end
274
-
275
- def default_mask(new_mask = nil)
276
- if new_mask
277
- line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
278
- GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
279
- @own_default_mask = new_mask
280
- else
281
- @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
282
- end
283
- end
284
-
285
320
  def static_validator
286
321
  GraphQL::StaticValidation::Validator.new(schema: self)
287
322
  end
@@ -302,7 +337,7 @@ module GraphQL
302
337
  # Build a map of `{ name => type }` and return it
303
338
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
304
339
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
305
- def types(context = GraphQL::Query::NullContext)
340
+ def types(context = GraphQL::Query::NullContext.instance)
306
341
  all_types = non_introspection_types.merge(introspection_system.types)
307
342
  visible_types = {}
308
343
  all_types.each do |k, v|
@@ -329,7 +364,7 @@ module GraphQL
329
364
 
330
365
  # @param type_name [String]
331
366
  # @return [Module, nil] A type, or nil if there's no type called `type_name`
332
- def get_type(type_name, context = GraphQL::Query::NullContext)
367
+ def get_type(type_name, context = GraphQL::Query::NullContext.instance)
333
368
  local_entry = own_types[type_name]
334
369
  type_defn = case local_entry
335
370
  when nil
@@ -360,6 +395,11 @@ module GraphQL
360
395
  (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
361
396
  end
362
397
 
398
+ # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
399
+ def has_defined_type?(type_name)
400
+ own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false)
401
+ end
402
+
363
403
  # @api private
364
404
  attr_writer :connections
365
405
 
@@ -460,13 +500,13 @@ module GraphQL
460
500
  # @param type [Module] The type definition whose possible types you want to see
461
501
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
462
502
  # @return [Array<Module>] Possible types for `type`, if it's given.
463
- def possible_types(type = nil, context = GraphQL::Query::NullContext)
503
+ def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
464
504
  if type
465
505
  # TODO duck-typing `.possible_types` would probably be nicer here
466
506
  if type.kind.union?
467
507
  type.possible_types(context: context)
468
508
  else
469
- stored_possible_types = own_possible_types[type.graphql_name]
509
+ stored_possible_types = own_possible_types[type]
470
510
  visible_possible_types = if stored_possible_types && type.kind.interface?
471
511
  stored_possible_types.select do |possible_type|
472
512
  possible_type.interfaces(context).include?(type)
@@ -475,7 +515,7 @@ module GraphQL
475
515
  stored_possible_types
476
516
  end
477
517
  visible_possible_types ||
478
- introspection_system.possible_types[type.graphql_name] ||
518
+ introspection_system.possible_types[type] ||
479
519
  (
480
520
  superclass.respond_to?(:possible_types) ?
481
521
  superclass.possible_types(type, context) :
@@ -513,18 +553,12 @@ module GraphQL
513
553
  attr_writer :dataloader_class
514
554
 
515
555
  def references_to(to_type = nil, from: nil)
516
- @own_references_to ||= Hash.new { |h, k| h[k] = [] }
517
556
  if to_type
518
- if !to_type.is_a?(String)
519
- to_type = to_type.graphql_name
520
- end
521
-
522
557
  if from
523
- @own_references_to[to_type] << from
558
+ refs = own_references_to[to_type] ||= []
559
+ refs << from
524
560
  else
525
- own_refs = @own_references_to[to_type]
526
- inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
527
- own_refs + inherited_refs
561
+ get_references_to(to_type) || EMPTY_ARRAY
528
562
  end
529
563
  else
530
564
  # `@own_references_to` can be quite large for big schemas,
@@ -532,9 +566,9 @@ module GraphQL
532
566
  # So optimize the most common case -- don't create a duplicate Hash.
533
567
  inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
534
568
  if inherited_value.any?
535
- inherited_value.merge(@own_references_to)
569
+ inherited_value.merge(own_references_to)
536
570
  else
537
- @own_references_to
571
+ own_references_to
538
572
  end
539
573
  end
540
574
  end
@@ -544,7 +578,7 @@ module GraphQL
544
578
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
545
579
  end
546
580
 
547
- def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
581
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
548
582
  parent_type = case type_or_name
549
583
  when LateBoundType
550
584
  get_type(type_or_name.name, context)
@@ -567,7 +601,7 @@ module GraphQL
567
601
  end
568
602
  end
569
603
 
570
- def get_fields(type, context = GraphQL::Query::NullContext)
604
+ def get_fields(type, context = GraphQL::Query::NullContext.instance)
571
605
  type.fields(context)
572
606
  end
573
607
 
@@ -604,6 +638,17 @@ module GraphQL
604
638
  end
605
639
  end
606
640
 
641
+ # A limit on the number of tokens to accept on incoming query strings.
642
+ # Use this to prevent parsing maliciously-large query strings.
643
+ # @return [nil, Integer]
644
+ def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED)
645
+ if NOT_CONFIGURED.equal?(new_max_tokens)
646
+ defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens)
647
+ else
648
+ @max_query_string_tokens = new_max_tokens
649
+ end
650
+ end
651
+
607
652
  def default_page_size(new_default_page_size = nil)
608
653
  if new_default_page_size
609
654
  @default_page_size = new_default_page_size
@@ -612,27 +657,39 @@ module GraphQL
612
657
  end
613
658
  end
614
659
 
615
- def query_execution_strategy(new_query_execution_strategy = nil)
660
+ def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true)
661
+ if deprecation_warning
662
+ warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
663
+ warn " #{caller(1, 1).first}"
664
+ end
616
665
  if new_query_execution_strategy
617
666
  @query_execution_strategy = new_query_execution_strategy
618
667
  else
619
- @query_execution_strategy || find_inherited_value(:query_execution_strategy, self.default_execution_strategy)
668
+ @query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
620
669
  end
621
670
  end
622
671
 
623
- def mutation_execution_strategy(new_mutation_execution_strategy = nil)
672
+ def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true)
673
+ if deprecation_warning
674
+ warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
675
+ warn " #{caller(1, 1).first}"
676
+ end
624
677
  if new_mutation_execution_strategy
625
678
  @mutation_execution_strategy = new_mutation_execution_strategy
626
679
  else
627
- @mutation_execution_strategy || find_inherited_value(:mutation_execution_strategy, self.default_execution_strategy)
680
+ @mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
628
681
  end
629
682
  end
630
683
 
631
- def subscription_execution_strategy(new_subscription_execution_strategy = nil)
684
+ def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true)
685
+ if deprecation_warning
686
+ warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
687
+ warn " #{caller(1, 1).first}"
688
+ end
632
689
  if new_subscription_execution_strategy
633
690
  @subscription_execution_strategy = new_subscription_execution_strategy
634
691
  else
635
- @subscription_execution_strategy || find_inherited_value(:subscription_execution_strategy, self.default_execution_strategy)
692
+ @subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
636
693
  end
637
694
  end
638
695
 
@@ -657,7 +714,7 @@ module GraphQL
657
714
  else
658
715
  string_or_document
659
716
  end
660
- query = GraphQL::Query.new(self, document: doc, context: context)
717
+ query = query_class.new(self, document: doc, context: context)
661
718
  validator_opts = { schema: self }
662
719
  rules && (validator_opts[:rules] = rules)
663
720
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
@@ -665,6 +722,14 @@ module GraphQL
665
722
  res[:errors]
666
723
  end
667
724
 
725
+ def query_class(new_query_class = NOT_CONFIGURED)
726
+ if NOT_CONFIGURED.equal?(new_query_class)
727
+ @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
728
+ else
729
+ @query_class = new_query_class
730
+ end
731
+ end
732
+
668
733
  attr_writer :validate_max_errors
669
734
 
670
735
  def validate_max_errors(new_validate_max_errors = nil)
@@ -679,9 +744,10 @@ module GraphQL
679
744
 
680
745
  attr_writer :max_complexity
681
746
 
682
- def max_complexity(max_complexity = nil)
747
+ def max_complexity(max_complexity = nil, count_introspection_fields: true)
683
748
  if max_complexity
684
749
  @max_complexity = max_complexity
750
+ @max_complexity_count_introspection_fields = count_introspection_fields
685
751
  elsif defined?(@max_complexity)
686
752
  @max_complexity
687
753
  else
@@ -689,6 +755,14 @@ module GraphQL
689
755
  end
690
756
  end
691
757
 
758
+ def max_complexity_count_introspection_fields
759
+ if defined?(@max_complexity_count_introspection_fields)
760
+ @max_complexity_count_introspection_fields
761
+ else
762
+ find_inherited_value(:max_complexity_count_introspection_fields, true)
763
+ end
764
+ end
765
+
692
766
  attr_writer :analysis_engine
693
767
 
694
768
  def analysis_engine
@@ -707,6 +781,7 @@ module GraphQL
707
781
 
708
782
  def error_bubbling(new_error_bubbling = nil)
709
783
  if !new_error_bubbling.nil?
784
+ warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
710
785
  @error_bubbling = new_error_bubbling
711
786
  else
712
787
  @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
@@ -717,9 +792,10 @@ module GraphQL
717
792
 
718
793
  attr_writer :max_depth
719
794
 
720
- def max_depth(new_max_depth = nil)
795
+ def max_depth(new_max_depth = nil, count_introspection_fields: true)
721
796
  if new_max_depth
722
797
  @max_depth = new_max_depth
798
+ @count_introspection_fields = count_introspection_fields
723
799
  elsif defined?(@max_depth)
724
800
  @max_depth
725
801
  else
@@ -727,6 +803,14 @@ module GraphQL
727
803
  end
728
804
  end
729
805
 
806
+ def count_introspection_fields
807
+ if defined?(@count_introspection_fields)
808
+ @count_introspection_fields
809
+ else
810
+ find_inherited_value(:count_introspection_fields, true)
811
+ end
812
+ end
813
+
730
814
  def disable_introspection_entry_points
731
815
  @disable_introspection_entry_points = true
732
816
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
@@ -769,14 +853,54 @@ module GraphQL
769
853
  end
770
854
  end
771
855
 
856
+ # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
857
+ # @return [Array<Module>] Type definitions added to this schema
858
+ def extra_types(*new_extra_types)
859
+ if new_extra_types.any?
860
+ new_extra_types = new_extra_types.flatten
861
+ @own_extra_types ||= []
862
+ @own_extra_types.concat(new_extra_types)
863
+ end
864
+ inherited_et = find_inherited_value(:extra_types, nil)
865
+ if inherited_et
866
+ if @own_extra_types
867
+ inherited_et + @own_extra_types
868
+ else
869
+ inherited_et
870
+ end
871
+ else
872
+ @own_extra_types || EMPTY_ARRAY
873
+ end
874
+ end
875
+
772
876
  def orphan_types(*new_orphan_types)
773
877
  if new_orphan_types.any?
774
878
  new_orphan_types = new_orphan_types.flatten
879
+ non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
880
+ if non_object_types.any?
881
+ raise ArgumentError, <<~ERR
882
+ Only object type classes should be added as `orphan_types(...)`.
883
+
884
+ - Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
885
+ - See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
886
+
887
+ To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
888
+ ERR
889
+ end
775
890
  add_type_and_traverse(new_orphan_types, root: false)
776
891
  own_orphan_types.concat(new_orphan_types.flatten)
777
892
  end
778
893
 
779
- find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
894
+ inherited_ot = find_inherited_value(:orphan_types, nil)
895
+ if inherited_ot
896
+ if own_orphan_types.any?
897
+ inherited_ot + own_orphan_types
898
+ else
899
+ inherited_ot
900
+ end
901
+ else
902
+ own_orphan_types
903
+ end
780
904
  end
781
905
 
782
906
  def default_execution_strategy
@@ -795,6 +919,26 @@ module GraphQL
795
919
  end
796
920
  end
797
921
 
922
+ def default_logger(new_default_logger = NOT_CONFIGURED)
923
+ if NOT_CONFIGURED.equal?(new_default_logger)
924
+ if defined?(@default_logger)
925
+ @default_logger
926
+ elsif superclass.respond_to?(:default_logger)
927
+ superclass.default_logger
928
+ elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
929
+ rails_logger
930
+ else
931
+ def_logger = Logger.new($stdout)
932
+ def_logger.info! # It doesn't output debug info by default
933
+ def_logger
934
+ end
935
+ elsif new_default_logger == nil
936
+ @default_logger = Logger.new(IO::NULL)
937
+ else
938
+ @default_logger = new_default_logger
939
+ end
940
+ end
941
+
798
942
  def context_class(new_context_class = nil)
799
943
  if new_context_class
800
944
  @context_class = new_context_class
@@ -870,17 +1014,19 @@ module GraphQL
870
1014
  end
871
1015
 
872
1016
  def resolve_type(type, obj, ctx)
873
- if type.kind.object?
874
- type
875
- else
876
- raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
877
- end
1017
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types, Interface types, or `loads:` (tried to resolve: #{type.name})"
878
1018
  end
879
1019
  # rubocop:enable Lint/DuplicateMethods
880
1020
 
881
1021
  def inherited(child_class)
882
1022
  if self == GraphQL::Schema
883
1023
  child_class.directives(default_directives.values)
1024
+ child_class.extend(SubclassGetReferencesTo)
1025
+ end
1026
+ # Make sure the child class has these built out, so that
1027
+ # subclasses can be modified by later calls to `trace_with`
1028
+ own_trace_modes.each do |name, _class|
1029
+ child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
884
1030
  end
885
1031
  child_class.singleton_class.prepend(ResolveTypeWithType)
886
1032
  super
@@ -968,6 +1114,12 @@ module GraphQL
968
1114
  end
969
1115
 
970
1116
  def instrument(instrument_step, instrumenter, options = {})
1117
+ warn <<~WARN
1118
+ Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
1119
+ (From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
1120
+
1121
+ WARN
1122
+ trace_with(Tracing::LegacyHooksTrace)
971
1123
  own_instrumenters[instrument_step] << instrumenter
972
1124
  end
973
1125
 
@@ -978,7 +1130,12 @@ module GraphQL
978
1130
  new_directives.flatten.each { |d| directive(d) }
979
1131
  end
980
1132
 
981
- find_inherited_value(:directives, default_directives).merge(own_directives)
1133
+ inherited_dirs = find_inherited_value(:directives, default_directives)
1134
+ if own_directives.any?
1135
+ inherited_dirs.merge(own_directives)
1136
+ else
1137
+ inherited_dirs
1138
+ end
982
1139
  end
983
1140
 
984
1141
  # Attach a single directive to this schema
@@ -994,11 +1151,17 @@ module GraphQL
994
1151
  "skip" => GraphQL::Schema::Directive::Skip,
995
1152
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
996
1153
  "oneOf" => GraphQL::Schema::Directive::OneOf,
1154
+ "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
997
1155
  }.freeze
998
1156
  end
999
1157
 
1000
- def tracer(new_tracer)
1001
- if !(trace_class_for(:default) < GraphQL::Tracing::CallLegacyTracers)
1158
+ def tracer(new_tracer, silence_deprecation_warning: false)
1159
+ if !silence_deprecation_warning
1160
+ warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
1161
+ warn " #{caller(1, 1).first}"
1162
+ end
1163
+ default_trace = trace_class_for(:default, build: true)
1164
+ if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1002
1165
  trace_with(GraphQL::Tracing::CallLegacyTracers)
1003
1166
  end
1004
1167
 
@@ -1020,13 +1183,26 @@ module GraphQL
1020
1183
  if mode.is_a?(Array)
1021
1184
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
1022
1185
  else
1023
- tc = trace_class_for(mode)
1186
+ tc = own_trace_modes[mode] ||= build_trace_mode(mode)
1024
1187
  tc.include(trace_mod)
1025
- if mode != :default
1026
- own_trace_modules[mode] << trace_mod
1188
+ own_trace_modules[mode] << trace_mod
1189
+ add_trace_options_for(mode, options)
1190
+ if mode == :default
1191
+ # This module is being added as a default tracer. If any other mode classes
1192
+ # have already been created, but get their default behavior from a superclass,
1193
+ # Then mix this into this schema's subclass.
1194
+ # (But don't mix it into mode classes that aren't default-based.)
1195
+ own_trace_modes.each do |other_mode_name, other_mode_class|
1196
+ if other_mode_class < DefaultTraceClass
1197
+ # Don't add it back to the inheritance tree if it's already there
1198
+ if !(other_mode_class < trace_mod)
1199
+ other_mode_class.include(trace_mod)
1200
+ end
1201
+ # Add any options so they'll be available
1202
+ add_trace_options_for(other_mode_name, options)
1203
+ end
1204
+ end
1027
1205
  end
1028
- t_opts = trace_options_for(mode)
1029
- t_opts.merge!(options)
1030
1206
  end
1031
1207
  nil
1032
1208
  end
@@ -1036,16 +1212,22 @@ module GraphQL
1036
1212
  def trace_options_for(mode)
1037
1213
  @trace_options_for_mode ||= {}
1038
1214
  @trace_options_for_mode[mode] ||= begin
1215
+ # It may be time to create an options hash for a mode that wasn't registered yet.
1216
+ # Mix in the default options in that case.
1217
+ default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
1218
+ # Make sure this returns a new object so that other hashes aren't modified later
1039
1219
  if superclass.respond_to?(:trace_options_for)
1040
- superclass.trace_options_for(mode).dup
1220
+ superclass.trace_options_for(mode).merge(default_options)
1041
1221
  else
1042
- {}
1222
+ default_options.dup
1043
1223
  end
1044
1224
  end
1045
1225
  end
1046
1226
 
1047
1227
  # Create a trace instance which will include the trace modules specified for the optional mode.
1048
1228
  #
1229
+ # If no `mode:` is given, then {default_trace_mode} will be used.
1230
+ #
1049
1231
  # @param mode [Symbol] Trace modules for this trade mode will be included
1050
1232
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1051
1233
  # @return [Tracing::Trace]
@@ -1056,14 +1238,21 @@ module GraphQL
1056
1238
  trace_mode = if mode
1057
1239
  mode
1058
1240
  elsif target && target.context[:backtrace]
1059
- :default_backtrace
1241
+ if default_trace_mode != :default
1242
+ raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1243
+ else
1244
+ own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1245
+ options_trace_mode = :default
1246
+ :default_backtrace
1247
+ end
1060
1248
  else
1061
- :default
1249
+ default_trace_mode
1062
1250
  end
1063
1251
 
1064
- base_trace_options = trace_options_for(trace_mode)
1252
+ options_trace_mode ||= trace_mode
1253
+ base_trace_options = trace_options_for(options_trace_mode)
1065
1254
  trace_options = base_trace_options.merge(options)
1066
- trace_class_for_mode = trace_class_for(trace_mode)
1255
+ trace_class_for_mode = trace_class_for(trace_mode, build: true)
1067
1256
  trace_class_for_mode.new(**trace_options)
1068
1257
  end
1069
1258
 
@@ -1093,7 +1282,7 @@ module GraphQL
1093
1282
 
1094
1283
  # Execute a query on itself.
1095
1284
  # @see {Query#initialize} for arguments.
1096
- # @return [Hash] query result, ready to be serialized as JSON
1285
+ # @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
1097
1286
  def execute(query_str = nil, **kwargs)
1098
1287
  if query_str
1099
1288
  kwargs[:query] = query_str
@@ -1133,7 +1322,7 @@ module GraphQL
1133
1322
  # @see {Execution::Multiplex#run_all} for multiplex keyword arguments
1134
1323
  # @param queries [Array<Hash>] Keyword arguments for each query
1135
1324
  # @param context [Hash] Multiplex-level context
1136
- # @return [Array<Hash>] One result for each query in the input
1325
+ # @return [Array<GraphQL::Query::Result>] One result for each query in the input
1137
1326
  def multiplex(queries, **kwargs)
1138
1327
  GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
1139
1328
  end
@@ -1217,6 +1406,12 @@ module GraphQL
1217
1406
 
1218
1407
  private
1219
1408
 
1409
+ def add_trace_options_for(mode, new_options)
1410
+ t_opts = trace_options_for(mode)
1411
+ t_opts.merge!(new_options)
1412
+ nil
1413
+ end
1414
+
1220
1415
  # @param t [Module, Array<Module>]
1221
1416
  # @return [void]
1222
1417
  def add_type_and_traverse(t, root:)
@@ -1260,7 +1455,8 @@ module GraphQL
1260
1455
  own_union_memberships.merge!(addition.union_memberships)
1261
1456
 
1262
1457
  addition.references.each { |thing, pointers|
1263
- pointers.each { |pointer| references_to(thing, from: pointer) }
1458
+ prev_refs = own_references_to[thing] || []
1459
+ own_references_to[thing] = prev_refs | pointers.to_a
1264
1460
  }
1265
1461
 
1266
1462
  addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
@@ -1278,7 +1474,7 @@ module GraphQL
1278
1474
  else
1279
1475
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1280
1476
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1281
- @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1477
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
1282
1478
  end
1283
1479
  end
1284
1480
  @lazy_methods
@@ -1288,6 +1484,10 @@ module GraphQL
1288
1484
  @own_types ||= {}
1289
1485
  end
1290
1486
 
1487
+ def own_references_to
1488
+ @own_references_to ||= {}.tap(&:compare_by_identity)
1489
+ end
1490
+
1291
1491
  def non_introspection_types
1292
1492
  find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
1293
1493
  end
@@ -1301,7 +1501,7 @@ module GraphQL
1301
1501
  end
1302
1502
 
1303
1503
  def own_possible_types
1304
- @own_possible_types ||= {}
1504
+ @own_possible_types ||= {}.tap(&:compare_by_identity)
1305
1505
  end
1306
1506
 
1307
1507
  def own_union_memberships
@@ -1327,6 +1527,27 @@ module GraphQL
1327
1527
  def own_multiplex_analyzers
1328
1528
  @own_multiplex_analyzers ||= []
1329
1529
  end
1530
+
1531
+ # This is overridden in subclasses to check the inheritance chain
1532
+ def get_references_to(type_defn)
1533
+ own_references_to[type_defn]
1534
+ end
1535
+ end
1536
+
1537
+ module SubclassGetReferencesTo
1538
+ def get_references_to(type_defn)
1539
+ own_refs = own_references_to[type_defn]
1540
+ inherited_refs = superclass.references_to(type_defn)
1541
+ if inherited_refs&.any?
1542
+ if own_refs&.any?
1543
+ own_refs + inherited_refs
1544
+ else
1545
+ inherited_refs
1546
+ end
1547
+ else
1548
+ own_refs
1549
+ end
1550
+ end
1330
1551
  end
1331
1552
 
1332
1553
  # Install these here so that subclasses will also install it.