graphql 2.4.5 → 2.5.21

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  4. data/lib/graphql/analysis/analyzer.rb +2 -1
  5. data/lib/graphql/analysis/query_complexity.rb +87 -7
  6. data/lib/graphql/analysis/visitor.rb +37 -40
  7. data/lib/graphql/analysis.rb +12 -9
  8. data/lib/graphql/autoload.rb +1 -0
  9. data/lib/graphql/backtrace/table.rb +118 -55
  10. data/lib/graphql/backtrace.rb +1 -19
  11. data/lib/graphql/current.rb +6 -1
  12. data/lib/graphql/dashboard/application_controller.rb +41 -0
  13. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  14. data/lib/graphql/dashboard/installable.rb +22 -0
  15. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  16. data/lib/graphql/dashboard/limiters.rb +93 -0
  17. data/lib/graphql/dashboard/operation_store.rb +199 -0
  18. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  19. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  20. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  21. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  22. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  23. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  24. data/lib/graphql/dashboard/statics/icon.png +0 -0
  25. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  26. data/lib/graphql/dashboard/subscriptions.rb +97 -0
  27. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  28. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  29. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  30. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  31. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  36. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  37. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  38. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  39. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  40. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  41. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  42. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  43. data/lib/graphql/dashboard.rb +96 -0
  44. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  45. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  46. data/lib/graphql/dataloader/async_dataloader.rb +38 -15
  47. data/lib/graphql/dataloader/null_dataloader.rb +55 -10
  48. data/lib/graphql/dataloader/source.rb +18 -6
  49. data/lib/graphql/dataloader.rb +110 -26
  50. data/lib/graphql/date_encoding_error.rb +1 -1
  51. data/lib/graphql/dig.rb +2 -1
  52. data/lib/graphql/execution/interpreter/resolve.rb +10 -16
  53. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +58 -5
  54. data/lib/graphql/execution/interpreter/runtime.rb +229 -93
  55. data/lib/graphql/execution/interpreter.rb +15 -24
  56. data/lib/graphql/execution/multiplex.rb +7 -6
  57. data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
  58. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  59. data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
  60. data/lib/graphql/execution/next/runner.rb +389 -0
  61. data/lib/graphql/execution/next/selections_step.rb +37 -0
  62. data/lib/graphql/execution/next.rb +69 -0
  63. data/lib/graphql/execution.rb +1 -0
  64. data/lib/graphql/execution_error.rb +13 -10
  65. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  66. data/lib/graphql/introspection/directive_type.rb +7 -3
  67. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  68. data/lib/graphql/introspection/entry_points.rb +11 -3
  69. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  70. data/lib/graphql/introspection/field_type.rb +13 -5
  71. data/lib/graphql/introspection/input_value_type.rb +21 -13
  72. data/lib/graphql/introspection/type_type.rb +64 -28
  73. data/lib/graphql/invalid_name_error.rb +1 -1
  74. data/lib/graphql/invalid_null_error.rb +25 -16
  75. data/lib/graphql/language/document_from_schema_definition.rb +2 -1
  76. data/lib/graphql/language/lexer.rb +16 -5
  77. data/lib/graphql/language/nodes.rb +8 -1
  78. data/lib/graphql/language/parser.rb +16 -8
  79. data/lib/graphql/language/static_visitor.rb +37 -33
  80. data/lib/graphql/language/visitor.rb +59 -55
  81. data/lib/graphql/language.rb +21 -12
  82. data/lib/graphql/pagination/connection.rb +2 -0
  83. data/lib/graphql/pagination/connections.rb +32 -0
  84. data/lib/graphql/query/context.rb +6 -10
  85. data/lib/graphql/query/null_context.rb +9 -3
  86. data/lib/graphql/query/partial.rb +179 -0
  87. data/lib/graphql/query.rb +64 -64
  88. data/lib/graphql/railtie.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +3 -1
  90. data/lib/graphql/schema/always_visible.rb +1 -0
  91. data/lib/graphql/schema/argument.rb +24 -8
  92. data/lib/graphql/schema/build_from_definition.rb +113 -54
  93. data/lib/graphql/schema/directive/flagged.rb +2 -0
  94. data/lib/graphql/schema/directive.rb +52 -2
  95. data/lib/graphql/schema/enum.rb +36 -1
  96. data/lib/graphql/schema/enum_value.rb +1 -1
  97. data/lib/graphql/schema/field/connection_extension.rb +15 -35
  98. data/lib/graphql/schema/field/scope_extension.rb +22 -13
  99. data/lib/graphql/schema/field.rb +101 -51
  100. data/lib/graphql/schema/field_extension.rb +33 -0
  101. data/lib/graphql/schema/input_object.rb +45 -38
  102. data/lib/graphql/schema/interface.rb +2 -1
  103. data/lib/graphql/schema/list.rb +1 -1
  104. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  105. data/lib/graphql/schema/member/has_arguments.rb +56 -19
  106. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  107. data/lib/graphql/schema/member/has_dataloader.rb +79 -0
  108. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  109. data/lib/graphql/schema/member/has_directives.rb +1 -1
  110. data/lib/graphql/schema/member/has_fields.rb +81 -5
  111. data/lib/graphql/schema/member/has_interfaces.rb +3 -3
  112. data/lib/graphql/schema/member/scoped.rb +1 -1
  113. data/lib/graphql/schema/member/type_system_helpers.rb +17 -3
  114. data/lib/graphql/schema/member.rb +6 -0
  115. data/lib/graphql/schema/object.rb +18 -8
  116. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  117. data/lib/graphql/schema/resolver.rb +52 -6
  118. data/lib/graphql/schema/scalar.rb +1 -6
  119. data/lib/graphql/schema/subscription.rb +50 -4
  120. data/lib/graphql/schema/timeout.rb +19 -2
  121. data/lib/graphql/schema/validator/required_validator.rb +71 -14
  122. data/lib/graphql/schema/visibility/migration.rb +3 -2
  123. data/lib/graphql/schema/visibility/profile.rb +115 -23
  124. data/lib/graphql/schema/visibility.rb +49 -32
  125. data/lib/graphql/schema/warden.rb +23 -2
  126. data/lib/graphql/schema.rb +333 -68
  127. data/lib/graphql/static_validation/all_rules.rb +2 -2
  128. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  129. data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
  130. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  131. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  132. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  133. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
  134. data/lib/graphql/static_validation/validator.rb +6 -1
  135. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
  136. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  137. data/lib/graphql/subscriptions/event.rb +12 -1
  138. data/lib/graphql/subscriptions/serialize.rb +1 -1
  139. data/lib/graphql/subscriptions.rb +1 -1
  140. data/lib/graphql/testing/helpers.rb +17 -11
  141. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  142. data/lib/graphql/testing.rb +1 -0
  143. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  144. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  145. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  146. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  147. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  148. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  149. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  150. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  151. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  152. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  153. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  154. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  155. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  156. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  157. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  158. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  159. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  160. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  161. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  162. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  163. data/lib/graphql/tracing/null_trace.rb +9 -0
  164. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  165. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  166. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  167. data/lib/graphql/tracing/platform_trace.rb +5 -0
  168. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  169. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  170. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  171. data/lib/graphql/tracing/scout_trace.rb +32 -55
  172. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  173. data/lib/graphql/tracing/sentry_trace.rb +64 -94
  174. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  175. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  176. data/lib/graphql/tracing/trace.rb +111 -1
  177. data/lib/graphql/tracing.rb +31 -30
  178. data/lib/graphql/type_kinds.rb +1 -0
  179. data/lib/graphql/types/relay/connection_behaviors.rb +9 -7
  180. data/lib/graphql/types/relay/edge_behaviors.rb +5 -4
  181. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  182. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  183. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  184. data/lib/graphql/unauthorized_error.rb +5 -1
  185. data/lib/graphql/version.rb +1 -1
  186. data/lib/graphql.rb +12 -31
  187. metadata +174 -11
  188. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  189. data/lib/graphql/backtrace/trace.rb +0 -93
  190. data/lib/graphql/backtrace/tracer.rb +0 -80
  191. data/lib/graphql/schema/null_mask.rb +0 -11
  192. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -7,7 +7,7 @@ require "graphql/schema/find_inherited_value"
