graphql 2.0.13 → 2.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  7. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  8. data/lib/generators/graphql/relay.rb +18 -1
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  12. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  13. data/lib/generators/graphql/templates/base_field.erb +2 -0
  14. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  16. data/lib/generators/graphql/templates/base_object.erb +2 -0
  17. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  18. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  19. data/lib/generators/graphql/templates/base_union.erb +2 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +8 -0
  26. data/lib/graphql/analysis/analyzer.rb +89 -0
  27. data/lib/graphql/analysis/field_usage.rb +82 -0
  28. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  29. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  30. data/lib/graphql/analysis/query_complexity.rb +183 -0
  31. data/lib/graphql/analysis/query_depth.rb +58 -0
  32. data/lib/graphql/analysis/visitor.rb +283 -0
  33. data/lib/graphql/analysis.rb +92 -1
  34. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  35. data/lib/graphql/backtrace/table.rb +2 -2
  36. data/lib/graphql/backtrace/trace.rb +93 -0
  37. data/lib/graphql/backtrace/tracer.rb +1 -1
  38. data/lib/graphql/backtrace.rb +2 -1
  39. data/lib/graphql/coercion_error.rb +1 -9
  40. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/request.rb +5 -0
  43. data/lib/graphql/dataloader/source.rb +89 -45
  44. data/lib/graphql/dataloader.rb +115 -142
  45. data/lib/graphql/duration_encoding_error.rb +16 -0
  46. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  47. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  48. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  49. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  50. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +331 -455
  52. data/lib/graphql/execution/interpreter.rb +125 -61
  53. data/lib/graphql/execution/lazy.rb +6 -12
  54. data/lib/graphql/execution/lookahead.rb +124 -46
  55. data/lib/graphql/execution/multiplex.rb +3 -117
  56. data/lib/graphql/execution.rb +0 -1
  57. data/lib/graphql/introspection/directive_type.rb +3 -3
  58. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  59. data/lib/graphql/introspection/entry_points.rb +11 -5
  60. data/lib/graphql/introspection/field_type.rb +2 -2
  61. data/lib/graphql/introspection/schema_type.rb +10 -13
  62. data/lib/graphql/introspection/type_type.rb +17 -10
  63. data/lib/graphql/introspection.rb +3 -2
  64. data/lib/graphql/language/block_string.rb +34 -18
  65. data/lib/graphql/language/definition_slice.rb +1 -1
  66. data/lib/graphql/language/document_from_schema_definition.rb +75 -59
  67. data/lib/graphql/language/lexer.rb +358 -1506
  68. data/lib/graphql/language/nodes.rb +166 -93
  69. data/lib/graphql/language/parser.rb +795 -1953
  70. data/lib/graphql/language/printer.rb +340 -160
  71. data/lib/graphql/language/sanitized_printer.rb +21 -23
  72. data/lib/graphql/language/static_visitor.rb +167 -0
  73. data/lib/graphql/language/visitor.rb +188 -141
  74. data/lib/graphql/language.rb +61 -1
  75. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  76. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  77. data/lib/graphql/pagination/array_connection.rb +6 -6
  78. data/lib/graphql/pagination/connection.rb +33 -6
  79. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  80. data/lib/graphql/query/context/scoped_context.rb +101 -0
  81. data/lib/graphql/query/context.rb +117 -112
  82. data/lib/graphql/query/null_context.rb +12 -25
  83. data/lib/graphql/query/validation_pipeline.rb +6 -5
  84. data/lib/graphql/query/variables.rb +3 -3
  85. data/lib/graphql/query.rb +86 -30
  86. data/lib/graphql/railtie.rb +9 -6
  87. data/lib/graphql/rake_task.rb +29 -11
  88. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +59 -23
  90. data/lib/graphql/schema/always_visible.rb +11 -0
  91. data/lib/graphql/schema/argument.rb +55 -26
  92. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  93. data/lib/graphql/schema/build_from_definition.rb +56 -32
  94. data/lib/graphql/schema/directive/one_of.rb +24 -0
  95. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  96. data/lib/graphql/schema/directive/transform.rb +1 -1
  97. data/lib/graphql/schema/directive.rb +15 -3
  98. data/lib/graphql/schema/enum.rb +35 -24
  99. data/lib/graphql/schema/enum_value.rb +2 -3
  100. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  101. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  102. data/lib/graphql/schema/field.rb +147 -107
  103. data/lib/graphql/schema/field_extension.rb +1 -4
  104. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  105. data/lib/graphql/schema/has_single_input_argument.rb +158 -0
  106. data/lib/graphql/schema/input_object.rb +47 -11
  107. data/lib/graphql/schema/interface.rb +15 -21
  108. data/lib/graphql/schema/introspection_system.rb +7 -17
  109. data/lib/graphql/schema/late_bound_type.rb +10 -0
  110. data/lib/graphql/schema/list.rb +2 -2
  111. data/lib/graphql/schema/loader.rb +2 -3
  112. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
  113. data/lib/graphql/schema/member/build_type.rb +11 -3
  114. data/lib/graphql/schema/member/has_arguments.rb +170 -130
  115. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  116. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  117. data/lib/graphql/schema/member/has_directives.rb +81 -61
  118. data/lib/graphql/schema/member/has_fields.rb +100 -38
  119. data/lib/graphql/schema/member/has_interfaces.rb +65 -10
  120. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  121. data/lib/graphql/schema/member/has_validators.rb +32 -6
  122. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  123. data/lib/graphql/schema/member/scoped.rb +19 -0
  124. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  125. data/lib/graphql/schema/member/validates_input.rb +3 -3
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +16 -5
  128. data/lib/graphql/schema/printer.rb +11 -8
  129. data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
  130. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  131. data/lib/graphql/schema/resolver.rb +47 -32
  132. data/lib/graphql/schema/scalar.rb +3 -3
  133. data/lib/graphql/schema/subscription.rb +11 -4
  134. data/lib/graphql/schema/subset.rb +397 -0
  135. data/lib/graphql/schema/timeout.rb +25 -29
  136. data/lib/graphql/schema/type_expression.rb +2 -2
  137. data/lib/graphql/schema/type_membership.rb +3 -0
  138. data/lib/graphql/schema/union.rb +11 -2
  139. data/lib/graphql/schema/unique_within_type.rb +1 -1
  140. data/lib/graphql/schema/validator/all_validator.rb +60 -0
  141. data/lib/graphql/schema/validator.rb +4 -2
  142. data/lib/graphql/schema/warden.rb +238 -93
  143. data/lib/graphql/schema.rb +498 -103
  144. data/lib/graphql/static_validation/all_rules.rb +2 -1
  145. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  146. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  147. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  148. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  149. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  150. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  151. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  152. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  153. data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
  154. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  155. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  156. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  157. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  158. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  159. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  160. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  161. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  163. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  164. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  165. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  166. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  167. data/lib/graphql/static_validation/validation_context.rb +5 -5
  168. data/lib/graphql/static_validation/validator.rb +4 -1
  169. data/lib/graphql/static_validation.rb +0 -1
  170. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
  171. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  172. data/lib/graphql/subscriptions/event.rb +11 -10
  173. data/lib/graphql/subscriptions/serialize.rb +2 -0
  174. data/lib/graphql/subscriptions.rb +20 -13
  175. data/lib/graphql/testing/helpers.rb +151 -0
  176. data/lib/graphql/testing.rb +2 -0
  177. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  178. data/lib/graphql/tracing/appoptics_trace.rb +251 -0
  179. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  180. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  181. data/lib/graphql/tracing/data_dog_trace.rb +183 -0
  182. data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
  183. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
  184. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  185. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  186. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  187. data/lib/graphql/tracing/platform_trace.rb +118 -0
  188. data/lib/graphql/tracing/platform_tracing.rb +17 -3
  189. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
  190. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  191. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  192. data/lib/graphql/tracing/scout_trace.rb +72 -0
  193. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  194. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  195. data/lib/graphql/tracing/trace.rb +76 -0
  196. data/lib/graphql/tracing.rb +20 -40
  197. data/lib/graphql/type_kinds.rb +7 -4
  198. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  199. data/lib/graphql/types/relay/base_connection.rb +1 -1
  200. data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
  201. data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
  202. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  203. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  204. data/lib/graphql/types/relay.rb +0 -1
  205. data/lib/graphql/types/string.rb +1 -1
  206. data/lib/graphql/types.rb +1 -0
  207. data/lib/graphql/version.rb +1 -1
  208. data/lib/graphql.rb +27 -20
  209. data/readme.md +13 -3
  210. metadata +96 -47
  211. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  212. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  213. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  214. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  215. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  216. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  217. data/lib/graphql/analysis/ast/visitor.rb +0 -269
  218. data/lib/graphql/analysis/ast.rb +0 -81
  219. data/lib/graphql/deprecation.rb +0 -9
  220. data/lib/graphql/filter.rb +0 -53
  221. data/lib/graphql/language/lexer.rl +0 -280
  222. data/lib/graphql/language/parser.y +0 -554
  223. data/lib/graphql/language/token.rb +0 -34
  224. data/lib/graphql/schema/base_64_bp.rb +0 -26
  225. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  226. data/lib/graphql/static_validation/type_stack.rb +0 -216
  227. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
  228. data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+ require "logger"
