graphql 2.0.31 → 2.5.18

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 (287) 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 +49 -0
  6. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  7. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  8. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  9. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  10. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  11. data/lib/generators/graphql/templates/base_field.erb +2 -0
  12. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  14. data/lib/generators/graphql/templates/base_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  16. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  17. data/lib/generators/graphql/templates/base_union.erb +2 -0
  18. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  19. data/lib/generators/graphql/templates/loader.erb +2 -0
  20. data/lib/generators/graphql/templates/mutation.erb +2 -0
  21. data/lib/generators/graphql/templates/node_type.erb +2 -0
  22. data/lib/generators/graphql/templates/query_type.erb +2 -0
  23. data/lib/generators/graphql/templates/schema.erb +5 -0
  24. data/lib/generators/graphql/type_generator.rb +1 -1
  25. data/lib/graphql/analysis/analyzer.rb +90 -0
  26. data/lib/graphql/analysis/field_usage.rb +82 -0
  27. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  28. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  29. data/lib/graphql/analysis/query_complexity.rb +263 -0
  30. data/lib/graphql/analysis/query_depth.rb +58 -0
  31. data/lib/graphql/analysis/visitor.rb +280 -0
  32. data/lib/graphql/analysis.rb +95 -1
  33. data/lib/graphql/autoload.rb +38 -0
  34. data/lib/graphql/backtrace/table.rb +118 -55
  35. data/lib/graphql/backtrace.rb +1 -19
  36. data/lib/graphql/coercion_error.rb +1 -9
  37. data/lib/graphql/current.rb +57 -0
  38. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  39. data/lib/graphql/dashboard/installable.rb +22 -0
  40. data/lib/graphql/dashboard/limiters.rb +93 -0
  41. data/lib/graphql/dashboard/operation_store.rb +199 -0
  42. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  43. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  44. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  45. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  46. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  47. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  48. data/lib/graphql/dashboard/statics/icon.png +0 -0
  49. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  50. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  51. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  52. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  53. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  54. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  65. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  66. data/lib/graphql/dashboard.rb +161 -0
  67. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  68. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  69. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  70. data/lib/graphql/dataloader/null_dataloader.rb +51 -10
  71. data/lib/graphql/dataloader/request.rb +5 -0
  72. data/lib/graphql/dataloader/source.rb +35 -12
  73. data/lib/graphql/dataloader.rb +224 -149
  74. data/lib/graphql/date_encoding_error.rb +1 -1
  75. data/lib/graphql/dig.rb +2 -1
  76. data/lib/graphql/duration_encoding_error.rb +16 -0
  77. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  78. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  79. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  80. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  81. data/lib/graphql/execution/interpreter/runtime.rb +363 -434
  82. data/lib/graphql/execution/interpreter.rb +91 -164
  83. data/lib/graphql/execution/lookahead.rb +105 -31
  84. data/lib/graphql/execution/multiplex.rb +6 -5
  85. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  86. data/lib/graphql/introspection/directive_type.rb +1 -1
  87. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  88. data/lib/graphql/introspection/entry_points.rb +11 -5
  89. data/lib/graphql/introspection/field_type.rb +1 -1
  90. data/lib/graphql/introspection/schema_type.rb +8 -11
  91. data/lib/graphql/introspection/type_type.rb +5 -5
  92. data/lib/graphql/invalid_name_error.rb +1 -1
  93. data/lib/graphql/invalid_null_error.rb +20 -17
  94. data/lib/graphql/language/block_string.rb +34 -18
  95. data/lib/graphql/language/cache.rb +13 -0
  96. data/lib/graphql/language/comment.rb +18 -0
  97. data/lib/graphql/language/definition_slice.rb +1 -1
  98. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  99. data/lib/graphql/language/lexer.rb +319 -193
  100. data/lib/graphql/language/nodes.rb +139 -77
  101. data/lib/graphql/language/parser.rb +807 -1985
  102. data/lib/graphql/language/printer.rb +324 -151
  103. data/lib/graphql/language/sanitized_printer.rb +21 -23
  104. data/lib/graphql/language/static_visitor.rb +171 -0
  105. data/lib/graphql/language/visitor.rb +62 -119
  106. data/lib/graphql/language.rb +71 -1
  107. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  108. data/lib/graphql/pagination/array_connection.rb +6 -6
  109. data/lib/graphql/pagination/connection.rb +28 -1
  110. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  111. data/lib/graphql/query/context/scoped_context.rb +101 -0
  112. data/lib/graphql/query/context.rb +81 -144
  113. data/lib/graphql/query/null_context.rb +7 -16
  114. data/lib/graphql/query/partial.rb +179 -0
  115. data/lib/graphql/query/validation_pipeline.rb +4 -4
  116. data/lib/graphql/query/variable_validation_error.rb +1 -1
  117. data/lib/graphql/query/variables.rb +3 -3
  118. data/lib/graphql/query.rb +126 -81
  119. data/lib/graphql/railtie.rb +16 -6
  120. data/lib/graphql/rake_task.rb +3 -12
  121. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  122. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  123. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  124. data/lib/graphql/rubocop.rb +2 -0
  125. data/lib/graphql/schema/addition.rb +26 -13
  126. data/lib/graphql/schema/always_visible.rb +7 -2
  127. data/lib/graphql/schema/argument.rb +73 -14
  128. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  129. data/lib/graphql/schema/build_from_definition.rb +123 -66
  130. data/lib/graphql/schema/directive/flagged.rb +4 -2
  131. data/lib/graphql/schema/directive/one_of.rb +12 -0
  132. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  133. data/lib/graphql/schema/directive.rb +55 -3
  134. data/lib/graphql/schema/enum.rb +110 -27
  135. data/lib/graphql/schema/enum_value.rb +11 -3
  136. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  137. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  138. data/lib/graphql/schema/field.rb +173 -119
  139. data/lib/graphql/schema/field_extension.rb +1 -1
  140. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  141. data/lib/graphql/schema/input_object.rb +123 -65
  142. data/lib/graphql/schema/interface.rb +34 -16
  143. data/lib/graphql/schema/introspection_system.rb +8 -17
  144. data/lib/graphql/schema/late_bound_type.rb +4 -0
  145. data/lib/graphql/schema/list.rb +3 -3
  146. data/lib/graphql/schema/loader.rb +3 -4
  147. data/lib/graphql/schema/member/base_dsl_methods.rb +17 -1
  148. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  149. data/lib/graphql/schema/member/has_dataloader.rb +62 -0
  150. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  151. data/lib/graphql/schema/member/has_directives.rb +5 -5
  152. data/lib/graphql/schema/member/has_fields.rb +111 -16
  153. data/lib/graphql/schema/member/has_interfaces.rb +27 -13
  154. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  155. data/lib/graphql/schema/member/has_validators.rb +1 -1
  156. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  157. data/lib/graphql/schema/member/scoped.rb +19 -0
  158. data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
  159. data/lib/graphql/schema/member/validates_input.rb +3 -3
  160. data/lib/graphql/schema/member.rb +1 -0
  161. data/lib/graphql/schema/mutation.rb +7 -0
  162. data/lib/graphql/schema/object.rb +33 -8
  163. data/lib/graphql/schema/printer.rb +9 -7
  164. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  165. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  166. data/lib/graphql/schema/resolver.rb +45 -31
  167. data/lib/graphql/schema/scalar.rb +4 -9
  168. data/lib/graphql/schema/subscription.rb +63 -10
  169. data/lib/graphql/schema/timeout.rb +19 -2
  170. data/lib/graphql/schema/type_expression.rb +2 -2
  171. data/lib/graphql/schema/union.rb +2 -2
  172. data/lib/graphql/schema/unique_within_type.rb +1 -1
  173. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  174. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  175. data/lib/graphql/schema/validator.rb +3 -1
  176. data/lib/graphql/schema/visibility/migration.rb +188 -0
  177. data/lib/graphql/schema/visibility/profile.rb +445 -0
  178. data/lib/graphql/schema/visibility/visit.rb +190 -0
  179. data/lib/graphql/schema/visibility.rb +311 -0
  180. data/lib/graphql/schema/warden.rb +275 -103
  181. data/lib/graphql/schema.rb +924 -210
  182. data/lib/graphql/static_validation/all_rules.rb +3 -3
  183. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  184. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  185. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  186. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  187. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  188. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  189. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  190. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  191. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  192. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  193. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  194. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  195. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  196. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  197. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  198. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  199. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  200. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  201. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
  202. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  203. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  204. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  205. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  206. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  207. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  208. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  209. data/lib/graphql/static_validation/validation_context.rb +21 -5
  210. data/lib/graphql/static_validation/validator.rb +9 -1
  211. data/lib/graphql/static_validation.rb +0 -1
  212. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  213. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  214. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  215. data/lib/graphql/subscriptions/event.rb +21 -4
  216. data/lib/graphql/subscriptions/serialize.rb +3 -1
  217. data/lib/graphql/subscriptions.rb +21 -17
  218. data/lib/graphql/testing/helpers.rb +161 -0
  219. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  220. data/lib/graphql/testing.rb +3 -0
  221. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  222. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  223. data/lib/graphql/tracing/appoptics_trace.rb +11 -3
  224. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  225. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  226. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  227. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  228. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  229. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  230. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  231. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  232. data/lib/graphql/tracing/detailed_trace.rb +141 -0
  233. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  234. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  235. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  236. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  237. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  238. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  239. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  240. data/lib/graphql/tracing/null_trace.rb +9 -0
  241. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  242. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  243. data/lib/graphql/tracing/perfetto_trace.rb +818 -0
  244. data/lib/graphql/tracing/platform_trace.rb +5 -0
  245. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  246. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  247. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  248. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  249. data/lib/graphql/tracing/scout_trace.rb +32 -55
  250. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  251. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  252. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  253. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  254. data/lib/graphql/tracing/trace.rb +112 -1
  255. data/lib/graphql/tracing.rb +31 -28
  256. data/lib/graphql/type_kinds.rb +2 -1
  257. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  258. data/lib/graphql/types/relay/connection_behaviors.rb +43 -3
  259. data/lib/graphql/types/relay/edge_behaviors.rb +18 -1
  260. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  261. data/lib/graphql/types.rb +18 -10
  262. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  263. data/lib/graphql/version.rb +1 -1
  264. data/lib/graphql.rb +68 -54
  265. data/readme.md +12 -2
  266. metadata +220 -40
  267. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  268. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  269. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  270. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  271. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  272. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  273. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  274. data/lib/graphql/analysis/ast.rb +0 -81
  275. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  276. data/lib/graphql/backtrace/trace.rb +0 -96
  277. data/lib/graphql/backtrace/tracer.rb +0 -80
  278. data/lib/graphql/deprecation.rb +0 -9
  279. data/lib/graphql/filter.rb +0 -59
  280. data/lib/graphql/language/parser.y +0 -560
  281. data/lib/graphql/language/token.rb +0 -34
  282. data/lib/graphql/schema/base_64_bp.rb +0 -26
  283. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  284. data/lib/graphql/schema/null_mask.rb +0 -11
  285. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  286. data/lib/graphql/static_validation/type_stack.rb +0 -216
  287. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/types/string"
