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
data/lib/graphql/query.rb CHANGED
@@ -10,12 +10,47 @@ module GraphQL
10
10
  autoload :Context, "graphql/query/context"
11
11
  autoload :Fingerprint, "graphql/query/fingerprint"
12
12
  autoload :NullContext, "graphql/query/null_context"
13
+ autoload :Partial, "graphql/query/partial"
13
14
  autoload :Result, "graphql/query/result"
14
15
  autoload :Variables, "graphql/query/variables"
15
16
  autoload :InputValidationResult, "graphql/query/input_validation_result"
16
17
  autoload :VariableValidationError, "graphql/query/variable_validation_error"
17
18
  autoload :ValidationPipeline, "graphql/query/validation_pipeline"
18
19
 
20
+ # Code shared with {Partial}
21
+ module Runnable
22
+ def after_lazy(value, &block)
23
+ if !defined?(@runtime_instance)
24
+ @runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
25
+ end
26
+
27
+ if @runtime_instance
28
+ @runtime_instance.minimal_after_lazy(value, &block)
29
+ else
30
+ @schema.after_lazy(value, &block)
31
+ end
32
+ end
33
+
34
+ # Node-level cache for calculating arguments. Used during execution and query analysis.
35
+ # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
36
+ # @param definition [GraphQL::Schema::Field]
37
+ # @param parent_object [GraphQL::Schema::Object]
38
+ # @return [Hash{Symbol => Object}]
39
+ def arguments_for(ast_node, definition, parent_object: nil)
40
+ arguments_cache.fetch(ast_node, definition, parent_object)
41
+ end
42
+
43
+ def arguments_cache
44
+ @arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
45
+ end
46
+
47
+ # @api private
48
+ def handle_or_reraise(err)
49
+ @schema.handle_or_reraise(context, err)
50
+ end
51
+ end
52
+
53
+ include Runnable
19
54
  class OperationNameMissingError < GraphQL::ExecutionError
20
55
  def initialize(name)
21
56
  msg = if name.nil?
@@ -50,7 +85,7 @@ module GraphQL
50
85
  # @return [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules.
51
86
  attr_reader :static_validator
52
87
 
53
- # @param new_validate [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules. This can't be reasssigned after validation.
88
+ # @param new_validator [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules. This can't be reasssigned after validation.
54
89
  def static_validator=(new_validator)
55
90
  if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated?
56
91
  raise ArgumentError, "Can't reassign Query#static_validator= after validation has run, remove this assignment."
@@ -97,21 +132,23 @@ module GraphQL
97
132
  # @param root_value [Object] the object used to resolve fields on the root type
98
133
  # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
99
134
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
100
- # @param visibility_profile [Symbol]
101
- def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
135
+ # @param visibility_profile [Symbol] Another way to assign `context[:visibility_profile]`
136
+ def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, multiplex: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
102
137
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
103
138
  variables ||= {}
139
+ @multiplex = multiplex
104
140
  @schema = schema
105
141
  @context = schema.context_class.new(query: self, values: context)
142
+ if visibility_profile
143
+ @context[:visibility_profile] ||= visibility_profile
144
+ end
106
145
 
107
146
  if use_visibility_profile.nil?
108
147
  use_visibility_profile = warden ? false : schema.use_visibility_profile?
109
148
  end
110
149
 
111
- @visibility_profile = visibility_profile
112
-
113
150
  if use_visibility_profile
114
- @visibility_profile = @schema.visibility.profile_for(@context, visibility_profile)
151
+ @visibility_profile = @schema.visibility.profile_for(@context)
115
152
  @warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
116
153
  else
117
154
  @visibility_profile = nil
@@ -127,14 +164,6 @@ module GraphQL
127
164
  context_tracers = (context ? context.fetch(:tracers, []) : [])
128
165
  @tracers = schema.tracers + context_tracers
129
166
 
130
- # Support `ctx[:backtrace] = true` for wrapping backtraces
131
- if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
132
- if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
133
- context_tracers += [GraphQL::Backtrace::Tracer]
134
- @tracers << GraphQL::Backtrace::Tracer
135
- end
136
- end
137
-
138
167
  if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