2
3
  require "graphql/schema/addition"
4
+ require "graphql/schema/always_visible"
3
5
  require "graphql/schema/base_64_encoder"
4
6
  require "graphql/schema/find_inherited_value"
5
7
  require "graphql/schema/finder"
6
- require "graphql/schema/invalid_type_error"
7
8
  require "graphql/schema/introspection_system"
8
9
  require "graphql/schema/late_bound_type"
9
10
  require "graphql/schema/null_mask"
@@ -31,16 +32,20 @@ require "graphql/schema/union"
31
32
  require "graphql/schema/directive"
32
33
  require "graphql/schema/directive/deprecated"
33
34
  require "graphql/schema/directive/include"
35
+ require "graphql/schema/directive/one_of"
34
36
  require "graphql/schema/directive/skip"
35
37
  require "graphql/schema/directive/feature"
36
38
  require "graphql/schema/directive/flagged"
37
39
  require "graphql/schema/directive/transform"
40
+ require "graphql/schema/directive/specified_by"
38
41
  require "graphql/schema/type_membership"
39
42
 
40
43
  require "graphql/schema/resolver"
41
44
  require "graphql/schema/mutation"
45
+ require "graphql/schema/has_single_input_argument"
42
46
  require "graphql/schema/relay_classic_mutation"
43
47
  require "graphql/schema/subscription"
48
+ require "graphql/schema/subset"
44
49
 
45
50
  module GraphQL
46
51
  # A GraphQL schema which may be queried with {GraphQL::Query}.
@@ -58,10 +63,6 @@ module GraphQL
58
63
  # Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
59
64
  # (These configurations can be overridden by specific calls to {Schema#execute})
60
65
  #
61
- # Schemas can specify how queries should be executed against them.
62
- # `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
63
- # each apply to corresponding root types.
64
- # #
65
66
  # @example defining a schema
66
67
  # class MySchema < GraphQL::Schema
67
68
  # query QueryType
@@ -109,9 +110,10 @@ module GraphQL
109
110
  # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
110
111
  # @return [Class] the schema described by `document`
111
112
  def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
112
- # If the file ends in `.graphql`, treat it like a filepath
113
- if definition_or_path.end_with?(".graphql")
113
+ # If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
114
+ if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
114
115
  GraphQL::Schema::BuildFromDefinition.from_definition_path(
116
+ self,
115
117
  definition_or_path,
116
118
  default_resolve: default_resolve,
117
119
  parser: parser,
@@ -119,6 +121,7 @@ module GraphQL
119
121
  )
