graphql 2.2.17 → 2.5.16

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 (240) 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_generator.rb +46 -0
  4. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  5. data/lib/generators/graphql/templates/base_resolver.erb +2 -0
  6. data/lib/generators/graphql/templates/schema.erb +3 -0
  7. data/lib/generators/graphql/type_generator.rb +1 -1
  8. data/lib/graphql/analysis/analyzer.rb +90 -0
  9. data/lib/graphql/analysis/field_usage.rb +82 -0
  10. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  11. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  12. data/lib/graphql/analysis/query_complexity.rb +263 -0
  13. data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
  14. data/lib/graphql/analysis/visitor.rb +280 -0
  15. data/lib/graphql/analysis.rb +95 -1
  16. data/lib/graphql/autoload.rb +38 -0
  17. data/lib/graphql/backtrace/table.rb +118 -55
  18. data/lib/graphql/backtrace.rb +1 -19
  19. data/lib/graphql/current.rb +57 -0
  20. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  21. data/lib/graphql/dashboard/installable.rb +22 -0
  22. data/lib/graphql/dashboard/limiters.rb +93 -0
  23. data/lib/graphql/dashboard/operation_store.rb +199 -0
  24. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  25. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  26. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  27. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  28. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  29. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  30. data/lib/graphql/dashboard/statics/icon.png +0 -0
  31. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  36. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  37. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  38. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  39. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  40. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  41. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  42. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  43. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  44. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  45. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  46. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  47. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  48. data/lib/graphql/dashboard.rb +158 -0
  49. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  50. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  51. data/lib/graphql/dataloader/async_dataloader.rb +46 -19
  52. data/lib/graphql/dataloader/null_dataloader.rb +51 -10
  53. data/lib/graphql/dataloader/source.rb +20 -9
  54. data/lib/graphql/dataloader.rb +153 -45
  55. data/lib/graphql/date_encoding_error.rb +1 -1
  56. data/lib/graphql/dig.rb +2 -1
  57. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  58. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  59. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  60. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +63 -5
  61. data/lib/graphql/execution/interpreter/runtime.rb +321 -222
  62. data/lib/graphql/execution/interpreter.rb +23 -30
  63. data/lib/graphql/execution/lookahead.rb +18 -11
  64. data/lib/graphql/execution/multiplex.rb +6 -5
  65. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  66. data/lib/graphql/introspection/directive_type.rb +1 -1
  67. data/lib/graphql/introspection/entry_points.rb +2 -2
  68. data/lib/graphql/introspection/field_type.rb +1 -1
  69. data/lib/graphql/introspection/schema_type.rb +6 -11
  70. data/lib/graphql/introspection/type_type.rb +5 -5
  71. data/lib/graphql/invalid_name_error.rb +1 -1
  72. data/lib/graphql/invalid_null_error.rb +20 -17
  73. data/lib/graphql/language/cache.rb +13 -0
  74. data/lib/graphql/language/comment.rb +18 -0
  75. data/lib/graphql/language/document_from_schema_definition.rb +64 -35
  76. data/lib/graphql/language/lexer.rb +72 -42
  77. data/lib/graphql/language/nodes.rb +93 -52
  78. data/lib/graphql/language/parser.rb +168 -61
  79. data/lib/graphql/language/printer.rb +31 -15
  80. data/lib/graphql/language/sanitized_printer.rb +1 -1
  81. data/lib/graphql/language.rb +61 -1
  82. data/lib/graphql/pagination/connection.rb +1 -1
  83. data/lib/graphql/query/context/scoped_context.rb +1 -1
  84. data/lib/graphql/query/context.rb +46 -47
  85. data/lib/graphql/query/null_context.rb +3 -5
  86. data/lib/graphql/query/partial.rb +179 -0
  87. data/lib/graphql/query/validation_pipeline.rb +2 -2
  88. data/lib/graphql/query/variable_validation_error.rb +1 -1
  89. data/lib/graphql/query.rb +123 -69
  90. data/lib/graphql/railtie.rb +7 -0
  91. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  92. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  93. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  94. data/lib/graphql/rubocop.rb +2 -0
  95. data/lib/graphql/schema/addition.rb +26 -13
  96. data/lib/graphql/schema/always_visible.rb +7 -2
  97. data/lib/graphql/schema/argument.rb +57 -8
  98. data/lib/graphql/schema/build_from_definition.rb +116 -49
  99. data/lib/graphql/schema/directive/flagged.rb +4 -2
  100. data/lib/graphql/schema/directive.rb +54 -2
  101. data/lib/graphql/schema/enum.rb +107 -24
  102. data/lib/graphql/schema/enum_value.rb +10 -2
  103. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  104. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  105. data/lib/graphql/schema/field.rb +134 -45
  106. data/lib/graphql/schema/field_extension.rb +1 -1
  107. data/lib/graphql/schema/has_single_input_argument.rb +6 -2
  108. data/lib/graphql/schema/input_object.rb +122 -64
  109. data/lib/graphql/schema/interface.rb +23 -5
  110. data/lib/graphql/schema/introspection_system.rb +6 -17
  111. data/lib/graphql/schema/late_bound_type.rb +4 -0
  112. data/lib/graphql/schema/list.rb +3 -3
  113. data/lib/graphql/schema/loader.rb +3 -2
  114. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
  115. data/lib/graphql/schema/member/has_arguments.rb +44 -58
  116. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  117. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  118. data/lib/graphql/schema/member/has_directives.rb +4 -4
  119. data/lib/graphql/schema/member/has_fields.rb +26 -6
  120. data/lib/graphql/schema/member/has_interfaces.rb +6 -6
  121. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  122. data/lib/graphql/schema/member/has_validators.rb +1 -1
  123. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  124. data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
  125. data/lib/graphql/schema/member.rb +1 -0
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +25 -8
  128. data/lib/graphql/schema/printer.rb +1 -0
  129. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  130. data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
  131. data/lib/graphql/schema/resolver.rb +29 -23
  132. data/lib/graphql/schema/scalar.rb +1 -6
  133. data/lib/graphql/schema/subscription.rb +52 -6
  134. data/lib/graphql/schema/timeout.rb +19 -2
  135. data/lib/graphql/schema/type_expression.rb +2 -2
  136. data/lib/graphql/schema/union.rb +1 -1
  137. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  138. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  139. data/lib/graphql/schema/validator.rb +3 -1
  140. data/lib/graphql/schema/visibility/migration.rb +188 -0
  141. data/lib/graphql/schema/visibility/profile.rb +445 -0
  142. data/lib/graphql/schema/visibility/visit.rb +190 -0
  143. data/lib/graphql/schema/visibility.rb +311 -0
  144. data/lib/graphql/schema/warden.rb +190 -20
  145. data/lib/graphql/schema.rb +695 -167
  146. data/lib/graphql/static_validation/all_rules.rb +2 -2
  147. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  148. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  149. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  150. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  151. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  152. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  153. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  154. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  155. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  156. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  157. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  158. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  159. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  160. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  161. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  162. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  163. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  164. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  165. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  166. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  167. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  168. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  169. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  170. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  171. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  172. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  173. data/lib/graphql/static_validation/validation_context.rb +18 -2
  174. data/lib/graphql/static_validation/validator.rb +6 -1
  175. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -3
  176. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  177. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  178. data/lib/graphql/subscriptions/event.rb +13 -2
  179. data/lib/graphql/subscriptions/serialize.rb +1 -1
  180. data/lib/graphql/subscriptions.rb +7 -5
  181. data/lib/graphql/testing/helpers.rb +48 -16
  182. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  183. data/lib/graphql/testing.rb +1 -0
  184. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  185. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  186. data/lib/graphql/tracing/appoptics_trace.rb +5 -1
  187. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  188. data/lib/graphql/tracing/appsignal_trace.rb +32 -59
  189. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  190. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  191. data/lib/graphql/tracing/data_dog_trace.rb +46 -162
  192. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  193. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  194. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  195. data/lib/graphql/tracing/detailed_trace.rb +141 -0
  196. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  197. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  198. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  199. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  200. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  201. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  202. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  203. data/lib/graphql/tracing/null_trace.rb +9 -0
  204. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  205. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  206. data/lib/graphql/tracing/perfetto_trace.rb +818 -0
  207. data/lib/graphql/tracing/platform_tracing.rb +1 -1
  208. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  209. data/lib/graphql/tracing/prometheus_trace.rb +73 -73
  210. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  211. data/lib/graphql/tracing/scout_trace.rb +32 -58
  212. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  213. data/lib/graphql/tracing/sentry_trace.rb +64 -98
  214. data/lib/graphql/tracing/statsd_trace.rb +33 -45
  215. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  216. data/lib/graphql/tracing/trace.rb +111 -1
  217. data/lib/graphql/tracing.rb +31 -30
  218. data/lib/graphql/type_kinds.rb +2 -1
  219. data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
  220. data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
  221. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  222. data/lib/graphql/types.rb +18 -11
  223. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  224. data/lib/graphql/version.rb +1 -1
  225. data/lib/graphql.rb +64 -54
  226. metadata +197 -22
  227. data/lib/graphql/analysis/ast/analyzer.rb +0 -91
  228. data/lib/graphql/analysis/ast/field_usage.rb +0 -82
  229. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  230. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  231. data/lib/graphql/analysis/ast/query_complexity.rb +0 -182
  232. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  233. data/lib/graphql/analysis/ast.rb +0 -94
  234. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  235. data/lib/graphql/backtrace/trace.rb +0 -93
  236. data/lib/graphql/backtrace/tracer.rb +0 -80
  237. data/lib/graphql/language/token.rb +0 -34
  238. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  239. data/lib/graphql/schema/null_mask.rb +0 -11
  240. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -50,12 +50,13 @@ module GraphQL