7
7
  require "graphql/schema/finder"
8
8
  require "graphql/schema/introspection_system"
9
9
  require "graphql/schema/late_bound_type"
10
- require "graphql/schema/null_mask"
10
+ require "graphql/schema/ractor_shareable"
11
11
  require "graphql/schema/timeout"
12
12
  require "graphql/schema/type_expression"
13
13
  require "graphql/schema/unique_within_type"
@@ -47,8 +47,6 @@ require "graphql/schema/relay_classic_mutation"
47
47
  require "graphql/schema/subscription"
48
48
  require "graphql/schema/visibility"
49
49
 
50
- GraphQL.ensure_eager_load!
51
-
52
50
  module GraphQL
53
51
  # A GraphQL schema which may be queried with {GraphQL::Query}.
54
52
  #
@@ -63,7 +61,7 @@ module GraphQL
63
61
  # Any undiscoverable types may be provided with the `types` configuration.
64
62
  #
65
63
  # Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
66
- # (These configurations can be overridden by specific calls to {Schema#execute})
64
+ # (These configurations can be overridden by specific calls to {Schema.execute})
67
65
  #
68
66
  # @example defining a schema
69
67
  # class MySchema < GraphQL::Schema
@@ -75,6 +73,9 @@ module GraphQL
75
73
  class Schema
76
74
  extend GraphQL::Schema::Member::HasAstNode
77
75
  extend GraphQL::Schema::FindInheritedValue
76
+ extend Autoload
77
+
78
+ autoload :BUILT_IN_TYPES, "graphql/schema/built_in_types"
78
79
 
79
80
  class DuplicateNamesError < GraphQL::Error
80
81
  attr_reader :duplicated_name
@@ -111,7 +112,7 @@ module GraphQL
111
112
  # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
112
113
  # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
113
114
  # @return [Class] the schema described by `document`
114
- def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
115
+ def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}, base_types: {})
115
116
  # If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
116
117
  if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