120
122
  else
121
123
  GraphQL::Schema::BuildFromDefinition.from_definition(
124
+ self,
122
125
  definition_or_path,
123
126
  default_resolve: default_resolve,
124
127
  parser: parser,
@@ -140,6 +143,119 @@ module GraphQL
140
143
  @subscriptions = new_implementation
141
144
  end
142
145
 
146
+ # @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
147
+ def default_trace_mode(new_mode = nil)
148
+ if new_mode
149
+ @default_trace_mode = new_mode
150
+ elsif defined?(@default_trace_mode)
151
+ @default_trace_mode
152
+ elsif superclass.respond_to?(:default_trace_mode)
153
+ superclass.default_trace_mode
154
+ else
155
+ :default
156
+ end
157
+ end
158
+
159
+ def trace_class(new_class = nil)
160
+ if new_class
161
+ # If any modules were already added for `:default`,
162
+ # re-apply them here
163
+ mods = trace_modules_for(:default)
164
+ mods.each { |mod| new_class.include(mod) }
165
+ trace_mode(:default, new_class)
166
+ backtrace_class = Class.new(new_class)
167
+ backtrace_class.include(GraphQL::Backtrace::Trace)
168
+ trace_mode(:default_backtrace, backtrace_class)
169
+ end
170
+ trace_class_for(:default, build: true)
171
+ end
172
+
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.
174
+ def trace_class_for(mode, build: false)
175
+ if (trace_class = own_trace_modes[mode])
176
+ trace_class
177
+ elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false))
178
+ trace_class
179
+ elsif build
180
+ own_trace_modes[mode] = build_trace_mode(mode)
181
+ else
182
+ nil
183
+ end
184
+ end
185
+
186
+ # Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
187
+ # {default_trace_mode} is used when no `trace_mode: ...` is requested.
188
+ #
189
+ # When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
190
+ # unless `trace_mode` is explicitly given. (This class will not receive any default trace modules.)
191
+ #
192
+ # Subclasses of the schema will use `trace_class` as a base class for this mode and those
193
+ # subclass also will _not_ receive default tracing modules.
194
+ #
195
+ # @param mode_name [Symbol]
196
+ # @param trace_class [Class] subclass of GraphQL::Tracing::Trace
197
+ # @return void
198
+ def trace_mode(mode_name, trace_class)
199
+ own_trace_modes[mode_name] = trace_class
200
+ nil
201
+ end
202
+
203
+ def own_trace_modes
204
+ @own_trace_modes ||= {}
205
+ end
206
+
207
+ module DefaultTraceClass
208
+ end
209
+
210
+ private_constant :DefaultTraceClass
211
+
212
+ def build_trace_mode(mode)
213
+ case mode
214
+ when :default
215
+ # Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
216
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
217
+ Class.new(base_class) do
218
+ include DefaultTraceClass
219
+ end
220
+ when :default_backtrace
221
+ schema_base_class = trace_class_for(:default, build: true)
222
+ Class.new(schema_base_class) do
223
+ include(GraphQL::Backtrace::Trace)
224
+ end
225
+ else
226
+ # First, see if the superclass has a custom-defined class for this.
227
+ # Then, if it doesn't, use this class's default trace
228
+ base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true)
229
+ # Prepare the default trace class if it hasn't been initialized yet
230
+ base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
231
+ mods = trace_modules_for(mode)
232
+ if base_class < DefaultTraceClass
233
+ mods = trace_modules_for(:default) + mods
234
+ end
235
+ # Copy the existing default options into this mode's options
236
+ default_options = trace_options_for(:default)
237
+ add_trace_options_for(mode, default_options)
238
+
239
+ Class.new(base_class) do
240
+ mods.any? && include(*mods)
241
+ end
242
+ end
243
+ end
244
+
245
+ def own_trace_modules
246
+ @own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
247
+ end
248
+
249
+ # @return [Array<Module>] Modules added for tracing in `trace_mode`, including inherited ones
250
+ def trace_modules_for(trace_mode)
251
+ modules = own_trace_modules[trace_mode]
252
+ if superclass.respond_to?(:trace_modules_for)
253
+ modules += superclass.trace_modules_for(trace_mode)
254
+ end
255
+ modules
256
+ end
257
+
258
+
143
259
  # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
144
260
  # @see {#as_json}
145
261
  # @return [String]
@@ -151,18 +267,29 @@ module GraphQL
151
267
  # @param context [Hash]
152
268
  # @param only [<#call(member, ctx)>]
153
269
  # @param except [<#call(member, ctx)>]
270
+ # @param include_deprecated_args [Boolean] If true, deprecated arguments will be included in the JSON response
271
+ # @param include_schema_description [Boolean] If true, the schema's description will be queried and included in the response
272
+ # @param include_is_repeatable [Boolean] If true, `isRepeatable: true|false` will be included with the schema's directives
273
+ # @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
274
+ # @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
154
275
  # @return [Hash] GraphQL result
155
- def as_json(only: nil, except: nil, context: {})
156
- execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
276
+ def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
277
+ introspection_query = Introspection.query(
278
+ include_deprecated_args: include_deprecated_args,
279
+ include_schema_description: include_schema_description,
280
+ include_is_repeatable: include_is_repeatable,
281
+ include_is_one_of: include_is_one_of,
282
+ include_specified_by_url: include_specified_by_url,
283
+ )
284
+
285
+ execute(introspection_query, context: context).to_h
157
286
  end
158
287
 
159
288
  # Return the GraphQL IDL for the schema
160
289
  # @param context [Hash]
161
- # @param only [<#call(member, ctx)>]
162
- # @param except [<#call(member, ctx)>]
163
290
  # @return [String]