50
50
  # @param deprecation_reason [String]
51
51
  # @param validates [Hash, nil] Options for building validators, if any should be applied
52
52
  # @param replace_null_with_default [Boolean] if `true`, incoming values of `null` will be replaced with the configured `default_value`
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)
53
+ 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
54
  arg_name ||= name
55
55
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
56
56
  NameValidator.validate!(@name)
57
57
  @type_expr = type_expr || type
58
58
  @description = desc || description
59
+ @comment = comment
59
60
  @null = required != true
60
61
  @default_value = default_value
61
62
  if replace_null_with_default
@@ -127,6 +128,17 @@ module GraphQL
127
128
  end
128
129
  end
129
130
 
131
+ attr_writer :comment
132
+
133
+ # @return [String] Comment for this argument
134
+ def comment(text = nil)
135
+ if text
136
+ @comment = text
137
+ else
138
+ @comment
139
+ end
140
+ end
141
+
130
142
  # @return [String] Deprecation reason for this argument
131
143
  def deprecation_reason(text = nil)
132
144
  if text
@@ -200,12 +212,17 @@ module GraphQL
200
212
  @statically_coercible = !requires_parent_object
201
213
  end
202
214
 
215
+ def freeze
216
+ statically_coercible?
217
+ super
218
+ end
219
+
203
220
  # Apply the {prepare} configuration to `value`, using methods from `obj`.
