graphql 2.0.13 → 2.3.10

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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

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
@@ -7,9 +7,7 @@ module GraphQL
7
7
  include GraphQL::Schema::Member::HasDirectives
8
8
  include GraphQL::Schema::Member::HasDeprecationReason
9
9
  include GraphQL::Schema::Member::HasValidators
10
- include GraphQL::Schema::FindInheritedValue::EmptyObjects
11
-
12
- NO_DEFAULT = :__no_default__
10
+ include GraphQL::EmptyObjects
13
11
 
14
12
  # @return [String] the GraphQL name for this argument, camelized unless `camelize: false` is provided
15
13
  attr_reader :name
@@ -20,8 +18,8 @@ module GraphQL
20
18
 
21
19
  # @param new_prepare [Method, Proc]
22
20
  # @return [Symbol] A method or proc to call to transform this value before sending it to field resolution method
23
- def prepare(new_prepare = NO_DEFAULT)
24
- if new_prepare != NO_DEFAULT
21
+ def prepare(new_prepare = NOT_CONFIGURED)
22
+ if new_prepare != NOT_CONFIGURED
25
23
  @prepare = new_prepare
26
24
  end
27
25
  @prepare
@@ -52,7 +50,7 @@ module GraphQL
52
50
  # @param deprecation_reason [String]
53
51
  # @param validates [Hash, nil] Options for building validators, if any should be applied
54
52
  # @param replace_null_with_default [Boolean] if `true`, incoming values of `null` will be replaced with the configured `default_value`
55
- def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
53
+ def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: 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)
56
54
  arg_name ||= name
57
55
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
58
56
  @type_expr = type_expr || type
@@ -104,8 +102,8 @@ module GraphQL
104
102
 
105
103
  # @param default_value [Object] The value to use when the client doesn't provide one
106
104
  # @return [Object] the value used when the client doesn't provide a value for this argument
107
- def default_value(new_default_value = NO_DEFAULT)
108
- if new_default_value != NO_DEFAULT
105
+ def default_value(new_default_value = NOT_CONFIGURED)
106
+ if new_default_value != NOT_CONFIGURED
109
107
  @default_value = new_default_value
110
108
  end
111
109
  @default_value
@@ -113,7 +111,7 @@ module GraphQL
113
111
 
114
112
  # @return [Boolean] True if this argument has a default value
115
113
  def default_value?
116
- @default_value != NO_DEFAULT
114
+ @default_value != NOT_CONFIGURED
117
115
  end
118
116
 
119
117
  def replace_null_with_default?
@@ -149,10 +147,6 @@ module GraphQL
149
147
  true
150
148
  end
151
149
 
152
- def accessible?(context)
153
- true
154
- end
155
-
156
150
  def authorized?(obj, value, ctx)
157
151
  authorized_as_type?(obj, value, ctx, as_type: type)
158
152
  end
@@ -204,16 +198,16 @@ module GraphQL
204
198
 
205
199
  def statically_coercible?
206
200
  return @statically_coercible if defined?(@statically_coercible)
207
-
208
- @statically_coercible = !@prepare.is_a?(String) && !@prepare.is_a?(Symbol)
201
+ requires_parent_object = @prepare.is_a?(String) || @prepare.is_a?(Symbol) || @own_validators
202
+ @statically_coercible = !requires_parent_object
209
203
  end
210
204
 
211
205
  # Apply the {prepare} configuration to `value`, using methods from `obj`.
212
206
  # Used by the runtime.
213
207
  # @api private
214
208
  def prepare_value(obj, value, context: nil)
215
- if value.is_a?(GraphQL::Schema::InputObject)
216
- value = value.prepare
209
+ if type.unwrap.kind.input_object?
210
+ value = recursively_prepare_input_object(value, type)
217
211
  end
218
212
 
219
213
  Schema::Validator.validate!(validators, obj, context, value)
@@ -227,8 +221,13 @@ module GraphQL
227
221
  #
228
222
  # This will have to be called later, when the runtime object _is_ available.
229
223
  value
230
- else
224
+ elsif obj.respond_to?(@prepare)
231
225
  obj.public_send(@prepare, value)
226
+ elsif owner.respond_to?(@prepare)
227
+ owner.public_send(@prepare, value, context || obj.context)
228
+ else
229
+ raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}. "\
230
+ "Could not find prepare method #{@prepare} on #{obj.class} or #{owner}."
232
231
  end