164
- def to_definition(only: nil, except: nil, context: {})
165
- GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
291
+ def to_definition(context: {})
292
+ GraphQL::Schema::Printer.print_schema(self, context: context)
166
293
  end
167
294
 
168
295
  # Return the GraphQL::Language::Document IDL AST for the schema
@@ -190,18 +317,6 @@ module GraphQL
190
317
  @find_cache[path] ||= @finder.find(path)
191
318
  end
192
319
 
193
- def default_filter
194
- GraphQL::Filter.new(except: default_mask)
195
- end
196
-
197
- def default_mask(new_mask = nil)
198
- if new_mask
199
- @own_default_mask = new_mask
200
- else
201
- @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
202
- end
203
- end
204
-
205
320
  def static_validator
206
321
  GraphQL::StaticValidation::Validator.new(schema: self)
207
322
  end
@@ -222,7 +337,7 @@ module GraphQL
222
337
  # Build a map of `{ name => type }` and return it
223
338
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
224
339
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
225
- def types(context = GraphQL::Query::NullContext)
340
+ def types(context = GraphQL::Query::NullContext.instance)
226
341
  all_types = non_introspection_types.merge(introspection_system.types)
227
342
  visible_types = {}
228
343
  all_types.each do |k, v|
@@ -249,26 +364,30 @@ module GraphQL
249
364
 
250
365
  # @param type_name [String]
251
366
  # @return [Module, nil] A type, or nil if there's no type called `type_name`
252
- def get_type(type_name, context = GraphQL::Query::NullContext)
367
+ def get_type(type_name, context = GraphQL::Query::NullContext.instance)
253
368
  local_entry = own_types[type_name]
254
369
  type_defn = case local_entry
255
370
  when nil
256
371
  nil
257
372
  when Array
258
- visible_t = nil
259
- warden = Warden.from_context(context)
260
- local_entry.each do |t|
261
- if warden.visible_type?(t, context)
262
- if visible_t.nil?
263
- visible_t = t
264
- else
265
- raise DuplicateNamesError.new(
266
- duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
267
- )
373
+ if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Subset)
374
+ local_entry
375
+ else
376
+ visible_t = nil
377
+ warden = Warden.from_context(context)
378
+ local_entry.each do |t|
379
+ if warden.visible_type?(t, context)
380
+ if visible_t.nil?
381
+ visible_t = t
382
+ else
383
+ raise DuplicateNamesError.new(
384
+ duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
385
+ )
386
+ end
268
387
  end
269
388
  end
389
+ visible_t
270
390
  end
271
- visible_t
272
391
  when Module
273
392
  local_entry
274
393
  else
@@ -280,6 +399,11 @@ module GraphQL
280
399
  (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
281
400
  end
282
401
 
402
+ # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
403
+ def has_defined_type?(type_name)
404
+ own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false)
405
+ end
406
+
283
407
  # @api private
284
408
  attr_writer :connections
285
409
 
@@ -365,16 +489,50 @@ module GraphQL
365
489
  @root_types
366
490
  end
367
491
 
492
+ def warden_class
493
+ if defined?(@warden_class)
494
+ @warden_class
495
+ elsif superclass.respond_to?(:warden_class)
496
+ superclass.warden_class
497
+ else
498
+ GraphQL::Schema::Warden
499
+ end
500
+ end
501
+
502
+ attr_writer :warden_class
503
+
504
+ def subset_class
505
+ if defined?(@subset_class)
506
+ @subset_class
507
+ elsif superclass.respond_to?(:subset_class)
508
+ superclass.subset_class
509
+ else
510
+ GraphQL::Schema::Subset
511
+ end
512
+ end
513
+
514
+ attr_writer :subset_class, :use_schema_subset
515
+
516
+ def use_schema_subset?
517
+ if defined?(@use_schema_subset)
518
+ @use_schema_subset
519
+ elsif superclass.respond_to?(:use_schema_subset?)
520
+ superclass.use_schema_subset?
521
+ else
522
+ false
523
+ end
524
+ end
525
+
368
526
  # @param type [Module] The type definition whose possible types you want to see
369
527
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
370
528
  # @return [Array<Module>] Possible types for `type`, if it's given.
371
- def possible_types(type = nil, context = GraphQL::Query::NullContext)
529
+ def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
372
530
  if type
373
531
  # TODO duck-typing `.possible_types` would probably be nicer here
374
532
  if type.kind.union?
375
533
  type.possible_types(context: context)
376
534
  else
377
- stored_possible_types = own_possible_types[type.graphql_name]
535
+ stored_possible_types = own_possible_types[type]
378
536
  visible_possible_types = if stored_possible_types && type.kind.interface?
379
537
  stored_possible_types.select do |possible_type|
380
538
  possible_type.interfaces(context).include?(type)
@@ -383,7 +541,7 @@ module GraphQL
383
541
  stored_possible_types
384
542
  end
385
543
  visible_possible_types ||
386
- introspection_system.possible_types[type.graphql_name] ||
544
+ introspection_system.possible_types[type] ||
387
545
  (
388
546
  superclass.respond_to?(:possible_types) ?
389
547
  superclass.possible_types(type, context) :
@@ -421,18 +579,12 @@ module GraphQL
421
579
  attr_writer :dataloader_class
422
580
 
423
581
  def references_to(to_type = nil, from: nil)
424
- @own_references_to ||= Hash.new { |h, k| h[k] = [] }
425
582
  if to_type
426
- if !to_type.is_a?(String)
427
- to_type = to_type.graphql_name
428
- end
429
-
430
583
  if from
431
- @own_references_to[to_type] << from
584
+ refs = own_references_to[to_type] ||= []
585
+ refs << from
432
586
  else
433
- own_refs = @own_references_to[to_type]
434
- inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
435
- own_refs + inherited_refs
587
+ get_references_to(to_type) || EMPTY_ARRAY
436
588
  end
437
589
  else
438
590
  # `@own_references_to` can be quite large for big schemas,
@@ -440,19 +592,18 @@ module GraphQL
440
592
  # So optimize the most common case -- don't create a duplicate Hash.
441
593
  inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
442
594
  if inherited_value.any?
443
- inherited_value.merge(@own_references_to)
595
+ inherited_value.merge(own_references_to)
444
596
  else
445
- @own_references_to
597
+ own_references_to
446
598
  end
447
599
  end
448
600
  end
449
601
 
450
- def type_from_ast(ast_node, context: nil)
451
- type_owner = context ? context.warden : self
452
- GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
602
+ def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context)
603
+ GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
453
604
  end