3
2
 
4
3
  module GraphQL
5
4
  class Schema
@@ -21,6 +20,10 @@ module GraphQL
21
20
  # @see {GraphQL::Schema::Mutation} for an example, it's basically the same.
22
21
  #
23
22
  class RelayClassicMutation < GraphQL::Schema::Mutation
23
+ include GraphQL::Schema::HasSingleInputArgument
24
+
25
+ argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
26
+
24
27
  # The payload should always include this field
25
28
  field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
26
29
  # Relay classic default:
@@ -31,34 +34,14 @@ module GraphQL
31
34
  def resolve_with_support(**inputs)
32
35
  input = inputs[:input].to_kwargs
33
36
 
34
- new_extras = field ? field.extras : []
35
- all_extras = self.class.extras + new_extras
36
-
37
- # Transfer these from the top-level hash to the
38
- # shortcutted `input:` object
39
- all_extras.each do |ext|
40
- # It's possible that the `extra` was not passed along by this point,
41
- # don't re-add it if it wasn't given here.
42
- if inputs.key?(ext)
43
- input[ext] = inputs[ext]
44
- end
45
- end
46
-
47
37
  if input
48
38
  # This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
49
39
  input_kwargs = input.to_h
50
40
  client_mutation_id = input_kwargs.delete(:client_mutation_id)