233
232
  elsif @prepare.respond_to?(:call)
234
233
  @prepare.call(value, context || obj.context)
@@ -270,7 +269,7 @@ module GraphQL
270
269
 
271
270
  # If this isn't lazy, then the block returns eagerly and assigns the result here
272
271
  # If it _is_ lazy, then we write the lazy to the hash, then update it later
273
- argument_values[arg_key] = context.schema.after_lazy(coerced_value) do |resolved_coerced_value|
272
+ argument_values[arg_key] = context.query.after_lazy(coerced_value) do |resolved_coerced_value|
274
273
  owner.validate_directive_argument(self, resolved_coerced_value)
275
274
  prepared_value = begin
276
275
  prepare_value(parent_object, resolved_coerced_value, context: context)
@@ -287,10 +286,11 @@ module GraphQL
287
286
  end
288
287
 
289
288
  maybe_loaded_value = loaded_value || prepared_value
290
- context.schema.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
289
+ context.query.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
291
290
  # TODO code smell to access such a deeply-nested constant in a distant module
292
291
  argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
293
292
  value: resolved_loaded_value,
293
+ original_value: resolved_coerced_value,
294
294
  definition: self,
295
295
  default_used: default_used,
296
296
  )
@@ -309,13 +309,18 @@ module GraphQL
309
309
  else
310
310
  load_method_owner.public_send(arg_load_method, coerced_value)
311
311
  end
312
- context.schema.after_lazy(custom_loaded_value) do |custom_value|
312
+ context.query.after_lazy(custom_loaded_value) do |custom_value|
313
313
  if loads
314
314
  if type.list?
315
- loaded_values = custom_value.each_with_index.map { |custom_val, idx|
316
- id = coerced_value[idx]
317
- load_method_owner.authorize_application_object(self, id, context, custom_val)
318
- }
315
+ loaded_values = []
316
+ context.dataloader.run_isolated do
317
+ custom_value.each_with_index.map { |custom_val, idx|
318
+ id = coerced_value[idx]
319
+ context.dataloader.append_job do
320
+ loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val)
321
+ end
322
+ }
323
+ end
319
324
  context.schema.after_any_lazies(loaded_values, &:itself)
320
325
  else
321
326
  load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
@@ -326,7 +331,16 @@ module GraphQL
326
331
  end
327
332
  elsif loads
328
333
  if type.list?
329
- loaded_values = coerced_value.map { |val| load_method_owner.load_and_authorize_application_object(self, val, context) }
334
+ loaded_values = []
335
+ # We want to run these list items all together,
336
+ # but we also need to wait for the result so we can return it :S
337
+ context.dataloader.run_isolated do
338
+ coerced_value.each_with_index { |val, idx|
339
+ context.dataloader.append_job do
340
+ loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context)
341
+ end
342
+ }
343
+ end
330
344
  context.schema.after_any_lazies(loaded_values, &:itself)
331
345
  else
332
346
  load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
@@ -373,6 +387,21 @@ module GraphQL
373
387
 
374
388
  private
375
389
 
390
+ def recursively_prepare_input_object(value, type)
391
+ if type.non_null?
392
+ type = type.of_type
393
+ end
394
+
395
+ if type.list? && !value.nil?
396
+ inner_type = type.of_type
397
+ value.map { |v| recursively_prepare_input_object(v, inner_type) }
398
+ elsif value.is_a?(GraphQL::Schema::InputObject)
399
+ value.prepare
400
+ else
401
+ value
402
+ end
403
+ end
404
+
376
405
  def validate_input_type(input_type)
377
406
  if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
378
407
  # Do nothing; assume this will be validated later
@@ -1,18 +1,16 @@
1
1
  # frozen_string_literal: true
2
-
3
- require 'graphql/schema/base_64_bp'
4
-
2
+ require "base64"
5
3
  module GraphQL
6
4
  class Schema
7
5
  # @api private
8
6
  module Base64Encoder
9
7
  def self.encode(unencoded_text, nonce: false)
10
- Base64Bp.urlsafe_encode64(unencoded_text, padding: false)
8
+ Base64.urlsafe_encode64(unencoded_text, padding: false)
11
9
  end
12
10
 
13
11
  def self.decode(encoded_text, nonce: false)
14
12
  # urlsafe_decode64 is for forward compatibility
