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
@@ -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