graphql 2.0.28 → 2.2.11

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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  3. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  4. data/lib/generators/graphql/install_generator.rb +3 -0
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  7. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  8. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  9. data/lib/generators/graphql/templates/base_field.erb +2 -0
  10. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  11. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  12. data/lib/generators/graphql/templates/base_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  14. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  15. data/lib/generators/graphql/templates/base_union.erb +2 -0
  16. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/node_type.erb +2 -0
  20. data/lib/generators/graphql/templates/query_type.erb +2 -0
  21. data/lib/generators/graphql/templates/schema.erb +2 -0
  22. data/lib/graphql/analysis/ast/analyzer.rb +7 -0
  23. data/lib/graphql/analysis/ast/field_usage.rb +32 -7
  24. data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
  25. data/lib/graphql/analysis/ast/query_depth.rb +7 -2
  26. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  27. data/lib/graphql/analysis/ast.rb +21 -11
  28. data/lib/graphql/backtrace/trace.rb +12 -15
  29. data/lib/graphql/coercion_error.rb +1 -9
  30. data/lib/graphql/dataloader/async_dataloader.rb +85 -0
  31. data/lib/graphql/dataloader/request.rb +5 -0
  32. data/lib/graphql/dataloader/source.rb +11 -3
  33. data/lib/graphql/dataloader.rb +109 -142
  34. data/lib/graphql/duration_encoding_error.rb +16 -0
  35. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  36. data/lib/graphql/execution/interpreter/runtime.rb +79 -248
  37. data/lib/graphql/execution/interpreter.rb +91 -157
  38. data/lib/graphql/execution/lookahead.rb +88 -21
  39. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  40. data/lib/graphql/introspection/entry_points.rb +11 -5
  41. data/lib/graphql/introspection/schema_type.rb +3 -1
  42. data/lib/graphql/language/block_string.rb +34 -18
  43. data/lib/graphql/language/definition_slice.rb +1 -1
  44. data/lib/graphql/language/document_from_schema_definition.rb +37 -37
  45. data/lib/graphql/language/lexer.rb +271 -177
  46. data/lib/graphql/language/nodes.rb +75 -57
  47. data/lib/graphql/language/parser.rb +707 -1986
  48. data/lib/graphql/language/printer.rb +303 -146
  49. data/lib/graphql/language/sanitized_printer.rb +20 -22
  50. data/lib/graphql/language/static_visitor.rb +167 -0
  51. data/lib/graphql/language/visitor.rb +20 -81
  52. data/lib/graphql/language.rb +1 -0
  53. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  54. data/lib/graphql/pagination/array_connection.rb +3 -3
  55. data/lib/graphql/pagination/connection.rb +28 -1
  56. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  57. data/lib/graphql/pagination/relation_connection.rb +3 -3
  58. data/lib/graphql/query/context/scoped_context.rb +101 -0
  59. data/lib/graphql/query/context.rb +36 -98
  60. data/lib/graphql/query/null_context.rb +4 -11
  61. data/lib/graphql/query/validation_pipeline.rb +2 -2
  62. data/lib/graphql/query/variables.rb +3 -3
  63. data/lib/graphql/query.rb +13 -22
  64. data/lib/graphql/railtie.rb +9 -6
  65. data/lib/graphql/rake_task.rb +3 -12
  66. data/lib/graphql/schema/argument.rb +6 -1
  67. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  68. data/lib/graphql/schema/build_from_definition.rb +0 -11
  69. data/lib/graphql/schema/directive/one_of.rb +12 -0
  70. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  71. data/lib/graphql/schema/directive.rb +1 -1
  72. data/lib/graphql/schema/enum.rb +3 -3
  73. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  74. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  75. data/lib/graphql/schema/field.rb +39 -35
  76. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  77. data/lib/graphql/schema/input_object.rb +2 -2
  78. data/lib/graphql/schema/interface.rb +15 -11
  79. data/lib/graphql/schema/introspection_system.rb +2 -0
  80. data/lib/graphql/schema/loader.rb +0 -2
  81. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  82. data/lib/graphql/schema/member/has_arguments.rb +61 -38
  83. data/lib/graphql/schema/member/has_fields.rb +8 -5
  84. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  85. data/lib/graphql/schema/member/scoped.rb +19 -0
  86. data/lib/graphql/schema/member/validates_input.rb +3 -3
  87. data/lib/graphql/schema/object.rb +8 -0
  88. data/lib/graphql/schema/printer.rb +8 -7
  89. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  90. data/lib/graphql/schema/resolver.rb +16 -8
  91. data/lib/graphql/schema/scalar.rb +3 -3
  92. data/lib/graphql/schema/subscription.rb +11 -4
  93. data/lib/graphql/schema/union.rb +1 -1
  94. data/lib/graphql/schema/unique_within_type.rb +1 -1
  95. data/lib/graphql/schema/warden.rb +96 -94
  96. data/lib/graphql/schema.rb +252 -78
  97. data/lib/graphql/static_validation/all_rules.rb +1 -1
  98. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  99. data/lib/graphql/static_validation/literal_validator.rb +2 -3
  100. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  101. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  102. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
  103. data/lib/graphql/static_validation/validation_context.rb +5 -5
  104. data/lib/graphql/static_validation/validator.rb +3 -0
  105. data/lib/graphql/static_validation.rb +0 -1
  106. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  107. data/lib/graphql/subscriptions/event.rb +8 -2
  108. data/lib/graphql/subscriptions/serialize.rb +2 -0
  109. data/lib/graphql/subscriptions.rb +14 -12
  110. data/lib/graphql/testing/helpers.rb +129 -0
  111. data/lib/graphql/testing.rb +2 -0
  112. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  113. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  114. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  115. data/lib/graphql/tracing/platform_tracing.rb +2 -0
  116. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  117. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  118. data/lib/graphql/tracing/trace.rb +1 -0
  119. data/lib/graphql/tracing.rb +3 -1
  120. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  121. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  122. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  123. data/lib/graphql/types.rb +1 -0
  124. data/lib/graphql/version.rb +1 -1
  125. data/lib/graphql.rb +6 -5
  126. data/readme.md +12 -2
  127. metadata +46 -38
  128. data/lib/graphql/deprecation.rb +0 -9
  129. data/lib/graphql/filter.rb +0 -59
  130. data/lib/graphql/language/parser.y +0 -560
  131. data/lib/graphql/schema/base_64_bp.rb +0 -26
  132. data/lib/graphql/static_validation/type_stack.rb +0 -216
  133. 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
 
