graphql 2.0.27 → 2.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) 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/source.rb +11 -3
  32. data/lib/graphql/dataloader.rb +109 -142
  33. data/lib/graphql/duration_encoding_error.rb +16 -0
  34. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  35. data/lib/graphql/execution/interpreter/runtime.rb +70 -248
  36. data/lib/graphql/execution/interpreter.rb +91 -157
  37. data/lib/graphql/execution/lookahead.rb +88 -21
  38. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  39. data/lib/graphql/introspection/entry_points.rb +11 -5
  40. data/lib/graphql/introspection/schema_type.rb +3 -1
  41. data/lib/graphql/language/block_string.rb +34 -18
  42. data/lib/graphql/language/definition_slice.rb +1 -1
  43. data/lib/graphql/language/document_from_schema_definition.rb +37 -37
  44. data/lib/graphql/language/lexer.rb +271 -177
  45. data/lib/graphql/language/nodes.rb +74 -56
  46. data/lib/graphql/language/parser.rb +697 -1986
  47. data/lib/graphql/language/printer.rb +299 -146
  48. data/lib/graphql/language/sanitized_printer.rb +20 -22
  49. data/lib/graphql/language/static_visitor.rb +167 -0
  50. data/lib/graphql/language/visitor.rb +20 -81
  51. data/lib/graphql/language.rb +1 -0
  52. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  53. data/lib/graphql/pagination/array_connection.rb +3 -3
  54. data/lib/graphql/pagination/connection.rb +28 -1
  55. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  56. data/lib/graphql/pagination/relation_connection.rb +3 -3
  57. data/lib/graphql/query/context/scoped_context.rb +101 -0
  58. data/lib/graphql/query/context.rb +36 -98
  59. data/lib/graphql/query/null_context.rb +4 -11
  60. data/lib/graphql/query/validation_pipeline.rb +2 -2
  61. data/lib/graphql/query/variables.rb +3 -3
  62. data/lib/graphql/query.rb +13 -22
  63. data/lib/graphql/railtie.rb +9 -6
  64. data/lib/graphql/rake_task.rb +3 -12
  65. data/lib/graphql/schema/argument.rb +6 -1
  66. data/lib/graphql/schema/build_from_definition.rb +0 -11
  67. data/lib/graphql/schema/directive/one_of.rb +12 -0
  68. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  69. data/lib/graphql/schema/directive.rb +1 -1
  70. data/lib/graphql/schema/enum.rb +3 -3
  71. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  72. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  73. data/lib/graphql/schema/field.rb +8 -5
  74. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  75. data/lib/graphql/schema/input_object.rb +2 -2
  76. data/lib/graphql/schema/interface.rb +10 -10
  77. data/lib/graphql/schema/introspection_system.rb +2 -0
  78. data/lib/graphql/schema/loader.rb +0 -2
  79. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  80. data/lib/graphql/schema/member/has_arguments.rb +61 -38
  81. data/lib/graphql/schema/member/has_fields.rb +8 -5
  82. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  83. data/lib/graphql/schema/member/scoped.rb +19 -0
  84. data/lib/graphql/schema/member/validates_input.rb +3 -3
  85. data/lib/graphql/schema/object.rb +8 -0
  86. data/lib/graphql/schema/printer.rb +8 -7
  87. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  88. data/lib/graphql/schema/resolver.rb +7 -3
  89. data/lib/graphql/schema/scalar.rb +3 -3
  90. data/lib/graphql/schema/subscription.rb +11 -4
  91. data/lib/graphql/schema/union.rb +1 -1
  92. data/lib/graphql/schema/warden.rb +96 -94
  93. data/lib/graphql/schema.rb +219 -72
  94. data/lib/graphql/static_validation/all_rules.rb +1 -1
  95. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  96. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  97. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  98. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  99. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  100. data/lib/graphql/static_validation/validation_context.rb +5 -5
  101. data/lib/graphql/static_validation/validator.rb +3 -0
  102. data/lib/graphql/static_validation.rb +0 -1
  103. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  104. data/lib/graphql/subscriptions/event.rb +8 -2
  105. data/lib/graphql/subscriptions.rb +14 -12
  106. data/lib/graphql/testing/helpers.rb +125 -0
  107. data/lib/graphql/testing.rb +2 -0
  108. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  109. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  110. data/lib/graphql/tracing/data_dog_trace.rb +21 -34
  111. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  112. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  113. data/lib/graphql/tracing/platform_tracing.rb +2 -0
  114. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  115. data/lib/graphql/tracing/sentry_trace.rb +94 -0
  116. data/lib/graphql/tracing/trace.rb +1 -0
  117. data/lib/graphql/tracing.rb +3 -1
  118. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  119. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  120. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  121. data/lib/graphql/types.rb +1 -0
  122. data/lib/graphql/version.rb +1 -1
  123. data/lib/graphql.rb +3 -3
  124. data/readme.md +12 -2
  125. metadata +33 -25
  126. data/lib/graphql/deprecation.rb +0 -9
  127. data/lib/graphql/filter.rb +0 -59
  128. data/lib/graphql/language/parser.y +0 -560
  129. data/lib/graphql/static_validation/type_stack.rb +0 -216
  130. 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)