51
- else
52
- # Relay Classic Mutations with no `argument`s
53
- # don't require `input:`
54
- input_kwargs = {}
41
+ inputs[:input] = input_kwargs
55
42
  end
56
43
 
57
- return_value = if input_kwargs.any?
58
- super(**input_kwargs)
59
- else
60
- super()
61
- end
44
+ return_value = super(**inputs)
62
45
 
63
46
  context.query.after_lazy(return_value) do |return_hash|
64
47
  # It might be an error
@@ -68,112 +51,6 @@ module GraphQL
68
51
  return_hash
69
52
  end
70
53
  end
71
-
72
- class << self
73
- def dummy
74
- @dummy ||= begin
75
- d = Class.new(GraphQL::Schema::Resolver)
76
- d.argument_class(self.argument_class)
77
- # TODO make this lazier?
78
- d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
79
- d
80
- end
81
- end
82
-
83
- def field_arguments(context = GraphQL::Query::NullContext)
84
- dummy.arguments(context)
85
- end
86
-
87
- def get_field_argument(name, context = GraphQL::Query::NullContext)
88
- dummy.get_argument(name, context)
89
- end
90
-
91
- def own_field_arguments
92
- dummy.own_arguments
93
- end
94
-
95
- def all_field_argument_definitions
96
- dummy.all_argument_definitions
97
- end
98
-
99
- # Also apply this argument to the input type:
100
- def argument(*args, own_argument: false, **kwargs, &block)
101
- it = input_type # make sure any inherited arguments are already added to it
102
- arg = super(*args, **kwargs, &block)
103
-
104
- # This definition might be overriding something inherited;
105
- # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
106
- prev_args = it.own_arguments[arg.graphql_name]
107
- case prev_args
108
- when GraphQL::Schema::Argument
109
- if prev_args.owner != self
110
- it.own_arguments.delete(arg.graphql_name)
111
- end
112
- when Array
113
- prev_args.reject! { |a| a.owner != self }
114
- if prev_args.empty?
115
- it.own_arguments.delete(arg.graphql_name)
116
- end
117
- end
118
-
119
- it.add_argument(arg)
120
- arg
121
- end
122
-
123
- # The base class for generated input object types
124
- # @param new_class [Class] The base class to use for generating input object definitions
125
- # @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
126
- def input_object_class(new_class = nil)
127
- if new_class
128
- @input_object_class = new_class
129
- end
130
- @input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
131
- end
132
-
133
- # @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
134
- # @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
135
- def input_type(new_input_type = nil)
136
- if new_input_type
137
- @input_type = new_input_type
138
- end
139
- @input_type ||= generate_input_type
140
- end
141
-
142
- private
143
-
144
- # Generate the input type for the `input:` argument
145
- # To customize how input objects are generated, override this method
146
- # @return [Class] a subclass of {.input_object_class}
147
- def generate_input_type
148
- mutation_args = all_argument_definitions
149
- mutation_class = self
150
- Class.new(input_object_class) do
151
- class << self
152
- def default_graphql_name
153
- "#{self.mutation.graphql_name}Input"
154
- end
155
-
156
- def description(new_desc = nil)
157
- super || "Autogenerated input type of #{self.mutation.graphql_name}"
158
- end
159
- end
160
- mutation(mutation_class)
161
- # these might be inherited:
162
- mutation_args.each do |arg|
163
- add_argument(arg)
164
- end
165
- argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
166
- end
167
- end
168
- end
169
-
170
- private
171
-
172
- def authorize_arguments(args, values)
173
- # remove the `input` wrapper to match values
174
- input_args = args["input"].type.unwrap.arguments(context)
175
- super(input_args, values)
176
- end
177
54
  end