454
605
 
455
- def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
606
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
456
607
  parent_type = case type_or_name
457
608
  when LateBoundType
458
609
  get_type(type_or_name.name, context)
@@ -475,7 +626,7 @@ module GraphQL
475
626
  end
476
627
  end
477
628
 
478
- def get_fields(type, context = GraphQL::Query::NullContext)
629
+ def get_fields(type, context = GraphQL::Query::NullContext.instance)
479
630
  type.fields(context)
480
631
  end
481
632
 
@@ -512,6 +663,17 @@ module GraphQL
512
663
  end
513
664
  end
514
665
 
666
+ # A limit on the number of tokens to accept on incoming query strings.
667
+ # Use this to prevent parsing maliciously-large query strings.
668
+ # @return [nil, Integer]
669
+ def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED)
670
+ if NOT_CONFIGURED.equal?(new_max_tokens)
671
+ defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens)
672
+ else
673
+ @max_query_string_tokens = new_max_tokens
674
+ end
675
+ end
676
+
515
677
  def default_page_size(new_default_page_size = nil)
516
678
  if new_default_page_size
517
679
  @default_page_size = new_default_page_size
@@ -520,27 +682,39 @@ module GraphQL
520
682
  end
521
683
  end
522
684
 
523
- def query_execution_strategy(new_query_execution_strategy = nil)
685
+ def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true)
686
+ if deprecation_warning
687
+ warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
688
+ warn " #{caller(1, 1).first}"
689
+ end
524
690
  if new_query_execution_strategy
525
691
  @query_execution_strategy = new_query_execution_strategy
526
692
  else
527
- @query_execution_strategy || find_inherited_value(:query_execution_strategy, self.default_execution_strategy)
693
+ @query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
528
694
  end
529
695
  end
530
696
 
531
- def mutation_execution_strategy(new_mutation_execution_strategy = nil)
697
+ def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true)
698
+ if deprecation_warning
699
+ warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
700
+ warn " #{caller(1, 1).first}"
701
+ end
532
702
  if new_mutation_execution_strategy
533
703
  @mutation_execution_strategy = new_mutation_execution_strategy
534
704
  else
535
- @mutation_execution_strategy || find_inherited_value(:mutation_execution_strategy, self.default_execution_strategy)
705
+ @mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
536
706
  end
537
707
  end
538
708
 
539
- def subscription_execution_strategy(new_subscription_execution_strategy = nil)
709
+ def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true)
710
+ if deprecation_warning
711
+ warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
712
+ warn " #{caller(1, 1).first}"
713
+ end
540
714
  if new_subscription_execution_strategy
541
715
  @subscription_execution_strategy = new_subscription_execution_strategy
542
716
  else
543
- @subscription_execution_strategy || find_inherited_value(:subscription_execution_strategy, self.default_execution_strategy)
717
+ @subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
544
718
  end
545
719
  end
546
720
 
@@ -565,7 +739,7 @@ module GraphQL
565
739
  else
566
740
  string_or_document
567
741
  end
568
- query = GraphQL::Query.new(self, document: doc, context: context)
742
+ query = query_class.new(self, document: doc, context: context)
569
743
  validator_opts = { schema: self }
570
744
  rules && (validator_opts[:rules] = rules)
571
745
  validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
@@ -573,6 +747,14 @@ module GraphQL
573
747
  res[:errors]
574
748
  end
575
749
 
750
+ def query_class(new_query_class = NOT_CONFIGURED)
751
+ if NOT_CONFIGURED.equal?(new_query_class)
752
+ @query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
753
+ else
754
+ @query_class = new_query_class
755
+ end
756
+ end
757
+
576
758
  attr_writer :validate_max_errors
577
759
 
578
760
  def validate_max_errors(new_validate_max_errors = nil)
@@ -587,9 +769,10 @@ module GraphQL
587
769
 
588
770
  attr_writer :max_complexity
589
771
 
590
- def max_complexity(max_complexity = nil)
772
+ def max_complexity(max_complexity = nil, count_introspection_fields: true)
591
773
  if max_complexity
592
774
  @max_complexity = max_complexity
775
+ @max_complexity_count_introspection_fields = count_introspection_fields
593
776
  elsif defined?(@max_complexity)
594
777
  @max_complexity
595
778
  else
@@ -597,6 +780,14 @@ module GraphQL
597
780
  end
598
781
  end
599
782
 
783
+ def max_complexity_count_introspection_fields
784
+ if defined?(@max_complexity_count_introspection_fields)
785
+ @max_complexity_count_introspection_fields
786
+ else
787
+ find_inherited_value(:max_complexity_count_introspection_fields, true)
788
+ end
789
+ end
790
+
600
791
  attr_writer :analysis_engine
601
792
 
602
793
  def analysis_engine
@@ -615,6 +806,7 @@ module GraphQL
615
806
 
616
807
  def error_bubbling(new_error_bubbling = nil)
617
808
  if !new_error_bubbling.nil?
809
+ warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
618
810
  @error_bubbling = new_error_bubbling
619
811
  else
620
812
  @error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
@@ -625,9 +817,10 @@ module GraphQL
625
817
 
626
818
  attr_writer :max_depth
627
819
 
628
- def max_depth(new_max_depth = nil)
820
+ def max_depth(new_max_depth = nil, count_introspection_fields: true)
629
821
  if new_max_depth