@@ -155,42 +171,66 @@ module GraphQL
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
180
- end
174
+ def trace_class_for(mode, build: true)
175
+ own_trace_modes[mode] ||
176
+ (superclass.respond_to?(:trace_class_for) ? superclass.trace_class_for(mode, build: build) : (build ? (own_trace_modes[mode] = build_trace_mode(mode)) : nil))
181
177
  end
182
178
 
183
179
  # Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
184
- # `:default` is used when no `trace_mode: ...` is requested.
180
+ # {default_trace_mode} is used when no `trace_mode: ...` is requested.
181
+ #
182
+ # When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
183
+ # unless `trace_mode` is explicitly given. (This class will not recieve any default trace modules.)
184
+ #
185
+ # Subclasses of the schema will use `trace_class` as a base class for this mode and those
186
+ # subclass also will _not_ receive default tracing modules.
187
+ #
185
188
  # @param mode_name [Symbol]
186
189
  # @param trace_class [Class] subclass of GraphQL::Tracing::Trace
187
190
  # @return void
188
191
  def trace_mode(mode_name, trace_class)
189
- @trace_modes ||= {}
190
- @trace_modes[mode_name] = trace_class
192
+ own_trace_modes[mode_name] = trace_class
191
193
  nil
192
194
  end
193
195
 
196
+ def own_trace_modes
197
+ @own_trace_modes ||= {}
198
+ end
199
+
200
+ module DefaultTraceClass
201
+ end
202
+
203
+ private_constant :DefaultTraceClass
204
+
205
+ def build_trace_mode(mode)
206
+ case mode
207
+ when :default
208
+ # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
209
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
210
+ Class.new(base_class) do
211
+ include DefaultTraceClass
212
+ end
213
+ when :default_backtrace
214
+ schema_base_class = trace_class_for(:default)
215
+ Class.new(schema_base_class) do
216
+ include(GraphQL::Backtrace::Trace)
217
+ end
218
+ else
219
+ # First, see if the superclass has a custom-defined class for this.
220
+ # Then, if it doesn't, use this class's default trace
221
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode, build: false)) || trace_class_for(:default)
222
+ # Prepare the default trace class if it hasn't been initialized yet
223
+ base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
224
+ mods = trace_modules_for(mode)
225
+ if base_class < DefaultTraceClass
226
+ mods = trace_modules_for(:default) + mods
227
+ end
228
+ Class.new(base_class) do
229
+ mods.any? && include(*mods)
230
+ end
231
+ end
232
+ end
233
+
194
234
  def own_trace_modules
195
235
  @own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
196
236
  end
@@ -222,7 +262,7 @@ module GraphQL
222
262
  # @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
223
263
  # @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
224
264
  # @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)
265
+ 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
266
  introspection_query = Introspection.query(
227
267
  include_deprecated_args: include_deprecated_args,
228
268
  include_schema_description: include_schema_description,
@@ -231,16 +271,14 @@ module GraphQL
231
271
  include_specified_by_url: include_specified_by_url,
232
272
  )
233
273
 
234
- execute(introspection_query, only: only, except: except, context: context).to_h
274
+ execute(introspection_query, context: context).to_h
235
275
  end
236
276
 
237
277
  # Return the GraphQL IDL for the schema
238
278
  # @param context [Hash]