178
55
  end
179
56
  end
@@ -8,6 +8,7 @@ module GraphQL
8
8
  # - Arguments, via `.argument(...)` helper, which will be applied to the field.
9
9
  # - Return type, via `.type(..., null: ...)`, which will be applied to the field.
10
10
  # - Description, via `.description(...)`, which will be applied to the field
11
+ # - Comment, via `.comment(...)`, which will be applied to the field
11
12
  # - Resolution, via `#resolve(**args)` method, which will be called to resolve the field.
12
13
  # - `#object` and `#context` accessors for use during `#resolve`.
13
14
  #
@@ -19,12 +20,15 @@ module GraphQL
19
20
  # @see {GraphQL::Function} `Resolver` is a replacement for `GraphQL::Function`
20
21
  class Resolver
21
22
  include Schema::Member::GraphQLTypeNames
22
- # Really we only need description from here, but:
23
+ # Really we only need description & comment from here, but:
23
24
  extend Schema::Member::BaseDSLMethods
24
25
  extend GraphQL::Schema::Member::HasArguments
25
26
  extend GraphQL::Schema::Member::HasValidators
26
27
  include Schema::Member::HasPath
27
28
  extend Schema::Member::HasPath
29
+ extend Schema::Member::HasDirectives
30
+ include Schema::Member::HasDataloader
31
+ extend Schema::Member::HasDeprecationReason
28
32
 