117
118
  GraphQL::Schema::BuildFromDefinition.from_definition_path(
@@ -120,6 +121,7 @@ module GraphQL
120
121
  default_resolve: default_resolve,
121
122
  parser: parser,
122
123
  using: using,
124
+ base_types: base_types,
123
125
  )
124
126
  else
125
127
  GraphQL::Schema::BuildFromDefinition.from_definition(
@@ -128,6 +130,7 @@ module GraphQL
128
130
  default_resolve: default_resolve,
129
131
  parser: parser,
130
132
  using: using,
133
+ base_types: base_types,
131
134
  )
132
135
  end
133
136
  end
@@ -146,10 +149,12 @@ module GraphQL
146
149
  end
147
150
 
148
151
  # @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
149
- def default_trace_mode(new_mode = nil)
150
- if new_mode
152
+ def default_trace_mode(new_mode = NOT_CONFIGURED)
153
+ if !NOT_CONFIGURED.equal?(new_mode)
151
154
  @default_trace_mode = new_mode
152
- elsif defined?(@default_trace_mode)
155
+ elsif defined?(@default_trace_mode) &&
156
+ !@default_trace_mode.nil? # This `nil?` check seems necessary because of
157
+ # Ractors silently initializing @default_trace_mode somehow
153
158
  @default_trace_mode
154
159
  elsif superclass.respond_to?(:default_trace_mode)
155
160
  superclass.default_trace_mode
@@ -166,9 +171,6 @@ module GraphQL
166
171
  mods.each { |mod| new_class.include(mod) }
167
172
  new_class.include(DefaultTraceClass)
168
173
  trace_mode(:default, new_class)
169
- backtrace_class = Class.new(new_class)
170
- backtrace_class.include(GraphQL::Backtrace::Trace)
171
- trace_mode(:default_backtrace, backtrace_class)
172
174
  end
173
175
  trace_class_for(:default, build: true)
174
176
  end
@@ -215,11 +217,6 @@ module GraphQL
215
217
  const_set(:DefaultTrace, Class.new(base_class) do
216
218
  include DefaultTraceClass
217
219
  end)
218
- when :default_backtrace
219
- schema_base_class = trace_class_for(:default, build: true)
220
- const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
221
- include(GraphQL::Backtrace::Trace)
222
- end)
223
220
  else
224
221
  # First, see if the superclass has a custom-defined class for this.
225
222
  # Then, if it doesn't, use this class's default trace
@@ -255,7 +252,7 @@ module GraphQL
255
252
 
256
253
 
257
254
  # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
258
- # @see {#as_json}
255
+ # @see #as_json Return a Hash representation of the schema
259
256
  # @return [String]
260
257
  def to_json(**args)
261
258
  JSON.pretty_generate(as_json(**args))
@@ -263,8 +260,6 @@ module GraphQL
263
260
 
264
261
  # Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
265
262
  # @param context [Hash]
266
- # @param only [<#call(member, ctx)>]
267
- # @param except [<#call(member, ctx)>]
268
263
  # @param include_deprecated_args [Boolean] If true, deprecated arguments will be included in the JSON response
269
264
  # @param include_schema_description [Boolean] If true, the schema's description will be queried and included in the response
270
265
  # @param include_is_repeatable [Boolean] If true, `isRepeatable: true|false` will be included with the schema's directives
@@ -335,10 +330,16 @@ module GraphQL
335
330
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
336
331
  end
337
332
 
333
+ attr_writer :null_context
334
+
335
+ def null_context
336
+ @null_context || GraphQL::Query::NullContext.instance
337
+ end
338
+
338
339
  # Build a map of `{ name => type }` and return it
339
340
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
340
341
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
341
- def types(context = GraphQL::Query::NullContext.instance)
342
+ def types(context = null_context)
342
343
  if use_visibility_profile?
343
344
  types = Visibility::Profile.from_context(context, self)
344
345
  return types.all_types_h
@@ -371,9 +372,10 @@ module GraphQL
371
372
  # @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
372
373
  # @param use_visibility_profile Private, for migration to {Schema::Visibility}
373
374
  # @return [Module, nil] A type, or nil if there's no type called `type_name`
374
- def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
375
+ def get_type(type_name, context = null_context, use_visibility_profile = use_visibility_profile?)
375
376
  if use_visibility_profile
376
- return Visibility::Profile.from_context(context, self).type(type_name)
377
+ profile = Visibility::Profile.from_context(context, self)
378
+ return profile.type(type_name)
377
379
  end
378
380
  local_entry = own_types[type_name]
379
381
  type_defn = case local_entry
@@ -621,7 +623,7 @@ module GraphQL
621
623
  # @param use_visibility_profile Private, for migration to {Schema::Visibility}
622
624
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
623
625
  # @return [Array<Module>] Possible types for `type`, if it's given.
624
- def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
626
+ def possible_types(type = nil, context = null_context, use_visibility_profile = use_visibility_profile?)
625
627
  if use_visibility_profile
626
628
  if type
627
629
  return Visibility::Profile.from_context(context, self).possible_types(type)
@@ -705,7 +707,21 @@ module GraphQL
705
707
  GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