239
- # @param only [<#call(member, ctx)>]
240
- # @param except [<#call(member, ctx)>]
241
279
  # @return [String]
242
- def to_definition(only: nil, except: nil, context: {})
243
- GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
280
+ def to_definition(context: {})
281
+ GraphQL::Schema::Printer.print_schema(self, context: context)
244
282
  end
245
283
 
246
284
  # Return the GraphQL::Language::Document IDL AST for the schema
@@ -268,20 +306,6 @@ module GraphQL
268
306
  @find_cache[path] ||= @finder.find(path)
269
307
  end
270
308
 
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
309
  def static_validator
286
310
  GraphQL::StaticValidation::Validator.new(schema: self)
287
311
  end
@@ -302,7 +326,7 @@ module GraphQL
302
326
  # Build a map of `{ name => type }` and return it
303
327
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
304
328
  # @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)
329
+ def types(context = GraphQL::Query::NullContext.instance)
306
330
  all_types = non_introspection_types.merge(introspection_system.types)
307
331
  visible_types = {}
308
332
  all_types.each do |k, v|
@@ -329,7 +353,7 @@ module GraphQL
329
353
 
330
354
  # @param type_name [String]
331
355
  # @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)
356
+ def get_type(type_name, context = GraphQL::Query::NullContext.instance)
333
357
  local_entry = own_types[type_name]
334
358
  type_defn = case local_entry
335
359
  when nil
@@ -360,6 +384,11 @@ module GraphQL
360
384
  (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
361
385
  end
362
386
 
387
+ # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
388
+ def has_defined_type?(type_name)
389
+ own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false)
390
+ end
391
+
363
392
  # @api private
364
393
  attr_writer :connections
365
394
 
@@ -460,7 +489,7 @@ module GraphQL
460
489
  # @param type [Module] The type definition whose possible types you want to see
461
490
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
462
491
  # @return [Array<Module>] Possible types for `type`, if it's given.
463
- def possible_types(type = nil, context = GraphQL::Query::NullContext)
492
+ def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
464
493
  if type
465
494
  # TODO duck-typing `.possible_types` would probably be nicer here
466
495
  if type.kind.union?
@@ -513,18 +542,17 @@ module GraphQL
513
542
  attr_writer :dataloader_class
514
543
 
515
544
  def references_to(to_type = nil, from: nil)
516
- @own_references_to ||= Hash.new { |h, k| h[k] = [] }
545
+ @own_references_to ||= {}
517
546
  if to_type
518
547
  if !to_type.is_a?(String)
519
548
  to_type = to_type.graphql_name
520
549
  end
521
550
 
522
551
  if from
523
- @own_references_to[to_type] << from
552
+ refs = @own_references_to[to_type] ||= []
553
+ refs << from
524
554
  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
555
+ get_references_to(to_type) || EMPTY_ARRAY
528
556
  end
529
557
  else
530
558
  # `@own_references_to` can be quite large for big schemas,
@@ -544,7 +572,7 @@ module GraphQL
544
572
  GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
545
573
  end
546
574
 
547
- def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
575
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
548
576
  parent_type = case type_or_name
549
577
  when LateBoundType
550
578
  get_type(type_or_name.name, context)
@@ -567,7 +595,7 @@ module GraphQL
567
595
  end
568
596
  end
569
597
 
570
- def get_fields(type, context = GraphQL::Query::NullContext)
598
+ def get_fields(type, context = GraphQL::Query::NullContext.instance)
571
599
  type.fields(context)
572
600
  end
573
601
 
@@ -657,7 +685,7 @@ module GraphQL
657
685
  else
658
686
  string_or_document
659
687
  end
660
- query = GraphQL::Query.new(self, document: doc, context: context)
688
+ query = query_class.new(self, document: doc, context: context)
661
689
  validator_opts = { schema: self }
662
690
  rules && (validator_opts[:rules] = rules)
663
691
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
@@ -665,6 +693,14 @@ module GraphQL
665
693
  res[:errors]
666
694
  end
667
695
 
696
+ def query_class(new_query_class = NOT_CONFIGURED)
697
+ if NOT_CONFIGURED.equal?(new_query_class)
698
+ @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
699
+ else
700
+ @query_class = new_query_class
701
+ end
702
+ end
703
+
668
704
  attr_writer :validate_max_errors
669
705
 