139
168
  raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
140
169
  end
@@ -178,13 +207,7 @@ module GraphQL
178
207
  @result_values = nil
179
208
  @executed = false
180
209
 
181
- @logger = if context && context[:logger] == false
182
- Logger.new(IO::NULL)
183
- elsif context && (l = context[:logger])
184
- l
185
- else
186
- schema.default_logger
187
- end
210
+ @logger = schema.logger_for(context)
188
211
  end
189
212
 
190
213
  # If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
@@ -210,19 +233,10 @@ module GraphQL
210
233
  # @return [GraphQL::Execution::Lookahead]
211
234
  def lookahead
212
235
  @lookahead ||= begin
213
- ast_node = selected_operation
214
- if ast_node.nil?
236
+ if selected_operation.nil?
215
237
  GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
216
238
  else
217
- root_type = case ast_node.operation_type
218
- when nil, "query"
219
- types.query_root # rubocop:disable Development/ContextIsPassedCop
220
- when "mutation"
221
- types.mutation_root # rubocop:disable Development/ContextIsPassedCop
222
- when "subscription"
223
- types.subscription_root # rubocop:disable Development/ContextIsPassedCop
224
- end
225
- GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
239
+ GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [selected_operation])
226
240
  end
227
241
  end
228
242
  end
@@ -248,6 +262,18 @@ module GraphQL
248
262
  with_prepared_ast { @operations }
249
263
  end
250
264
 
265
+ # Run subtree partials of this query and return their results.
266
+ # Each partial is identified with a `path:` and `object:`
267
+ # where the path references a field in the AST and the object will be treated
268
+ # as the return value from that field. Subfields of the field named by `path`
269
+ # will be executed with `object` as the starting point
270
+ # @param partials_hashes [Array<Hash{Symbol => Object}>] Hashes with `path:` and `object:` keys
271
+ # @return [Array<GraphQL::Query::Result>]
272
+ def run_partials(partials_hashes)
273
+ partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
274
+ Execution::Interpreter.run_all(@schema, partials, context: @context)
275
+ end
276
+
251
277
  # Get the result for this query, executing it once
252
278
  # @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
253
279
  def result
@@ -290,19 +316,6 @@ module GraphQL
290
316
  end
291
317
  end
292
318
 
293
- # Node-level cache for calculating arguments. Used during execution and query analysis.
294
- # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
295
- # @param definition [GraphQL::Schema::Field]
296
- # @param parent_object [GraphQL::Schema::Object]
297
- # @return Hash{Symbol => Object}
298
- def arguments_for(ast_node, definition, parent_object: nil)
299
- arguments_cache.fetch(ast_node, definition, parent_object)
300
- end
301
-
302
- def arguments_cache
303
- @arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
304
- end
305
-
306
319
  # A version of the given query string, with:
307
320
  # - Variables inlined to the query
308
321
  # - Strings replaced with `<REDACTED>`
@@ -369,17 +382,21 @@ module GraphQL
369
382
 
370
383
  def root_type_for_operation(op_type)
371
384
  case op_type
372
- when "query"
385
+ when "query", nil
373
386
  types.query_root # rubocop:disable Development/ContextIsPassedCop
374
387
  when "mutation"
375
388
  types.mutation_root # rubocop:disable Development/ContextIsPassedCop
376
389
  when "subscription"
377
390
  types.subscription_root # rubocop:disable Development/ContextIsPassedCop
378
391
  else
379
- raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'"
392
+ raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected nil, 'query', 'mutation', or 'subscription'"
380
393
  end
381
394
  end
382
395
 
396
+ def root_type
397
+ root_type_for_operation(selected_operation.operation_type)
398
+ end
399
+
383
400
  def types
384
401
  @visibility_profile || warden.visibility_profile
385
402
  end
@@ -412,23 +429,6 @@ module GraphQL
412
429
  with_prepared_ast { @subscription }
413
430
  end
414
431
 