204
221
  # Used by the runtime.
205
222
  # @api private
206
223
  def prepare_value(obj, value, context: nil)
207
- if value.is_a?(GraphQL::Schema::InputObject)
208
- value = value.prepare
224
+ if type.unwrap.kind.input_object?
225
+ value = recursively_prepare_input_object(value, type, context)
209
226
  end
210
227
 
211
228
  Schema::Validator.validate!(validators, obj, context, value)
@@ -288,6 +305,7 @@ module GraphQL
288
305
  # TODO code smell to access such a deeply-nested constant in a distant module
289
306
  argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
290
307
  value: resolved_loaded_value,
308
+ original_value: resolved_coerced_value,
291
309
  definition: self,
292
310
  default_used: default_used,
293
311
  )
@@ -309,10 +327,15 @@ module GraphQL
309
327
  context.query.after_lazy(custom_loaded_value) do |custom_value|
310
328
  if loads
311
329
  if type.list?
312
- loaded_values = custom_value.each_with_index.map { |custom_val, idx|
313
- id = coerced_value[idx]
314
- load_method_owner.authorize_application_object(self, id, context, custom_val)
315
- }
330
+ loaded_values = []
331
+ context.dataloader.run_isolated do
332
+ custom_value.each_with_index.map { |custom_val, idx|
333
+ id = coerced_value[idx]
334
+ context.dataloader.append_job do
335
+ loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val)
336
+ end
337
+ }
338
+ end
316
339
  context.schema.after_any_lazies(loaded_values, &:itself)
317
340
  else
318
341
  load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
@@ -323,7 +346,16 @@ module GraphQL
323
346
  end
324
347
  elsif loads
325
348
  if type.list?