29
33
  # @param object [Object] The application object that this field is being resolved on
30
34
  # @param context [GraphQL::Query::Context]
@@ -35,7 +39,7 @@ module GraphQL
35
39
  @field = field
36
40
  # Since this hash is constantly rebuilt, cache it for this call
37
41
  @arguments_by_keyword = {}
38
- self.class.arguments(context).each do |name, arg|
42
+ context.types.arguments(self.class).each do |arg|
39
43
  @arguments_by_keyword[arg.keyword] = arg
40
44
  end
41
45
  @prepared_arguments = nil
@@ -47,11 +51,6 @@ module GraphQL
47
51
  # @return [GraphQL::Query::Context]
48
52
  attr_reader :context
49
53
 
50
- # @return [GraphQL::Dataloader]
51
- def dataloader
52
- context.dataloader
53
- end
54
-
55
54
  # @return [GraphQL::Schema::Field]
56
55
  attr_reader :field
57
56
 
@@ -65,7 +64,7 @@ module GraphQL
65
64
  # @api private
66
65
  def resolve_with_support(**args)
67
66
  # First call the ready? hook which may raise
68
- raw_ready_val = if args.any?
67
+ raw_ready_val = if !args.empty?
69
68
  ready?(**args)
70
69
  else
71
70
  ready?
@@ -86,7 +85,7 @@ module GraphQL
86
85
  @prepared_arguments = loaded_args
87
86
  Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
88
87
  # Then call `authorized?`, which may raise or may return a lazy object
89
- raw_authorized_val = if loaded_args.any?
88
+ raw_authorized_val = if !loaded_args.empty?
90
89
  authorized?(**loaded_args)
91
90
  else
92
91
  authorized?
@@ -103,11 +102,7 @@ module GraphQL
103
102
  end
104
103
  elsif authorized_val
105
104
  # Finally, all the hooks have passed, so resolve it
106
- if loaded_args.any?
107
- public_send(self.class.resolve_method, **loaded_args)
108
- else
109
- public_send(self.class.resolve_method)
110
- end
105
+ call_resolve(loaded_args)
111
106
  else
112
107
  raise GraphQL::UnauthorizedFieldError.new(context: context, object: object, type: field.owner, field: field)
113
108
  end
@@ -117,6 +112,15 @@ module GraphQL
117
112
  end
118
113
  end
119
114
 
115
+ # @api private {GraphQL::Schema::Mutation} uses this to clear the dataloader cache
116
+ def call_resolve(args_hash)
117
+ if !args_hash.empty?
118
+ public_send(self.class.resolve_method, **args_hash)
119
+ else
120
+ public_send(self.class.resolve_method)
121
+ end
122
+ end
123
+
120
124
  # Do the work. Everything happens here.
121
125
  # @return [Object] An object corresponding to the return type
122
126
  def resolve(**args)
@@ -146,7 +150,7 @@ module GraphQL
146
150
  # @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
147
151
  def authorized?(**inputs)
148
152
  arg_owner = @field # || self.class
149
- args = arg_owner.arguments(context)
153
+ args = context.types.arguments(arg_owner)
150
154
  authorize_arguments(args, inputs)
151
155
  end
152
156
 
@@ -163,19 +167,22 @@ module GraphQL
163
167
  private
164
168
 
165
169
  def authorize_arguments(args, inputs)
166
- args.each_value do |argument|
170
+ args.each do |argument|
167
171
  arg_keyword = argument.keyword
168
172
  if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
169
- arg_auth, err = argument.authorized?(self, arg_value, context)
170
- if !arg_auth
171
- return arg_auth, err
172
- else
173
- true
173
+ auth_result = argument.authorized?(self, arg_value, context)
174
+ if auth_result.is_a?(Array)
175
+ # only return this second value if the application returned a second value
176
+ arg_auth, err = auth_result
177
+ if !arg_auth
178
+ return arg_auth, err
179
+ end
180
+ elsif auth_result == false
181
+ return auth_result
174
182
  end
175
- else
176
- true
177
183
  end
178
184
  end
185
+ true
179
186
  end
180
187
 
181
188
  def load_arguments(args)