415
- # @api private
416
- def handle_or_reraise(err)
417
- schema.handle_or_reraise(context, err)
418
- end
419
-
420
- def after_lazy(value, &block)
421
- if !defined?(@runtime_instance)
422
- @runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
423
- end
424
-
425
- if @runtime_instance
426
- @runtime_instance.minimal_after_lazy(value, &block)
427
- else
428
- @schema.after_lazy(value, &block)
429
- end
430
- end
431
-
432
432
  attr_reader :logger
433
433
 
434
434
  private
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphQL
4
- # Support {GraphQL::Parser::Cache}
4
+ # Support {GraphQL::Parser::Cache} and {GraphQL.eager_load!}
5
5
  #
6
6
  # @example Enable the parser cache with default directory
7
7
  #
@@ -258,7 +258,9 @@ module GraphQL
258
258
  # We can get these now; we'll have to get late-bound types later
259
259
  if interface_type.is_a?(Module) && type.is_a?(Class)
260
260
  implementers = @possible_types[interface_type] ||= []
261
- implementers << type
261
+ if !implementers.include?(type)
262
+ implementers << type
263
+ end
262
264
  end
263
265
  when String, Schema::LateBoundType
264
266
  interface_type = interface_type_membership
@@ -3,6 +3,7 @@ module GraphQL
3
3
  class Schema
4
4
  module AlwaysVisible
5
5
  def self.use(schema, **opts)
6
+ schema.use(GraphQL::Schema::Visibility, profiles: { nil => {} })
6
7
  schema.extend(self)
7
8
  end
8
9
 
@@ -4,6 +4,7 @@ module GraphQL
4
4
  class Argument
5
5
  include GraphQL::Schema::Member::HasPath
6
6
  include GraphQL::Schema::Member::HasAstNode
7
+ include GraphQL::Schema::Member::HasAuthorization
7
8
  include GraphQL::Schema::Member::HasDirectives
8
9
  include GraphQL::Schema::Member::HasDeprecationReason
9
10
  include GraphQL::Schema::Member::HasValidators
@@ -39,9 +40,14 @@ module GraphQL
39
40
  # @param arg_name [Symbol]
40
41
  # @param type_expr
41
42
  # @param desc [String]
43
+ # @param type [Class, Array<Class>] Input type; positional argument also accepted
44
+ # @param name [Symbol] positional argument also accepted # @param loads [Class, Array<Class>] A GraphQL type to load for the given ID when one is present
45
+ # @param definition_block [Proc] Called with the newly-created {Argument}
46
+ # @param owner [Class] Private, used by GraphQL-Ruby during schema definition
42
47
  # @param required [Boolean, :nullable] if true, this argument is non-null; if false, this argument is nullable. If `:nullable`, then the argument must be provided, though it may be `null`.
43
48
  # @param description [String]
44
49
  # @param default_value [Object]
50
+ # @param loads [Class, Array<Class>] A GraphQL type to load for the given ID when one is present
45
51
  # @param as [Symbol] Override the keyword name when passed to a method
46
52
  # @param prepare [Symbol] A method to call to transform this argument's valuebefore sending it to field resolution
47
53
  # @param camelize [Boolean] if true, the name will be camelized when building the schema
@@ -50,9 +56,12 @@ module GraphQL
50
56
  # @param deprecation_reason [String]
51
57
  # @param validates [Hash, nil] Options for building validators, if any should be applied
52
58
  # @param replace_null_with_default [Boolean] if `true`, incoming values of `null` will be replaced with the configured `default_value`
59
+ # @param comment [String] Private, used by GraphQL-Ruby when parsing GraphQL schema files
60
+ # @param ast_node [GraphQL::Language::Nodes::InputValueDefinition] Private, used by GraphQL-Ruby when parsing schema files
53
61
  def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, comment: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
54
62
  arg_name ||= name
55
63
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
64
+ NameValidator.validate!(@name)
56
65
  @type_expr = type_expr || type
57
66
  @description = desc || description
58
67
  @comment = comment
@@ -89,11 +98,8 @@ module GraphQL
89
98
  end
90
99
 
91
100
  if definition_block
