graphql 2.0.31 → 2.6.1

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 (316) 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 +102 -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/field_resolve_step.rb +631 -0
  83. data/lib/graphql/execution/finalize.rb +217 -0
  84. data/lib/graphql/execution/input_values.rb +261 -0
  85. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  86. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  87. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  88. data/lib/graphql/execution/interpreter/resolve.rb +23 -25
  89. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
  90. data/lib/graphql/execution/interpreter/runtime.rb +365 -435
  91. data/lib/graphql/execution/interpreter.rb +87 -163
  92. data/lib/graphql/execution/lazy.rb +1 -1
  93. data/lib/graphql/execution/load_argument_step.rb +64 -0
  94. data/lib/graphql/execution/lookahead.rb +105 -31
  95. data/lib/graphql/execution/multiplex.rb +7 -6
  96. data/lib/graphql/execution/next.rb +90 -0
  97. data/lib/graphql/execution/prepare_object_step.rb +128 -0
  98. data/lib/graphql/execution/runner.rb +410 -0
  99. data/lib/graphql/execution/selections_step.rb +91 -0
  100. data/lib/graphql/execution.rb +8 -4
  101. data/lib/graphql/execution_error.rb +17 -10
  102. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  103. data/lib/graphql/introspection/directive_type.rb +7 -3
  104. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  105. data/lib/graphql/introspection/entry_points.rb +20 -6
  106. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  107. data/lib/graphql/introspection/field_type.rb +13 -5
  108. data/lib/graphql/introspection/input_value_type.rb +21 -13
  109. data/lib/graphql/introspection/schema_type.rb +8 -11
  110. data/lib/graphql/introspection/type_type.rb +64 -28
  111. data/lib/graphql/invalid_name_error.rb +1 -1
  112. data/lib/graphql/invalid_null_error.rb +26 -17
  113. data/lib/graphql/language/block_string.rb +34 -18
  114. data/lib/graphql/language/cache.rb +13 -0
  115. data/lib/graphql/language/comment.rb +18 -0
  116. data/lib/graphql/language/definition_slice.rb +1 -1
  117. data/lib/graphql/language/document_from_schema_definition.rb +90 -61
  118. data/lib/graphql/language/lexer.rb +323 -193
  119. data/lib/graphql/language/nodes.rb +139 -77
  120. data/lib/graphql/language/parser.rb +807 -1985
  121. data/lib/graphql/language/printer.rb +324 -151
  122. data/lib/graphql/language/sanitized_printer.rb +21 -23
  123. data/lib/graphql/language/static_visitor.rb +171 -0
  124. data/lib/graphql/language/visitor.rb +62 -119
  125. data/lib/graphql/language.rb +71 -1
  126. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  127. data/lib/graphql/pagination/array_connection.rb +6 -6
  128. data/lib/graphql/pagination/connection.rb +30 -1
  129. data/lib/graphql/pagination/connections.rb +32 -0
  130. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  131. data/lib/graphql/query/context/scoped_context.rb +101 -0
  132. data/lib/graphql/query/context.rb +88 -144
  133. data/lib/graphql/query/null_context.rb +15 -18
  134. data/lib/graphql/query/partial.rb +194 -0
  135. data/lib/graphql/query/validation_pipeline.rb +4 -4
  136. data/lib/graphql/query/variable_validation_error.rb +1 -1
  137. data/lib/graphql/query/variables.rb +3 -3
  138. data/lib/graphql/query.rb +135 -81
  139. data/lib/graphql/railtie.rb +16 -6
  140. data/lib/graphql/rake_task.rb +3 -12
  141. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  142. data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
  143. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  144. data/lib/graphql/rubocop.rb +2 -0
  145. data/lib/graphql/runtime_error.rb +6 -0
  146. data/lib/graphql/schema/addition.rb +26 -13
  147. data/lib/graphql/schema/always_visible.rb +7 -2
  148. data/lib/graphql/schema/argument.rb +78 -14
  149. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  150. data/lib/graphql/schema/build_from_definition.rb +140 -66
  151. data/lib/graphql/schema/directive/flagged.rb +4 -2
  152. data/lib/graphql/schema/directive/one_of.rb +12 -0
  153. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  154. data/lib/graphql/schema/directive.rb +78 -12
  155. data/lib/graphql/schema/enum.rb +110 -27
  156. data/lib/graphql/schema/enum_value.rb +11 -3
  157. data/lib/graphql/schema/field/connection_extension.rb +4 -51
  158. data/lib/graphql/schema/field/scope_extension.rb +19 -7
  159. data/lib/graphql/schema/field.rb +245 -119
  160. data/lib/graphql/schema/field_extension.rb +12 -9
  161. data/lib/graphql/schema/has_single_input_argument.rb +160 -0
  162. data/lib/graphql/schema/input_object.rb +123 -65
  163. data/lib/graphql/schema/interface.rb +60 -16
  164. data/lib/graphql/schema/introspection_system.rb +8 -17
  165. data/lib/graphql/schema/late_bound_type.rb +4 -0
  166. data/lib/graphql/schema/list.rb +8 -4
  167. data/lib/graphql/schema/loader.rb +3 -4
  168. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -12
  169. data/lib/graphql/schema/member/has_arguments.rb +132 -100
  170. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  171. data/lib/graphql/schema/member/has_dataloader.rb +99 -0
  172. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  173. data/lib/graphql/schema/member/has_directives.rb +5 -5
  174. data/lib/graphql/schema/member/has_fields.rb +121 -17
  175. data/lib/graphql/schema/member/has_interfaces.rb +27 -13
  176. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  177. data/lib/graphql/schema/member/has_validators.rb +1 -1
  178. data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
  179. data/lib/graphql/schema/member/scoped.rb +19 -0
  180. data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
  181. data/lib/graphql/schema/member/validates_input.rb +3 -3
  182. data/lib/graphql/schema/member.rb +6 -0
  183. data/lib/graphql/schema/mutation.rb +7 -0
  184. data/lib/graphql/schema/non_null.rb +1 -1
  185. data/lib/graphql/schema/object.rb +34 -8
  186. data/lib/graphql/schema/printer.rb +9 -7
  187. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  188. data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
  189. data/lib/graphql/schema/resolver.rb +128 -32
  190. data/lib/graphql/schema/scalar.rb +4 -9
  191. data/lib/graphql/schema/subscription.rb +63 -12
  192. data/lib/graphql/schema/timeout.rb +19 -2
  193. data/lib/graphql/schema/type_expression.rb +2 -2
  194. data/lib/graphql/schema/union.rb +2 -2
  195. data/lib/graphql/schema/unique_within_type.rb +1 -1
  196. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  197. data/lib/graphql/schema/validator/required_validator.rb +92 -11
  198. data/lib/graphql/schema/validator.rb +3 -1
  199. data/lib/graphql/schema/visibility/migration.rb +188 -0
  200. data/lib/graphql/schema/visibility/profile.rb +464 -0
  201. data/lib/graphql/schema/visibility/visit.rb +190 -0
  202. data/lib/graphql/schema/visibility.rb +311 -0
  203. data/lib/graphql/schema/warden.rb +275 -103
  204. data/lib/graphql/schema/wrapper.rb +7 -1
  205. data/lib/graphql/schema.rb +954 -212
  206. data/lib/graphql/static_validation/all_rules.rb +3 -3
  207. data/lib/graphql/static_validation/base_visitor.rb +96 -71
  208. data/lib/graphql/static_validation/literal_validator.rb +6 -7
  209. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +2 -2
  210. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
  211. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +6 -2
  212. data/lib/graphql/static_validation/rules/directives_are_defined.rb +6 -3
  213. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
  214. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +14 -3
  215. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +59 -15
  216. data/lib/graphql/static_validation/rules/fields_will_merge.rb +391 -262
  217. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  218. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +6 -6
  219. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +15 -2
  220. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  221. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  222. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
  223. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  224. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  225. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +28 -8
  226. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  227. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  228. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
  229. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  230. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
  231. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  232. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +14 -2
  233. data/lib/graphql/static_validation/validation_context.rb +22 -6
  234. data/lib/graphql/static_validation/validator.rb +9 -1
  235. data/lib/graphql/static_validation.rb +0 -1
  236. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
  237. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  238. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +45 -19
  239. data/lib/graphql/subscriptions/event.rb +22 -4
  240. data/lib/graphql/subscriptions/serialize.rb +3 -1
  241. data/lib/graphql/subscriptions.rb +56 -17
  242. data/lib/graphql/testing/helpers.rb +161 -0
  243. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  244. data/lib/graphql/testing.rb +3 -0
  245. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  246. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  247. data/lib/graphql/tracing/appoptics_trace.rb +11 -3
  248. data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
  249. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  250. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  251. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  252. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  253. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  254. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  255. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  256. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  257. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  258. data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
  259. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  260. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  261. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  262. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  263. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  264. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  265. data/lib/graphql/tracing/null_trace.rb +9 -0
  266. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  267. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  268. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  269. data/lib/graphql/tracing/platform_trace.rb +5 -0
  270. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  271. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
  272. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  273. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  274. data/lib/graphql/tracing/scout_trace.rb +32 -55
  275. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  276. data/lib/graphql/tracing/sentry_trace.rb +82 -0
  277. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  278. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  279. data/lib/graphql/tracing/trace.rb +118 -1
  280. data/lib/graphql/tracing.rb +31 -28
  281. data/lib/graphql/type_kinds.rb +2 -1
  282. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  283. data/lib/graphql/types/relay/connection_behaviors.rb +45 -3
  284. data/lib/graphql/types/relay/edge_behaviors.rb +19 -1
  285. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  286. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  287. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  288. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  289. data/lib/graphql/types.rb +18 -10
  290. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  291. data/lib/graphql/unauthorized_error.rb +9 -1
  292. data/lib/graphql/version.rb +1 -1
  293. data/lib/graphql.rb +69 -54
  294. data/readme.md +12 -2
  295. metadata +236 -40
  296. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  297. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  298. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  299. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  300. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  301. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  302. data/lib/graphql/analysis/ast/visitor.rb +0 -276
  303. data/lib/graphql/analysis/ast.rb +0 -81
  304. data/lib/graphql/backtrace/inspect_result.rb +0 -50
  305. data/lib/graphql/backtrace/trace.rb +0 -96
  306. data/lib/graphql/backtrace/tracer.rb +0 -80
  307. data/lib/graphql/deprecation.rb +0 -9
  308. data/lib/graphql/filter.rb +0 -59
  309. data/lib/graphql/language/parser.y +0 -560
  310. data/lib/graphql/language/token.rb +0 -34
  311. data/lib/graphql/schema/base_64_bp.rb +0 -26
  312. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  313. data/lib/graphql/schema/null_mask.rb +0 -11
  314. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
  315. data/lib/graphql/static_validation/type_stack.rb +0 -216
  316. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -0,0 +1,464 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Visibility