15
- Base64Bp.urlsafe_decode64(encoded_text)
13
+ Base64.urlsafe_decode64(encoded_text)
16
14
  rescue ArgumentError
17
15
  raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
18
16
  end
@@ -6,24 +6,31 @@ module GraphQL
6
6
  module BuildFromDefinition
7
7
  class << self
8
8
  # @see {Schema.from_definition}
9
- def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs)
10
- from_document(parser.parse(definition_string), **kwargs)
9
+ def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
10
+ if defined?(parser::SchemaParser)
11
+ parser = parser::SchemaParser
12
+ end
13
+ from_document(schema_superclass, parser.parse(definition_string), **kwargs)
11
14
  end
12
15
 
13
- def from_definition_path(definition_path, parser: GraphQL.default_parser, **kwargs)
14
- from_document(parser.parse_file(definition_path), **kwargs)
16
+ def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
17
+ if defined?(parser::SchemaParser)
18
+ parser = parser::SchemaParser
19
+ end
20
+ from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
15
21
  end
16
22
 
17
- def from_document(document, default_resolve:, using: {}, relay: false)
18
- Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using)
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)
19
25
  end
20
26
  end
21
27
 
22
28
  # @api private
23
29
  module Builder
30
+ include GraphQL::EmptyObjects
24
31
  extend self
25
32
 
26
- def build(document, default_resolve:, using: {}, relay:)
33
+ def build(schema_superclass, document, default_resolve:, using: {}, relay:)
27
34
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
28
35
 
29
36
  if default_resolve.is_a?(Hash)
@@ -36,7 +43,7 @@ module GraphQL
36
43
  end
37
44
  schema_definition = schema_defns.first
38
45
  types = {}
39
- directives = {}
46
+ directives = schema_superclass.directives.dup
40
47
  type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)})
41
48
  # Make a different type resolver because we need to coerce directive arguments
42
49
  # _while_ building the schema.
@@ -55,21 +62,24 @@ module GraphQL
55
62
  end
56
63
  })
57
64
 
65
+ directives.merge!(GraphQL::Schema.default_directives)
58
66
  document.definitions.each do |definition|
59
67
  if definition.is_a?(GraphQL::Language::Nodes::DirectiveDefinition)
60
68
  directives[definition.name] = build_directive(definition, directive_type_resolver)
61
69
  end
62
70
  end
63
71
 
64
- directives = GraphQL::Schema.default_directives.merge(directives)
65
-
66
72
  # In case any directives referenced built-in types for their arguments:
67
73
  replace_late_bound_types_with_built_in(types)
68
74
 
75
+ schema_extensions = nil
69
76
  document.definitions.each do |definition|
70
77
  case definition
71
78
  when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
72
79
  nil # already handled
80
+ when GraphQL::Language::Nodes::SchemaExtension
81
+ schema_extensions ||= []
82
+ schema_extensions << definition
73
83
  else
74
84
  # It's possible that this was already loaded by the directives
75
85
  prev_type = types[definition.name]
@@ -96,6 +106,16 @@ module GraphQL
96
106
  raise InvalidDocumentError.new("Specified subscription type \"#{schema_definition.subscription}\" not found in document.") unless types[schema_definition.subscription]
97
107
  subscription_root_type = types[schema_definition.subscription]
98
108
  end
109
+
110
+ if schema_definition.query.nil? &&
111
+ schema_definition.mutation.nil? &&
112
+ schema_definition.subscription.nil?
113
+ # This schema may have been given with directives only,
114
+ # check for defaults:
115
+ query_root_type = types['Query']
116
+ mutation_root_type = types['Mutation']
117
+ subscription_root_type = types['Subscription']
118
+ end
99
119
  else
100
120
  query_root_type = types['Query']
101
121
  mutation_root_type = types['Mutation']
@@ -104,10 +124,14 @@ module GraphQL
104
124
 
105
125
  raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
106
126
 
107
- Class.new(GraphQL::Schema) do
127
+ builder = self
128
+
129
+ found_types = types.values
130
+ schema_class = Class.new(schema_superclass) do
108
131
  begin
109
132
  # Add these first so that there's some chance of resolving late-bound types
110
- orphan_types types.values
133
+ add_type_and_traverse(found_types, root: false)
134
+ orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
111
135
  query query_root_type
112
136
  mutation mutation_root_type
113
137
  subscription subscription_root_type
@@ -131,6 +155,7 @@ module GraphQL
131
155
 