630
822
  @max_depth = new_max_depth
823
+ @count_introspection_fields = count_introspection_fields
631
824
  elsif defined?(@max_depth)
632
825
  @max_depth
633
826
  else
@@ -635,6 +828,14 @@ module GraphQL
635
828
  end
636
829
  end
637
830
 
831
+ def count_introspection_fields
832
+ if defined?(@count_introspection_fields)
833
+ @count_introspection_fields
834
+ else
835
+ find_inherited_value(:count_introspection_fields, true)
836
+ end
837
+ end
838
+
638
839
  def disable_introspection_entry_points
639
840
  @disable_introspection_entry_points = true
640
841
  # TODO: this clears the cache made in `def types`. But this is not a great solution.
@@ -677,14 +878,54 @@ module GraphQL
677
878
  end
678
879
  end
679
880
 
881
+ # @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
882
+ # @return [Array<Module>] Type definitions added to this schema
883
+ def extra_types(*new_extra_types)
884
+ if new_extra_types.any?
885
+ new_extra_types = new_extra_types.flatten
886
+ @own_extra_types ||= []
887
+ @own_extra_types.concat(new_extra_types)
888
+ end
889
+ inherited_et = find_inherited_value(:extra_types, nil)
890
+ if inherited_et
891
+ if @own_extra_types
892
+ inherited_et + @own_extra_types
893
+ else
894
+ inherited_et
895
+ end
896
+ else
897
+ @own_extra_types || EMPTY_ARRAY
898
+ end
899
+ end
900
+
680
901
  def orphan_types(*new_orphan_types)
681
902
  if new_orphan_types.any?
682
903
  new_orphan_types = new_orphan_types.flatten
904
+ non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
905
+ if non_object_types.any?
906
+ raise ArgumentError, <<~ERR
907
+ Only object type classes should be added as `orphan_types(...)`.
908
+
909
+ - Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
910
+ - See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
911
+
912
+ To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
913
+ ERR
914
+ end
683
915
  add_type_and_traverse(new_orphan_types, root: false)
684
916
  own_orphan_types.concat(new_orphan_types.flatten)
685
917
  end
686
918
 
687
- find_inherited_value(:orphan_types, EMPTY_ARRAY) + own_orphan_types
919
+ inherited_ot = find_inherited_value(:orphan_types, nil)
920
+ if inherited_ot
921
+ if own_orphan_types.any?
922
+ inherited_ot + own_orphan_types
923
+ else
924
+ inherited_ot
925
+ end
926
+ else
927
+ own_orphan_types
928
+ end
688
929
  end
689
930
 
690
931
  def default_execution_strategy
@@ -703,6 +944,26 @@ module GraphQL
703
944
  end
704
945
  end
705
946
 
947
+ def default_logger(new_default_logger = NOT_CONFIGURED)
948
+ if NOT_CONFIGURED.equal?(new_default_logger)
949
+ if defined?(@default_logger)
950
+ @default_logger
951
+ elsif superclass.respond_to?(:default_logger)
952
+ superclass.default_logger
953
+ elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
954
+ rails_logger
955
+ else
956
+ def_logger = Logger.new($stdout)
957
+ def_logger.info! # It doesn't output debug info by default
958
+ def_logger
959
+ end
960
+ elsif new_default_logger == nil
961
+ @default_logger = Logger.new(IO::NULL)
962
+ else
963
+ @default_logger = new_default_logger
964
+ end
965
+ end
966
+
706
967
  def context_class(new_context_class = nil)
707
968
  if new_context_class
708
969
  @context_class = new_context_class
@@ -737,11 +998,10 @@ module GraphQL
737
998
  def handle_or_reraise(context, err)
738
999
  handler = Execution::Errors.find_handler_for(self, err.class)
739
1000
  if handler
740
- runtime_info = context.namespace(:interpreter) || {}
741
- obj = runtime_info[:current_object]
742
- args = runtime_info[:current_arguments]
743
- args = args && args.keyword_arguments
744
- field = runtime_info[:current_field]
1001
+ obj = context[:current_object]
1002
+ args = context[:current_arguments]
1003
+ args = args && args.respond_to?(:keyword_arguments) ? args.keyword_arguments : nil
1004
+ field = context[:current_field]
745
1005
  if obj.is_a?(GraphQL::Schema::Object)
746
1006
  obj = obj.object
747
1007
  end
@@ -770,11 +1030,7 @@ module GraphQL
770
1030
  end
771
1031
 
772
1032
  if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind))
773
- if resolved_value
774
- [resolved_type, resolved_value]
775
- else
776
- resolved_type
777
- end
1033
+ [resolved_type, resolved_value]
778
1034
  else
779
1035
  raise ".resolve_type should return a type definition, but got #{resolved_type.inspect} (#{resolved_type.class}) from `resolve_type(#{type}, #{obj}, #{ctx})`"
780
1036
  end
@@ -783,17 +1039,19 @@ module GraphQL
783
1039
  end
784
1040
 
785
1041
  def resolve_type(type, obj, ctx)
786
- if type.kind.object?
787
- type
788
- else
789
- 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})"
790
- end
1042
+ 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})"
791
1043
  end
792
1044
  # rubocop:enable Lint/DuplicateMethods
793
1045
 
794
1046
  def inherited(child_class)
795
1047
  if self == GraphQL::Schema
796
1048
  child_class.directives(default_directives.values)
1049
+ child_class.extend(SubclassGetReferencesTo)
1050
+ end
1051
+ # Make sure the child class has these built out, so that
1052
+ # subclasses can be modified by later calls to `trace_with`
1053
+ own_trace_modes.each do |name, _class|
1054
+ child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
797
1055
  end
798
1056
  child_class.singleton_class.prepend(ResolveTypeWithType)
799
1057
  super
@@ -811,22 +1069,19 @@ module GraphQL
811
1069
  member.visible?(ctx)