326
- loaded_values = coerced_value.map { |val| load_method_owner.load_and_authorize_application_object(self, val, context) }
349
+ loaded_values = []
350
+ # We want to run these list items all together,
351
+ # but we also need to wait for the result so we can return it :S
352
+ context.dataloader.run_isolated do
353
+ coerced_value.each_with_index { |val, idx|
354
+ context.dataloader.append_job do
355
+ loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context)
356
+ end
357
+ }
358
+ end
327
359
  context.schema.after_any_lazies(loaded_values, &:itself)
328
360
  else
329
361
  load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
@@ -335,6 +367,7 @@ module GraphQL
335
367
 
336
368
  # @api private
337
369
  def validate_default_value
370
+ return unless default_value?
338
371
  coerced_default_value = begin
339
372
  # This is weird, but we should accept single-item default values for list-type arguments.
340
373
  # If we used `coerce_isolated_input` below, it would do this for us, but it's not really
@@ -370,6 +403,22 @@ module GraphQL
370
403
 
371
404
  private
372
405
 
406
+ def recursively_prepare_input_object(value, type, context)
407
+ if type.non_null?
408
+ type = type.of_type
409
+ end
410
+
411
+ if type.list? && !value.nil?
412
+ inner_type = type.of_type
413
+ value.map { |v| recursively_prepare_input_object(v, inner_type, context) }
414
+ elsif value.is_a?(GraphQL::Schema::InputObject)
415
+ value.validate_for(context)
416
+ value.prepare
417
+ else
418
+ value
419
+ end
420
+ end
421
+
373
422
  def validate_input_type(input_type)
374
423
  if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
375
424
  # Do nothing; assume this will be validated later
@@ -7,15 +7,21 @@ module GraphQL
7
7
  class << self
8
8
  # @see {Schema.from_definition}
9
9
  def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
10
+ if defined?(parser::SchemaParser)
11
+ parser = parser::SchemaParser
12
+ end
10
13
  from_document(schema_superclass, parser.parse(definition_string), **kwargs)
11
14
  end
12
15
 
13
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
14
20
  from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
15
21
  end
16
22
 
17
- def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false)
18
- 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)
19
25
  end
20
26
  end
21
27
 
@@ -24,9 +30,18 @@ module GraphQL
24
30
  include GraphQL::EmptyObjects
25
31
  extend self
26
32
 
27
- def build(schema_superclass, document, default_resolve:, using: {}, relay:)
33
+ def build(schema_superclass, document, default_resolve:, using: {}, base_types: {}, relay:)
28
34
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
29
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
+
30
45
  if default_resolve.is_a?(Hash)
31
46
  default_resolve = ResolveMap.new(default_resolve)
32
47
  end
@@ -47,7 +62,7 @@ module GraphQL
47
62
  types[type_name] ||= begin
48
63
  defn = document.definitions.find { |d| d.respond_to?(:name) && d.name == type_name }
49
64
  if defn
50
- build_definition_from_node(defn, directive_type_resolver, default_resolve)
65
+ build_definition_from_node(defn, directive_type_resolver, default_resolve, base_types)
51
66
  elsif (built_in_defn = GraphQL::Schema::BUILT_IN_TYPES[type_name])
52
67
  built_in_defn
53
68
  else
@@ -71,14 +86,20 @@ module GraphQL
71
86
  case definition
72
87
  when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
73
88
  nil # already handled
74
- 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
75
96
  schema_extensions ||= []
76
97
  schema_extensions << definition
77
98
  else
78
99
  # It's possible that this was already loaded by the directives
79
100
  prev_type = types[definition.name]
80
101
  if prev_type.nil? || prev_type.is_a?(Schema::LateBoundType)
81
- 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)
82
103
  end
83
104
  end
84
105
  end
@@ -118,12 +139,43 @@ module GraphQL
118
139
 
119
140
  raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
120
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
+
121
170
  builder = self
122
171
 
172
+ found_types = types.values
173
+ object_types = found_types.select { |t| t.respond_to?(:kind) && t.kind.object? }
123
174
  schema_class = Class.new(schema_superclass) do
124
175
  begin
125
176
  # Add these first so that there's some chance of resolving late-bound types
126
- orphan_types types.values
177
+ add_type_and_traverse(found_types, root: false)
178
+ orphan_types(object_types)
127
179
  query query_root_type
