graphql 2.0.32 → 2.5.22

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 (308) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  4. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  5. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  6. data/lib/generators/graphql/install_generator.rb +49 -0
  7. data/lib/generators/graphql/orm_mutations_base.rb +1 -1
  8. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  9. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  10. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  11. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  12. data/lib/generators/graphql/templates/base_field.erb +2 -0
  13. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  14. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  15. data/lib/generators/graphql/templates/base_object.erb +2 -0
  16. data/lib/generators/graphql/templates/base_resolver.erb +8 -0
  17. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  18. data/lib/generators/graphql/templates/base_union.erb +2 -0
  19. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -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 +5 -0
  26. data/lib/generators/graphql/type_generator.rb +1 -1
  27. data/lib/graphql/analysis/analyzer.rb +90 -0
  28. data/lib/graphql/analysis/field_usage.rb +82 -0
  29. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  30. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  31. data/lib/graphql/analysis/query_complexity.rb +263 -0
  32. data/lib/graphql/analysis/query_depth.rb +58 -0
  33. data/lib/graphql/analysis/visitor.rb +280 -0
  34. data/lib/graphql/analysis.rb +95 -1
  35. data/lib/graphql/autoload.rb +38 -0
  36. data/lib/graphql/backtrace/table.rb +118 -55
  37. data/lib/graphql/backtrace.rb +1 -19
  38. data/lib/graphql/coercion_error.rb +1 -9
  39. data/lib/graphql/current.rb +57 -0
  40. data/lib/graphql/dashboard/application_controller.rb +41 -0
  41. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  42. data/lib/graphql/dashboard/installable.rb +22 -0
  43. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  44. data/lib/graphql/dashboard/limiters.rb +93 -0
  45. data/lib/graphql/dashboard/operation_store.rb +199 -0
  46. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  47. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  48. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  49. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  50. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  51. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  52. data/lib/graphql/dashboard/statics/icon.png +0 -0
  53. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  54. data/lib/graphql/dashboard/subscriptions.rb +97 -0
  55. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  56. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  57. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  58. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  59. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  60. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  61. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  62. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  63. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  64. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  65. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  66. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  67. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  68. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  69. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  70. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  71. data/lib/graphql/dashboard.rb +96 -0
  72. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  73. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  74. data/lib/graphql/dataloader/async_dataloader.rb +112 -0
  75. data/lib/graphql/dataloader/null_dataloader.rb +55 -10
  76. data/lib/graphql/dataloader/request.rb +5 -0
  77. data/lib/graphql/dataloader/source.rb +35 -12
  78. data/lib/graphql/dataloader.rb +224 -149
  79. data/lib/graphql/date_encoding_error.rb +1 -1
  80. data/lib/graphql/dig.rb +2 -1
  81. data/lib/graphql/duration_encoding_error.rb +16 -0
  82. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  83. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  84. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  85. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  86. data/lib/graphql/execution/interpreter/runtime.rb +363 -434
  87. data/lib/graphql/execution/interpreter.rb +91 -164
  88. data/lib/graphql/execution/lookahead.rb +105 -31
  89. data/lib/graphql/execution/multiplex.rb +7 -6
  90. data/lib/graphql/execution/next/field_resolve_step.rb +711 -0
  91. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  92. data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
  93. data/lib/graphql/execution/next/runner.rb +389 -0
  94. data/lib/graphql/execution/next/selections_step.rb +37 -0
  95. data/lib/graphql/execution/next.rb +70 -0
  96. data/lib/graphql/execution.rb +1 -0
  97. data/lib/graphql/execution_error.rb +13 -10
  98. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  99. data/lib/graphql/introspection/directive_type.rb +7 -3
  100. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  101. data/lib/graphql/introspection/entry_points.rb +20 -6
  102. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  103. data/lib/graphql/introspection/field_type.rb +13 -5
  104. data/lib/graphql/introspection/input_value_type.rb +21 -13
  105. data/lib/graphql/introspection/schema_type.rb +8 -11
  106. data/lib/graphql/introspection/type_type.rb +64 -28
  107. data/lib/graphql/invalid_name_error.rb +1 -1
  108. data/lib/graphql/invalid_null_error.rb +26 -17
  109. data/lib/graphql/language/block_string.rb +34 -18
  110. data/lib/graphql/language/cache.rb +13 -0
  111. data/lib/graphql/language/comment.rb +18 -0
  112. data/lib/graphql/language/definition_slice.rb +1 -1
  113. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  114. data/lib/graphql/language/lexer.rb +319 -193
  115. data/lib/graphql/language/nodes.rb +136 -77
  116. data/lib/graphql/language/parser.rb +807 -1985
  117. data/lib/graphql/language/printer.rb +324 -151
  118. data/lib/graphql/language/sanitized_printer.rb +21 -23
  119. data/lib/graphql/language/static_visitor.rb +171 -0
  120. data/lib/graphql/language/visitor.rb +23 -83
  121. data/lib/graphql/language.rb +71 -1
  122. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  123. data/lib/graphql/pagination/array_connection.rb +6 -6
  124. data/lib/graphql/pagination/connection.rb +30 -1
  125. data/lib/graphql/pagination/connections.rb +32 -0
  126. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  127. data/lib/graphql/query/context/scoped_context.rb +101 -0
  128. data/lib/graphql/query/context.rb +82 -144
  129. data/lib/graphql/query/null_context.rb +15 -18
  130. data/lib/graphql/query/partial.rb +179 -0
  131. data/lib/graphql/query/validation_pipeline.rb +4 -4
  132. data/lib/graphql/query/variable_validation_error.rb +1 -1
  133. data/lib/graphql/query/variables.rb +3 -3
  134. data/lib/graphql/query.rb +126 -81
  135. data/lib/graphql/railtie.rb +16 -6
  136. data/lib/graphql/rake_task.rb +3 -12
  137. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  138. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  139. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  140. data/lib/graphql/rubocop.rb +2 -0
  141. data/lib/graphql/schema/addition.rb +26 -13
  142. data/lib/graphql/schema/always_visible.rb +7 -2
  143. data/lib/graphql/schema/argument.rb +75 -9
  144. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  145. data/lib/graphql/schema/build_from_definition.rb +123 -60
  146. data/lib/graphql/schema/directive/flagged.rb +4 -2
  147. data/lib/graphql/schema/directive/one_of.rb +12 -0
  148. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  149. data/lib/graphql/schema/directive.rb +54 -2
  150. data/lib/graphql/schema/enum.rb +110 -27
  151. data/lib/graphql/schema/enum_value.rb +10 -2
  152. data/lib/graphql/schema/field/connection_extension.rb +15 -49
  153. data/lib/graphql/schema/field/scope_extension.rb +23 -7
  154. data/lib/graphql/schema/field.rb +245 -118
  155. data/lib/graphql/schema/field_extension.rb +34 -1
  156. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  157. data/lib/graphql/schema/input_object.rb +116 -60
  158. data/lib/graphql/schema/interface.rb +34 -16
  159. data/lib/graphql/schema/introspection_system.rb +8 -17
  160. data/lib/graphql/schema/late_bound_type.rb +4 -0
  161. data/lib/graphql/schema/list.rb +3 -3
  162. data/lib/graphql/schema/loader.rb +3 -4
  163. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -2
  164. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  165. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  166. data/lib/graphql/schema/member/has_dataloader.rb +99 -0
  167. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  168. data/lib/graphql/schema/member/has_directives.rb +4 -4
  169. data/lib/graphql/schema/member/has_fields.rb +115 -15
  170. data/lib/graphql/schema/member/has_interfaces.rb +26 -12
  171. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  172. data/lib/graphql/schema/member/has_validators.rb +1 -1
  173. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  174. data/lib/graphql/schema/member/scoped.rb +19 -0
  175. data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
  176. data/lib/graphql/schema/member/validates_input.rb +3 -3
  177. data/lib/graphql/schema/member.rb +6 -0
  178. data/lib/graphql/schema/mutation.rb +7 -0
  179. data/lib/graphql/schema/object.rb +34 -8
  180. data/lib/graphql/schema/printer.rb +9 -7
  181. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  182. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  183. data/lib/graphql/schema/resolver.rb +90 -32
  184. data/lib/graphql/schema/scalar.rb +4 -9
  185. data/lib/graphql/schema/subscription.rb +63 -10
  186. data/lib/graphql/schema/timeout.rb +19 -2
  187. data/lib/graphql/schema/type_expression.rb +2 -2
  188. data/lib/graphql/schema/union.rb +2 -2
  189. data/lib/graphql/schema/unique_within_type.rb +1 -1
  190. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  191. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  192. data/lib/graphql/schema/validator.rb +3 -1
  193. data/lib/graphql/schema/visibility/migration.rb +188 -0
  194. data/lib/graphql/schema/visibility/profile.rb +445 -0
  195. data/lib/graphql/schema/visibility/visit.rb +190 -0
  196. data/lib/graphql/schema/visibility.rb +311 -0
  197. data/lib/graphql/schema/warden.rb +275 -103
  198. data/lib/graphql/schema.rb +950 -210
  199. data/lib/graphql/static_validation/all_rules.rb +3 -3
  200. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  201. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  202. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  203. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
  204. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
  205. data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
  206. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  207. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
  208. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  209. data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
  210. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  211. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  212. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
  213. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  214. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  215. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  216. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  217. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  218. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
  219. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  220. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  221. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  222. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  223. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  224. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  225. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
  226. data/lib/graphql/static_validation/validation_context.rb +21 -5
  227. data/lib/graphql/static_validation/validator.rb +9 -1
  228. data/lib/graphql/static_validation.rb +0 -1
  229. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  230. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  231. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  232. data/lib/graphql/subscriptions/event.rb +21 -4
  233. data/lib/graphql/subscriptions/serialize.rb +3 -1
  234. data/lib/graphql/subscriptions.rb +21 -17
  235. data/lib/graphql/testing/helpers.rb +161 -0
  236. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  237. data/lib/graphql/testing.rb +3 -0
  238. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  239. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  240. data/lib/graphql/tracing/appoptics_trace.rb +7 -3
  241. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  242. data/lib/graphql/tracing/appsignal_trace.rb +32 -59
  243. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  244. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  245. data/lib/graphql/tracing/data_dog_trace.rb +46 -162
  246. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  247. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  248. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  249. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  250. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  251. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  252. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  253. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  254. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  255. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  256. data/lib/graphql/tracing/notifications_trace.rb +183 -37
  257. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  258. data/lib/graphql/tracing/null_trace.rb +9 -0
  259. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  260. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  261. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  262. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  263. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  264. data/lib/graphql/tracing/prometheus_trace.rb +73 -73
  265. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  266. data/lib/graphql/tracing/scout_trace.rb +32 -58
  267. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  268. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  269. data/lib/graphql/tracing/statsd_trace.rb +33 -45
  270. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  271. data/lib/graphql/tracing/trace.rb +112 -1
  272. data/lib/graphql/tracing.rb +31 -28
  273. data/lib/graphql/type_kinds.rb +2 -1
  274. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  275. data/lib/graphql/types/relay/connection_behaviors.rb +44 -2
  276. data/lib/graphql/types/relay/edge_behaviors.rb +18 -0
  277. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  278. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  279. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  280. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  281. data/lib/graphql/types.rb +18 -10
  282. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  283. data/lib/graphql/unauthorized_error.rb +5 -1
  284. data/lib/graphql/version.rb +1 -1
  285. data/lib/graphql.rb +71 -54
  286. data/readme.md +12 -2
  287. metadata +233 -37
  288. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  289. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  290. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  291. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  292. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  293. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  294. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  295. data/lib/graphql/analysis/ast.rb +0 -81
  296. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  297. data/lib/graphql/backtrace/trace.rb +0 -96
  298. data/lib/graphql/backtrace/tracer.rb +0 -80
  299. data/lib/graphql/deprecation.rb +0 -9
  300. data/lib/graphql/filter.rb +0 -59
  301. data/lib/graphql/language/parser.y +0 -560
  302. data/lib/graphql/language/token.rb +0 -34
  303. data/lib/graphql/schema/base_64_bp.rb +0 -26
  304. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  305. data/lib/graphql/schema/null_mask.rb +0 -11
  306. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  307. data/lib/graphql/static_validation/type_stack.rb +0 -216
  308. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- class FieldUsage < Analyzer