92
- if definition_block.arity == 1
93
- instance_exec(self, &definition_block)
94
- else
95
- instance_eval(&definition_block)
96
- end
101
+ # `self` will still be self, it will also be the first argument to the block:
102
+ instance_exec(self, &definition_block)
97
103
  end
98
104
  end
99
105
 
@@ -159,6 +165,10 @@ module GraphQL
159
165
  true
160
166
  end
161
167
 
168
+ def authorizes?(_context)
169
+ self.method(:authorized?).owner != GraphQL::Schema::Argument
170
+ end
171
+
162
172
  def authorized?(obj, value, ctx)
163
173
  authorized_as_type?(obj, value, ctx, as_type: type)
164
174
  end
@@ -214,12 +224,17 @@ module GraphQL
214
224
  @statically_coercible = !requires_parent_object
215
225
  end
216
226
 
227
+ def freeze
228
+ statically_coercible?
229
+ super
230
+ end
231
+
217
232
  # Apply the {prepare} configuration to `value`, using methods from `obj`.
218
233
  # Used by the runtime.
219
234
  # @api private
220
235
  def prepare_value(obj, value, context: nil)
221
236
  if type.unwrap.kind.input_object?
222
- value = recursively_prepare_input_object(value, type)
237
+ value = recursively_prepare_input_object(value, type, context)
223
238
  end
224
239
 
225
240
  Schema::Validator.validate!(validators, obj, context, value)
@@ -400,15 +415,16 @@ module GraphQL
400
415
 
401
416
  private
402
417
 
403
- def recursively_prepare_input_object(value, type)
418
+ def recursively_prepare_input_object(value, type, context)
404
419
  if type.non_null?
405
420
  type = type.of_type
406
421
  end
407
422
 
408
423
  if type.list? && !value.nil?
409
424
  inner_type = type.of_type
410
- value.map { |v| recursively_prepare_input_object(v, inner_type) }
425
+ value.map { |v| recursively_prepare_input_object(v, inner_type, context) }
411
426
  elsif value.is_a?(GraphQL::Schema::InputObject)
427
+ value.validate_for(context)
412
428
  value.prepare
413
429
  else
414
430
  value
@@ -20,8 +20,8 @@ module GraphQL
20
20
  from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
21
21
  end
22
22
 
23
- def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false)
24
- Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using)
23
+ def from_document(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay: false)
24
+ Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using, base_types: base_types)
25
25
  end
26
26
  end
27
27
 
@@ -30,9 +30,18 @@ module GraphQL
30
30
  include GraphQL::EmptyObjects
31
31
  extend self
32
32
 
33
- def build(schema_superclass, document, default_resolve:, using: {}, relay:)
33
+ def build(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay:)
34
34
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
35
35
 
36
+ base_types = {
37
+ object: GraphQL::Schema::Object,
38
+ interface: GraphQL::Schema::Interface,
39
+ union: GraphQL::Schema::Union,
40
+ scalar: GraphQL::Schema::Scalar,
41
+ enum: GraphQL::Schema::Enum,
42
+ input_object: GraphQL::Schema::InputObject,
43
+ }.merge!(base_types)
44
+
36
45
  if default_resolve.is_a?(Hash)
37
46
  default_resolve = ResolveMap.new(default_resolve)
38
47
  end
@@ -53,7 +62,7 @@ module GraphQL
53
62
  types[type_name] ||= begin
54
63
  defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name }
55
64
  if defn
56
- build_definition_from_node(defn, directive_type_resolver, default_resolve)
65
+ build_definition_from_node(defn, directive_type_resolver, default_resolve, base_types)
57
66
  elsif (built_in_defn = GraphQL::Schema::BUILT_IN_TYPES[type_name])
58
67
  built_in_defn
59
68
  else
@@ -77,14 +86,20 @@ module GraphQL
77
86
  case definition
78
87
  when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
79
88
  nil # already handled
80
- when GraphQL::Language::Nodes::SchemaExtension
89
+ when GraphQL::Language::Nodes::SchemaExtension,
90
+ GraphQL::Language::Nodes::ScalarTypeExtension,
91
+ GraphQL::Language::Nodes::ObjectTypeExtension,
92
+ GraphQL::Language::Nodes::InterfaceTypeExtension,
93
+ GraphQL::Language::Nodes::UnionTypeExtension,
94
+ GraphQL::Language::Nodes::EnumTypeExtension,
95
+ GraphQL::Language::Nodes::InputObjectTypeExtension
81
96
  schema_extensions ||= []