670
706
  def validate_max_errors(new_validate_max_errors = nil)
@@ -717,9 +753,10 @@ module GraphQL
717
753
 
718
754
  attr_writer :max_depth
719
755
 
720
- def max_depth(new_max_depth = nil)
756
+ def max_depth(new_max_depth = nil, count_introspection_fields: true)
721
757
  if new_max_depth
722
758
  @max_depth = new_max_depth
759
+ @count_introspection_fields = count_introspection_fields
723
760
  elsif defined?(@max_depth)
724
761
  @max_depth
725
762
  else
@@ -727,6 +764,14 @@ module GraphQL
727
764
  end
728
765
  end
729
766
 
767
+ def count_introspection_fields
768
+ if defined?(@count_introspection_fields)
769
+ @count_introspection_fields
770
+ else
771
+ find_inherited_value(:count_introspection_fields, true)
772
+ end
773
+ end
774
+
730
775
  def disable_introspection_entry_points
731
776
  @disable_introspection_entry_points = true
732
777
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
@@ -769,6 +814,26 @@ module GraphQL
769
814
  end
770
815
  end
771
816
 
817
+ # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
818
+ # @return [Array<Module>] Type definitions added to this schema
819
+ def extra_types(*new_extra_types)
820
+ if new_extra_types.any?
821
+ new_extra_types = new_extra_types.flatten
822
+ @own_extra_types ||= []
823
+ @own_extra_types.concat(new_extra_types)
824
+ end
825
+ inherited_et = find_inherited_value(:extra_types, nil)
826
+ if inherited_et
827
+ if @own_extra_types
828
+ inherited_et + @own_extra_types
829
+ else
830
+ inherited_et
831
+ end
832
+ else
833
+ @own_extra_types || EMPTY_ARRAY
834
+ end
835
+ end
836
+
772
837
  def orphan_types(*new_orphan_types)
773
838
  if new_orphan_types.any?
774
839
  new_orphan_types = new_orphan_types.flatten
@@ -776,7 +841,16 @@ module GraphQL
776
841
  own_orphan_types.concat(new_orphan_types.flatten)
777
842
  end
778
843
 
779
- find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
844
+ inherited_ot = find_inherited_value(:orphan_types, nil)
845
+ if inherited_ot
846
+ if own_orphan_types.any?
847
+ inherited_ot + own_orphan_types
848
+ else
849
+ inherited_ot
850
+ end
851
+ else
852
+ own_orphan_types
853
+ end
780
854
  end
781
855
 
782
856
  def default_execution_strategy
@@ -795,6 +869,26 @@ module GraphQL
795
869
  end
796
870
  end
797
871
 
872
+ def default_logger(new_default_logger = NOT_CONFIGURED)
873
+ if NOT_CONFIGURED.equal?(new_default_logger)
874
+ if defined?(@default_logger)
875
+ @default_logger
876
+ elsif superclass.respond_to?(:default_logger)
877
+ superclass.default_logger
878
+ elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
879
+ rails_logger
880
+ else
881
+ def_logger = Logger.new($stdout)
882
+ def_logger.info! # It doesn't output debug info by default
883
+ def_logger
884
+ end
885
+ elsif new_default_logger == nil
886
+ @default_logger = Logger.new(IO::NULL)
887
+ else
888
+ @default_logger = new_default_logger
889
+ end
890
+ end
891
+
798
892
  def context_class(new_context_class = nil)
799
893
  if new_context_class
800
894
  @context_class = new_context_class
@@ -870,17 +964,19 @@ module GraphQL
870
964
  end
871
965
 
872
966
  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
967
+ 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
968
  end
879
969
  # rubocop:enable Lint/DuplicateMethods
880
970
 
881
971
  def inherited(child_class)
882
972
  if self == GraphQL::Schema
883
973
  child_class.directives(default_directives.values)
974
+ child_class.extend(SubclassGetReferencesTo)
975
+ end
976
+ # Make sure the child class has these built out, so that
977
+ # subclasses can be modified by later calls to `trace_with`
978
+ own_trace_modes.each do |name, _class|
979
+ child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
884
980
  end
885
981
  child_class.singleton_class.prepend(ResolveTypeWithType)
886
982
  super
@@ -968,6 +1064,12 @@ module GraphQL
968
1064
  end
969
1065
 