128
180
  mutation mutation_root_type
129
181
  subscription subscription_root_type
@@ -133,6 +185,16 @@ module GraphQL
133
185
  raise InvalidDocumentError, "Type \"#{type_name}\" not found in document.", err_backtrace
134
186
  end
135
187
 
188
+ object_types.each do |t|
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
194
+ int_t.orphan_types(t)
195
+ end
196
+ end
197
+
136
198
  if default_resolve.respond_to?(:resolve_type)
137
199
  def self.resolve_type(*args)
138
200
  self.definition_default_resolve.resolve_type(*args)
@@ -173,11 +235,12 @@ module GraphQL
173
235
 
174
236
  def self.inherited(child_class)
175
237
  child_class.definition_default_resolve = self.definition_default_resolve
238
+ super
176
239
  end
177
240
  end
178
241
 
179
- if schema_extensions
180
- schema_extensions.each do |ext|
242
+ schema_extensions&.each do |ext|
243
+ if ext.is_a?(GraphQL::Language::Nodes::SchemaExtension)
181
244
  build_directives(schema_class, ext, type_resolver)
182
245
  end
183
246
  end
@@ -189,20 +252,22 @@ module GraphQL
189
252
  raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
190
253
  }
191
254
 
192
- def build_definition_from_node(definition, type_resolver, default_resolve)
255
+ def build_definition_from_node(definition, type_resolver, default_resolve, base_types)
193
256
  case definition
194
257
  when GraphQL::Language::Nodes::EnumTypeDefinition
195
- build_enum_type(definition, type_resolver)
258
+ build_enum_type(definition, type_resolver, base_types[:enum])
196
259
  when GraphQL::Language::Nodes::ObjectTypeDefinition
197
- build_object_type(definition, type_resolver)
260
+ build_object_type(definition, type_resolver, base_types[:object])
198
261
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
199
- build_interface_type(definition, type_resolver)
262
+ build_interface_type(definition, type_resolver, base_types[:interface])
200
263
  when GraphQL::Language::Nodes::UnionTypeDefinition
201
- build_union_type(definition, type_resolver)
264
+ build_union_type(definition, type_resolver, base_types[:union])
202
265
  when GraphQL::Language::Nodes::ScalarTypeDefinition
203
- build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
266
+ build_scalar_type(definition, type_resolver, base_types[:scalar], default_resolve: default_resolve)
204
267
  when GraphQL::Language::Nodes::InputObjectTypeDefinition
205
- 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)
206
271
  end
207
272
  end
208
273
 
@@ -268,22 +333,26 @@ module GraphQL
268
333
  end
269
334
  end
270
335
 
271
- def build_enum_type(enum_type_definition, type_resolver)
336
+ def build_enum_type(enum_type_definition, type_resolver, base_type)
272
337
  builder = self
273
- Class.new(GraphQL::Schema::Enum) do
338
+ Class.new(base_type) do
274
339
  graphql_name(enum_type_definition.name)
275
340
  builder.build_directives(self, enum_type_definition, type_resolver)
276
341
  description(enum_type_definition.description)
277
342
  ast_node(enum_type_definition)
278
- enum_type_definition.values.each do |enum_value_definition|
279
- value(enum_value_definition.name,
280
- value: enum_value_definition.name,
281
- deprecation_reason: builder.build_deprecation_reason(enum_value_definition.directives),
282
- description: enum_value_definition.description,
283
- directives: builder.prepare_directives(enum_value_definition, type_resolver),
284
- ast_node: enum_value_definition,
285
- )
286
- 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
+ )
287
356
  end
288
357
  end
289
358
 
@@ -297,9 +366,9 @@ module GraphQL
297
366
  reason.value
298
367
  end
299
368
 
300
- 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:)
301
370
  builder = self
302
- Class.new(GraphQL::Schema::Scalar) do
371
+ Class.new(base_type) do
303
372
  graphql_name(scalar_type_definition.name)
304
373
  description(scalar_type_definition.description)
305
374
  ast_node(scalar_type_definition)
@@ -320,9 +389,9 @@ module GraphQL
320
389
  end
321
390
  end
322
391
 
323
- def build_union_type(union_type_definition, type_resolver)
392
+ def build_union_type(union_type_definition, type_resolver, base_type)
324
393
  builder = self