@@ -198,23 +205,27 @@ module GraphQL
198
205
  end
199
206
 
200
207
  # Avoid returning a lazy if none are needed
201
- if prepare_lazies.any?
208
+ if !prepare_lazies.empty?
202
209
  GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args }
203
210
  else
204
211
  prepared_args
205
212
  end
206
213
  end
207
214
 
208
- def get_argument(name, context = GraphQL::Query::NullContext)
215
+ def get_argument(name, context = GraphQL::Query::NullContext.instance)
209
216
  self.class.get_argument(name, context)
210
217
  end
211
218
 
212
219
  class << self
213
- def field_arguments(context = GraphQL::Query::NullContext)
220
+ def field_arguments(context = GraphQL::Query::NullContext.instance)
214
221
  arguments(context)
215
222
  end
216
223
 
217
- def get_field_argument(name, context = GraphQL::Query::NullContext)
224
+ def any_field_arguments?
225
+ any_arguments?
226
+ end
227
+
228
+ def get_field_argument(name, context = GraphQL::Query::NullContext.instance)
218
229
  get_argument(name, context)
219
230
  end
220
231
 
@@ -380,7 +391,7 @@ module GraphQL
380
391
  if superclass.respond_to?(:extensions)
381
392
  s_exts = superclass.extensions
382
393
  if own_exts
383
- if s_exts.any?
394
+ if !s_exts.empty?
384
395
  own_exts + s_exts
385
396
  else
386
397
  own_exts
@@ -393,11 +404,14 @@ module GraphQL
393
404
  end
394
405
  end
395
406
 
407
+ def inherited(child_class)
408
+ child_class.description(description)
409
+ super
410
+ end
411
+
396
412
  private
397
413
 
398
- def own_extensions
399
- @own_extensions
400
- end
414
+ attr_reader :own_extensions
401
415
  end
402
416
  end
403
417
  end
@@ -19,9 +19,9 @@ module GraphQL
19
19
 
20
20
  def specified_by_url(new_url = nil)
21
21
  if new_url
22
- @specified_by_url = new_url
23
- elsif defined?(@specified_by_url)
24
- @specified_by_url
22
+ directive(GraphQL::Schema::Directive::SpecifiedBy, url: new_url)
23
+ elsif (directive = directives.find { |dir| dir.graphql_name == "specifiedBy" })
24
+ directive.arguments[:url] # rubocop:disable Development/ContextIsPassedCop
25
25
  elsif superclass.respond_to?(:specified_by_url)
26
26
  superclass.specified_by_url
27
27
  else
@@ -50,12 +50,7 @@ module GraphQL
50
50
  end
51
51
 
52
52
  if coerced_result.nil?
53
- str_value = if value == Float::INFINITY
54
- ""
55
- else
56
- " #{GraphQL::Language.serialize(value)}"
57
- end
58
- Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}")
53
+ Query::InputValidationResult.from_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{graphql_name}")
59
54
  elsif coerced_result.is_a?(GraphQL::CoercionError)
60
55
  Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
61
56
  else
@@ -19,29 +19,45 @@ module GraphQL
19
19
  # propagate null.
20
20
  null false
21
21
 
22
+ # @api private
22
23
  def initialize(object:, context:, field:)
23
24
  super
24
25
  # Figure out whether this is an update or an initial subscription
25
26
  @mode = context.query.subscription_update? ? :update : :subscribe
27
+ @subscription_written = false
28
+ @original_arguments = nil
29
+ if (subs_ns = context.namespace(:subscriptions)) &&
30
+ (sub_insts = subs_ns[:subscriptions])
31
+ sub_insts[context.current_path] = self
32
+ end
26
33
  end
27
34
 
35
+ # @api private
28
36
  def resolve_with_support(**args)
37
+ @original_arguments = args # before `loads:` have been run
29
38
  result = nil
30
39
  unsubscribed = true
31
- catch :graphql_subscription_unsubscribed do
40
+ unsubscribed_result = catch :graphql_subscription_unsubscribed do
32
41
  result = super
33
42
  unsubscribed = false
34
43
  end
35
44
 
36
45
 
37
46
  if unsubscribed
38
- context.skip
47
+ if unsubscribed_result
48
+ context.namespace(:subscriptions)[:final_update] = true
49
+ unsubscribed_result
50
+ else
51
+ context.skip
52
+ end
39
53
  else
40
54
  result
41
55
  end
42
56
  end
43
57
 