82
97
  schema_extensions << definition
83
98
  else
84
99
  # It's possible that this was already loaded by the directives
85
100
  prev_type = types[definition.name]
86
101
  if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
87
- types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve)
102
+ types[definition.name] = build_definition_from_node(definition, type_resolver, default_resolve, base_types)
88
103
  end
89
104
  end
90
105
  end
@@ -124,6 +139,34 @@ module GraphQL
124
139
 
125
140
  raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
126
141
 
142
+ schema_extensions&.each do |ext|
143
+ next if ext.is_a?(GraphQL::Language::Nodes::SchemaExtension)
144
+
145
+ built_type = types[ext.name]
146
+
147
+ case ext
148
+ when GraphQL::Language::Nodes::ScalarTypeExtension
149
+ build_directives(built_type, ext, type_resolver)
150
+ when GraphQL::Language::Nodes::ObjectTypeExtension
151
+ build_directives(built_type, ext, type_resolver)
152
+ build_fields(built_type, ext.fields, type_resolver, default_resolve: true)
153
+ build_interfaces(built_type, ext.interfaces, type_resolver)
154
+ when GraphQL::Language::Nodes::InterfaceTypeExtension
155
+ build_directives(built_type, ext, type_resolver)
156
+ build_fields(built_type, ext.fields, type_resolver, default_resolve: nil)
157
+ build_interfaces(built_type, ext.interfaces, type_resolver)
158
+ when GraphQL::Language::Nodes::UnionTypeExtension
159
+ build_directives(built_type, ext, type_resolver)
160
+ built_type.possible_types(*ext.types.map { |type_name| type_resolver.call(type_name) })
161
+ when GraphQL::Language::Nodes::EnumTypeExtension
162
+ build_directives(built_type, ext, type_resolver)
163
+ build_values(built_type, ext.values, type_resolver)
164
+ when GraphQL::Language::Nodes::InputObjectTypeExtension
165
+ build_directives(built_type, ext, type_resolver)
166
+ build_arguments(built_type, ext.fields, type_resolver)
167
+ end
168
+ end
169
+
127
170
  builder = self
128
171
 
129
172
  found_types = types.values
@@ -144,6 +187,10 @@ module GraphQL
144
187
 
145
188
  object_types.each do |t|
146
189
  t.interfaces.each do |int_t|
190
+ if int_t.is_a?(LateBoundType)
191
+ int_t = types[int_t.graphql_name]
192
+ t.implements(int_t)
193
+ end
147
194
  int_t.orphan_types(t)
148
195
  end
149
196
  end
@@ -192,8 +239,8 @@ module GraphQL
192
239
  end
193
240
  end
194
241
 
195
- if schema_extensions
196
- schema_extensions.each do |ext|
242
+ schema_extensions&.each do |ext|
243
+ if ext.is_a?(GraphQL::Language::Nodes::SchemaExtension)
197
244
  build_directives(schema_class, ext, type_resolver)
198
245
  end
199
246
  end
@@ -205,20 +252,22 @@ module GraphQL
205
252
  raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
206
253
  }
207
254
 
208
- def build_definition_from_node(definition, type_resolver, default_resolve)
255
+ def build_definition_from_node(definition, type_resolver, default_resolve, base_types)
209
256
  case definition
210
257
  when GraphQL::Language::Nodes::EnumTypeDefinition
211
- build_enum_type(definition, type_resolver)
258
+ build_enum_type(definition, type_resolver, base_types[:enum])
212
259
  when GraphQL::Language::Nodes::ObjectTypeDefinition
213
- build_object_type(definition, type_resolver)
260
+ build_object_type(definition, type_resolver, base_types[:object])
214
261
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
215
- build_interface_type(definition, type_resolver)
262
+ build_interface_type(definition, type_resolver, base_types[:interface])
216
263
  when GraphQL::Language::Nodes::UnionTypeDefinition