325
- Class.new(GraphQL::Schema::Union) do
394
+ Class.new(base_type) do
326
395
  graphql_name(union_type_definition.name)
327
396
  description(union_type_definition.description)
328
397
  possible_types(*union_type_definition.types.map { |type_name| type_resolver.call(type_name) })
@@ -331,27 +400,28 @@ module GraphQL
331
400
  end
332
401
  end
333
402
 
334
- def build_object_type(object_type_definition, type_resolver)
403
+ def build_object_type(object_type_definition, type_resolver, base_type)
335
404
  builder = self
336
405
 
337
- Class.new(GraphQL::Schema::Object) do
406
+ Class.new(base_type) do
338
407
  graphql_name(object_type_definition.name)
339
408
  description(object_type_definition.description)
340
409
  ast_node(object_type_definition)
341
410
  builder.build_directives(self, object_type_definition, type_resolver)
342
-
343
- object_type_definition.interfaces.each do |interface_name|
344
- interface_defn = type_resolver.call(interface_name)
345
- implements(interface_defn)
346
- end
347
-
411
+ builder.build_interfaces(self, object_type_definition.interfaces, type_resolver)
348
412
  builder.build_fields(self, object_type_definition.fields, type_resolver, default_resolve: true)
349
413
  end
350
414
  end
351
415
 
352
- 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)
353
423
  builder = self
354
- Class.new(GraphQL::Schema::InputObject) do
424
+ Class.new(base_type) do
355
425
  graphql_name(input_object_type_definition.name)
356
426
  description(input_object_type_definition.description)
357
427
  ast_node(input_object_type_definition)
@@ -411,16 +481,13 @@ module GraphQL
411
481
  end
412
482
  end
413
483
 
414
- def build_interface_type(interface_type_definition, type_resolver)
484
+ def build_interface_type(interface_type_definition, type_resolver, base_type)
415
485
  builder = self
416
486
  Module.new do
417
- include GraphQL::Schema::Interface
487
+ include base_type
418
488
  graphql_name(interface_type_definition.name)
419
489
  description(interface_type_definition.description)
420
- interface_type_definition.interfaces.each do |interface_name|
421
- interface_defn = type_resolver.call(interface_name)
422
- implements(interface_defn)
423
- end
490
+ builder.build_interfaces(self, interface_type_definition.interfaces, type_resolver)
424
491
  ast_node(interface_type_definition)
425
492
  builder.build_directives(self, interface_type_definition, type_resolver)
426
493
 
@@ -458,7 +525,7 @@ module GraphQL
458
525
 
459
526
  def define_field_resolve_method(owner, method_name, field_name)
460
527
  owner.define_method(method_name) { |**args|
461
- field_instance = self.class.get_field(field_name)
528
+ field_instance = context.types.field(owner, field_name)
462
529
  context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
463
530
  }
464
531
  end
@@ -479,7 +546,7 @@ module GraphQL
479
546
  when GraphQL::Language::Nodes::ListType
480
547
  resolve_type_proc.call(ast_node.of_type).to_list_type
481
548
  when String
482
- directives[ast_node]
549
+ directives[ast_node] ||= missing_type_handler.call(ast_node)
483
550
  else
484
551
  raise "Unexpected ast_node: #{ast_node.inspect}"
485
552
  end
@@ -7,7 +7,7 @@ module GraphQL
7
7
  # In this case, the server hides types and fields _entirely_, unless the current context has certain `:flags` present.
8
8
  class Flagged < GraphQL::Schema::Directive
9
9
  def initialize(target, **options)
10
- if target.is_a?(Module) && !target.ancestors.include?(VisibleByFlag)
10
+ if target.is_a?(Module)
11
11
  # This is type class of some kind, `include` will put this module
12
12
  # in between the type class itself and its super class, so `super` will work fine
13
13
  target.include(VisibleByFlag)
@@ -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)
@@ -45,7 +47,7 @@ module GraphQL
45
47
  def visible?(context)
46
48
  if dir = self.directives.find { |d| d.is_a?(Flagged) }
47
49
  relevant_flags = (f = context[:flags]) && dir.arguments[:by] & f # rubocop:disable Development/ContextIsPassedCop -- definition-related
48
- relevant_flags && relevant_flags.any? && super
50
+ relevant_flags && !relevant_flags.empty? && super
49
51
  else
50
52
  super