812
1070
  end
813
1071
 
814
- def accessible?(member, ctx)
815
- member.accessible?(ctx)
1072
+ def schema_directive(dir_class, **options)
1073
+ @own_schema_directives ||= []
1074
+ Member::HasDirectives.add_directive(self, @own_schema_directives, dir_class, options)
816
1075
  end
817
1076
 
818
- # This hook is called when a client tries to access one or more
819
- # fields that fail the `accessible?` check.
820
- #
821
- # By default, an error is added to the response. Override this hook to
822
- # track metrics or return a different error to the client.
823
- #
824
- # @param error [InaccessibleFieldsError] The analysis error for this check
825
- # @return [AnalysisError, nil] Return an error to skip the query
826
- def inaccessible_fields(error)
827
- error
1077
+ def schema_directives
1078
+ Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
828
1079
  end
829
1080
 
1081
+ # Called when a type is needed by name at runtime
1082
+ def load_type(type_name, ctx)
1083
+ get_type(type_name, ctx)
1084
+ end
830
1085
  # This hook is called when an object fails an `authorized?` check.
831
1086
  # You might report to your bug tracker here, so you can correct
832
1087
  # the field resolvers not to return unauthorized objects.
@@ -876,7 +1131,7 @@ module GraphQL
876
1131
  # A function to call when {#execute} receives an invalid query string
877
1132
  #
878
1133
  # The default is to add the error to `context.errors`
879
- # @param err [GraphQL::ParseError] The error encountered during parsing
1134
+ # @param parse_err [GraphQL::ParseError] The error encountered during parsing
880
1135
  # @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
881
1136
  # @return void
882
1137
  def parse_error(parse_err, ctx)
@@ -888,6 +1143,12 @@ module GraphQL
888
1143
  end
889
1144
 
890
1145
  def instrument(instrument_step, instrumenter, options = {})
1146
+ warn <<~WARN
1147
+ Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
1148
+ (From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
1149
+
1150
+ WARN
1151
+ trace_with(Tracing::LegacyHooksTrace)
891
1152
  own_instrumenters[instrument_step] << instrumenter
892
1153
  end
893
1154
 
@@ -898,7 +1159,12 @@ module GraphQL
898
1159
  new_directives.flatten.each { |d| directive(d) }
899
1160
  end
900
1161
 
901
- find_inherited_value(:directives, default_directives).merge(own_directives)
1162
+ inherited_dirs = find_inherited_value(:directives, default_directives)
1163
+ if own_directives.any?
1164
+ inherited_dirs.merge(own_directives)
1165
+ else
1166
+ inherited_dirs
1167
+ end
902
1168
  end
903
1169
 
904
1170
  # Attach a single directive to this schema
@@ -913,10 +1179,21 @@ module GraphQL
913
1179
  "include" => GraphQL::Schema::Directive::Include,
914
1180
  "skip" => GraphQL::Schema::Directive::Skip,
915
1181
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
1182
+ "oneOf" => GraphQL::Schema::Directive::OneOf,
1183
+ "specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
916
1184
  }.freeze
917
1185
  end
918
1186
 
919
- def tracer(new_tracer)
1187
+ def tracer(new_tracer, silence_deprecation_warning: false)
1188
+ if !silence_deprecation_warning
1189
+ warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
1190
+ warn " #{caller(1, 1).first}"
1191
+ end
1192
+ default_trace = trace_class_for(:default, build: true)
1193
+ if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1194
+ trace_with(GraphQL::Tracing::CallLegacyTracers)
1195
+ end
1196
+
920
1197
  own_tracers << new_tracer
921
1198
  end
922
1199
 
@@ -924,6 +1201,90 @@ module GraphQL
924
1201
  find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
925
1202
  end
926
1203
 
1204
+ # Mix `trace_mod` into this schema's `Trace` class so that its methods
1205
+ # will be called at runtime.
1206
+ #
1207
+ # @param trace_mod [Module] A module that implements tracing methods
1208
+ # @param mode [Symbol] Trace module will only be used for this trade mode
1209
+ # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1210
+ # @return [void]
1211
+ def trace_with(trace_mod, mode: :default, **options)
1212
+ if mode.is_a?(Array)
1213
+ mode.each { |m| trace_with(trace_mod, mode: m, **options) }
1214
+ else
1215
+ tc = own_trace_modes[mode] ||= build_trace_mode(mode)
1216
+ tc.include(trace_mod)
1217
+ own_trace_modules[mode] << trace_mod
1218
+ add_trace_options_for(mode, options)
1219
+ if mode == :default
1220
+ # This module is being added as a default tracer. If any other mode classes
1221
+ # have already been created, but get their default behavior from a superclass,
1222
+ # Then mix this into this schema's subclass.
1223
+ # (But don't mix it into mode classes that aren't default-based.)
1224
+ own_trace_modes.each do |other_mode_name, other_mode_class|
1225
+ if other_mode_class < DefaultTraceClass
1226
+ # Don't add it back to the inheritance tree if it's already there
1227
+ if !(other_mode_class < trace_mod)
1228
+ other_mode_class.include(trace_mod)
1229
+ end
1230
+ # Add any options so they'll be available
1231
+ add_trace_options_for(other_mode_name, options)
1232
+ end
1233
+ end
1234
+ end
1235
+ end
1236
+ nil
1237
+ end
1238
+
1239
+ # The options hash for this trace mode
1240
+ # @return [Hash]
1241
+ def trace_options_for(mode)
1242
+ @trace_options_for_mode ||= {}
1243
+ @trace_options_for_mode[mode] ||= begin
1244
+ # It may be time to create an options hash for a mode that wasn't registered yet.
1245
+ # Mix in the default options in that case.
1246
+ default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
1247
+ # Make sure this returns a new object so that other hashes aren't modified later
1248
+ if superclass.respond_to?(:trace_options_for)
1249
+ superclass.trace_options_for(mode).merge(default_options)
1250
+ else
1251
+ default_options.dup
1252
+ end
1253
+ end
1254
+ end
1255
+
1256
+ # Create a trace instance which will include the trace modules specified for the optional mode.
1257
+ #
1258
+ # If no `mode:` is given, then {default_trace_mode} will be used.
1259
+ #
1260
+ # @param mode [Symbol] Trace modules for this trade mode will be included
1261
+ # @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
1262
+ # @return [Tracing::Trace]
1263
+ def new_trace(mode: nil, **options)
1264
+ target = options[:query] || options[:multiplex]
1265
+ mode ||= target && target.context[:trace_mode]
1266
+
1267
+ trace_mode = if mode
1268
+ mode
1269
+ elsif target && target.context[:backtrace]
1270
+ if default_trace_mode != :default
1271
+ raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1272
+ else
1273
+ own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1274
+ options_trace_mode = :default
1275
+ :default_backtrace
1276
+ end
1277
+ else
1278
+ default_trace_mode
1279
+ end
1280
+
1281
+ options_trace_mode ||= trace_mode
1282
+ base_trace_options = trace_options_for(options_trace_mode)
1283
+ trace_options = base_trace_options.merge(options)
1284
+ trace_class_for_mode = trace_class_for(trace_mode, build: true)
1285
+ trace_class_for_mode.new(**trace_options)
1286
+ end
1287
+
927
1288
  def query_analyzer(new_analyzer)