970
1066
  def instrument(instrument_step, instrumenter, options = {})
1067
+ warn <<~WARN
1068
+ Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
1069
+ (From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
1070
+
1071
+ WARN
1072
+ trace_with(Tracing::LegacyHooksTrace)
971
1073
  own_instrumenters[instrument_step] << instrumenter
972
1074
  end
973
1075
 
@@ -978,7 +1080,12 @@ module GraphQL
978
1080
  new_directives.flatten.each { |d| directive(d) }
979
1081
  end
980
1082
 
981
- find_inherited_value(:directives, default_directives).merge(own_directives)
1083
+ inherited_dirs = find_inherited_value(:directives, default_directives)
1084
+ if own_directives.any?
1085
+ inherited_dirs.merge(own_directives)
1086
+ else
1087
+ inherited_dirs
1088
+ end
982
1089
  end
983
1090
 
984
1091
  # Attach a single directive to this schema
@@ -994,11 +1101,13 @@ module GraphQL
994
1101
  "skip" => GraphQL::Schema::Directive::Skip,
995
1102
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
996
1103
  "oneOf" => GraphQL::Schema::Directive::OneOf,
1104
+ "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
997
1105
  }.freeze
998
1106
  end
999
1107
 
1000
1108
  def tracer(new_tracer)
1001
- if !(trace_class_for(:default) < GraphQL::Tracing::CallLegacyTracers)
1109
+ default_trace = trace_class_for(:default)
1110
+ if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1002
1111
  trace_with(GraphQL::Tracing::CallLegacyTracers)
1003
1112
  end
1004
1113
 
@@ -1020,10 +1129,20 @@ module GraphQL
1020
1129
  if mode.is_a?(Array)
1021
1130
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
1022
1131
  else
1023
- tc = trace_class_for(mode)
1132
+ tc = own_trace_modes[mode] ||= build_trace_mode(mode)
1024
1133
  tc.include(trace_mod)
1025
- if mode != :default
1026
- own_trace_modules[mode] << trace_mod
1134
+ own_trace_modules[mode] << trace_mod
1135
+
1136
+ if mode == :default
1137
+ # This module is being added as a default tracer. If any other mode classes
1138
+ # have already been created, but get their default behavior from a superclass,
1139
+ # Then mix this into this schema's subclass.
1140
+ # (But don't mix it into mode classes that aren't default-based.)
1141
+ own_trace_modes.each do |other_mode_name, other_mode_class|
1142
+ if other_mode_class < DefaultTraceClass && !(other_mode_class < trace_mod)
1143
+ other_mode_class.include(trace_mod)
1144
+ end
1145
+ end
1027
1146
  end
1028
1147
  t_opts = trace_options_for(mode)
1029
1148
  t_opts.merge!(options)
@@ -1046,6 +1165,8 @@ module GraphQL
1046
1165
 
1047
1166
  # Create a trace instance which will include the trace modules specified for the optional mode.
1048
1167
  #
1168
+ # If no `mode:` is given, then {default_trace_mode} will be used.
1169
+ #
1049
1170
  # @param mode [Symbol] Trace modules for this trade mode will be included
1050
1171
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1051
1172
  # @return [Tracing::Trace]
@@ -1056,14 +1177,19 @@ module GraphQL
1056
1177
  trace_mode = if mode
1057
1178
  mode
1058
1179
  elsif target && target.context[:backtrace]
1059
- :default_backtrace
1180
+ if default_trace_mode != :default
1181
+ raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1182
+ else
1183
+ own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1184
+ :default_backtrace
1185
+ end
1060
1186
  else
1061
- :default
1187
+ default_trace_mode
1062
1188
  end
1063
1189
 
1064
1190
  base_trace_options = trace_options_for(trace_mode)
1065
1191
  trace_options = base_trace_options.merge(options)
1066
- trace_class_for_mode = trace_class_for(trace_mode)
1192
+ trace_class_for_mode = trace_class_for(trace_mode) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
1067
1193
  trace_class_for_mode.new(**trace_options)
1068
1194
  end
1069
1195
 
@@ -1327,6 +1453,27 @@ module GraphQL
1327
1453
  def own_multiplex_analyzers
1328
1454
  @own_multiplex_analyzers ||= []
1329
1455
  end