6
+ # This class filters the types, fields, arguments, enum values, and directives in a schema
7
+ # based on the given `context`.
8
+ #
9
+ # It's like {Warden}, but has some differences:
10
+ #
11
+ # - It doesn't use {Schema}'s top-level caches (eg {Schema.references_to}, {Schema.possible_types}, {Schema.types})
12
+ # - It doesn't hide Interface or Union types when all their possible types are hidden. (Instead, those types should implement `.visible?` to hide in that case.)
13
+ # - It checks `.visible?` on root introspection types
14
+ # - It can be used to cache profiles by name for re-use across queries
15
+ class Profile
16
+ # @return [Schema::Visibility::Profile]
17
+ def self.from_context(ctx, schema)
18
+ if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
19
+ types
20
+ else
21
+ schema.visibility.profile_for(ctx)
22
+ end
23
+ end
24
+
25
+ def self.null_profile(context:, schema:)
26
+ profile = self.new(name: "NullProfile", context: context, schema: schema)
27
+ profile.instance_variable_set(:@cached_visible, Hash.new { |k, v| k[v] = true }.compare_by_identity)
28
+ profile
29
+ end
30
+
31
+ # @return [Symbol, nil]
32
+ attr_reader :name
33
+
34
+ def freeze
35
+ @cached_visible.default_proc = nil
36
+ @cached_visible_fields.default_proc = nil
37
+ @cached_visible_fields.each do |type, fields|
38
+ fields.default_proc = nil
39
+ end
40
+ @cached_visible_arguments.default_proc = nil
41
+ @cached_visible_arguments.each do |type, fields|
42
+ fields.default_proc = nil
43
+ end
44
+ @cached_parent_fields.default_proc = nil
45
+ @cached_parent_fields.each do |type, fields|
46
+ fields.default_proc = nil
47
+ end
48
+ @cached_parent_arguments.default_proc = nil
49
+ @cached_parent_arguments.each do |type, args|
50
+ args.default_proc = nil
51
+ end
52
+ @cached_possible_types.default_proc = nil
53
+ @cached_enum_values.default_proc = nil
54
+ @cached_fields.default_proc = nil
55
+ @cached_arguments.default_proc = nil
56
+ @loadable_possible_types.default_proc = nil
57
+ @cached_field_result.default_proc = nil
58
+ @cached_field_result.each { |_, h| h.default_proc = nil }
59
+ @cached_type_result.default_proc = nil
60
+ super
61
+ end
62
+
63
+ def initialize(name: nil, context:, schema:, visibility:)
64
+ @name = name
65
+ @context = context
66
+ @schema = schema
67
+ @visibility = visibility
68
+ @all_types = {}
69
+ @all_types_loaded = false
70
+ @unvisited_types = []
71
+ @all_directives = nil
72
+ @cached_visible = Hash.new { |h, member| h[member] = @schema.visible?(member, @context) }.compare_by_identity
73
+
74
+ @cached_visible_fields = Hash.new { |h, owner|
75
+ h[owner] = Hash.new do |h2, field|
76
+ h2[field] = visible_field_for(owner, field)
77
+ end.compare_by_identity
78
+ }.compare_by_identity
79
+
80
+ @cached_visible_arguments = Hash.new do |h, owner|
81
+ h[owner] = Hash.new do |h2, arg|
82
+ h2[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
83
+ case owner
84
+ when GraphQL::Schema::Field
85
+ @cached_visible_fields[owner.owner][owner]
86
+ when Class
87
+ @cached_visible[owner]
88
+ else
89
+ raise "Unexpected argument owner for `#{arg.path}`: #{owner.inspect}"
90
+ end
91
+ else
92
+ false
93
+ end
94
+ end.compare_by_identity
95
+ end.compare_by_identity
96
+
97
+ @cached_parent_fields = Hash.new do |h, type|
98
+ h[type] = Hash.new do |h2, field_name|
99
+ h2[field_name] = type.get_field(field_name, @context)
100
+ end
101
+ end.compare_by_identity
102
+
103
+ @cached_parent_arguments = Hash.new do |h, arg_owner|
104
+ h[arg_owner] = Hash.new do |h2, arg_name|
105
+ h2[arg_name] = arg_owner.get_argument(arg_name, @context)
106
+ end
107
+ end.compare_by_identity
108
+
109
+ @cached_possible_types = Hash.new { |h, type| h[type] = possible_types_for(type) }.compare_by_identity
110
+
111
+ @cached_enum_values = Hash.new do |h, enum_t|
112
+ values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible)
113
+ if values.size == 0
114
+ raise GraphQL::Schema::Enum::MissingValuesError.new(enum_t)
115
+ end
116
+ h[enum_t] = values
117
+ end.compare_by_identity
118
+
119
+ @cached_fields = Hash.new do |h, owner|
120
+ h[owner] = non_duplicate_items(owner.all_field_definitions.each(&:ensure_loaded), @cached_visible_fields[owner])
121
+ end.compare_by_identity
122
+
123
+ @cached_arguments = Hash.new do |h, owner|
124
+ h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments[owner])
125
+ end.compare_by_identity
126
+
127
+ @loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
128
+
129
+ # Combined cache for field(owner, field_name) — avoids repeated kind check + parent lookup + visibility check
130
+ @cached_field_result = Hash.new { |h, owner|
131
+ h[owner] = Hash.new { |h2, field_name| h2[field_name] = compute_field(owner, field_name) }
132
+ }.compare_by_identity
133
+
134
+ # Cache for type(type_name) — avoids repeated get_type + visibility + referenced? checks
135
+ @cached_type_result = Hash.new { |h, type_name| h[type_name] = compute_type(type_name) }
136
+ end
137
+
138
+ def field_on_visible_interface?(field, owner)
139
+ ints = owner.interface_type_memberships.map(&:abstract_type)
140
+ field_name = field.graphql_name
141
+ filtered_ints = interfaces(owner)
142
+ any_interface_has_field = false
143
+ any_interface_has_visible_field = false
144
+ ints.each do |int_t|
145
+ if (_int_f_defn = @cached_parent_fields[int_t][field_name])
146
+ any_interface_has_field = true
147
+
148
+ if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field]
149
+ any_interface_has_visible_field = true
150
+ break
151
+ end
152
+ end
153
+ end
154
+
155
+ if any_interface_has_field
156
+ any_interface_has_visible_field
157
+ else
158
+ true
159
+ end
160
+ end
161
+
162
+ def type(type_name)
163
+ @cached_type_result[type_name]
164
+ end
165
+
166
+ def field(owner, field_name)
167
+ @cached_field_result[owner][field_name]
168
+ end
169
+
170
+ def fields(owner)
171
+ @cached_fields[owner]
172
+ end
173
+
174
+ def arguments(owner)
175
+ @cached_arguments[owner]
176
+ end
177
+
178
+ def argument(owner, arg_name)
179
+ arg = @cached_parent_arguments[owner][arg_name]
180
+ if arg.is_a?(Array)
181
+ visible_arg = nil
182
+ arg.each do |arg_defn|
183
+ if @cached_visible_arguments[owner][arg_defn]
184
+ if visible_arg.nil?
185
+ visible_arg = arg_defn
186
+ else
187
+ raise_duplicate_definition(visible_arg, arg_defn)
188
+ end
189
+ end
190
+ end
191
+ visible_arg
192
+ else
193
+ if arg && @cached_visible_arguments[owner][arg]
194
+ arg
195
+ else
196
+ nil
197
+ end
198
+ end
199
+ end
200
+
201
+ def possible_types(type)
202
+ @cached_possible_types[type]
203
+ end
204
+
205
+ def interfaces(obj_or_int_type)
206
+ ints = obj_or_int_type.interface_type_memberships
207
+ .select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] }
208
+ .map!(&:abstract_type)
209
+ ints.uniq! # Remove any duplicate interfaces implemented via other interfaces
210
+ ints
211
+ end
212
+
213
+ def query_root
214
+ ((t = @schema.query) && @cached_visible[t]) ? t : nil
215
+ end
216
+
217
+ def mutation_root
218
+ ((t = @schema.mutation) && @cached_visible[t]) ? t : nil
219
+ end
220
+
221
+ def subscription_root
222
+ ((t = @schema.subscription) && @cached_visible[t]) ? t : nil
223
+ end
224
+
225
+ def all_types
226
+ load_all_types
227
+ @all_types.values
228
+ end
229
+
230
+ def all_types_h
231
+ load_all_types
232
+ @all_types
233
+ end
234
+
235
+ def enum_values(owner)
236
+ @cached_enum_values[owner]
237
+ end
238
+
239
+ def directive_exists?(dir_name)
240
+ directives.any? { |d| d.graphql_name == dir_name }
241
+ end
242
+
243
+ def directives
244
+ @all_directives ||= @visibility.all_directives.select { |dir|
245
+ @cached_visible[dir] && @visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) }
246
+ }
247
+ end
248
+
249
+ def loadable?(t, _ctx)
250
+ @cached_visible[t] && !referenced?(t)
251
+ end
252
+
253
+ def loadable_possible_types(t, _ctx)
254
+ @loadable_possible_types[t]
255
+ end
256
+
257
+ def loaded_types
258
+ @all_types.values
259
+ end
260
+
261
+ def reachable_type?(type_name)
262
+ load_all_types
263
+ !!@all_types[type_name]
264
+ end
265
+
266
+ def visible_enum_value?(enum_value, _ctx = nil)
267
+ @cached_visible[enum_value]
268
+ end
269
+
270
+ def preload
271
+ load_all_types
272
+ @all_types.each do |type_name, type_defn|
273
+ type(type_name)
274
+ if type_defn.kind.fields?
275
+ fields(type_defn).each do |f|
276
+ field(type_defn, f.graphql_name)
277
+ arguments(f).each do |arg|
278
+ argument(f, arg.graphql_name)
279
+ end
280
+ end
281
+ @schema.introspection_system.dynamic_fields.each do |f|
282
+ field(type_defn, f.graphql_name)
283
+ end
284
+ elsif type_defn.kind.input_object?
285
+ arguments(type_defn).each do |arg|
286
+ argument(type_defn, arg.graphql_name)
287
+ end
288
+ elsif type_defn.kind.enum?
289
+ enum_values(type_defn)
290
+ end
291
+ # Lots more to do here
292
+ end
293
+ @schema.introspection_system.entry_points.each do |f|
294
+ arguments(f).each do |arg|
295
+ argument(f, arg.graphql_name)
296
+ end
297
+ field(@schema.query, f.graphql_name)
298
+ end
299
+ @schema.introspection_system.dynamic_fields.each do |f|
300
+ arguments(f).each do |arg|
301
+ argument(f, arg.graphql_name)
302
+ end
303
+ end
304
+
305
+ end
306
+
307
+ private
308
+
309
+ def compute_type(type_name)
310
+ t = @visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
311
+ if t
312
+ if t.is_a?(Array)
313
+ vis_t = nil
314
+ t.each do |t_defn|
315
+ if @cached_visible[t_defn] && referenced?(t_defn)
316
+ if vis_t.nil?
317
+ vis_t = t_defn
318
+ else
319
+ raise_duplicate_definition(vis_t, t_defn)
320
+ end
321
+ end
322
+ end
323
+ vis_t
324
+ else
325
+ if t && @cached_visible[t] && referenced?(t)
326
+ t
327
+ else
328
+ nil
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ def compute_field(owner, field_name)
335
+ f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
336
+ field
337
+ elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
338
+ entry_point_field
339
+ elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
340
+ dynamic_field
341
+ else
342
+ nil
343
+ end
344
+ if f.is_a?(Array)
345
+ visible_f = nil
346
+ f.each do |f_defn|
347
+ if @cached_visible_fields[owner][f_defn]
348
+ if visible_f.nil?
349
+ visible_f = f_defn
350
+ else
351
+ raise_duplicate_definition(visible_f, f_defn)
352
+ end
353
+ end
354
+ end
355
+ visible_f&.ensure_loaded
356
+ elsif f && @cached_visible_fields[owner][f.ensure_loaded]
357
+ f
358
+ else
359
+ nil
360
+ end
361
+ end
362
+
363
+ def non_duplicate_items(definitions, visibility_cache)
364
+ non_dups = []
365
+ names = Set.new
366
+ definitions.each do |defn|
367
+ if visibility_cache[defn]
368
+ if !names.add?(defn.graphql_name)
369
+ dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name }
370
+ raise_duplicate_definition(dup_defn, defn)
371
+ end
372
+ non_dups << defn
373
+ end
374
+ end
375
+ non_dups
376
+ end
377
+
378
+ def raise_duplicate_definition(first_defn, second_defn)
379
+ raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
380
+ end
381
+
382
+ def load_all_types
383
+ return if @all_types_loaded
384
+ @all_types_loaded = true
385
+ visit = Visibility::Visit.new(@schema) do |member|
386
+ if member.is_a?(Module) && member.respond_to?(:kind)
387
+ if @cached_visible[member] && referenced?(member)
388
+ type_name = member.graphql_name
389
+ if (prev_t = @all_types[type_name]) && !prev_t.equal?(member)
390
+ raise_duplicate_definition(member, prev_t)
391
+ end
392
+ @all_types[type_name] = member
393
+ true
394
+ else
395
+ false
396
+ end
397
+ else
398
+ @cached_visible[member]
399
+ end
400
+ end
401
+ visit.visit_each
402
+ @all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
403
+ nil
404
+ end
405
+
406
+ def referenced?(type_defn)
407
+ @visibility.all_references[type_defn].any? do |ref|
408
+ case ref
409
+ when GraphQL::Schema::Argument
410
+ @cached_visible_arguments[ref.owner][ref]
411
+ when GraphQL::Schema::Field
412
+ @cached_visible_fields[ref.owner][ref]
413
+ when Module
414
+ @cached_visible[ref]
415
+ when true
416
+ true
417
+ end
418
+ end
419
+ end
420
+
421
+ def possible_types_for(type)
422
+ case type.kind.name
423
+ when "INTERFACE"
424
+ pts = []
425
+ @visibility.all_interface_type_memberships[type].each do |impl_type, type_memberships|
426
+ if impl_type.kind.object? && referenced?(impl_type) && @cached_visible[impl_type]
427
+ if type_memberships.any? { |itm| @cached_visible[itm] }
428
+ pts << impl_type
429
+ end
430
+ end
431
+ end
432
+ pts
433
+ when "UNION"
434
+ pts = []
435
+ type.type_memberships.each { |tm|
436
+ if @cached_visible[tm] &&
437
+ (ot = tm.object_type) &&
438
+ @cached_visible[ot] &&
439
+ referenced?(ot)
440
+ pts << ot
441
+ end
442
+ }
443
+ pts
444
+ when "OBJECT"
445
+ if @cached_visible[type]
446
+ [type]
447
+ else
448
+ EmptyObjects::EMPTY_ARRAY
449
+ end
450
+ else
451
+ GraphQL::EmptyObjects::EMPTY_ARRAY
452
+ end
453
+ end
454
+
455
+ def visible_field_for(owner, field)
456
+ @cached_visible[field] &&
457
+ (ret_type = field.type.unwrap) &&
458
+ @cached_visible[ret_type] &&
459
+ (owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
460
+ end
461
+ end
462
+ end
463
+ end
464
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Visibility
5
+ class Visit
6
+ def initialize(schema, &visit_block)
7
+ @schema = schema
8
+ @late_bound_types = nil
9
+ @unvisited_types = nil
10
+ # These accumulate between calls to prevent re-visiting the same types
11
+ @visited_types = Set.new.compare_by_identity
12
+ @visited_directives = Set.new.compare_by_identity
13
+ @visit_block = visit_block
14
+ end
15
+
16
+ def entry_point_types
17
+ ept = [
18
+ @schema.query,
19
+ @schema.mutation,
20
+ @schema.subscription,
21
+ *@schema.introspection_system.types.values,
22
+ *@schema.orphan_types,
23
+ ]
24
+ ept.compact!
25
+ ept
26
+ end
27
+
28
+ def entry_point_directives
29
+ @schema.directives.values
30
+ end
31
+
32
+ def visit_each(types: entry_point_types, directives: entry_point_directives)
33
+ @unvisited_types && raise("Can't re-enter `visit_each` on this Visit (another visit is already in progress)")
34
+ @unvisited_types = types
35
+ @late_bound_types = []
36
+ directives_to_visit = directives
37
+
38
+ while !@unvisited_types.empty? || !@late_bound_types.empty?
39
+ while (type = @unvisited_types.pop)
40
+ if @visited_types.add?(type) && @visit_block.call(type)
41
+ directives_to_visit.concat(type.directives)
42
+ case type.kind.name
43
+ when "OBJECT", "INTERFACE"
44
+ type.interface_type_memberships.each do |itm|
45
+ append_unvisited_type(type, itm.abstract_type)
46
+ end
47
+ if type.kind.interface?
48
+ type.orphan_types.each do |orphan_type|
49
+ append_unvisited_type(type, orphan_type)
50
+ end
51
+ end
52
+
53
+ type.all_field_definitions.each do |field|
54
+ field.ensure_loaded
55
+ if @visit_block.call(field)
56
+ directives_to_visit.concat(field.directives)
57
+ append_unvisited_type(type, field.type.unwrap)
58
+ field.all_argument_definitions.each do |argument|
59
+ if @visit_block.call(argument)
60
+ directives_to_visit.concat(argument.directives)
61
+ append_unvisited_type(field, argument.type.unwrap)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ when "INPUT_OBJECT"
67
+ type.all_argument_definitions.each do |argument|
68
+ if @visit_block.call(argument)
69
+ directives_to_visit.concat(argument.directives)
70
+ append_unvisited_type(type, argument.type.unwrap)
71
+ end
72
+ end
73
+ when "UNION"
74
+ type.type_memberships.each do |tm|
75
+ append_unvisited_type(type, tm.object_type)
76
+ end
77
+ when "ENUM"
78
+ type.all_enum_value_definitions.each do |val|
79
+ if @visit_block.call(val)
80
+ directives_to_visit.concat(val.directives)
81
+ end
82
+ end
83
+ when "SCALAR"
84
+ # pass -- nothing else to visit
85
+ else
86
+ raise "Invariant: unhandled type kind: #{type.kind.inspect}"
87
+ end
88
+ end
89
+ end
90
+
91
+ directives_to_visit.each do |dir|
92
+ dir_class = dir.is_a?(Class) ? dir : dir.class
93
+ if @visited_directives.add?(dir_class) && @visit_block.call(dir_class)
94
+ dir_class.all_argument_definitions.each do |arg_defn|
95
+ if @visit_block.call(arg_defn)
96
+ directives_to_visit.concat(arg_defn.directives)
97
+ append_unvisited_type(dir_class, arg_defn.type.unwrap)
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ missed_late_types_streak = 0
104
+ while (owner, late_type = @late_bound_types.shift)
105
+ if (late_type.is_a?(String) && (type = Member::BuildType.constantize(type))) ||
106
+ (late_type.is_a?(LateBoundType) && (type = @visited_types.find { |t| t.graphql_name == late_type.graphql_name }))
107
+ missed_late_types_streak = 0 # might succeed next round
108
+ update_type_owner(owner, type)
109
+ append_unvisited_type(owner, type)
110
+ else
111
+ # Didn't find it -- keep trying
112
+ missed_late_types_streak += 1
113
+ @late_bound_types << [owner, late_type]
114
+ if missed_late_types_streak == @late_bound_types.size
115
+ raise UnresolvedLateBoundTypeError.new(type: late_type)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ @unvisited_types = nil
122
+ nil
123
+ end
124
+
125
+ private
126
+
127
+ def append_unvisited_type(owner, type)
128
+ if type.is_a?(LateBoundType) || type.is_a?(String)
129
+ @late_bound_types << [owner, type]
130
+ else
131
+ @unvisited_types << type
132
+ end
133
+ end
134
+
135
+ def update_type_owner(owner, type)
136
+ case owner
137
+ when Module
138
+ if owner.kind.union?
139
+ owner.assign_type_membership_object_type(type)
140
+ elsif type.kind.interface?
141
+ new_interfaces = []
142
+ owner.interfaces.each do |int_t|
143
+ if int_t.is_a?(String) && int_t == type.graphql_name
144
+ new_interfaces << type
145
+ elsif int_t.is_a?(LateBoundType) && int_t.graphql_name == type.graphql_name
146
+ new_interfaces << type
147
+ else
148
+ # Don't re-add proper interface definitions,
149
+ # they were probably already added, maybe with options.
150
+ end
151
+ end
152
+ owner.implements(*new_interfaces)
153
+ new_interfaces.each do |int|
154
+ pt = @possible_types[int] ||= []
155
+ if !pt.include?(owner) && owner.is_a?(Class)
156
+ pt << owner
157
+ end
158
+ int.interfaces.each do |indirect_int|
159
+ if indirect_int.is_a?(LateBoundType) && (indirect_int_type = get_type(indirect_int.graphql_name)) # rubocop:disable Development/ContextIsPassedCop
160
+ update_type_owner(owner, indirect_int_type)
161
+ end
162
+ end
163
+ end
164
+ end
165
+ when GraphQL::Schema::Argument, GraphQL::Schema::Field
166
+ orig_type = owner.type
167
+ # Apply list/non-null wrapper as needed
168
+ if orig_type.respond_to?(:of_type)
169
+ transforms = []
170
+ while (orig_type.respond_to?(:of_type))
171
+ if orig_type.kind.non_null?
172
+ transforms << :to_non_null_type
173
+ elsif orig_type.kind.list?
174
+ transforms << :to_list_type
175
+ else
176
+ raise "Invariant: :of_type isn't non-null or list"
177
+ end
178
+ orig_type = orig_type.of_type
179
+ end
180
+ transforms.reverse_each { |t| type = type.public_send(t) }
181
+ end
182
+ owner.type = type
183
+ else
184
+ raise "Unexpected update: #{owner.inspect} #{type.inspect}"
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end