graphql 2.0.27 → 2.2.5

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 (114) 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/query_complexity.rb +80 -128
  24. data/lib/graphql/analysis/ast/query_depth.rb +7 -2
  25. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  26. data/lib/graphql/analysis/ast.rb +15 -11
  27. data/lib/graphql/backtrace/trace.rb +12 -15
  28. data/lib/graphql/dataloader/async_dataloader.rb +85 -0
  29. data/lib/graphql/dataloader/source.rb +11 -3
  30. data/lib/graphql/dataloader.rb +109 -142
  31. data/lib/graphql/duration_encoding_error.rb +16 -0
  32. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  33. data/lib/graphql/execution/interpreter/runtime.rb +70 -248
  34. data/lib/graphql/execution/interpreter.rb +1 -7
  35. data/lib/graphql/execution/lookahead.rb +88 -21
  36. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  37. data/lib/graphql/introspection/entry_points.rb +2 -2
  38. data/lib/graphql/language/block_string.rb +34 -18
  39. data/lib/graphql/language/definition_slice.rb +1 -1
  40. data/lib/graphql/language/document_from_schema_definition.rb +36 -35
  41. data/lib/graphql/language/lexer.rb +271 -177
  42. data/lib/graphql/language/nodes.rb +74 -56
  43. data/lib/graphql/language/parser.rb +697 -1986
  44. data/lib/graphql/language/printer.rb +299 -146
  45. data/lib/graphql/language/sanitized_printer.rb +20 -22
  46. data/lib/graphql/language/static_visitor.rb +167 -0
  47. data/lib/graphql/language/visitor.rb +20 -81
  48. data/lib/graphql/language.rb +1 -0
  49. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  50. data/lib/graphql/pagination/connection.rb +28 -1
  51. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  52. data/lib/graphql/query/context/scoped_context.rb +101 -0
  53. data/lib/graphql/query/context.rb +36 -98
  54. data/lib/graphql/query/null_context.rb +4 -11
  55. data/lib/graphql/query.rb +12 -21
  56. data/lib/graphql/railtie.rb +9 -6
  57. data/lib/graphql/rake_task.rb +3 -12
  58. data/lib/graphql/schema/argument.rb +6 -1
  59. data/lib/graphql/schema/build_from_definition.rb +0 -11
  60. data/lib/graphql/schema/directive/one_of.rb +12 -0
  61. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  62. data/lib/graphql/schema/directive.rb +1 -1
  63. data/lib/graphql/schema/enum.rb +3 -3
  64. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  65. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  66. data/lib/graphql/schema/field.rb +8 -5
  67. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  68. data/lib/graphql/schema/input_object.rb +2 -2
  69. data/lib/graphql/schema/interface.rb +10 -10
  70. data/lib/graphql/schema/introspection_system.rb +2 -0
  71. data/lib/graphql/schema/loader.rb +0 -2
  72. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  73. data/lib/graphql/schema/member/has_arguments.rb +61 -38
  74. data/lib/graphql/schema/member/has_fields.rb +8 -5
  75. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  76. data/lib/graphql/schema/member/scoped.rb +19 -0
  77. data/lib/graphql/schema/member/validates_input.rb +3 -3
  78. data/lib/graphql/schema/object.rb +8 -0
  79. data/lib/graphql/schema/printer.rb +8 -7
  80. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  81. data/lib/graphql/schema/resolver.rb +7 -3
  82. data/lib/graphql/schema/scalar.rb +3 -3
  83. data/lib/graphql/schema/subscription.rb +11 -4
  84. data/lib/graphql/schema/union.rb +1 -1
  85. data/lib/graphql/schema/warden.rb +96 -94
  86. data/lib/graphql/schema.rb +193 -72
  87. data/lib/graphql/static_validation/all_rules.rb +1 -1
  88. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  89. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  90. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  91. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  92. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  93. data/lib/graphql/static_validation/validation_context.rb +5 -5
  94. data/lib/graphql/static_validation.rb +0 -1
  95. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  96. data/lib/graphql/subscriptions/event.rb +8 -2
  97. data/lib/graphql/subscriptions.rb +14 -9
  98. data/lib/graphql/testing/helpers.rb +125 -0
  99. data/lib/graphql/testing.rb +2 -0
  100. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  101. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  102. data/lib/graphql/tracing/trace.rb +1 -0
  103. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  104. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  105. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  106. data/lib/graphql/types.rb +1 -0
  107. data/lib/graphql/version.rb +1 -1
  108. data/lib/graphql.rb +3 -3
  109. data/readme.md +12 -2
  110. metadata +29 -22
  111. data/lib/graphql/deprecation.rb +0 -9
  112. data/lib/graphql/filter.rb +0 -59
  113. data/lib/graphql/language/parser.y +0 -560
  114. data/lib/graphql/static_validation/type_stack.rb +0 -216
