graphql 2.0.27 → 2.2.6

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