graphql 2.0.28 → 2.2.11

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