@@ -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.
@@ -776,7 +821,16 @@ module GraphQL
776
821
  own_orphan_types.concat(new_orphan_types.flatten)
777
822
  end
778
823
 
779
- find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
824
+ inherited_ot = find_inherited_value(:orphan_types, nil)
825
+ if inherited_ot
826
+ if own_orphan_types.any?
827
+ inherited_ot + own_orphan_types
828
+ else
829
+ inherited_ot
830
+ end
831
+ else
832
+ own_orphan_types
833
+ end
780
834
  end
781
835
 
782
836
  def default_execution_strategy
@@ -795,6 +849,26 @@ module GraphQL
795
849
  end
796
850
  end
797
851
 
852
+ def default_logger(new_default_logger = NOT_CONFIGURED)
853
+ if NOT_CONFIGURED.equal?(new_default_logger)
854
+ if defined?(@default_logger)
855
+ @default_logger
856
+ elsif superclass.respond_to?(:default_logger)
857
+ superclass.default_logger
858
+ elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
859
+ rails_logger
860
+ else
861
+ def_logger = Logger.new($stdout)
862
+ def_logger.info! # It doesn't output debug info by default
863
+ def_logger
864
+ end
865
+ elsif new_default_logger == nil
866
+ @default_logger = Logger.new(IO::NULL)
867
+ else
868
+ @default_logger = new_default_logger
869
+ end
870
+ end
871
+
798
872
  def context_class(new_context_class = nil)
799
873
  if new_context_class
800
874
  @context_class = new_context_class
@@ -870,17 +944,19 @@ module GraphQL
870
944
  end
871
945
 
872
946
  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
947
+ 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
948
  end
879
949
  # rubocop:enable Lint/DuplicateMethods
880
950
 
881
951
  def inherited(child_class)
882
952
  if self == GraphQL::Schema
883
953
  child_class.directives(default_directives.values)
954
+ child_class.extend(SubclassGetReferencesTo)
955
+ end
956
+ # Make sure the child class has these built out, so that
957
+ # subclasses can be modified by later calls to `trace_with`
958
+ own_trace_modes.each do |name, _class|
959
+ child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
884
960
  end
885
961
  child_class.singleton_class.prepend(ResolveTypeWithType)
886
962
  super
@@ -978,7 +1054,12 @@ module GraphQL
978
1054
  new_directives.flatten.each { |d| directive(d) }
979
1055
  end
980
1056
 
981
- find_inherited_value(:directives, default_directives).merge(own_directives)
1057
+ inherited_dirs = find_inherited_value(:directives, default_directives)
1058
+ if own_directives.any?
1059
+ inherited_dirs.merge(own_directives)
1060
+ else
1061
+ inherited_dirs
1062
+ end
982
1063
  end
983
1064
 
984
1065
  # Attach a single directive to this schema
@@ -994,11 +1075,13 @@ module GraphQL
994
1075
  "skip" => GraphQL::Schema::Directive::Skip,
995
1076
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
996
1077
  "oneOf" => GraphQL::Schema::Directive::OneOf,
1078
+ "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
997
1079
  }.freeze
998
1080
  end
999
1081
 
1000
1082
  def tracer(new_tracer)
1001
- if !(trace_class_for(:default) < GraphQL::Tracing::CallLegacyTracers)
1083
+ default_trace = trace_class_for(:default)
1084
+ if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1002
1085
  trace_with(GraphQL::Tracing::CallLegacyTracers)
1003
1086
  end
1004
1087
 
@@ -1020,10 +1103,20 @@ module GraphQL
1020
1103
  if mode.is_a?(Array)
1021
1104
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
1022
1105
  else