44
- # Implement the {Resolve} API
58
+ # Implement the {Resolve} API.
59
+ # You can implement this if you want code to run for _both_ the initial subscription
60
+ # and for later updates. Or, implement {#subscribe} and {#update}
45
61
  def resolve(**args)
46
62
  # Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
47
63
  # have an unexpected `@mode`
@@ -49,8 +65,9 @@ module GraphQL
49
65
  end
50
66
 
51
67
  # Wrap the user-defined `#subscribe` hook
68
+ # @api private
52
69
  def resolve_subscribe(**args)
53
- ret_val = args.any? ? subscribe(**args) : subscribe
70
+ ret_val = !args.empty? ? subscribe(**args) : subscribe
54
71
  if ret_val == :no_response
55
72
  context.skip
56
73
  else
@@ -66,8 +83,9 @@ module GraphQL
66
83
  end
67
84
 
68
85
  # Wrap the user-provided `#update` hook
86
+ # @api private
69
87
  def resolve_update(**args)
70
- ret_val = args.any? ? update(**args) : update
88
+ ret_val = !args.empty? ? update(**args) : update
71
89
  if ret_val == NO_UPDATE
72
90
  context.namespace(:subscriptions)[:no_update] = true
73
91
  context.skip
@@ -94,19 +112,20 @@ module GraphQL
94
112
  end
95
113
 
96
114
  # Call this to halt execution and remove this subscription from the system
97
- def unsubscribe
115
+ # @param update_value [Object] if present, deliver this update before unsubscribing
116
+ # @return [void]
117
+ def unsubscribe(update_value = nil)
98
118
  context.namespace(:subscriptions)[:unsubscribed] = true
99
- throw :graphql_subscription_unsubscribed
119
+ throw :graphql_subscription_unsubscribed, update_value
100
120
  end
101
121
 
102
- READING_SCOPE = ::Object.new
103
122
  # Call this method to provide a new subscription_scope; OR
104
123
  # call it without an argument to get the subscription_scope
105
124
  # @param new_scope [Symbol]
106
125
  # @param optional [Boolean] If true, then don't require `scope:` to be provided to updates to this subscription.
107
126
  # @return [Symbol]
108
- def self.subscription_scope(new_scope = READING_SCOPE, optional: false)
109
- if new_scope != READING_SCOPE
127
+ def self.subscription_scope(new_scope = NOT_CONFIGURED, optional: false)
128
+ if new_scope != NOT_CONFIGURED
110
129
  @subscription_scope = new_scope
111
130
  @subscription_scope_optional = optional
112
131
  elsif defined?(@subscription_scope)
@@ -143,6 +162,40 @@ module GraphQL
143
162
  def self.topic_for(arguments:, field:, scope:)
144
163
  Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments])
145
164
  end
165
+
166
+ # Calls through to `schema.subscriptions` to register this subscription with the backend.
167
+ # This is automatically called by GraphQL-Ruby after a query finishes successfully,
168
+ # but if you need to commit the subscription during `#subscribe`, you can call it there.
169
+ # (This method also sets a flag showing that this subscription was already written.)
170
+ #
171
+ # If you call this method yourself, you may also need to {#unsubscribe}
172
+ # or call `subscriptions.delete_subscription` to clean up the database if the query crashes with an error
173
+ # later in execution.
174
+ # @return [void]
175
+ def write_subscription
176
+ if subscription_written?
177
+ raise GraphQL::Error, "`write_subscription` was called but `#{self.class}#subscription_written?` is already true. Remove a call to `write subscription`."
178
+ else
179
+ @subscription_written = true
180
+ context.schema.subscriptions.write_subscription(context.query, [event])
181
+ end
182
+ nil
183
+ end
184
+
185
+ # @return [Boolean] `true` if {#write_subscription} was called already
186
+ def subscription_written?
187
+ @subscription_written
188
+ end
189
+
190
+ # @return [Subscriptions::Event] This object is used as a representation of this subscription for the backend
191
+ def event
192
+ @event ||= Subscriptions::Event.new(
193
+ name: field.name,
194
+ arguments: @original_arguments,
195
+ context: context,
196
+ field: field,
197
+ )
198
+ end
146
199
  end
147
200
  end
148
201
  end
@@ -71,15 +71,23 @@ module GraphQL
71
71
  def execute_field(query:, field:, **_rest)
72
72
  timeout_state = query.context.namespace(@timeout).fetch(:state)