217
- build_union_type(definition, type_resolver)
264
+ build_union_type(definition, type_resolver, base_types[:union])
218
265
  when GraphQL::Language::Nodes::ScalarTypeDefinition
219
- build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
266
+ build_scalar_type(definition, type_resolver, base_types[:scalar], default_resolve: default_resolve)
220
267
  when GraphQL::Language::Nodes::InputObjectTypeDefinition
221
- build_input_object_type(definition, type_resolver)
268
+ build_input_object_type(definition, type_resolver, base_types[:input_object])
269
+ when GraphQL::Language::Nodes::DirectiveDefinition
270
+ build_directive(definition, type_resolver)
222
271
  end
223
272
  end
224
273
 
@@ -284,22 +333,26 @@ module GraphQL
284
333
  end
285
334
  end
286
335
 
287
- def build_enum_type(enum_type_definition, type_resolver)
336
+ def build_enum_type(enum_type_definition, type_resolver, base_type)
288
337
  builder = self
289
- Class.new(GraphQL::Schema::Enum) do
338
+ Class.new(base_type) do
290
339
  graphql_name(enum_type_definition.name)
291
340
  builder.build_directives(self, enum_type_definition, type_resolver)
292
341
  description(enum_type_definition.description)
293
342
  ast_node(enum_type_definition)
294
- enum_type_definition.values.each do |enum_value_definition|
295
- value(enum_value_definition.name,
296
- value: enum_value_definition.name,
297
- deprecation_reason: builder.build_deprecation_reason(enum_value_definition.directives),
298
- description: enum_value_definition.description,
299
- directives: builder.prepare_directives(enum_value_definition, type_resolver),
300
- ast_node: enum_value_definition,
301
- )
302
- end
343
+ builder.build_values(self, enum_type_definition.values, type_resolver)
344
+ end
345
+ end
346
+
347
+ def build_values(type_class, enum_value_definitions, type_resolver)
348
+ enum_value_definitions.each do |enum_value_definition|
349
+ type_class.value(enum_value_definition.name,
350
+ value: enum_value_definition.name,
351
+ deprecation_reason: build_deprecation_reason(enum_value_definition.directives),
352
+ description: enum_value_definition.description,
353
+ directives: prepare_directives(enum_value_definition, type_resolver),
354
+ ast_node: enum_value_definition,
355
+ )
303
356
  end
304
357
  end
305
358
 
@@ -313,9 +366,9 @@ module GraphQL
313
366
  reason.value
314
367
  end
315
368
 
316
- def build_scalar_type(scalar_type_definition, type_resolver, default_resolve:)
369
+ def build_scalar_type(scalar_type_definition, type_resolver, base_type, default_resolve:)
317
370
  builder = self
318
- Class.new(GraphQL::Schema::Scalar) do
371
+ Class.new(base_type) do
319
372
  graphql_name(scalar_type_definition.name)
320
373
  description(scalar_type_definition.description)
321
374
  ast_node(scalar_type_definition)
@@ -336,9 +389,9 @@ module GraphQL
336
389
  end
337
390
  end
338
391
 
339
- def build_union_type(union_type_definition, type_resolver)
392
+ def build_union_type(union_type_definition, type_resolver, base_type)
340
393
  builder = self
341
- Class.new(GraphQL::Schema::Union) do
394
+ Class.new(base_type) do
342
395
  graphql_name(union_type_definition.name)
343
396
  description(union_type_definition.description)
344
397
  possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
@@ -347,27 +400,28 @@ module GraphQL
347
400
  end
348
401
  end
349
402
 
350
- def build_object_type(object_type_definition, type_resolver)
403
+ def build_object_type(object_type_definition, type_resolver, base_type)
351
404
  builder = self
352
405
 
353
- Class.new(GraphQL::Schema::Object) do
406
+ Class.new(base_type) do
354
407
  graphql_name(object_type_definition.name)
355
408
  description(object_type_definition.description)
356
409
  ast_node(object_type_definition)
357
410
  builder.build_directives(self, object_type_definition, type_resolver)