@@ -144,6 +147,19 @@ module GraphQL
144
147
  @subscriptions = new_implementation
145
148
  end
146
149
 
150
+ # @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
151
+ def default_trace_mode(new_mode = nil)
152
+ if new_mode
153
+ @default_trace_mode = new_mode
154
+ elsif defined?(@default_trace_mode)
155
+ @default_trace_mode
156
+ elsif superclass.respond_to?(:default_trace_mode)
157
+ superclass.default_trace_mode
158
+ else
159
+ :default
160
+ end
161
+ end
162
+
147
163
  def trace_class(new_class = nil)
148
164
  if new_class
149
165
  trace_mode(:default, new_class)
@@ -151,46 +167,81 @@ module GraphQL
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 recieve 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,7 +500,7 @@ 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?
@@ -513,18 +553,17 @@ 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] = [] }
556
+ @own_references_to ||= {}
517
557
  if to_type
518
558
  if !to_type.is_a?(String)
519
559
  to_type = to_type.graphql_name
520
560
  end
521
561
 
522
562
  if from
523
- @own_references_to[to_type] << from
563
+ refs = @own_references_to[to_type] ||= []
564
+ refs << from
524
565
  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
566
+ get_references_to(to_type) || EMPTY_ARRAY
528
567
  end
529
568
  else
530
569
  # `@own_references_to` can be quite large for big schemas,