1023
- tc = trace_class_for(mode)
1106
+ tc = own_trace_modes[mode] ||= build_trace_mode(mode)
1024
1107
  tc.include(trace_mod)
1025
- if mode != :default
1026
- own_trace_modules[mode] << trace_mod
1108
+ own_trace_modules[mode] << trace_mod
1109
+
1110
+ if mode == :default
1111
+ # This module is being added as a default tracer. If any other mode classes
1112
+ # have already been created, but get their default behavior from a superclass,
1113
+ # Then mix this into this schema's subclass.
1114
+ # (But don't mix it into mode classes that aren't default-based.)
1115
+ own_trace_modes.each do |other_mode_name, other_mode_class|
1116
+ if other_mode_class < DefaultTraceClass && !(other_mode_class < trace_mod)
1117
+ other_mode_class.include(trace_mod)
1118
+ end
1119
+ end
1027
1120
  end
1028
1121
  t_opts = trace_options_for(mode)
1029
1122
  t_opts.merge!(options)
@@ -1046,6 +1139,8 @@ module GraphQL
1046
1139
 
1047
1140
  # Create a trace instance which will include the trace modules specified for the optional mode.
1048
1141
  #
1142
+ # If no `mode:` is given, then {default_trace_mode} will be used.
1143
+ #
1049
1144
  # @param mode [Symbol] Trace modules for this trade mode will be included
1050
1145
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1051
1146
  # @return [Tracing::Trace]
@@ -1056,14 +1151,19 @@ module GraphQL
1056
1151
  trace_mode = if mode
1057
1152
  mode
1058
1153
  elsif target && target.context[:backtrace]
1059
- :default_backtrace
1154
+ if default_trace_mode != :default
1155
+ raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1156
+ else
1157
+ own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1158
+ :default_backtrace
1159
+ end
1060
1160
  else
1061
- :default
1161
+ default_trace_mode
1062
1162
  end
1063
1163
 
1064
1164
  base_trace_options = trace_options_for(trace_mode)
1065
1165
  trace_options = base_trace_options.merge(options)
1066
- trace_class_for_mode = trace_class_for(trace_mode)
1166
+ trace_class_for_mode = trace_class_for(trace_mode) || raise(ArgumentError, "#{self} has no trace class for mode: #{trace_mode.inspect}")
1067
1167
  trace_class_for_mode.new(**trace_options)
1068
1168
  end
1069
1169
 
@@ -1327,6 +1427,27 @@ module GraphQL
1327
1427
  def own_multiplex_analyzers
1328
1428
  @own_multiplex_analyzers ||= []
1329
1429
  end
1430
+
1431
+ # This is overridden in subclasses to check the inheritance chain
1432
+ def get_references_to(type_name)
1433
+ @own_references_to[type_name]
1434
+ end
1435
+ end
1436
+
1437
+ module SubclassGetReferencesTo
1438
+ def get_references_to(type_name)
1439
+ own_refs = @own_references_to[type_name]
1440
+ inherited_refs = superclass.references_to(type_name)
1441
+ if inherited_refs&.any?
1442
+ if own_refs&.any?
1443
+ own_refs + inherited_refs
1444
+ else
1445
+ inherited_refs
1446
+ end
1447
+ else
1448
+ own_refs
1449
+ end
1450
+ end
1330
1451
  end
1331
1452
 
1332
1453
  # 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
@@ -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
 
@@ -37,7 +37,7 @@ module GraphQL
37
37
  end
38
38
 
39
39
  # @return [String] an identifier for this unit of subscription
40
- def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
40
+ def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance)
41
41
  subscription = field.resolver || GraphQL::Schema::Subscription
42
42
  normalized_args = stringify_args(field, arguments.to_h, context)
43
43
  subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
@@ -126,7 +126,13 @@ module GraphQL
126
126
  when GraphQL::Schema::InputObject
127
127
  stringify_args(arg_owner, args.to_h, context)
128
128
  else
129
- args
129
+ if arg_owner.is_a?(Class) && arg_owner < GraphQL::Schema::Enum
130
+ # `prepare:` may have made the value something other than
131
+ # a defined value of this enum -- use _that_ in this case.
132
+ arg_owner.coerce_isolated_input(args) || args
133
+ else
134
+ args
135
+ end
130
136
  end
131
137
  end
132
138