358
-
359
- object_type_definition.interfaces.each do |interface_name|
360
- interface_defn = type_resolver.call(interface_name)
361
- implements(interface_defn)
362
- end
363
-
411
+ builder.build_interfaces(self, object_type_definition.interfaces, type_resolver)
364
412
  builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true)
365
413
  end
366
414
  end
367
415
 
368
- def build_input_object_type(input_object_type_definition, type_resolver)
416
+ def build_interfaces(type_class, interface_names, type_resolver)
417
+ interface_names.each do |interface_name|
418
+ type_class.implements(type_resolver.call(interface_name))
419
+ end
420
+ end
421
+
422
+ def build_input_object_type(input_object_type_definition, type_resolver, base_type)
369
423
  builder = self
370
- Class.new(GraphQL::Schema::InputObject) do
424
+ Class.new(base_type) do
371
425
  graphql_name(input_object_type_definition.name)
372
426
  description(input_object_type_definition.description)
373
427
  ast_node(input_object_type_definition)
@@ -427,16 +481,13 @@ module GraphQL
427
481
  end
428
482
  end
429
483
 
430
- def build_interface_type(interface_type_definition, type_resolver)
484
+ def build_interface_type(interface_type_definition, type_resolver, base_type)
431
485
  builder = self
432
486
  Module.new do
433
- include GraphQL::Schema::Interface
487
+ include base_type
434
488
  graphql_name(interface_type_definition.name)
435
489
  description(interface_type_definition.description)
436
- interface_type_definition.interfaces.each do |interface_name|
437
- interface_defn = type_resolver.call(interface_name)
438
- implements(interface_defn)
439
- end
490
+ builder.build_interfaces(self, interface_type_definition.interfaces, type_resolver)
440
491
  ast_node(interface_type_definition)
441
492
  builder.build_directives(self, interface_type_definition, type_resolver)
442
493
 
@@ -461,23 +512,31 @@ module GraphQL
461
512
  camelize: false,
462
513
  directives: prepare_directives(field_definition, type_resolver),
463
514
  resolver_method: resolve_method_name,
515
+ resolve_batch: resolve_method_name,
464
516
  )
465
517
 
466
518
  builder.build_arguments(schema_field_defn, field_definition.arguments, type_resolver)
467
519
 
468
520
  # Don't do this for interfaces
469
521
  if default_resolve
470
- owner.class_eval <<-RUBY, __FILE__, __LINE__
471
- # frozen_string_literal: true
472
- def #{resolve_method_name}(**args)
473
- field_instance = self.class.get_field("#{field_definition.name}")
474
- context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
475
- end
476
- RUBY
522
+ define_field_resolve_method(owner, resolve_method_name, field_definition.name)
477
523
  end
478
524
  end
479
525
  end
480
526
 
527
+ def define_field_resolve_method(owner, method_name, field_name)
528
+ owner.define_method(method_name) { |**args|
529
+ field_instance = context.types.field(owner, field_name)
530
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
531
+ }
532
+ owner.define_singleton_method(method_name) { |objects, context, **args|
533
+ field_instance = context.types.field(owner, field_name)
534
+ objects.map do |object|
535
+ context.schema.definition_default_resolve.call(self, field_instance, object, args, context)
536
+ end
537
+ }
538
+ end
539
+
481
540
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
482
541
  resolve_type_proc = nil
483
542
  resolve_type_proc = ->(ast_node) {
@@ -494,7 +553,7 @@ module GraphQL
494
553
  when GraphQL::Language::Nodes::ListType
495
554
  resolve_type_proc.call(ast_node.of_type).to_list_type
496
555
  when String
497
- directives[ast_node]
556
+ directives[ast_node] ||= missing_type_handler.call(ast_node)
498
557
  else
499
558
  raise "Unexpected ast_node: #{ast_node.inspect}"
500
559
  end
@@ -37,6 +37,8 @@ module GraphQL
37
37
 
38
38
  argument :by, [String], "Flags to check for this schema member"
39
39
 
40
+ repeatable(true)
41
+
40
42
  module VisibleByFlag
41
43
  def self.included(schema_class)
42
44
  schema_class.extend(self)