@@ -544,7 +583,7 @@ module GraphQL
544
583
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
545
584
  end
546
585
 
547
- def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
586
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
548
587
  parent_type = case type_or_name
549
588
  when LateBoundType
550
589
  get_type(type_or_name.name, context)
@@ -567,7 +606,7 @@ module GraphQL
567
606
  end
568
607
  end
569
608
 
570
- def get_fields(type, context = GraphQL::Query::NullContext)
609
+ def get_fields(type, context = GraphQL::Query::NullContext.instance)
571
610
  type.fields(context)
572
611
  end
573
612
 
@@ -657,7 +696,7 @@ module GraphQL
657
696
  else
658
697
  string_or_document
659
698
  end
660
- query = GraphQL::Query.new(self, document: doc, context: context)
699
+ query = query_class.new(self, document: doc, context: context)
661
700
  validator_opts = { schema: self }
662
701
  rules && (validator_opts[:rules] = rules)
663
702
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
@@ -665,6 +704,14 @@ module GraphQL
665
704
  res[:errors]
666
705
  end
667
706
 
707
+ def query_class(new_query_class = NOT_CONFIGURED)
708
+ if NOT_CONFIGURED.equal?(new_query_class)
709
+ @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
710
+ else
711
+ @query_class = new_query_class
712
+ end
713
+ end
714
+
668
715
  attr_writer :validate_max_errors
669
716
 
670
717
  def validate_max_errors(new_validate_max_errors = nil)
@@ -707,6 +754,7 @@ module GraphQL
707
754
 
708
755
  def error_bubbling(new_error_bubbling = nil)
709
756
  if !new_error_bubbling.nil?
757
+ 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
758
  @error_bubbling = new_error_bubbling
711
759
  else
712
760
  @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
@@ -717,9 +765,10 @@ module GraphQL
717
765
 
718
766
  attr_writer :max_depth
719
767
 
720
- def max_depth(new_max_depth = nil)
768
+ def max_depth(new_max_depth = nil, count_introspection_fields: true)
721
769
  if new_max_depth
722
770
  @max_depth = new_max_depth
771
+ @count_introspection_fields = count_introspection_fields
723
772
  elsif defined?(@max_depth)
724
773
  @max_depth
725
774
  else
@@ -727,6 +776,14 @@ module GraphQL
727
776
  end
728
777
  end
729
778
 
779
+ def count_introspection_fields
780
+ if defined?(@count_introspection_fields)
781
+ @count_introspection_fields
782
+ else
783
+ find_inherited_value(:count_introspection_fields, true)
784
+ end
785
+ end
786
+
730
787
  def disable_introspection_entry_points
731
788
  @disable_introspection_entry_points = true
732
789
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
@@ -769,6 +826,26 @@ module GraphQL
769
826
  end
770
827
  end
771
828
 
829
+ # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
830
+ # @return [Array<Module>] Type definitions added to this schema
831
+ def extra_types(*new_extra_types)
832
+ if new_extra_types.any?
833
+ new_extra_types = new_extra_types.flatten
834
+ @own_extra_types ||= []
835
+ @own_extra_types.concat(new_extra_types)
836
+ end
837
+ inherited_et = find_inherited_value(:extra_types, nil)
838
+ if inherited_et
839
+ if @own_extra_types
840
+ inherited_et + @own_extra_types
841
+ else
842
+ inherited_et
843
+ end
844
+ else
845
+ @own_extra_types || EMPTY_ARRAY
846
+ end
847
+ end
848
+
772
849
  def orphan_types(*new_orphan_types)
773
850
  if new_orphan_types.any?
774
851
  new_orphan_types = new_orphan_types.flatten
@@ -776,7 +853,16 @@ module GraphQL
776
853
  own_orphan_types.concat(new_orphan_types.flatten)
777
854
  end
778
855
 
779
- find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
856
+ inherited_ot = find_inherited_value(:orphan_types, nil)
857
+ if inherited_ot
858
+ if own_orphan_types.any?
859
+ inherited_ot + own_orphan_types
860
+ else
861
+ inherited_ot
862
+ end
863
+ else
864
+ own_orphan_types
865
+ end
780
866
  end