6
- def initialize(query)
7
- super
8
- @used_fields = Set.new
9
- @used_deprecated_fields = Set.new
10
- @used_deprecated_arguments = Set.new
11
- end
12
-
13
- def on_leave_field(node, parent, visitor)
14
- field_defn = visitor.field_definition
15
- field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
16
- @used_fields << field
17
- @used_deprecated_fields << field if field_defn.deprecation_reason
18
- arguments = visitor.query.arguments_for(node, visitor.field_definition)
19
- # If there was an error when preparing this argument object,
20
- # then this might be an error or something:
21
- if arguments.respond_to?(:argument_values)
22
- extract_deprecated_arguments(arguments.argument_values)
23
- end
24
- end
25
-
26
- def result
27
- {
28
- used_fields: @used_fields.to_a,
29
- used_deprecated_fields: @used_deprecated_fields.to_a,
30
- used_deprecated_arguments: @used_deprecated_arguments.to_a,
31
- }
32
- end
33
-
34
- private
35
-
36
- def extract_deprecated_arguments(argument_values)
37
- argument_values.each_pair do |_argument_name, argument|
38
- if argument.definition.deprecation_reason
39
- @used_deprecated_arguments << argument.definition.path
40
- end
41
-
42
- next if argument.value.nil?
43
-
44
- if argument.definition.type.kind.input_object?
45
- extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
46
- elsif argument.definition.type.list?
47
- argument
48
- .value
49
- .select { |value| value.respond_to?(:arguments) }
50
- .each { |value| extract_deprecated_arguments(value.arguments.argument_values) } # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
51
- end
52
- end
53
- end
54
- end
55
- end
56
- end
57
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- # Used under the hood to implement complexity validation,
6
- # see {Schema#max_complexity} and {Query#max_complexity}
7
- class MaxQueryComplexity < QueryComplexity
8
- def result
9
- return if subject.max_complexity.nil?
10
-
11
- total_complexity = max_possible_complexity
12
-
13
- if total_complexity > subject.max_complexity
14
- GraphQL::AnalysisError.new("Query has complexity of #{total_complexity}, which exceeds max complexity of #{subject.max_complexity}")
15
- else
16
- nil
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- class MaxQueryDepth < QueryDepth
6
- def result
7
- configured_max_depth = if query
8
- query.max_depth
9
- else
10
- multiplex.schema.max_depth
11
- end
12
-
13
- if configured_max_depth && @max_depth > configured_max_depth
14
- GraphQL::AnalysisError.new("Query has depth of #{@max_depth}, which exceeds max depth of #{configured_max_depth}")
15
- else
16
- nil
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,230 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- # Calculate the complexity of a query, using {Field#complexity} values.
5
- module AST
6
- class QueryComplexity < Analyzer
7
- # State for the query complexity calculation:
8
- # - `complexities_on_type` holds complexity scores for each type
9
- def initialize(query)
10
- super
11
- @complexities_on_type_by_query = {}
12
- end
13
-
14
- # Overide this method to use the complexity result
15
- def result
16
- max_possible_complexity
17
- end
18
-
19
- class ScopedTypeComplexity
20
- # A single proc for {#scoped_children} hashes. Use this to avoid repeated allocations,
21
- # since the lexical binding isn't important.
22
- HASH_CHILDREN = ->(h, k) { h[k] = {} }
23
-
24
- attr_reader :field_definition, :response_path, :query
25
-
26
- # @param parent_type [Class] The owner of `field_definition`
27
- # @param field_definition [GraphQL::Field, GraphQL::Schema::Field] Used for getting the `.complexity` configuration
28
- # @param query [GraphQL::Query] Used for `query.possible_types`
29
- # @param response_path [Array<String>] The path to the response key for the field
30
- def initialize(parent_type, field_definition, query, response_path)
31
- @parent_type = parent_type
32
- @field_definition = field_definition
33
- @query = query
34
- @response_path = response_path
35
- @scoped_children = nil
36
- @nodes = []
37
- end
38
-
39
- # @return [Array<GraphQL::Language::Nodes::Field>]
40
- attr_reader :nodes
41
-
42
- # Returns true if this field has no selections, ie, it's a scalar.
43
- # We need a quick way to check whether we should continue traversing.
44
- def terminal?
45
- @scoped_children.nil?
46
- end
47
-
48
- # This value is only calculated when asked for to avoid needless hash allocations.
49
- # Also, if it's never asked for, we determine that this scope complexity
50
- # is a scalar field ({#terminal?}).
51
- # @return [Hash<Hash<Class => ScopedTypeComplexity>]
52
- def scoped_children
53
- @scoped_children ||= Hash.new(&HASH_CHILDREN)
54
- end
55
-
56
- def own_complexity(child_complexity)
57
- @field_definition.calculate_complexity(query: @query, nodes: @nodes, child_complexity: child_complexity)
58
- end
59
- end
60
-
61
- def on_enter_field(node, parent, visitor)
62
- # We don't want to visit fragment definitions,
63
- # we'll visit them when we hit the spreads instead
64
- return if visitor.visiting_fragment_definition?
65
- return if visitor.skipping?
66
- parent_type = visitor.parent_type_definition
67
- field_key = node.alias || node.name
68
- # Find the complexity calculation for this field --
69
- # if we're re-entering a selection, we'll already have one.
70
- # Otherwise, make a new one and store it.
71
- #
72
- # `node` and `visitor.field_definition` may appear from a cache,
73
- # but I think that's ok. If the arguments _didn't_ match,
74
- # then the query would have been rejected as invalid.
75
- complexities_on_type = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query, visitor.response_path)]
76
-
77
- complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(parent_type, visitor.field_definition, visitor.query, visitor.response_path)
78
- complexity.nodes.push(node)
79
- # Push it on the stack.
80
- complexities_on_type.push(complexity)
81
- end
82
-
83
- def on_leave_field(node, parent, visitor)
84
- # We don't want to visit fragment definitions,
85
- # we'll visit them when we hit the spreads instead
86
- return if visitor.visiting_fragment_definition?
87
- return if visitor.skipping?
88
- complexities_on_type = @complexities_on_type_by_query[visitor.query]
89
- complexities_on_type.pop
90
- end
91
-
92
- private
93
-
94
- # @return [Integer]
95
- def max_possible_complexity
96
- @complexities_on_type_by_query.reduce(0) do |total, (query, complexities_on_type)|
97
- root_complexity = complexities_on_type.last
98
- # Use this entry point to calculate the total complexity
99
- total_complexity_for_query = merged_max_complexity_for_scopes(query, [root_complexity.scoped_children])
100
- total + total_complexity_for_query
101
- end
102
- end
103
-
104
- # @param query [GraphQL::Query] Used for `query.possible_types`
105
- # @param scoped_children_hashes [Array<Hash>] Array of scoped children hashes
106
- # @return [Integer]
107
- def merged_max_complexity_for_scopes(query, scoped_children_hashes)
108
- # Figure out what scopes are possible here.
109
- # Use a hash, but ignore the values; it's just a fast way to work with the keys.
110
- all_scopes = {}
111
- scoped_children_hashes.each do |h|
112
- all_scopes.merge!(h)
113
- end
114
-
115
- # If an abstract scope is present, but _all_ of its concrete types
116
- # are also in the list, remove it from the list of scopes to check,
117
- # because every possible type is covered by a concrete type.
118
- # (That is, there are no remainder types to check.)
119
- prev_keys = all_scopes.keys
120
- prev_keys.each do |scope|
121
- next unless scope.kind.abstract?
122
-
123
- missing_concrete_types = query.possible_types(scope).select { |t| !all_scopes.key?(t) }
124
- # This concrete type is possible _only_ as a member of the abstract type.
125
- # So, attribute to it the complexity which belongs to the abstract type.
126
- missing_concrete_types.each do |concrete_scope|
127
- all_scopes[concrete_scope] = all_scopes[scope]
128
- end
129
- all_scopes.delete(scope)
130
- end
131
-
132
- # This will hold `{ type => int }` pairs, one for each possible branch
133
- complexity_by_scope = {}
134
-
135
- # For each scope,
136
- # find the lexical selections that might apply to it,
137
- # and gather them together into an array.
138
- # Then, treat the set of selection hashes
139
- # as a set and calculate the complexity for them as a unit
140
- all_scopes.each do |scope, _|
141
- # These will be the selections on `scope`
142
- children_for_scope = []
143
- scoped_children_hashes.each do |sc_h|
144
- sc_h.each do |inner_scope, children_hash|
145
- if applies_to?(query, scope, inner_scope)
146
- children_for_scope << children_hash
147
- end
148
- end
149
- end
150
-
151
- # Calculate the complexity for `scope`, merging all
152
- # possible lexical branches.
153
- complexity_value = merged_max_complexity(query, children_for_scope)
154
- complexity_by_scope[scope] = complexity_value
155
- end
156
-
157
- # Return the max complexity among all scopes
158
- complexity_by_scope.each_value.max
159
- end
160
-
161
- def applies_to?(query, left_scope, right_scope)
162
- if left_scope == right_scope
163
- # This can happen when several branches are being analyzed together
164
- true
165
- else
166
- # Check if these two scopes have _any_ types in common.
167
- possible_right_types = query.possible_types(right_scope)
168
- possible_left_types = query.possible_types(left_scope)
169
- !(possible_right_types & possible_left_types).empty?
170
- end
171
- end
172
-
173
- # A hook which is called whenever a field's max complexity is calculated.
174
- # Override this method to capture individual field complexity details.
175
- #
176
- # @param scoped_type_complexity [ScopedTypeComplexity]
177
- # @param max_complexity [Numeric] Field's maximum complexity including child complexity
178
- # @param child_complexity [Numeric, nil] Field's child complexity
179
- def field_complexity(scoped_type_complexity, max_complexity:, child_complexity: nil)
180
- end
181
-
182
- # @param children_for_scope [Array<Hash>] An array of `scoped_children[scope]` hashes
183
- # (`{field_key => complexity}`)
184
- # @return [Integer] Complexity value for all these selections in the current scope
185
- def merged_max_complexity(query, children_for_scope)
186
- all_keys = []
187
- children_for_scope.each do |c|
188
- all_keys.concat(c.keys)
189
- end
190
- all_keys.uniq!
191
- complexity_for_keys = {}
192
-
193
- all_keys.each do |child_key|
194
- scoped_children_for_key = nil
195
- complexity_for_key = nil
196
- children_for_scope.each do |children_hash|
197
- next unless children_hash.key?(child_key)
198
-
199
- complexity_for_key = children_hash[child_key]
200
- if complexity_for_key.terminal?
201
- # Assume that all terminals would return the same complexity
202
- # Since it's a terminal, its child complexity is zero.
203
- complexity = complexity_for_key.own_complexity(0)
204
- complexity_for_keys[child_key] = complexity
205
-
206
- field_complexity(complexity_for_key, max_complexity: complexity, child_complexity: nil)
207
- else
208
- scoped_children_for_key ||= []
209
- scoped_children_for_key << complexity_for_key.scoped_children
210
- end
211
- end
212
-
213
- next unless scoped_children_for_key
214
-
215
- child_complexity = merged_max_complexity_for_scopes(query, scoped_children_for_key)
216
- # This is the _last_ one we visited; assume it's representative.
217
- max_complexity = complexity_for_key.own_complexity(child_complexity)
218
-
219
- field_complexity(complexity_for_key, max_complexity: max_complexity, child_complexity: child_complexity)
220
-
221
- complexity_for_keys[child_key] = max_complexity
222
- end
223
-
224
- # Calculate the child complexity by summing the complexity of all selections
225
- complexity_for_keys.each_value.inject(0, &:+)
226
- end
227
- end
228
- end
229
- end
230
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- # A query reducer for measuring the depth of a given query.
5
- #
6
- # See https://graphql-ruby.org/queries/ast_analysis.html for more examples.
7
- #
8
- # @example Logging the depth of a query
9
- # class LogQueryDepth < GraphQL::Analysis::QueryDepth
10
- # def result
11
- # log("GraphQL query depth: #{@max_depth}")
12
- # end
13
- # end
14
- #
15
- # # In your Schema file:
16
- #
17
- # class MySchema < GraphQL::Schema
18
- # query_analyzer LogQueryDepth
19
- # end
20
- #
21
- # # When you run the query, the depth will get logged:
22
- #
23
- # Schema.execute(query_str)
24
- # # GraphQL query depth: 8
25
- #
26
- module AST
27
- class QueryDepth < Analyzer
28
- def initialize(query)
29
- @max_depth = 0
30
- @current_depth = 0
31
- super
32
- end
33
-
34
- def on_enter_field(node, parent, visitor)
35
- return if visitor.skipping? || visitor.visiting_fragment_definition?
36
-
37
- @current_depth += 1
38
- end
39
-
40
- def on_leave_field(node, parent, visitor)
41
- return if visitor.skipping? || visitor.visiting_fragment_definition?
42
-
43
- if @max_depth < @current_depth
44
- @max_depth = @current_depth
45
- end
46
- @current_depth -= 1
47
- end
48
-
49
- def result
50
- @max_depth
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,276 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- # Depth first traversal through a query AST, calling AST analyzers
6
- # along the way.
7
- #
8
- # The visitor is a special case of GraphQL::Language::Visitor, visiting
9
- # only the selected operation, providing helpers for common use cases such
10
- # as skipped fields and visiting fragment spreads.
11
- #
12
- # @see {GraphQL::Analysis::AST::Analyzer} AST Analyzers for queries
13
- class Visitor < GraphQL::Language::Visitor
14
- def initialize(query:, analyzers:)
15
- @analyzers = analyzers
16
- @path = []
17
- @object_types = []
18
- @directives = []
19
- @field_definitions = []
20
- @argument_definitions = []
21
- @directive_definitions = []
22
- @rescued_errors = []
23
- @query = query
24
- @schema = query.schema
25
- @response_path = []
26
- @skip_stack = [false]
27
- super(query.selected_operation)
28
- end
29
-
30
- # @return [GraphQL::Query] the query being visited
31
- attr_reader :query
32
-
33
- # @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
34
- attr_reader :object_types
35
-
36
- # @return [Array<GraphQL::AnalysisError]
37
- attr_reader :rescued_errors
38
-
39
- def visit
40
- return unless @document
41
- super
42
- end
43
-
44
- # Visit Helpers
45
-
46
- # @return [GraphQL::Execution::Interpreter::Arguments] Arguments for this node, merging default values, literal values and query variables
47
- # @see {GraphQL::Query#arguments_for}
48
- def arguments_for(ast_node, field_definition)
49
- @query.arguments_for(ast_node, field_definition)
50
- end
51
-
52
- # @return [Boolean] If the visitor is currently inside a fragment definition
53
- def visiting_fragment_definition?
54
- @in_fragment_def
55
- end
56
-
57
- # @return [Boolean] If the current node should be skipped because of a skip or include directive
58
- def skipping?
59
- @skipping
60
- end
61
-
62
- # @return [Array<String>] The path to the response key for the current field
63
- def response_path
64
- @response_path.dup
65
- end
66
-
67
- # Visitor Hooks
68
- [
69
- :operation_definition, :fragment_definition,
70
- :inline_fragment, :field, :directive, :argument, :fragment_spread
71
- ].each do |node_type|
72
- module_eval <<-RUBY, __FILE__, __LINE__
73
- def call_on_enter_#{node_type}(node, parent)
74
- @analyzers.each do |a|
75
- begin
76
- a.on_enter_#{node_type}(node, parent, self)
77
- rescue AnalysisError => err
78
- @rescued_errors << err
79
- end
80
- end
81
- end
82
-
83
- def call_on_leave_#{node_type}(node, parent)
84
- @analyzers.each do |a|
85
- begin
86
- a.on_leave_#{node_type}(node, parent, self)
87
- rescue AnalysisError => err
88
- @rescued_errors << err
89
- end
90
- end
91
- end
92
-
93
- RUBY
94
- end
95
-
96
- def on_operation_definition(node, parent)
97
- object_type = @schema.root_type_for_operation(node.operation_type)
98
- @object_types.push(object_type)
99
- @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
100
- call_on_enter_operation_definition(node, parent)
101
- super
102
- call_on_leave_operation_definition(node, parent)
103
- @object_types.pop
104
- @path.pop
105
- end
106
-
107
- def on_fragment_definition(node, parent)
108
- on_fragment_with_type(node) do
109
- @path.push("fragment #{node.name}")
110
- @in_fragment_def = false
111
- call_on_enter_fragment_definition(node, parent)
112
- super
113
- @in_fragment_def = false
114
- call_on_leave_fragment_definition(node, parent)
115
- end
116
- end
117
-
118
- def on_inline_fragment(node, parent)
119
- on_fragment_with_type(node) do
120
- @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
121
- call_on_enter_inline_fragment(node, parent)
122
- super
123
- call_on_leave_inline_fragment(node, parent)
124
- end
125
- end
126
-
127
- def on_field(node, parent)
128
- @response_path.push(node.alias || node.name)
129
- parent_type = @object_types.last
130
- # This could be nil if the previous field wasn't found:
131
- field_definition = parent_type && @schema.get_field(parent_type, node.name, @query.context)
132
- @field_definitions.push(field_definition)
133
- if !field_definition.nil?
134
- next_object_type = field_definition.type.unwrap
135
- @object_types.push(next_object_type)
136
- else
137
- @object_types.push(nil)
138
- end
139
- @path.push(node.alias || node.name)
140
-
141
- @skipping = @skip_stack.last || skip?(node)
142
- @skip_stack << @skipping
143
-
144
- call_on_enter_field(node, parent)
145
- super
146
- @skipping = @skip_stack.pop
147
- call_on_leave_field(node, parent)
148
- @response_path.pop
149
- @field_definitions.pop
150
- @object_types.pop
151
- @path.pop
152
- end
153
-
154
- def on_directive(node, parent)
155
- directive_defn = @schema.directives[node.name]
156
- @directive_definitions.push(directive_defn)
157
- call_on_enter_directive(node, parent)
158
- super
159
- call_on_leave_directive(node, parent)
160
- @directive_definitions.pop
161
- end
162
-
163
- def on_argument(node, parent)
164
- argument_defn = if (arg = @argument_definitions.last)
165
- arg_type = arg.type.unwrap
166
- if arg_type.kind.input_object?
167
- arg_type.get_argument(node.name, @query.context)
168
- else
169
- nil
170
- end
171
- elsif (directive_defn = @directive_definitions.last)
172
- directive_defn.get_argument(node.name, @query.context)
173
- elsif (field_defn = @field_definitions.last)
174
- field_defn.get_argument(node.name, @query.context)
175
- else
176
- nil
177
- end
178
-
179
- @argument_definitions.push(argument_defn)
180
- @path.push(node.name)
181
- call_on_enter_argument(node, parent)
182
- super
183
- call_on_leave_argument(node, parent)
184
- @argument_definitions.pop
185
- @path.pop
186
- end
187
-
188
- def on_fragment_spread(node, parent)
189
- @path.push("... #{node.name}")
190
- call_on_enter_fragment_spread(node, parent)
191
- enter_fragment_spread_inline(node)
192
- super
193
- leave_fragment_spread_inline(node)
194
- call_on_leave_fragment_spread(node, parent)
195
- @path.pop
196
- end
197
-
198
- # @return [GraphQL::BaseType] The current object type
199
- def type_definition
200
- @object_types.last
201
- end
202
-
203
- # @return [GraphQL::BaseType] The type which the current type came from
204
- def parent_type_definition
205
- @object_types[-2]
206
- end
207
-
208
- # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
209
- def field_definition
210
- @field_definitions.last
211
- end
212
-
213
- # @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
214
- def previous_field_definition
215
- @field_definitions[-2]
216
- end
217
-
218
- # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
219
- def directive_definition
220
- @directive_definitions.last
221
- end
222
-
223
- # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
224
- def argument_definition
225
- @argument_definitions.last
226
- end
227
-
228
- # @return [GraphQL::Argument, nil] The previous GraphQL argument
229
- def previous_argument_definition
230
- @argument_definitions[-2]
231
- end
232
-
233
- private
234
-
235
- # Visit a fragment spread inline instead of visiting the definition
236
- # by itself.
237
- def enter_fragment_spread_inline(fragment_spread)
238
- fragment_def = query.fragments[fragment_spread.name]
239
-
240
- object_type = if fragment_def.type
241
- @query.warden.get_type(fragment_def.type.name)
242
- else
243
- object_types.last
244
- end
245
-
246
- object_types << object_type
247
-
248
- on_fragment_definition_children(fragment_def)
249
- end
250
-
251
- # Visit a fragment spread inline instead of visiting the definition
252
- # by itself.
253
- def leave_fragment_spread_inline(_fragment_spread)
254
- object_types.pop
255
- end
256
-
257
- def skip?(ast_node)
258
- dir = ast_node.directives
259
- dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
260
- end
261
-
262
- def on_fragment_with_type(node)
263
- object_type = if node.type
264
- @query.warden.get_type(node.type.name)
265
- else
266
- @object_types.last
267
- end
268
- @object_types.push(object_type)
269
- yield(node)
270
- @object_types.pop
271
- @path.pop
272
- end
273
- end
274
- end
275
- end
276
- end