73
73
  # If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
74
- if timeout_state != false && Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
74
+ if timeout_state == false
75
+ super
76
+ elsif Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
75
77
  error = GraphQL::Schema::Timeout::TimeoutError.new(field)
76
78
  # Only invoke the timeout callback for the first timeout
77
79
  if !timeout_state[:timed_out]
78
80
  timeout_state[:timed_out] = true
79
81
  @timeout.handle_timeout(error, query)
82
+ timeout_state = query.context.namespace(@timeout).fetch(:state)
80
83
  end
81
84
 
82
- error
85
+ # `handle_timeout` may have set this to be `false`
86
+ if timeout_state != false
87
+ error
88
+ else
89
+ super
90
+ end
83
91
  else
84
92
  super
85
93
  end
@@ -102,6 +110,15 @@ module GraphQL
102
110
  # override to do something interesting
103
111
  end
104
112
 
113
+ # Call this method (eg, from {#handle_timeout}) to disable timeout tracking
114
+ # for the given query.
115
+ # @param query [GraphQL::Query]
116
+ # @return [void]
117
+ def disable_timeout(query)
118
+ query.context.namespace(self)[:state] = false
119
+ nil
120
+ end
121
+
105
122
  # This error is raised when a query exceeds `max_seconds`.
106
123
  # Since it's a child of {GraphQL::ExecutionError},
107
124
  # its message will be added to the response's `errors` key.
@@ -5,13 +5,13 @@ module GraphQL
5
5
  module TypeExpression
6
6
  # Fetch a type from a type map by its AST specification.
7
7
  # Return `nil` if not found.
8
- # @param type_owner [#get_type] A thing for looking up types by name
8
+ # @param type_owner [#type] A thing for looking up types by name
9
9
  # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
10
10
  # @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List]
11
11
  def self.build_type(type_owner, ast_node)
12
12
  case ast_node
13
13
  when GraphQL::Language::Nodes::TypeName
14
- type_owner.get_type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
14
+ type_owner.type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
15
15
  when GraphQL::Language::Nodes::NonNullType
16
16
  ast_inner_type = ast_node.of_type
17
17
  inner_type = build_type(type_owner, ast_inner_type)
@@ -10,8 +10,8 @@ module GraphQL
10
10
  super
11
11
  end
12
12
 
13
- def possible_types(*types, context: GraphQL::Query::NullContext, **options)
14
- if types.any?
13
+ def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options)
14
+ if !types.empty?
15
15
  types.each do |t|
16
16
  assert_valid_union_member(t)
17
17
  type_memberships << type_membership_class.new(self, t, **options)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'graphql/schema/base_64_bp'
2
+ require "base64"
3
3
 
4
4
  module GraphQL
5
5
  class Schema
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Validator
6
+ # Use this to validate each member of an array value.
7
+ #
8
+ # @example validate format of all strings in an array
9
+ #
10
+ # argument :handles, [String],
11
+ # validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ } } }
12
+ #
13
+ # @example multiple validators can be combined
14
+ #
15
+ # argument :handles, [String],
16
+ # validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ }, length: { maximum: 32 } } }
17
+ #
18
+ # @example any type can be used
19
+ #
20
+ # argument :choices, [Integer],
21
+ # validates: { all: { inclusion: { in: 1..12 } } }
22
+ #
23
+ class AllValidator < Validator
24
+ def initialize(validated:, allow_blank: false, allow_null: false, **validators)
25
+ super(validated: validated, allow_blank: allow_blank, allow_null: allow_null)
26
+
27
+ @validators = Validator.from_config(validated, validators)
28
+ end
29
+
30
+ def validate(object, context, value)
31
+ return EMPTY_ARRAY if permitted_empty_value?(value)
32
+
33
+ all_errors = EMPTY_ARRAY
34
+
35
+ value.each do |subvalue|
36
+ @validators.each do |validator|
37
+ errors = validator.validate(object, context, subvalue)
38
+ if errors &&
39
+ (errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
40
+ (errors.is_a?(String))
41
+ if all_errors.frozen? # It's empty
42
+ all_errors = []
43
+ end
44
+ if errors.is_a?(String)
45
+ all_errors << errors
46
+ else
47
+ all_errors.concat(errors)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ unless all_errors.frozen?
54
+ all_errors.uniq!
55
+ end
56
+
57
+ all_errors
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end