706
708
  end
707
709
 
708
- def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
710
+ def get_field(type_or_name, field_name, context = null_context, use_visibility_profile = use_visibility_profile?)
711
+ if use_visibility_profile
712
+ profile = Visibility::Profile.from_context(context, self)
713
+ parent_type = case type_or_name
714
+ when String
715
+ profile.type(type_or_name)
716
+ when Module
717
+ type_or_name
718
+ when LateBoundType
719
+ profile.type(type_or_name.name)
720
+ else
721
+ raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
722
+ end
723
+ return profile.field(parent_type, field_name)
724
+ end
709
725
  parent_type = case type_or_name
710
726
  when LateBoundType
711
727
  get_type(type_or_name.name, context)
@@ -728,7 +744,7 @@ module GraphQL
728
744
  end
729
745
  end
730
746
 
731
- def get_fields(type, context = GraphQL::Query::NullContext.instance)
747
+ def get_fields(type, context = null_context)
732
748
  type.fields(context)
733
749
  end
734
750
 
@@ -829,13 +845,13 @@ module GraphQL
829
845
 
830
846
  attr_writer :validate_timeout
831
847
 
832
- def validate_timeout(new_validate_timeout = nil)
833
- if new_validate_timeout
848
+ def validate_timeout(new_validate_timeout = NOT_CONFIGURED)
849
+ if !NOT_CONFIGURED.equal?(new_validate_timeout)
834
850
  @validate_timeout = new_validate_timeout
835
851
  elsif defined?(@validate_timeout)
836
852
  @validate_timeout
837
853
  else
838
- find_inherited_value(:validate_timeout)
854
+ find_inherited_value(:validate_timeout) || 3
839
855
  end
840
856
  end
841
857
 
@@ -1072,6 +1088,18 @@ module GraphQL
1072
1088
  end
1073
1089
  end
1074
1090
 
1091
+ # @param context [GraphQL::Query::Context, nil]
1092
+ # @return [Logger] A logger to use for this context configuration, falling back to {.default_logger}
1093
+ def logger_for(context)
1094
+ if context && context[:logger] == false
1095
+ Logger.new(IO::NULL)
1096
+ elsif context && (l = context[:logger])
1097
+ l
1098
+ else
1099
+ default_logger
1100
+ end
1101
+ end
1102
+
1075
1103
  # @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
1076
1104
  def context_class(new_context_class = nil)
1077
1105
  if new_context_class
@@ -1101,22 +1129,26 @@ module GraphQL
1101
1129
  end
1102
1130
  end
1103
1131
 
1104
- NEW_HANDLER_HASH = ->(h, k) {
1105
- h[k] = {
1106
- class: k,
1107
- handler: nil,
1108
- subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1109
- }
1110
- }
1111
-
1112
1132
  def error_handlers
1113
- @error_handlers ||= {
1114
- class: nil,
1115
- handler: nil,
1116
- subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
1117
- }
1133
+ @error_handlers ||= begin
1134
+ new_handler_hash = ->(h, k) {
1135
+ h[k] = {
1136
+ class: k,
1137
+ handler: nil,
1138
+ subclass_handlers: Hash.new(&new_handler_hash),
1139
+ }
1140
+ }
1141
+ {
1142
+ class: nil,
1143
+ handler: nil,
1144
+ subclass_handlers: Hash.new(&new_handler_hash),
1145
+ }
1146
+ end
1118
1147
  end
1119
1148
 
1149
+ # @api private
1150
+ attr_accessor :using_backtrace
1151
+
1120
1152
  # @api private
1121
1153
  def handle_or_reraise(context, err)
1122
1154
  handler = Execution::Errors.find_handler_for(self, err.class)
@@ -1130,6 +1162,10 @@ module GraphQL
1130
1162
  end
1131
1163
  handler[:handler].call(err, obj, args, context, field)
1132
1164
  else
1165
+ if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
1166
+ err = GraphQL::Backtrace::TracedError.new(err, context)
1167
+ end
1168
+
1133
1169
  raise err
1134
1170
  end
1135
1171
  end
@@ -1164,7 +1200,7 @@ module GraphQL
1164
1200
  # GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object.
1165
1201
  #
1166
1202
  # Usually, this object was returned from a field whose return type is an {GraphQL::Schema::Interface} or a {GraphQL::Schema::Union}.
1167
- # But this method is called in other cases, too -- for example, when {GraphQL::Schema::Argument.loads} cases an object to be directly loaded from the database.
1203
+ # But this method is called in other cases, too -- for example, when {GraphQL::Schema::Argument#loads} cases an object to be directly loaded from the database.
1168
1204
  #
1169
1205
  # @example Returning a GraphQL type based on the object's class name
1170
1206
  # class MySchema < GraphQL::Schema
@@ -1178,7 +1214,7 @@ module GraphQL
1178
1214
  # @param context [GraphQL::Query::Context] The query context for the currently-executing query
1179
1215
  # @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
1180
1216
  def resolve_type(abstract_type, application_object, context)
