graphql 2.0.30 → 2.3.6

Sign up to get free protection for your applications and to get access to all the features.
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.