928
1289
  own_query_analyzers << new_analyzer
929
1290
  end
@@ -950,7 +1311,7 @@ module GraphQL
950
1311
 
951
1312
  # Execute a query on itself.
952
1313
  # @see {Query#initialize} for arguments.
953
- # @return [Hash] query result, ready to be serialized as JSON
1314
+ # @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
954
1315
  def execute(query_str = nil, **kwargs)
955
1316
  if query_str
956
1317
  kwargs[:query] = query_str
@@ -960,7 +1321,9 @@ module GraphQL
960
1321
  {
961
1322
  backtrace: ctx[:backtrace],
962
1323
  tracers: ctx[:tracers],
1324
+ trace: ctx[:trace],
963
1325
  dataloader: ctx[:dataloader],
1326
+ trace_mode: ctx[:trace_mode],
964
1327
  }
965
1328
  else
966
1329
  {}
@@ -988,9 +1351,9 @@ module GraphQL
988
1351
  # @see {Execution::Multiplex#run_all} for multiplex keyword arguments
989
1352
  # @param queries [Array<Hash>] Keyword arguments for each query
990
1353
  # @param context [Hash] Multiplex-level context
991
- # @return [Array<Hash>] One result for each query in the input
1354
+ # @return [Array<GraphQL::Query::Result>] One result for each query in the input
992
1355
  def multiplex(queries, **kwargs)
993
- GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
1356
+ GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
994
1357
  end
995
1358
 
996
1359
  def instrumenters
@@ -1072,6 +1435,12 @@ module GraphQL
1072
1435
 
1073
1436
  private
1074
1437
 
1438
+ def add_trace_options_for(mode, new_options)
1439
+ t_opts = trace_options_for(mode)
1440
+ t_opts.merge!(new_options)
1441
+ nil
1442
+ end
1443
+
1075
1444
  # @param t [Module, Array<Module>]
1076
1445
  # @return [void]
1077
1446
  def add_type_and_traverse(t, root:)
@@ -1115,7 +1484,8 @@ module GraphQL
1115
1484
  own_union_memberships.merge!(addition.union_memberships)
1116
1485
 
1117
1486
  addition.references.each { |thing, pointers|
1118
- pointers.each { |pointer| references_to(thing, from: pointer) }
1487
+ prev_refs = own_references_to[thing] || []
1488
+ own_references_to[thing] = prev_refs | pointers.to_a
1119
1489
  }
1120
1490
 
1121
1491
  addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
@@ -1133,7 +1503,7 @@ module GraphQL
1133
1503
  else
1134
1504
  @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1135
1505
  @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1136
- @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1506
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
1137
1507
  end
1138
1508
  end
1139
1509
  @lazy_methods
@@ -1143,6 +1513,10 @@ module GraphQL
1143
1513
  @own_types ||= {}
1144
1514
  end
1145
1515
 
1516
+ def own_references_to
1517
+ @own_references_to ||= {}.tap(&:compare_by_identity)
1518
+ end
1519
+
1146
1520
  def non_introspection_types
1147
1521
  find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
1148
1522
  end
@@ -1156,7 +1530,7 @@ module GraphQL
1156
1530
  end
1157
1531
 
1158
1532
  def own_possible_types
1159
- @own_possible_types ||= {}
1533
+ @own_possible_types ||= {}.tap(&:compare_by_identity)
1160
1534
  end
1161
1535
 
1162
1536
  def own_union_memberships
@@ -1182,6 +1556,27 @@ module GraphQL
1182
1556
  def own_multiplex_analyzers
1183
1557
  @own_multiplex_analyzers ||= []
1184
1558
  end
1559
+
1560
+ # This is overridden in subclasses to check the inheritance chain
1561
+ def get_references_to(type_defn)
1562
+ own_references_to[type_defn]
1563
+ end
1564
+ end
1565
+
1566
+ module SubclassGetReferencesTo
1567
+ def get_references_to(type_defn)
1568
+ own_refs = own_references_to[type_defn]
1569
+ inherited_refs = superclass.references_to(type_defn)
1570
+ if inherited_refs&.any?
1571
+ if own_refs&.any?
1572
+ own_refs + inherited_refs
1573
+ else
1574
+ inherited_refs
1575
+ end
1576
+ else
1577
+ own_refs
1578
+ end
1579
+ end
1185
1580
  end
1186
1581
 
1187
1582
  # Install these here so that subclasses will also install it.