781
867
 
782
868
  def default_execution_strategy
@@ -795,6 +881,26 @@ module GraphQL
795
881
  end
796
882
  end
797
883
 
884
+ def default_logger(new_default_logger = NOT_CONFIGURED)
885
+ if NOT_CONFIGURED.equal?(new_default_logger)
886
+ if defined?(@default_logger)
887
+ @default_logger
888
+ elsif superclass.respond_to?(:default_logger)
889
+ superclass.default_logger
890
+ elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
891
+ rails_logger
892
+ else
893
+ def_logger = Logger.new($stdout)
894
+ def_logger.info! # It doesn't output debug info by default
895
+ def_logger
896
+ end
897
+ elsif new_default_logger == nil
898
+ @default_logger = Logger.new(IO::NULL)
899
+ else
900
+ @default_logger = new_default_logger
901
+ end
902
+ end
903
+
798
904
  def context_class(new_context_class = nil)
799
905
  if new_context_class
800
906
  @context_class = new_context_class
@@ -870,17 +976,19 @@ module GraphQL
870
976
  end
871
977
 
872
978
  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
979
+ 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
980
  end
879
981
  # rubocop:enable Lint/DuplicateMethods
880
982
 
881
983
  def inherited(child_class)
882
984
  if self == GraphQL::Schema
883
985
  child_class.directives(default_directives.values)
986
+ child_class.extend(SubclassGetReferencesTo)
987
+ end
988
+ # Make sure the child class has these built out, so that
989
+ # subclasses can be modified by later calls to `trace_with`
990
+ own_trace_modes.each do |name, _class|
991
+ child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
884
992
  end
885
993
  child_class.singleton_class.prepend(ResolveTypeWithType)
886
994
  super
@@ -968,6 +1076,12 @@ module GraphQL
968
1076
  end
969
1077
 
970
1078
  def instrument(instrument_step, instrumenter, options = {})
1079
+ warn <<~WARN
1080
+ Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
1081
+ (From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
1082
+
1083
+ WARN
1084
+ trace_with(Tracing::LegacyHooksTrace)
971
1085
  own_instrumenters[instrument_step] << instrumenter
972
1086
  end
973
1087
 
@@ -978,7 +1092,12 @@ module GraphQL
978
1092
  new_directives.flatten.each { |d| directive(d) }
979
1093
  end
980
1094
 
981
- find_inherited_value(:directives, default_directives).merge(own_directives)
1095
+ inherited_dirs = find_inherited_value(:directives, default_directives)
1096
+ if own_directives.any?
1097
+ inherited_dirs.merge(own_directives)
1098
+ else
1099
+ inherited_dirs
1100
+ end
982
1101
  end
983
1102
 
984
1103
  # Attach a single directive to this schema
@@ -994,11 +1113,13 @@ module GraphQL
994
1113
  "skip" => GraphQL::Schema::Directive::Skip,
995
1114
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
996
1115
  "oneOf" => GraphQL::Schema::Directive::OneOf,
1116
+ "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
997
1117
  }.freeze
998
1118
  end
999
1119
 
1000
1120
  def tracer(new_tracer)
1001
- if !(trace_class_for(:default) < GraphQL::Tracing::CallLegacyTracers)
1121
+ default_trace = trace_class_for(:default, build: true)
1122
+ if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1002
1123
  trace_with(GraphQL::Tracing::CallLegacyTracers)
1003
1124
  end
1004
1125
 
@@ -1020,13 +1141,26 @@ module GraphQL
1020
1141
  if mode.is_a?(Array)
1021
1142
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
1022
1143
  else
1023
- tc = trace_class_for(mode)
1144
+ tc = own_trace_modes[mode] ||= build_trace_mode(mode)
1024
1145
  tc.include(trace_mod)