132
156
  if schema_definition
133
157
  ast_node(schema_definition)
158
+ builder.build_directives(self, schema_definition, type_resolver)
134
159
  end
135
160
 
136
161
  using.each do |plugin, options|
@@ -158,6 +183,14 @@ module GraphQL
158
183
  child_class.definition_default_resolve = self.definition_default_resolve
159
184
  end
160
185
  end
186
+
187
+ if schema_extensions
188
+ schema_extensions.each do |ext|
189
+ build_directives(schema_class, ext, type_resolver)
190
+ end
191
+ end
192
+
193
+ schema_class
161
194
  end
162
195
 
163
196
  NullResolveType = ->(type, obj, ctx) {
@@ -197,13 +230,18 @@ module GraphQL
197
230
 
198
231
  def build_directives(definition, ast_node, type_resolver)
199
232
  dirs = prepare_directives(ast_node, type_resolver)
200
- dirs.each do |dir_class, options|
201
- definition.directive(dir_class, **options)
233
+ dirs.each do |(dir_class, options)|
234
+ if definition.respond_to?(:schema_directive)
235
+ # it's a schema
236
+ definition.schema_directive(dir_class, **options)
237
+ else
238
+ definition.directive(dir_class, **options)
239
+ end
202
240
  end
203
241
  end
204
242
 
205
243
  def prepare_directives(ast_node, type_resolver)
206
- dirs = {}
244
+ dirs = []
207
245
  ast_node.directives.each do |dir_node|
208
246
  if dir_node.name == "deprecated"
209
247
  # This is handled using `deprecation_reason`
@@ -211,10 +249,10 @@ module GraphQL
211
249
  else
212
250
  dir_class = type_resolver.call(dir_node.name)
213
251
  if dir_class.nil?
214
- raise ArgumentError, "No definition for @#{dir_node.name} on #{ast_node.name} at #{ast_node.line}:#{ast_node.col}"
252
+ raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}"
215
253
  end
216
254
  options = args_to_kwargs(dir_class, dir_node)
217
- dirs[dir_class] = options
255
+ dirs << [dir_class, options]
218
256
  end
219
257
  end
220
258
  dirs
@@ -345,8 +383,6 @@ module GraphQL
345
383
  end
346
384
  end
347
385
 
348
- NO_DEFAULT_VALUE = {}.freeze
349
-
350
386
  def build_arguments(type_class, arguments, type_resolver)
351
387
  builder = self
352
388
 
@@ -354,7 +390,7 @@ module GraphQL
354
390
  default_value_kwargs = if !argument_defn.default_value.nil?
355
391
  { default_value: builder.build_default_value(argument_defn.default_value) }
356
392
  else
357
- NO_DEFAULT_VALUE
393
+ EMPTY_HASH
358
394
  end
359
395
 