1456
+
1457
+ # This is overridden in subclasses to check the inheritance chain
1458
+ def get_references_to(type_name)
1459
+ @own_references_to[type_name]
1460
+ end
1461
+ end
1462
+
1463
+ module SubclassGetReferencesTo
1464
+ def get_references_to(type_name)
1465
+ own_refs = @own_references_to[type_name]
1466
+ inherited_refs = superclass.references_to(type_name)
1467
+ if inherited_refs&.any?
1468
+ if own_refs&.any?
1469
+ own_refs + inherited_refs
1470
+ else
1471
+ inherited_refs
1472
+ end
1473
+ else
1474
+ own_refs
1475
+ end
1476
+ end
1330
1477
  end
1331
1478
 
1332
1479
  # 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 = []
@@ -111,7 +111,7 @@ module GraphQL
111
111
  # that required fields are missing
112
112
  required_field_names = @warden.arguments(type)
113
113
  .select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
114
- .map(&:name)
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
@@ -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?
@@ -36,7 +36,7 @@ module GraphQL
36
36
 
37
37
  required_fields = context.warden.arguments(parent_type)
38
38
  .select{|arg| arg.type.kind.non_null?}
39
- .map(&:graphql_name)
39
+ .map!(&:graphql_name)
40
40
 
41
41
  present_fields = ast_node.arguments.map(&:name)
42
42
  missing_fields = required_fields - present_fields
@@ -8,20 +8,20 @@ module GraphQL
8
8
  # It provides access to the schema & fragments which validators may read from.
9
9
  #
10
10
  # It holds a list of errors which each validator may add to.
11
- #
12
- # It also provides limited access to the {TypeStack} instance,
13
- # which tracks state as you climb in and out of different fields.
14
11
  class ValidationContext
15
12
  extend Forwardable
16
13
 
17
14
  attr_reader :query, :errors, :visitor,
18
15
  :on_dependency_resolve_handlers,
19
- :max_errors
16
+ :max_errors, :warden, :schema
17
+
20
18
 
21
- def_delegators :@query, :schema, :document, :fragments, :operations, :warden
19
+ def_delegators :@query, :document, :fragments, :operations
22
20
 
23
21
  def initialize(query, visitor_class, max_errors)
24
22
  @query = query
23
+ @warden = query.warden
24
+ @schema = query.schema
25
25
  @literal_validator = LiteralValidator.new(context: query.context)
26
26
  @errors = []
27
27
  @max_errors = max_errors || Float::INFINITY
@@ -28,6 +28,7 @@ module GraphQL
28
28
  # @return [Array<Hash>]
29
29
  def validate(query, validate: true, timeout: nil, max_errors: nil)
30
30
  query.current_trace.validate(validate: validate, query: query) do
31
+ begin_t = Time.now
31
32
  errors = if validate == false
32
33
  []
33
34
  else
@@ -52,11 +53,13 @@ module GraphQL
52
53
  end
53
54
 
54
55
  {
56
+ remaining_timeout: timeout ? (timeout - (Time.now - begin_t)) : nil,
55
57
  errors: errors,
56
58
  }
57
59
  end
58
60
  rescue GraphQL::ExecutionError => e
59
61
  {
62
+ remaining_timeout: nil,
60
63
  errors: [e],
61
64
  }
62
65
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/static_validation/error"
3
3
  require "graphql/static_validation/definition_dependencies"
4
- require "graphql/static_validation/type_stack"
5
4
  require "graphql/static_validation/validator"
6
5
  require "graphql/static_validation/validation_context"
7
6
  require "graphql/static_validation/validation_timeout_error"
@@ -35,7 +35,7 @@ module GraphQL
35
35
  # }
36
36
  #
37
37
  # result = MySchema.execute(
38
- # query: query,
38
+ # query,
39
39
  # context: context,
40
40
  # variables: variables,
41
41
  # operation_name: operation_name
@@ -124,7 +124,8 @@ module GraphQL
124
124
  # This subscription was re-evaluated.
125
125
  # Send it to the specific stream where this client was waiting.
126
126
  def deliver(subscription_id, result)
127
- payload = { result: result.to_h, more: true }
127
+ has_more = !result.context.namespace(:subscriptions)[:final_update]
128
+ payload = { result: result.to_h, more: has_more }
128
129
  @action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
129
130
  end
130
131