1025
- if mode != :default
1026
- own_trace_modules[mode] << trace_mod
1146
+ own_trace_modules[mode] << trace_mod
1147
+ add_trace_options_for(mode, options)
1148
+ if mode == :default
1149
+ # This module is being added as a default tracer. If any other mode classes
1150
+ # have already been created, but get their default behavior from a superclass,
1151
+ # Then mix this into this schema's subclass.
1152
+ # (But don't mix it into mode classes that aren't default-based.)
1153
+ own_trace_modes.each do |other_mode_name, other_mode_class|
1154
+ if other_mode_class < DefaultTraceClass
1155
+ # Don't add it back to the inheritance tree if it's already there
1156
+ if !(other_mode_class < trace_mod)
1157
+ other_mode_class.include(trace_mod)
1158
+ end
1159
+ # Add any options so they'll be available
1160
+ add_trace_options_for(other_mode_name, options)
1161
+ end
1162
+ end
1027
1163
  end
1028
- t_opts = trace_options_for(mode)
1029
- t_opts.merge!(options)
1030
1164
  end
1031
1165
  nil
1032
1166
  end
@@ -1036,16 +1170,22 @@ module GraphQL
1036
1170
  def trace_options_for(mode)
1037
1171
  @trace_options_for_mode ||= {}
1038
1172
  @trace_options_for_mode[mode] ||= begin
1173
+ # It may be time to create an options hash for a mode that wasn't registered yet.
1174
+ # Mix in the default options in that case.
1175
+ default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
1176
+ # Make sure this returns a new object so that other hashes aren't modified later
1039
1177
  if superclass.respond_to?(:trace_options_for)
1040
- superclass.trace_options_for(mode).dup
1178
+ superclass.trace_options_for(mode).merge(default_options)
1041
1179
  else
1042
- {}
1180
+ default_options.dup
1043
1181
  end
1044
1182
  end
1045
1183
  end
1046
1184
 
1047
1185
  # Create a trace instance which will include the trace modules specified for the optional mode.
1048
1186
  #
1187
+ # If no `mode:` is given, then {default_trace_mode} will be used.
1188
+ #
1049
1189
  # @param mode [Symbol] Trace modules for this trade mode will be included
1050
1190
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1051
1191
  # @return [Tracing::Trace]
@@ -1056,14 +1196,21 @@ module GraphQL
1056
1196
  trace_mode = if mode
1057
1197
  mode
1058
1198
  elsif target && target.context[:backtrace]
1059
- :default_backtrace
1199
+ if default_trace_mode != :default
1200
+ raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1201
+ else
1202
+ own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1203
+ options_trace_mode = :default
1204
+ :default_backtrace
1205
+ end
1060
1206
  else
1061
- :default
1207
+ default_trace_mode
1062
1208
  end
1063
1209
 
1064
- base_trace_options = trace_options_for(trace_mode)
1210
+ options_trace_mode ||= trace_mode
1211
+ base_trace_options = trace_options_for(options_trace_mode)
1065
1212
  trace_options = base_trace_options.merge(options)
1066
- trace_class_for_mode = trace_class_for(trace_mode)
1213
+ trace_class_for_mode = trace_class_for(trace_mode, build: true)
1067
1214
  trace_class_for_mode.new(**trace_options)
1068
1215
  end
1069
1216
 
@@ -1217,6 +1364,12 @@ module GraphQL
1217
1364
 
1218
1365
  private
1219
1366
 
1367
+ def add_trace_options_for(mode, new_options)
1368
+ t_opts = trace_options_for(mode)
1369
+ t_opts.merge!(new_options)
1370
+ nil
1371
+ end
1372
+
1220
1373
  # @param t [Module, Array<Module>]
1221
1374
  # @return [void]
1222
1375
  def add_type_and_traverse(t, root:)
@@ -1278,7 +1431,7 @@ module GraphQL
1278
1431
  else
1279
1432
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1280
1433
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1281
- @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1434
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
1282
1435
  end
1283
1436
  end
1284
1437
  @lazy_methods