360
396
  type_class.argument(
@@ -390,7 +426,6 @@ module GraphQL
390
426
  graphql_name(interface_type_definition.name)
391
427
  description(interface_type_definition.description)
392
428
  interface_type_definition.interfaces.each do |interface_name|
393
- "Implements: #{interface_type_definition} -> #{interface_name}"
394
429
  interface_defn = type_resolver.call(interface_name)
395
430
  implements(interface_defn)
396
431
  end
@@ -405,14 +440,12 @@ module GraphQL
405
440
  builder = self
406
441
 
407
442
  field_definitions.each do |field_definition|
408
- type_name = resolve_type_name(field_definition.type)
409
443
  resolve_method_name = -"resolve_field_#{field_definition.name}"
410
444
  schema_field_defn = owner.field(
411
445
  field_definition.name,
412
446
  description: field_definition.description,
413
447
  type: type_resolver.call(field_definition.type),
414
448
  null: true,
415
- connection: type_name.end_with?("Connection"),
416
449
  connection_extension: nil,
417
450
  deprecation_reason: build_deprecation_reason(field_definition.directives),
418
451
  ast_node: field_definition,
@@ -460,15 +493,6 @@ module GraphQL
460
493
  }
461
494
  resolve_type_proc
462
495
  end
463
-
464
- def resolve_type_name(type)
465
- case type
466
- when GraphQL::Language::Nodes::TypeName
467
- return type.name
468
- else
469
- resolve_type_name(type.of_type)
470
- end
471
- end
472
496
  end
473
497
 
474
498
  private_constant :Builder
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Directive < GraphQL::Schema::Member
5
+ class OneOf < GraphQL::Schema::Directive
6
+ description "Requires that exactly one field must be supplied and that field must not be `null`."
7
+ locations(GraphQL::Schema::Directive::INPUT_OBJECT)
8
+ default_directive true
9
+
10
+ def initialize(...)
11
+ super
12
+
13
+ owner.extend(IsOneOf)
14
+ end
15
+
16
+ module IsOneOf
17
+ def one_of?
18
+ true
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Directive < GraphQL::Schema::Member
5
+ class SpecifiedBy < GraphQL::Schema::Directive
6
+ description "Exposes a URL that specifies the behavior of this scalar."
7
+ locations(GraphQL::Schema::Directive::SCALAR)
8
+ default_directive true
9
+
10
+ argument :url, String, description: "The URL that specifies the behavior of this scalar."
11
+ end
12
+ end
13
+ end
14
+ end
@@ -39,7 +39,7 @@ module GraphQL
39
39
  transform_name = arguments[:by]
40
40
  if TRANSFORMS.include?(transform_name) && return_value.respond_to?(transform_name)
41
41
  return_value = return_value.public_send(transform_name)
42
- response = context.namespace(:interpreter)[:runtime].final_result
42
+ response = context.namespace(:interpreter_runtime)[:runtime].final_result
43
43
  *keys, last = path
44
44
  keys.each do |key|
45
45
  if response && (response = response[key])
@@ -8,6 +8,7 @@ module GraphQL
8
8
  # - {.resolve}: Wraps field resolution (so it should call `yield` to continue)
9
9
  class Directive < GraphQL::Schema::Member
10
10
  extend GraphQL::Schema::Member::HasArguments
11
+ extend GraphQL::Schema::Member::HasArguments::HasDirectiveArguments
11
12
 
12
13
  class << self
13
14
  # Directives aren't types, they don't have kinds.
@@ -21,9 +22,9 @@ module GraphQL
21
22
  # but downcase the first letter.
22
23
  def default_graphql_name
23
24
  @default_graphql_name ||= begin
24
- camelized_name = super
25
+ camelized_name = super.dup
25
26
  camelized_name[0] = camelized_name[0].downcase
26
- camelized_name
27
+ -camelized_name
27
28
  end
28
29
  end
29
30
 
@@ -93,6 +94,15 @@ module GraphQL
93
94
  def repeatable(new_value)
94
95
  @repeatable = new_value
95
96
  end
97
+
98
+ private
99
+
100
+ def inherited(subclass)
101
+ super
102
+ subclass.class_eval do
103
+ @default_graphql_name ||= nil
104
+ end
105
+ end
96
106
  end
97
107
 
98
108
  # @return [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class, Module]
@@ -109,7 +119,7 @@ module GraphQL
109
119
  # - lazy resolution
110
120
  # Probably, those won't be needed here, since these are configuration arguments,
111
121
  # not runtime arguments.
112
- @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
122
+ @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
113
123
  end
114
124
 
115
125
  def graphql_name
@@ -178,6 +188,8 @@ module GraphQL
178
188
  assert_has_location(SCALAR)
179
189
  elsif @owner < GraphQL::Schema
180
190
  assert_has_location(SCHEMA)
191
+ elsif @owner < GraphQL::Schema::Resolver
192
+ assert_has_location(FIELD_DEFINITION)
181
193
  else
182
194
  raise "Unexpected directive owner class: #{@owner}"
183
195
  end
@@ -1,24 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphQL
4
- # Extend this class to define GraphQL enums in your schema.
5
- #
6
- # By default, GraphQL enum values are translated into Ruby strings.
7
- # You can provide a custom value with the `value:` keyword.
8
- #
9
- # @example
10
- # # equivalent to
11
- # # enum PizzaTopping {
12
- # # MUSHROOMS
13
- # # ONIONS
14
- # # PEPPERS
15
- # # }
16
- # class PizzaTopping < GraphQL::Enum
17
- # value :MUSHROOMS
18
- # value :ONIONS
19
- # value :PEPPERS
20
- # end
21
4
  class Schema
5
+ # Extend this class to define GraphQL enums in your schema.
6
+ #
7
+ # By default, GraphQL enum values are translated into Ruby strings.
8
+ # You can provide a custom value with the `value:` keyword.
9
+ #
10
+ # @example
11
+ # # equivalent to
12
+ # # enum PizzaTopping {
13
+ # # MUSHROOMS
14
+ # # ONIONS
15
+ # # PEPPERS
16
+ # # }
17
+ # class PizzaTopping < GraphQL::Schema::Enum
18
+ # value :MUSHROOMS
19
+ # value :ONIONS
20
+ # value :PEPPERS
21
+ # end
22
22
  class Enum < GraphQL::Schema::Member
23
23
  extend GraphQL::Schema::Member::ValidatesInput
24
24
 
@@ -34,6 +34,13 @@ module GraphQL
34
34
  end
35
35
  end
36
36
 
37
+ class MissingValuesError < GraphQL::Error
38
+ def initialize(enum_type)
39
+ @enum_type = enum_type
40
+ super("Enum types require at least one value, but #{enum_type.graphql_name} didn't provide any for this query. Make sure at least one value is defined and visible for this query.")
41
+ end
42
+ end
43
+
37
44
  class << self
38
45
  # Define a value for this enum
39
46
  # @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
@@ -61,7 +68,7 @@ module GraphQL
61
68
  end
62
69
 
63
70
  # @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
64
- def enum_values(context = GraphQL::Query::NullContext)
71
+ def enum_values(context = GraphQL::Query::NullContext.instance)
65
72
  inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
66
73
  visible_values = []
67
74
  warden = Warden.from_context(context)
@@ -103,7 +110,7 @@ module GraphQL
103
110
  end
104
111
 
105
112
  # @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
106
- def values(context = GraphQL::Query::NullContext)
113
+ def values(context = GraphQL::Query::NullContext.instance)
107
114
  enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
108
115
  end
109
116
 
@@ -123,7 +130,7 @@ module GraphQL
123
130
  end
124
131
 
125
132
  def validate_non_null_input(value_name, ctx, max_errors: nil)
126
- allowed_values = ctx.warden.enum_values(self)
133
+ allowed_values = ctx.types.enum_values(self)
127
134
  matching_value = allowed_values.find { |v| v.graphql_name == value_name }
128
135
 
129
136
  if matching_value.nil?
@@ -134,8 +141,8 @@ module GraphQL
134
141
  end
135
142
 
136
143
  def coerce_result(value, ctx)
137
- warden = ctx.warden
138
- all_values = warden ? warden.enum_values(self) : values.each_value
144
+ types = ctx.types
145
+ all_values = types ? types.enum_values(self) : values.each_value
139
146
  enum_value = all_values.find { |val| val.value == value }
140
147
  if enum_value
141
148
  enum_value.graphql_name
@@ -145,7 +152,7 @@ module GraphQL
145
152
  end
146
153
 
147
154
  def coerce_input(value_name, ctx)
148
- all_values = ctx.warden ? ctx.warden.enum_values(self) : values.each_value
155
+ all_values = ctx.types ? ctx.types.enum_values(self) : values.each_value
149
156
 
150
157
  if v = all_values.find { |val| val.graphql_name == value_name }
151
158
  v.value
@@ -159,7 +166,11 @@ module GraphQL
159
166
  end
160
167
 
161
168
  def inherited(child_class)
162
- child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
169
+ if child_class.name
170
+ # Don't assign a custom error class to anonymous classes
171
+ # because they would end up with names like `#<Class0x1234>::UnresolvedValueError` which messes up bug trackers
172
+ child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
173
+ end
163
174
  super
164
175
  end
165
176
 
@@ -30,11 +30,11 @@ module GraphQL
30
30
  # @return [Class] The enum type that owns this value
31
31
  attr_reader :owner
32
32
 
33
- def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, value: nil, deprecation_reason: nil, &block)
33
+ def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, value: NOT_CONFIGURED, deprecation_reason: nil, &block)
34
34
  @graphql_name = graphql_name.to_s
35
35
  GraphQL::NameValidator.validate!(@graphql_name)
36
36
  @description = desc || description
37
- @value = value.nil? ? @graphql_name : value
37
+ @value = value == NOT_CONFIGURED ? @graphql_name : value
38
38
  if deprecation_reason
39
39
  self.deprecation_reason = deprecation_reason
40
40
  end
@@ -70,7 +70,6 @@ module GraphQL
70
70
  end
71
71
 
72
72
  def visible?(_ctx); true; end
73
- def accessible?(_ctx); true; end
74
73
  def authorized?(_ctx); true; end
75
74
  end
76
75
  end