1181
- raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, or `loads:` (tried to resolve: #{abstract_type.name})"
1217
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, `loads:`, or `run_partials` (tried to resolve: #{abstract_type.name})"
1182
1218
  end
1183
1219
  # rubocop:enable Lint/DuplicateMethods
1184
1220
 
@@ -1198,6 +1234,7 @@ module GraphQL
1198
1234
  vis = self.visibility
1199
1235
  child_class.visibility = vis.dup_for(child_class)
1200
1236
  end
1237
+ child_class.null_context = Query::NullContext.new(schema: child_class)
1201
1238
  super
1202
1239
  end
1203
1240
 
@@ -1219,7 +1256,7 @@ module GraphQL
1219
1256
 
1220
1257
  # Return a stable ID string for `object` so that it can be refetched later, using {.object_from_id}.
1221
1258
  #
1222
- # {GlobalID}(https://github.com/rails/globalid) and {SQIDs}(https://sqids.org/ruby) can both be used to create IDs.
1259
+ # [GlobalID](https://github.com/rails/globalid) and [SQIDs](https://sqids.org/ruby) can both be used to create IDs.
1223
1260
  #
1224
1261
  # @example Using Rails's GlobalID to generate IDs
1225
1262
  # def self.id_from_object(application_object, graphql_type, context)
@@ -1296,10 +1333,14 @@ module GraphQL
1296
1333
  # @return [void]
1297
1334
  # @raise [GraphQL::ExecutionError] to return this error to the client
1298
1335
  # @raise [GraphQL::Error] to crash the query and raise a developer-facing error
1299
- def type_error(type_error, ctx)
1336
+ def type_error(type_error, context)
1300
1337
  case type_error
1301
1338
  when GraphQL::InvalidNullError
1302
- ctx.errors << type_error
1339
+ execution_error = GraphQL::ExecutionError.new(type_error.message, ast_nodes: type_error.ast_nodes)
1340
+ execution_error.path = type_error.path || context[:current_path]
1341
+
1342
+ context.errors << execution_error
1343
+ execution_error
1303
1344
  when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
1304
1345
  raise type_error
1305
1346
  when GraphQL::IntegerDecodingError
@@ -1307,7 +1348,7 @@ module GraphQL
1307
1348
  end
1308
1349
  end
1309
1350
 
1310
- # A function to call when {#execute} receives an invalid query string
1351
+ # A function to call when {.execute} receives an invalid query string
1311
1352
  #
1312
1353
  # The default is to add the error to `context.errors`
1313
1354
  # @param parse_err [GraphQL::ParseError] The error encountered during parsing
@@ -1321,6 +1362,24 @@ module GraphQL
1321
1362
  lazy_methods.set(lazy_class, value_method)
1322
1363
  end
1323
1364
 
1365
+ def uses_raw_value?
1366
+ !!@uses_raw_value
1367
+ end
1368
+
1369
+ def uses_raw_value(new_val)
1370
+ @uses_raw_value = new_val
1371
+ end
1372
+
1373
+ def resolves_lazies?
1374
+ lazy_method_count = 0
1375
+ lazy_methods.each do |k, v|
1376
+ if !v.nil?
1377
+ lazy_method_count += 1
1378
+ end
1379
+ end
1380
+ lazy_method_count > 2
1381
+ end
1382
+
1324
1383
  def instrument(instrument_step, instrumenter, options = {})
1325
1384
  warn <<~WARN
1326
1385
  Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
@@ -1367,6 +1426,16 @@ module GraphQL
1367
1426
  }.freeze
1368
1427
  end
1369
1428
 
1429
+ # @return [GraphQL::Tracing::DetailedTrace] if it has been configured for this schema
1430
+ attr_accessor :detailed_trace
1431
+
1432
+ # @param query [GraphQL::Query, GraphQL::Execution::Multiplex] Called with a multiplex when multiple queries are executed at once (with {.multiplex})
1433
+ # @return [Boolean] When `true`, save a detailed trace for this query.
1434
+ # @see Tracing::DetailedTrace DetailedTrace saves traces when this method returns true
1435
+ def detailed_trace?(query)
1436
+ raise "#{self} must implement `def.detailed_trace?(query)` to use DetailedTrace. Implement this method in your schema definition."
1437
+ end
1438
+
1370
1439
  def tracer(new_tracer, silence_deprecation_warning: false)
1371
1440
  if !silence_deprecation_warning
1372
1441
  warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
@@ -1384,14 +1453,22 @@ module GraphQL
1384
1453
  find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
1385
1454
  end
1386
1455
 
1387
- # Mix `trace_mod` into this schema's `Trace` class so that its methods
1388
- # will be called at runtime.
1456
+ # Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime.
1457
+ #
1458
+ # You can attach a module to run in only _some_ circumstances by using `mode:`. When a module is added with `mode:`,
1459
+ # it will only run for queries with a matching `context[:trace_mode]`.
1460
+ #
1461
+ # Any custom trace modes _also_ include the default `trace_with ...` modules (that is, those added _without_ any particular `mode: ...` configuration).
1462
+ #
1463
+ # @example Adding a trace in a special mode
1464
+ # # only runs when `query.context[:trace_mode]` is `:special`
1465
+ # trace_with SpecialTrace, mode: :special
1389
1466
  #
1390
1467
  # @param trace_mod [Module] A module that implements tracing methods
1391
1468
  # @param mode [Symbol] Trace module will only be used for this trade mode
1392
1469
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1393
1470
  # @return [void]
1394
- # @see GraphQL::Tracing::Trace for available tracing methods
1471
+ # @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods
1395
1472
  def trace_with(trace_mod, mode: :default, **options)
1396
1473
  if mode.is_a?(Array)
1397
1474
  mode.each { |m| trace_with(trace_mod, mode: m, **options) }
@@ -1441,29 +1518,36 @@ module GraphQL
1441
1518
  #
1442
1519
  # If no `mode:` is given, then {default_trace_mode} will be used.
1443
1520
  #
1521
+ # If this schema is using {Tracing::DetailedTrace} and {.detailed_trace?} returns `true`, then
1522
+ # DetailedTrace's mode will override the passed-in `mode`.
1523
+ #
1444
1524
  # @param mode [Symbol] Trace modules for this trade mode will be included
1445
1525
  # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1446
1526
  # @return [Tracing::Trace]
1447
1527
  def new_trace(mode: nil, **options)
1448
- target = options[:query] || options[:multiplex]
1449
- mode ||= target && target.context[:trace_mode]
1450
-
1451
- trace_mode = if mode
1452
- mode
1453
- elsif target && target.context[:backtrace]
1454
- if default_trace_mode != :default
1455
- raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1456
- else
1457
- own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1458
- options_trace_mode = :default
1459
- :default_backtrace
1528
+ should_sample = if detailed_trace
1529
+ if (query = options[:query])
1530
+ detailed_trace?(query)
1531
+ elsif (multiplex = options[:multiplex])
1532
+ if multiplex.queries.length == 1
1533
+ detailed_trace?(multiplex.queries.first)
1534
+ else
1535
+ detailed_trace?(multiplex)
1536
+ end
1460
1537
  end
1461
1538
  else
1462
- default_trace_mode
1539
+ false
1540
+ end
1541
+
1542
+ if should_sample
1543
+ mode = detailed_trace.trace_mode
1544
+ else
1545
+ target = options[:query] || options[:multiplex]
1546
+ mode ||= target && target.context[:trace_mode]
1463
1547
  end
1464
1548
 
1465
- options_trace_mode ||= trace_mode
1466
- base_trace_options = trace_options_for(options_trace_mode)
1549
+ trace_mode = mode || default_trace_mode
1550
+ base_trace_options = trace_options_for(trace_mode)
1467
1551
  trace_options = base_trace_options.merge(options)
1468
1552
  trace_class_for_mode = trace_class_for(trace_mode, build: true)
1469
1553
  trace_class_for_mode.new(**trace_options)
@@ -1538,7 +1622,8 @@ module GraphQL
1538
1622
  # @see {Query#initialize} for query keyword arguments
1539
1623
  # @see {Execution::Multiplex#run_all} for multiplex keyword arguments
1540
1624
  # @param queries [Array<Hash>] Keyword arguments for each query
1541
- # @param context [Hash] Multiplex-level context
1625
+ # @option kwargs [Hash] :context ({}) Multiplex-level context
1626
+ # @option kwargs [nil, Integer] :max_complexity (nil)
1542
1627
  # @return [Array<GraphQL::Query::Result>] One result for each query in the input
1543
1628
  def multiplex(queries, **kwargs)
1544
1629
  GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
@@ -1603,7 +1688,7 @@ module GraphQL
1603
1688
  end
1604
1689
  end
1605
1690
 
1606
- # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
1691
+ # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {.lazy_resolve}.
1607
1692
  def lazy_method_name(obj)
1608
1693
  lazy_methods.get(obj)
1609
1694
  end
@@ -1641,6 +1726,187 @@ module GraphQL
1641
1726
  end
1642
1727
  end
1643
1728
 
1729
+
1730
+ # This setting controls how GraphQL-Ruby handles empty selections on Union types.
1731
+ #
1732
+ # To opt into future, spec-compliant behavior where these selections are rejected, set this to `false`.
1733
+ #
1734
+ # If you need to support previous, non-spec behavior which allowed selecting union fields
1735
+ # but *not* selecting any fields on that union, set this to `true` to continue allowing that behavior.
1736
+ #
1737
+ # If this is `true`, then {.legacy_invalid_empty_selections_on_union_with_type} will be called with {Query} objects
1738
+ # with that kind of selections. You must implement that method
1739
+ # @param new_value [Boolean]
1740
+ # @return [true, false, nil]
1741
+ def allow_legacy_invalid_empty_selections_on_union(new_value = NOT_CONFIGURED)
1742
+ if NOT_CONFIGURED.equal?(new_value)
1743
+ if defined?(@allow_legacy_invalid_empty_selections_on_union)
1744
+ @allow_legacy_invalid_empty_selections_on_union
1745
+ else
1746
+ find_inherited_value(:allow_legacy_invalid_empty_selections_on_union)
1747
+ end
1748
+ else
1749
+ @allow_legacy_invalid_empty_selections_on_union = new_value
1750
+ end
1751
+ end
1752
+
1753
+ # This method is called during validation when a previously-allowed, but non-spec
1754
+ # query is encountered where a union field has no child selections on it.
1755
+ #
1756
+ # If `legacy_invalid_empty_selections_on_union_with_type` is overridden, this method will not be called.
1757
+ #
1758
+ # You should implement this method or `legacy_invalid_empty_selections_on_union_with_type`
1759
+ # to log the violation so that you can contact clients and notify them about changing their queries.
1760
+ # Then return a suitable value to tell GraphQL-Ruby how to continue.
1761
+ # @param query [GraphQL::Query]
1762
+ # @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
1763
+ # @return [String] A validation error to return for this query
1764
+ # @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
1765
+ def legacy_invalid_empty_selections_on_union(query)
1766
+ raise "Implement `def self.legacy_invalid_empty_selections_on_union_with_type(query, type)` or `def self.legacy_invalid_empty_selections_on_union(query)` to handle this scenario"
1767
+ end
1768
+
1769
+ # This method is called during validation when a previously-allowed, but non-spec
1770
+ # query is encountered where a union field has no child selections on it.
1771
+ #
1772
+ # You should implement this method to log the violation so that you can contact clients
1773
+ # and notify them about changing their queries. Then return a suitable value to
1774
+ # tell GraphQL-Ruby how to continue.
1775
+ # @param query [GraphQL::Query]
1776
+ # @param type [Module] A GraphQL type definition
1777
+ # @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
1778
+ # @return [String] A validation error to return for this query
1779
+ # @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
1780
+ def legacy_invalid_empty_selections_on_union_with_type(query, type)
1781
+ legacy_invalid_empty_selections_on_union(query)
1782
+ end
1783
+
1784
+ # This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types
1785
+ # don't match.
1786
+ #
1787
+ # When set to `false`, GraphQL-Ruby will reject those queries with a validation error (as per the GraphQL spec).
1788
+ #
1789
+ # When set to `true`, GraphQL-Ruby will call {.legacy_invalid_return_type_conflicts} when the scenario is encountered.
1790
+ #
1791
+ # @param new_value [Boolean] `true` permits the legacy behavior, `false` rejects it.
1792
+ # @return [true, false, nil]
1793
+ def allow_legacy_invalid_return_type_conflicts(new_value = NOT_CONFIGURED)
1794
+ if NOT_CONFIGURED.equal?(new_value)
1795
+ if defined?(@allow_legacy_invalid_return_type_conflicts)
1796
+ @allow_legacy_invalid_return_type_conflicts
1797
+ else
1798
+ find_inherited_value(:allow_legacy_invalid_return_type_conflicts)
1799
+ end
1800
+ else
1801
+ @allow_legacy_invalid_return_type_conflicts = new_value
1802
+ end
1803
+ end
1804
+
1805
+ # This method is called when the query contains fields which don't contain matching scalar types.
1806
+ # This was previously allowed by GraphQL-Ruby but it's a violation of the GraphQL spec.
1807
+ #
1808
+ # You should implement this method to log the violation so that you observe usage of these fields.
1809
+ # Fixing this scenario might mean adding new fields, and telling clients to use those fields.
1810
+ # (Changing the field return type would be a breaking change, but if it works for your client use cases,
1811
+ # that might work, too.)
1812
+ #
1813
+ # @param query [GraphQL::Query]
1814
+ # @param type1 [Module] A GraphQL type definition
1815
+ # @param type2 [Module] A GraphQL type definition
1816
+ # @param node1 [GraphQL::Language::Nodes::Field] This node is recognized as conflicting. You might call `.line` and `.col` for custom error reporting.
1817
+ # @param node2 [GraphQL::Language::Nodes::Field] The other node recognized as conflicting.
1818
+ # @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
1819
+ # @return [String] A validation error to return for this query
1820
+ # @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
1821
+ def legacy_invalid_return_type_conflicts(query, type1, type2, node1, node2)
1822
+ raise "Implement #{self}.legacy_invalid_return_type_conflicts to handle this invalid selection"
1823
+ end
1824
+
1825
+ # The legacy complexity implementation included several bugs:
1826
+ #
1827
+ # - In some cases, it used the lexically _last_ field to determine a cost, instead of calculating the maximum among selections
1828
+ # - In some cases, it called field complexity hooks repeatedly (when it should have only called them once)
1829
+ #
1830
+ # The future implementation may produce higher total complexity scores, so it's not active by default yet. You can opt into
1831
+ # the future default behavior by configuring `:future` here. Or, you can choose a mode for each query with {.complexity_cost_calculation_mode_for}.
1832
+ #
1833
+ # The legacy mode is currently maintained alongside the future one, but it will be removed in a future GraphQL-Ruby version.
1834
+ #
1835
+ # If you choose `:compare`, you must also implement {.legacy_complexity_cost_calculation_mismatch} to handle the input somehow.
1836
+ #
1837
+ # @example Opting into the future calculation mode
1838
+ # complexity_cost_calculation_mode(:future)
1839
+ #
1840
+ # @example Choosing the legacy mode (which will work until that mode is removed...)
1841
+ # complexity_cost_calculation_mode(:legacy)
1842
+ #
1843
+ # @example Run both modes for every query, call {.legacy_complexity_cost_calculation_mismatch} when they don't match:
1844
+ # complexity_cost_calculation_mode(:compare)
1845
+ def complexity_cost_calculation_mode(new_mode = NOT_CONFIGURED)
1846
+ if NOT_CONFIGURED.equal?(new_mode)
1847
+ if defined?(@complexity_cost_calculation_mode)
1848
+ @complexity_cost_calculation_mode
1849
+ else
1850
+ find_inherited_value(:complexity_cost_calculation_mode)
1851
+ end
1852
+ else
1853
+ @complexity_cost_calculation_mode = new_mode
1854
+ end
1855
+ end
1856
+
1857
+ # Implement this method to produce a per-query complexity cost calculation mode. (Technically, it's per-multiplex.)
1858
+ #
1859
+ # This is a way to check the compatibility of queries coming to your API without adding overhead of running `:compare`
1860
+ # for every query. You could sample traffic, turn it off/on with feature flags, or anything else.
1861
+ #
1862
+ # @example Sampling traffic
1863
+ # def self.complexity_cost_calculation_mode_for(_context)
1864
+ # if rand < 0.1 # 10% of the time
1865
+ # :compare
1866
+ # else
1867
+ # :legacy
1868
+ # end
1869
+ # end
1870
+ #
1871
+ # @example Using a feature flag to manage future mode
1872
+ # def complexity_cost_calculation_mode_for(context)
1873
+ # current_user = context[:current_user]
1874
+ # if Flipper.enabled?(:future_complexity_cost, current_user)
1875
+ # :future
1876
+ # elsif rand < 0.5 # 50%
1877
+ # :compare
1878
+ # else
1879
+ # :legacy
1880
+ # end
1881
+ # end
1882
+ #
1883
+ # @param multiplex_context [Hash] The context for the currently-running {Execution::Multiplex} (which contains one or more queries)
1884
+ # @return [:future] Use the new calculation algorithm -- may be higher than `:legacy`
1885
+ # @return [:legacy] Use the legacy calculation algorithm, warts and all
1886
+ # @return [:compare] Run both algorithms and call {.legacy_complexity_cost_calculation_mismatch} if they don't match
1887
+ def complexity_cost_calculation_mode_for(multiplex_context)
1888
+ complexity_cost_calculation_mode
1889
+ end
1890
+
1891
+ # Implement this method in your schema to handle mismatches when `:compare` is used.
1892
+ #
1893
+ # @example Logging the mismatch
1894
+ # def self.legacy_cost_calculation_mismatch(multiplex, future_cost, legacy_cost)
1895
+ # client_id = multiplex.context[:api_client].id
1896
+ # operation_names = multiplex.queries.map { |q| q.selected_operation_name || "anonymous" }.join(", ")
1897
+ # Stats.increment(:complexity_mismatch, tags: { client: client_id, ops: operation_names })
1898
+ # legacy_cost
1899
+ # end
1900
+ # @see Query::Context#add_error Adding an error to the response to notify the client
1901
+ # @see Query::Context#response_extensions Adding key-value pairs to the response `"extensions" => { ... }`
1902
+ # @param multiplex [GraphQL::Execution::Multiplex]
1903
+ # @param future_complexity_cost [Integer]
1904
+ # @param legacy_complexity_cost [Integer]
1905
+ # @return [Integer] the cost to use for this query (probably one of `future_complexity_cost` or `legacy_complexity_cost`)
1906
+ def legacy_complexity_cost_calculation_mismatch(multiplex, future_complexity_cost, legacy_complexity_cost)
1907
+ raise "Implement #{self}.legacy_complexity_cost(multiplex, future_complexity_cost, legacy_complexity_cost) to handle this mismatch (#{future_complexity_cost} vs. #{legacy_complexity_cost}) and return a value to use"
1908
+ end
1909
+
1644
1910
  private
1645
1911
 
1646
1912
  def add_trace_options_for(mode, new_options)
@@ -1805,6 +2071,5 @@ module GraphQL
1805
2071
  end
1806
2072
  end
1807
2073
 
1808
- require "graphql/schema/built_in_types"
1809
2074
  require "graphql/schema/loader"
1810
2075
  require "graphql/schema/printer"
@@ -34,9 +34,9 @@ module GraphQL
34
34
  GraphQL::StaticValidation::VariableUsagesAreAllowed,
35
35
  GraphQL::StaticValidation::MutationRootExists,
36
36
  GraphQL::StaticValidation::QueryRootExists,
37
- GraphQL::StaticValidation::SubscriptionRootExists,
37
+ GraphQL::StaticValidation::SubscriptionRootExistsAndSingleSubscriptionSelection,
38
38
  GraphQL::StaticValidation::InputObjectNamesAreUnique,
39
39
  GraphQL::StaticValidation::OneOfInputObjectsAreValid,
40
- ]
40
+ ].freeze
41
41
  end
42
42
  end