@@ -1327,6 +1480,27 @@ module GraphQL
1327
1480
  def own_multiplex_analyzers
1328
1481
  @own_multiplex_analyzers ||= []
1329
1482
  end
1483
+
1484
+ # This is overridden in subclasses to check the inheritance chain
1485
+ def get_references_to(type_name)
1486
+ @own_references_to[type_name]
1487
+ end
1488
+ end
1489
+
1490
+ module SubclassGetReferencesTo
1491
+ def get_references_to(type_name)
1492
+ own_refs = @own_references_to[type_name]
1493
+ inherited_refs = superclass.references_to(type_name)
1494
+ if inherited_refs&.any?
1495
+ if own_refs&.any?
1496
+ own_refs + inherited_refs
1497
+ else
1498
+ inherited_refs
1499
+ end
1500
+ else
1501
+ own_refs
1502
+ end
1503
+ end
1330
1504
  end
1331
1505
 
1332
1506
  # Install these here so that subclasses will also install it.
@@ -3,7 +3,7 @@ module GraphQL
3
3
  module StaticValidation
4
4
  # Default rules for {GraphQL::StaticValidation::Validator}
5
5
  #
6
- # Order is important here. Some validators return {GraphQL::Language::Visitor::SKIP}
6
+ # Order is important here. Some validators skip later hooks.
7
7
  # which stops the visit on that node. That way it doesn't try to find fields on types that
8
8
  # don't exist, etc.
9
9
  ALL_RULES = [
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module StaticValidation
4
- class BaseVisitor < GraphQL::Language::Visitor
4
+ class BaseVisitor < GraphQL::Language::StaticVisitor
5
5
  def initialize(document, context)
6
6
  @path = []
7
7
  @object_types = []
@@ -110,8 +110,8 @@ module GraphQL
110
110
  # TODO - would be nice to use these to create an error message so the caller knows
111
111
  # that required fields are missing
112
112
  required_field_names = @warden.arguments(type)
113
- .select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
114
- .map(&:name)
113
+ .select { |argument| argument.type.kind.non_null? && !argument.default_value? }
114
+ .map!(&:name)
115
115
 
116
116
  present_field_names = ast_node.arguments.map(&:name)
117
117
  missing_required_field_names = required_field_names - present_field_names
@@ -122,7 +122,6 @@ module GraphQL
122
122
  arg_type = @warden.get_argument(type, name).type
123
123
  recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
124
124
  end
125
-
126
125
  if type.one_of? && ast_node.arguments.size != 1
127
126
  results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})")
128
127
  end
@@ -340,7 +340,7 @@ module GraphQL
340
340
  selections.each do |node|
341
341
  case node
342
342
  when GraphQL::Language::Nodes::Field
343
- definition = context.query.get_field(owner_type, node.name)
343
+ definition = context.warden.get_field(owner_type, node.name)
344
344
  fields << Field.new(node, definition, owner_type, parents)
345
345
  when GraphQL::Language::Nodes::InlineFragment
346
346
  fragment_type = node.type ? context.warden.get_type(node.type.name) : owner_type
@@ -21,7 +21,7 @@ module GraphQL
21
21
  present_argument_names = ast_node.arguments.map(&:name)
22
22
  required_argument_names = context.warden.arguments(defn)
23
23
  .select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
24
- .map(&:name)
24
+ .map!(&:name)
25
25
 
26
26
  missing_names = required_argument_names - present_argument_names
27
27
  if missing_names.any?
@@ -35,8 +35,8 @@ module GraphQL
35
35
  return unless parent_type && parent_type.kind.input_object?
36
36
 
37
37
  required_fields = context.warden.arguments(parent_type)
38
- .select{|arg| arg.type.kind.non_null?}
39
- .map(&:graphql_name)
38
+ .select{ |arg| arg.type.kind.non_null? && !arg.default_value? }
39
+ .map!(&:graphql_name)
40
40
 
41
41
  present_fields = ast_node.arguments.map(&:name)
42
42
  missing_fields = required_fields - present_fields