51
53
  end
@@ -9,6 +9,7 @@ module GraphQL
9
9
  class Directive < GraphQL::Schema::Member
10
10
  extend GraphQL::Schema::Member::HasArguments
11
11
  extend GraphQL::Schema::Member::HasArguments::HasDirectiveArguments
12
+ extend GraphQL::Schema::Member::HasValidators
12
13
 
13
14
  class << self
14
15
  # Directives aren't types, they don't have kinds.
@@ -29,7 +30,7 @@ module GraphQL
29
30
  end
30
31
 
31
32
  def locations(*new_locations)
32
- if new_locations.any?
33
+ if !new_locations.empty?
33
34
  new_locations.each do |new_loc|
34
35
  if !LOCATIONS.include?(new_loc.to_sym)
35
36
  raise ArgumentError, "#{self} (#{self.graphql_name}) has an invalid directive location: `locations #{new_loc}` "
@@ -75,6 +76,10 @@ module GraphQL
75
76
  yield
76
77
  end
77
78
 
79
+ def validate!(arguments, context)
80
+ Schema::Validator.validate!(validators, self, context, arguments)
81
+ end
82
+
78
83
  def on_field?
79
84
  locations.include?(FIELD)
80
85
  end
@@ -111,6 +116,9 @@ module GraphQL
111
116
  # @return [GraphQL::Interpreter::Arguments]
112
117
  attr_reader :arguments
113
118
 
119
+ class InvalidArgumentError < GraphQL::Error
120
+ end
121
+
114
122
  def initialize(owner, **arguments)
115
123
  @owner = owner
116
124
  assert_valid_owner
@@ -119,7 +127,49 @@ module GraphQL
119
127
  # - lazy resolution
120
128
  # Probably, those won't be needed here, since these are configuration arguments,
121
129
  # not runtime arguments.
122
- @arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
130
+ context = Query::NullContext.instance
131
+ self.class.all_argument_definitions.each do |arg_defn|
132
+ keyword = arg_defn.keyword
133
+ arg_type = arg_defn.type
134
+ if arguments.key?(keyword)
135
+ value = arguments[keyword]
136
+ # This is a Ruby-land value; convert it to graphql for validation
137
+ graphql_value = begin
138
+ coerce_value = value
139
+ if arg_type.list? && (!coerce_value.nil?) && (!coerce_value.is_a?(Array))
140
+ # When validating inputs, GraphQL accepts a single item
141
+ # and implicitly converts it to a one-item list.
142
+ # However, we're using result coercion here to go from Ruby value
143
+ # to GraphQL value, so it doesn't have that feature.
144
+ # Keep the GraphQL-type behavior but implement it manually:
145
+ wrap_type = arg_type
146
+ while wrap_type.list?
147
+ if wrap_type.non_null?
148
+ wrap_type = wrap_type.of_type
149
+ end
150
+ wrap_type = wrap_type.of_type
151
+ coerce_value = [coerce_value]
152
+ end
153
+ end
154
+ arg_type.coerce_isolated_result(coerce_value)
155
+ rescue GraphQL::Schema::Enum::UnresolvedValueError
156
+ # Let validation handle this
157
+ value
158
+ end
159
+ else
160
+ value = graphql_value = nil
161
+ end
162
+
163
+ result = arg_type.validate_input(graphql_value, context)
164
+ if !result.valid?
165
+ raise InvalidArgumentError, "@#{graphql_name}.#{arg_defn.graphql_name} on #{owner.path} is invalid (#{value.inspect}): #{result.problems.first["explanation"]}"
166
+ end
167
+ end
168
+ self.class.validate!(arguments, context)
169
+ @arguments = self.class.coerce_arguments(nil, arguments, context)
170
+ if @arguments.is_a?(GraphQL::ExecutionError)
171
+ raise @arguments
172
+ end
123
173
  end
124
174
 
125
175
  def graphql_name
@@ -188,6 +238,8 @@ module GraphQL
188
238
  assert_has_location(SCALAR)
189
239
  elsif @owner < GraphQL::Schema
190
240
  assert_has_location(SCHEMA)
241
+ elsif @owner < GraphQL::Schema::Resolver
242
+ assert_has_location(FIELD_DEFINITION)
191
243
  else
192
244
  raise "Unexpected directive owner class: #{@owner}"
193
245
  end