graphql_cody 1.13.0

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 (444) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/MIT-LICENSE +20 -0
  4. data/lib/generators/graphql/core.rb +74 -0
  5. data/lib/generators/graphql/enum_generator.rb +33 -0
  6. data/lib/generators/graphql/install_generator.rb +190 -0
  7. data/lib/generators/graphql/interface_generator.rb +27 -0
  8. data/lib/generators/graphql/loader_generator.rb +21 -0
  9. data/lib/generators/graphql/mutation_generator.rb +55 -0
  10. data/lib/generators/graphql/object_generator.rb +79 -0
  11. data/lib/generators/graphql/relay.rb +63 -0
  12. data/lib/generators/graphql/relay_generator.rb +21 -0
  13. data/lib/generators/graphql/scalar_generator.rb +20 -0
  14. data/lib/generators/graphql/templates/base_argument.erb +6 -0
  15. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  16. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  17. data/lib/generators/graphql/templates/base_enum.erb +6 -0
  18. data/lib/generators/graphql/templates/base_field.erb +7 -0
  19. data/lib/generators/graphql/templates/base_input_object.erb +7 -0
  20. data/lib/generators/graphql/templates/base_interface.erb +9 -0
  21. data/lib/generators/graphql/templates/base_mutation.erb +10 -0
  22. data/lib/generators/graphql/templates/base_object.erb +7 -0
  23. data/lib/generators/graphql/templates/base_scalar.erb +6 -0
  24. data/lib/generators/graphql/templates/base_union.erb +6 -0
  25. data/lib/generators/graphql/templates/enum.erb +7 -0
  26. data/lib/generators/graphql/templates/graphql_controller.erb +52 -0
  27. data/lib/generators/graphql/templates/interface.erb +8 -0
  28. data/lib/generators/graphql/templates/loader.erb +19 -0
  29. data/lib/generators/graphql/templates/mutation.erb +16 -0
  30. data/lib/generators/graphql/templates/mutation_type.erb +12 -0
  31. data/lib/generators/graphql/templates/node_type.erb +9 -0
  32. data/lib/generators/graphql/templates/object.erb +8 -0
  33. data/lib/generators/graphql/templates/query_type.erb +15 -0
  34. data/lib/generators/graphql/templates/scalar.erb +15 -0
  35. data/lib/generators/graphql/templates/schema.erb +27 -0
  36. data/lib/generators/graphql/templates/union.erb +7 -0
  37. data/lib/generators/graphql/type_generator.rb +98 -0
  38. data/lib/generators/graphql/union_generator.rb +33 -0
  39. data/lib/graphql/analysis/analyze_query.rb +98 -0
  40. data/lib/graphql/analysis/ast/analyzer.rb +84 -0
  41. data/lib/graphql/analysis/ast/field_usage.rb +51 -0
  42. data/lib/graphql/analysis/ast/max_query_complexity.rb +23 -0
  43. data/lib/graphql/analysis/ast/max_query_depth.rb +22 -0
  44. data/lib/graphql/analysis/ast/query_complexity.rb +230 -0
  45. data/lib/graphql/analysis/ast/query_depth.rb +56 -0
  46. data/lib/graphql/analysis/ast/visitor.rb +268 -0
  47. data/lib/graphql/analysis/ast.rb +91 -0
  48. data/lib/graphql/analysis/field_usage.rb +45 -0
  49. data/lib/graphql/analysis/max_query_complexity.rb +26 -0
  50. data/lib/graphql/analysis/max_query_depth.rb +26 -0
  51. data/lib/graphql/analysis/query_complexity.rb +88 -0
  52. data/lib/graphql/analysis/query_depth.rb +43 -0
  53. data/lib/graphql/analysis/reducer_state.rb +48 -0
  54. data/lib/graphql/analysis.rb +9 -0
  55. data/lib/graphql/analysis_error.rb +5 -0
  56. data/lib/graphql/argument.rb +131 -0
  57. data/lib/graphql/authorization.rb +82 -0
  58. data/lib/graphql/backtrace/inspect_result.rb +50 -0
  59. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  60. data/lib/graphql/backtrace/table.rb +159 -0
  61. data/lib/graphql/backtrace/traced_error.rb +54 -0
  62. data/lib/graphql/backtrace/tracer.rb +81 -0
  63. data/lib/graphql/backtrace.rb +64 -0
  64. data/lib/graphql/backwards_compatibility.rb +61 -0
  65. data/lib/graphql/base_type.rb +230 -0
  66. data/lib/graphql/boolean_type.rb +2 -0
  67. data/lib/graphql/coercion_error.rb +13 -0
  68. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +53 -0
  69. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +200 -0
  70. data/lib/graphql/compatibility/execution_specification.rb +436 -0
  71. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +111 -0
  72. data/lib/graphql/compatibility/lazy_execution_specification.rb +215 -0
  73. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +87 -0
  74. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +79 -0
  75. data/lib/graphql/compatibility/query_parser_specification.rb +266 -0
  76. data/lib/graphql/compatibility/schema_parser_specification.rb +682 -0
  77. data/lib/graphql/compatibility.rb +5 -0
  78. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  79. data/lib/graphql/dataloader/request.rb +19 -0
  80. data/lib/graphql/dataloader/request_all.rb +19 -0
  81. data/lib/graphql/dataloader/source.rb +155 -0
  82. data/lib/graphql/dataloader.rb +308 -0
  83. data/lib/graphql/define/assign_argument.rb +12 -0
  84. data/lib/graphql/define/assign_connection.rb +13 -0
  85. data/lib/graphql/define/assign_enum_value.rb +18 -0
  86. data/lib/graphql/define/assign_global_id_field.rb +11 -0
  87. data/lib/graphql/define/assign_mutation_function.rb +34 -0
  88. data/lib/graphql/define/assign_object_field.rb +42 -0
  89. data/lib/graphql/define/defined_object_proxy.rb +53 -0
  90. data/lib/graphql/define/instance_definable.rb +240 -0
  91. data/lib/graphql/define/no_definition_error.rb +7 -0
  92. data/lib/graphql/define/non_null_with_bang.rb +16 -0
  93. data/lib/graphql/define/type_definer.rb +31 -0
  94. data/lib/graphql/define.rb +31 -0
  95. data/lib/graphql/deprecated_dsl.rb +55 -0
  96. data/lib/graphql/deprecation.rb +9 -0
  97. data/lib/graphql/dig.rb +19 -0
  98. data/lib/graphql/directive/deprecated_directive.rb +2 -0
  99. data/lib/graphql/directive/include_directive.rb +2 -0
  100. data/lib/graphql/directive/skip_directive.rb +2 -0
  101. data/lib/graphql/directive.rb +107 -0
  102. data/lib/graphql/enum_type.rb +133 -0
  103. data/lib/graphql/execution/directive_checks.rb +37 -0
  104. data/lib/graphql/execution/errors.rb +163 -0
  105. data/lib/graphql/execution/execute.rb +333 -0
  106. data/lib/graphql/execution/flatten.rb +40 -0
  107. data/lib/graphql/execution/instrumentation.rb +92 -0
  108. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  109. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  110. data/lib/graphql/execution/interpreter/arguments_cache.rb +103 -0
  111. data/lib/graphql/execution/interpreter/execution_errors.rb +29 -0
  112. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  113. data/lib/graphql/execution/interpreter/resolve.rb +70 -0
  114. data/lib/graphql/execution/interpreter/runtime.rb +949 -0
  115. data/lib/graphql/execution/interpreter.rb +122 -0
  116. data/lib/graphql/execution/lazy/lazy_method_map.rb +98 -0
  117. data/lib/graphql/execution/lazy/resolve.rb +91 -0
  118. data/lib/graphql/execution/lazy.rb +83 -0
  119. data/lib/graphql/execution/lookahead.rb +307 -0
  120. data/lib/graphql/execution/multiplex.rb +214 -0
  121. data/lib/graphql/execution/typecast.rb +50 -0
  122. data/lib/graphql/execution.rb +11 -0
  123. data/lib/graphql/execution_error.rb +58 -0
  124. data/lib/graphql/field/resolve.rb +59 -0
  125. data/lib/graphql/field.rb +226 -0
  126. data/lib/graphql/filter.rb +53 -0
  127. data/lib/graphql/float_type.rb +2 -0
  128. data/lib/graphql/function.rb +128 -0
  129. data/lib/graphql/id_type.rb +2 -0
  130. data/lib/graphql/input_object_type.rb +138 -0
  131. data/lib/graphql/int_type.rb +2 -0
  132. data/lib/graphql/integer_decoding_error.rb +17 -0
  133. data/lib/graphql/integer_encoding_error.rb +36 -0
  134. data/lib/graphql/interface_type.rb +72 -0
  135. data/lib/graphql/internal_representation/document.rb +27 -0
  136. data/lib/graphql/internal_representation/node.rb +206 -0
  137. data/lib/graphql/internal_representation/print.rb +51 -0
  138. data/lib/graphql/internal_representation/rewrite.rb +184 -0
  139. data/lib/graphql/internal_representation/scope.rb +88 -0
  140. data/lib/graphql/internal_representation/visit.rb +36 -0
  141. data/lib/graphql/internal_representation.rb +7 -0
  142. data/lib/graphql/introspection/base_object.rb +13 -0
  143. data/lib/graphql/introspection/directive_location_enum.rb +15 -0
  144. data/lib/graphql/introspection/directive_type.rb +29 -0
  145. data/lib/graphql/introspection/dynamic_fields.rb +17 -0
  146. data/lib/graphql/introspection/entry_points.rb +35 -0
  147. data/lib/graphql/introspection/enum_value_type.rb +23 -0
  148. data/lib/graphql/introspection/field_type.rb +28 -0
  149. data/lib/graphql/introspection/input_value_type.rb +67 -0
  150. data/lib/graphql/introspection/introspection_query.rb +7 -0
  151. data/lib/graphql/introspection/schema_type.rb +44 -0
  152. data/lib/graphql/introspection/type_kind_enum.rb +13 -0
  153. data/lib/graphql/introspection/type_type.rb +95 -0
  154. data/lib/graphql/introspection.rb +114 -0
  155. data/lib/graphql/invalid_name_error.rb +11 -0
  156. data/lib/graphql/invalid_null_error.rb +50 -0
  157. data/lib/graphql/language/block_string.rb +99 -0
  158. data/lib/graphql/language/cache.rb +37 -0
  159. data/lib/graphql/language/definition_slice.rb +41 -0
  160. data/lib/graphql/language/document_from_schema_definition.rb +347 -0
  161. data/lib/graphql/language/generation.rb +24 -0
  162. data/lib/graphql/language/lexer.rb +1467 -0
  163. data/lib/graphql/language/lexer.rl +258 -0
  164. data/lib/graphql/language/nodes.rb +707 -0
  165. data/lib/graphql/language/parser.rb +1974 -0
  166. data/lib/graphql/language/parser.y +544 -0
  167. data/lib/graphql/language/printer.rb +366 -0
  168. data/lib/graphql/language/sanitized_printer.rb +222 -0
  169. data/lib/graphql/language/token.rb +34 -0
  170. data/lib/graphql/language/visitor.rb +242 -0
  171. data/lib/graphql/language.rb +36 -0
  172. data/lib/graphql/list_type.rb +80 -0
  173. data/lib/graphql/load_application_object_failed_error.rb +22 -0
  174. data/lib/graphql/name_validator.rb +11 -0
  175. data/lib/graphql/non_null_type.rb +71 -0
  176. data/lib/graphql/object_type.rb +130 -0
  177. data/lib/graphql/pagination/active_record_relation_connection.rb +48 -0
  178. data/lib/graphql/pagination/array_connection.rb +77 -0
  179. data/lib/graphql/pagination/connection.rb +226 -0
  180. data/lib/graphql/pagination/connections.rb +160 -0
  181. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  182. data/lib/graphql/pagination/relation_connection.rb +196 -0
  183. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  184. data/lib/graphql/pagination.rb +6 -0
  185. data/lib/graphql/parse_error.rb +24 -0
  186. data/lib/graphql/query/arguments.rb +189 -0
  187. data/lib/graphql/query/arguments_cache.rb +24 -0
  188. data/lib/graphql/query/context.rb +371 -0
  189. data/lib/graphql/query/executor.rb +52 -0
  190. data/lib/graphql/query/fingerprint.rb +26 -0
  191. data/lib/graphql/query/input_validation_result.rb +43 -0
  192. data/lib/graphql/query/literal_input.rb +136 -0
  193. data/lib/graphql/query/null_context.rb +55 -0
  194. data/lib/graphql/query/result.rb +63 -0
  195. data/lib/graphql/query/serial_execution/field_resolution.rb +92 -0
  196. data/lib/graphql/query/serial_execution/operation_resolution.rb +19 -0
  197. data/lib/graphql/query/serial_execution/selection_resolution.rb +23 -0
  198. data/lib/graphql/query/serial_execution/value_resolution.rb +87 -0
  199. data/lib/graphql/query/serial_execution.rb +40 -0
  200. data/lib/graphql/query/validation_pipeline.rb +139 -0
  201. data/lib/graphql/query/variable_validation_error.rb +44 -0
  202. data/lib/graphql/query/variables.rb +78 -0
  203. data/lib/graphql/query.rb +454 -0
  204. data/lib/graphql/railtie.rb +117 -0
  205. data/lib/graphql/rake_task/validate.rb +63 -0
  206. data/lib/graphql/rake_task.rb +145 -0
  207. data/lib/graphql/relay/array_connection.rb +83 -0
  208. data/lib/graphql/relay/base_connection.rb +189 -0
  209. data/lib/graphql/relay/connection_instrumentation.rb +54 -0
  210. data/lib/graphql/relay/connection_resolve.rb +43 -0
  211. data/lib/graphql/relay/connection_type.rb +41 -0
  212. data/lib/graphql/relay/edge.rb +27 -0
  213. data/lib/graphql/relay/edge_type.rb +19 -0
  214. data/lib/graphql/relay/edges_instrumentation.rb +39 -0
  215. data/lib/graphql/relay/global_id_resolve.rb +18 -0
  216. data/lib/graphql/relay/mongo_relation_connection.rb +50 -0
  217. data/lib/graphql/relay/mutation/instrumentation.rb +23 -0
  218. data/lib/graphql/relay/mutation/resolve.rb +56 -0
  219. data/lib/graphql/relay/mutation/result.rb +38 -0
  220. data/lib/graphql/relay/mutation.rb +106 -0
  221. data/lib/graphql/relay/node.rb +39 -0
  222. data/lib/graphql/relay/page_info.rb +7 -0
  223. data/lib/graphql/relay/range_add.rb +59 -0
  224. data/lib/graphql/relay/relation_connection.rb +188 -0
  225. data/lib/graphql/relay/type_extensions.rb +32 -0
  226. data/lib/graphql/relay.rb +18 -0
  227. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  228. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  229. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  230. data/lib/graphql/rubocop.rb +4 -0
  231. data/lib/graphql/runtime_type_error.rb +5 -0
  232. data/lib/graphql/scalar_type.rb +91 -0
  233. data/lib/graphql/schema/addition.rb +247 -0
  234. data/lib/graphql/schema/argument.rb +383 -0
  235. data/lib/graphql/schema/base_64_bp.rb +26 -0
  236. data/lib/graphql/schema/base_64_encoder.rb +21 -0
  237. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
  238. data/lib/graphql/schema/build_from_definition/resolve_map.rb +78 -0
  239. data/lib/graphql/schema/build_from_definition.rb +477 -0
  240. data/lib/graphql/schema/built_in_types.rb +12 -0
  241. data/lib/graphql/schema/catchall_middleware.rb +35 -0
  242. data/lib/graphql/schema/default_parse_error.rb +10 -0
  243. data/lib/graphql/schema/default_type_error.rb +17 -0
  244. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  245. data/lib/graphql/schema/directive/feature.rb +66 -0
  246. data/lib/graphql/schema/directive/flagged.rb +57 -0
  247. data/lib/graphql/schema/directive/include.rb +25 -0
  248. data/lib/graphql/schema/directive/skip.rb +25 -0
  249. data/lib/graphql/schema/directive/transform.rb +60 -0
  250. data/lib/graphql/schema/directive.rb +210 -0
  251. data/lib/graphql/schema/enum.rb +193 -0
  252. data/lib/graphql/schema/enum_value.rb +97 -0
  253. data/lib/graphql/schema/field/connection_extension.rb +76 -0
  254. data/lib/graphql/schema/field/scope_extension.rb +22 -0
  255. data/lib/graphql/schema/field.rb +880 -0
  256. data/lib/graphql/schema/field_extension.rb +69 -0
  257. data/lib/graphql/schema/find_inherited_value.rb +36 -0
  258. data/lib/graphql/schema/finder.rb +155 -0
  259. data/lib/graphql/schema/input_object.rb +253 -0
  260. data/lib/graphql/schema/interface.rb +136 -0
  261. data/lib/graphql/schema/introspection_system.rb +169 -0
  262. data/lib/graphql/schema/invalid_type_error.rb +7 -0
  263. data/lib/graphql/schema/late_bound_type.rb +33 -0
  264. data/lib/graphql/schema/list.rb +75 -0
  265. data/lib/graphql/schema/loader.rb +226 -0
  266. data/lib/graphql/schema/member/accepts_definition.rb +159 -0
  267. data/lib/graphql/schema/member/base_dsl_methods.rb +129 -0
  268. data/lib/graphql/schema/member/build_type.rb +180 -0
  269. data/lib/graphql/schema/member/cached_graphql_definition.rb +31 -0
  270. data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
  271. data/lib/graphql/schema/member/has_arguments.rb +332 -0
  272. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  273. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  274. data/lib/graphql/schema/member/has_directives.rb +98 -0
  275. data/lib/graphql/schema/member/has_fields.rb +163 -0
  276. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  277. data/lib/graphql/schema/member/has_path.rb +25 -0
  278. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  279. data/lib/graphql/schema/member/has_validators.rb +31 -0
  280. data/lib/graphql/schema/member/instrumentation.rb +131 -0
  281. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -0
  282. data/lib/graphql/schema/member/scoped.rb +21 -0
  283. data/lib/graphql/schema/member/type_system_helpers.rb +38 -0
  284. data/lib/graphql/schema/member/validates_input.rb +33 -0
  285. data/lib/graphql/schema/member.rb +161 -0
  286. data/lib/graphql/schema/middleware_chain.rb +82 -0
  287. data/lib/graphql/schema/mutation.rb +94 -0
  288. data/lib/graphql/schema/non_null.rb +67 -0
  289. data/lib/graphql/schema/null_mask.rb +11 -0
  290. data/lib/graphql/schema/object.rb +150 -0
  291. data/lib/graphql/schema/possible_types.rb +44 -0
  292. data/lib/graphql/schema/printer.rb +100 -0
  293. data/lib/graphql/schema/relay_classic_mutation.rb +160 -0
  294. data/lib/graphql/schema/rescue_middleware.rb +60 -0
  295. data/lib/graphql/schema/resolver/has_payload_type.rb +96 -0
  296. data/lib/graphql/schema/resolver.rb +397 -0
  297. data/lib/graphql/schema/scalar.rb +69 -0
  298. data/lib/graphql/schema/subscription.rb +155 -0
  299. data/lib/graphql/schema/timeout.rb +123 -0
  300. data/lib/graphql/schema/timeout_middleware.rb +88 -0
  301. data/lib/graphql/schema/traversal.rb +228 -0
  302. data/lib/graphql/schema/type_expression.rb +43 -0
  303. data/lib/graphql/schema/type_membership.rb +48 -0
  304. data/lib/graphql/schema/union.rb +95 -0
  305. data/lib/graphql/schema/unique_within_type.rb +34 -0
  306. data/lib/graphql/schema/validation.rb +313 -0
  307. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  308. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  309. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  310. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  311. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  312. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  313. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  314. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  315. data/lib/graphql/schema/validator.rb +174 -0
  316. data/lib/graphql/schema/warden.rb +409 -0
  317. data/lib/graphql/schema/wrapper.rb +29 -0
  318. data/lib/graphql/schema.rb +1945 -0
  319. data/lib/graphql/static_validation/all_rules.rb +40 -0
  320. data/lib/graphql/static_validation/base_visitor.rb +217 -0
  321. data/lib/graphql/static_validation/default_visitor.rb +15 -0
  322. data/lib/graphql/static_validation/definition_dependencies.rb +198 -0
  323. data/lib/graphql/static_validation/error.rb +46 -0
  324. data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
  325. data/lib/graphql/static_validation/literal_validator.rb +139 -0
  326. data/lib/graphql/static_validation/no_validate_visitor.rb +10 -0
  327. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +66 -0
  328. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +48 -0
  329. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +31 -0
  330. data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
  331. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +71 -0
  332. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +37 -0
  333. data/lib/graphql/static_validation/rules/directives_are_defined.rb +23 -0
  334. data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
  335. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +65 -0
  336. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
  337. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +30 -0
  338. data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
  339. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +73 -0
  340. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
  341. data/lib/graphql/static_validation/rules/fields_will_merge.rb +418 -0
  342. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +53 -0
  343. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +30 -0
  344. data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
  345. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +73 -0
  346. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
  347. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +39 -0
  348. data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
  349. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +21 -0
  350. data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
  351. data/lib/graphql/static_validation/rules/fragments_are_named.rb +16 -0
  352. data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
  353. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +37 -0
  354. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
  355. data/lib/graphql/static_validation/rules/fragments_are_used.rb +32 -0
  356. data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
  357. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  358. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  359. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +17 -0
  360. data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
  361. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +41 -0
  362. data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
  363. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +36 -0
  364. data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
  365. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +37 -0
  366. data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
  367. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +59 -0
  368. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
  369. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +17 -0
  370. data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
  371. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +50 -0
  372. data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
  373. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +46 -0
  374. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
  375. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +24 -0
  376. data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
  377. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +153 -0
  378. data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
  379. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +39 -0
  380. data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
  381. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +155 -0
  382. data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
  383. data/lib/graphql/static_validation/type_stack.rb +216 -0
  384. data/lib/graphql/static_validation/validation_context.rb +49 -0
  385. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  386. data/lib/graphql/static_validation/validator.rb +96 -0
  387. data/lib/graphql/static_validation.rb +19 -0
  388. data/lib/graphql/string_encoding_error.rb +20 -0
  389. data/lib/graphql/string_type.rb +2 -0
  390. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +245 -0
  391. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  392. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  393. data/lib/graphql/subscriptions/event.rb +144 -0
  394. data/lib/graphql/subscriptions/instrumentation.rb +79 -0
  395. data/lib/graphql/subscriptions/serialize.rb +138 -0
  396. data/lib/graphql/subscriptions/subscription_root.rb +76 -0
  397. data/lib/graphql/subscriptions.rb +299 -0
  398. data/lib/graphql/tracing/active_support_notifications_tracing.rb +35 -0
  399. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  400. data/lib/graphql/tracing/appsignal_tracing.rb +51 -0
  401. data/lib/graphql/tracing/data_dog_tracing.rb +76 -0
  402. data/lib/graphql/tracing/new_relic_tracing.rb +51 -0
  403. data/lib/graphql/tracing/platform_tracing.rb +139 -0
  404. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +32 -0
  405. data/lib/graphql/tracing/prometheus_tracing.rb +67 -0
  406. data/lib/graphql/tracing/scout_tracing.rb +54 -0
  407. data/lib/graphql/tracing/skylight_tracing.rb +70 -0
  408. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  409. data/lib/graphql/tracing.rb +95 -0
  410. data/lib/graphql/type_kinds.rb +77 -0
  411. data/lib/graphql/types/big_int.rb +23 -0
  412. data/lib/graphql/types/boolean.rb +18 -0
  413. data/lib/graphql/types/float.rb +19 -0
  414. data/lib/graphql/types/id.rb +24 -0
  415. data/lib/graphql/types/int.rb +36 -0
  416. data/lib/graphql/types/iso_8601_date.rb +34 -0
  417. data/lib/graphql/types/iso_8601_date_time.rb +65 -0
  418. data/lib/graphql/types/json.rb +25 -0
  419. data/lib/graphql/types/relay/base_connection.rb +39 -0
  420. data/lib/graphql/types/relay/base_edge.rb +29 -0
  421. data/lib/graphql/types/relay/connection_behaviors.rb +156 -0
  422. data/lib/graphql/types/relay/default_relay.rb +27 -0
  423. data/lib/graphql/types/relay/edge_behaviors.rb +53 -0
  424. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  425. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  426. data/lib/graphql/types/relay/node.rb +15 -0
  427. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  428. data/lib/graphql/types/relay/node_field.rb +25 -0
  429. data/lib/graphql/types/relay/nodes_field.rb +27 -0
  430. data/lib/graphql/types/relay/page_info.rb +11 -0
  431. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  432. data/lib/graphql/types/relay.rb +41 -0
  433. data/lib/graphql/types/string.rb +29 -0
  434. data/lib/graphql/types.rb +11 -0
  435. data/lib/graphql/unauthorized_error.rb +29 -0
  436. data/lib/graphql/unauthorized_field_error.rb +23 -0
  437. data/lib/graphql/union_type.rb +115 -0
  438. data/lib/graphql/unresolved_type_error.rb +35 -0
  439. data/lib/graphql/upgrader/member.rb +937 -0
  440. data/lib/graphql/upgrader/schema.rb +38 -0
  441. data/lib/graphql/version.rb +4 -0
  442. data/lib/graphql.rb +168 -0
  443. data/readme.md +49 -0
  444. metadata +714 -0
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Subscriptions
4
+ # A subscriptions implementation that sends data
5
+ # as ActionCable broadcastings.
6
+ #
7
+ # Some things to keep in mind:
8
+ #
9
+ # - No queueing system; ActiveJob should be added
10
+ # - Take care to reload context when re-delivering the subscription. (see {Query#subscription_update?})
11
+ # - Avoid the async ActionCable adapter and use the redis or PostgreSQL adapters instead. Otherwise calling #trigger won't work from background jobs or the Rails console.
12
+ #
13
+ # @example Adding ActionCableSubscriptions to your schema
14
+ # class MySchema < GraphQL::Schema
15
+ # # ...
16
+ # use GraphQL::Subscriptions::ActionCableSubscriptions
17
+ # end
18
+ #
19
+ # @example Implementing a channel for GraphQL Subscriptions
20
+ # class GraphqlChannel < ApplicationCable::Channel
21
+ # def subscribed
22
+ # @subscription_ids = []
23
+ # end
24
+ #
25
+ # def execute(data)
26
+ # query = data["query"]
27
+ # variables = ensure_hash(data["variables"])
28
+ # operation_name = data["operationName"]
29
+ # context = {
30
+ # # Re-implement whatever context methods you need
31
+ # # in this channel or ApplicationCable::Channel
32
+ # # current_user: current_user,
33
+ # # Make sure the channel is in the context
34
+ # channel: self,
35
+ # }
36
+ #
37
+ # result = MySchema.execute(
38
+ # query: query,
39
+ # context: context,
40
+ # variables: variables,
41
+ # operation_name: operation_name
42
+ # )
43
+ #
44
+ # payload = {
45
+ # result: result.to_h,
46
+ # more: result.subscription?,
47
+ # }
48
+ #
49
+ # # Track the subscription here so we can remove it
50
+ # # on unsubscribe.
51
+ # if result.context[:subscription_id]
52
+ # @subscription_ids << result.context[:subscription_id]
53
+ # end
54
+ #
55
+ # transmit(payload)
56
+ # end
57
+ #
58
+ # def unsubscribed
59
+ # @subscription_ids.each { |sid|
60
+ # MySchema.subscriptions.delete_subscription(sid)
61
+ # }
62
+ # end
63
+ #
64
+ # private
65
+ #
66
+ # def ensure_hash(ambiguous_param)
67
+ # case ambiguous_param
68
+ # when String
69
+ # if ambiguous_param.present?
70
+ # ensure_hash(JSON.parse(ambiguous_param))
71
+ # else
72
+ # {}
73
+ # end
74
+ # when Hash, ActionController::Parameters
75
+ # ambiguous_param
76
+ # when nil
77
+ # {}
78
+ # else
79
+ # raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
80
+ # end
81
+ # end
82
+ # end
83
+ #
84
+ class ActionCableSubscriptions < GraphQL::Subscriptions
85
+ SUBSCRIPTION_PREFIX = "graphql-subscription:"
86
+ EVENT_PREFIX = "graphql-event:"
87
+
88
+ # @param serializer [<#dump(obj), #load(string)] Used for serializing messages before handing them to `.broadcast(msg)`
89
+ # @param namespace [string] Used to namespace events and subscriptions (default: '')
90
+ def initialize(serializer: Serialize, namespace: '', action_cable: ActionCable, action_cable_coder: ActiveSupport::JSON, **rest)
91
+ # A per-process map of subscriptions to deliver.
92
+ # This is provided by Rails, so let's use it
93
+ @subscriptions = Concurrent::Map.new
94
+ @events = Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new { |h2, k2| h2[k2] = Concurrent::Array.new } }
95
+ @action_cable = action_cable
96
+ @action_cable_coder = action_cable_coder
97
+ @serializer = serializer
98
+ @serialize_with_context = case @serializer.method(:load).arity
99
+ when 1
100
+ false
101
+ when 2
102
+ true
103
+ else
104
+ raise ArgumentError, "#{@serializer} must repond to `.load` accepting one or two arguments"
105
+ end
106
+ @transmit_ns = namespace
107
+ super
108
+ end
109
+
110
+ # An event was triggered; Push the data over ActionCable.
111
+ # Subscribers will re-evaluate locally.
112
+ def execute_all(event, object)
113
+ stream = stream_event_name(event)
114
+ message = @serializer.dump(object)
115
+ @action_cable.server.broadcast(stream, message)
116
+ end
117
+
118
+ # This subscription was re-evaluated.
119
+ # Send it to the specific stream where this client was waiting.
120
+ def deliver(subscription_id, result)
121
+ payload = { result: result.to_h, more: true }
122
+ @action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
123
+ end
124
+
125
+ # A query was run where these events were subscribed to.
126
+ # Store them in memory in _this_ ActionCable frontend.
127
+ # It will receive notifications when events come in
128
+ # and re-evaluate the query locally.
129
+ def write_subscription(query, events)
130
+ unless (channel = query.context[:channel])
131
+ raise GraphQL::Error, "This GraphQL Subscription client does not support the transport protocol expected"\
132
+ "by the backend Subscription Server implementation (graphql-ruby ActionCableSubscriptions in this case)."\
133
+ "Some official client implementation including Apollo (https://graphql-ruby.org/javascript_client/apollo_subscriptions.html), "\
134
+ "Relay Modern (https://graphql-ruby.org/javascript_client/relay_subscriptions.html#actioncable)."\
135
+ "GraphiQL via `graphiql-rails` may not work out of box (#1051)."
136
+ end
137
+ subscription_id = query.context[:subscription_id] ||= build_id
138
+ stream = stream_subscription_name(subscription_id)
139
+ channel.stream_from(stream)
140
+ @subscriptions[subscription_id] = query
141
+ events.each do |event|
142
+ # Setup a new listener to run all events with this topic in this process
143
+ setup_stream(channel, event)
144
+ # Add this event to the list of events to be updated
145
+ @events[event.topic][event.fingerprint] << event
146
+ end
147
+ end
148
+
149
+ # Every subscribing channel is listening here, but only one of them takes any action.
150
+ # This is so we can reuse payloads when possible, and make one payload to send to
151
+ # all subscribers.
152
+ #
153
+ # But the problem is, any channel could close at any time, so each channel has to
154
+ # be ready to take over the primary position.
155
+ #
156
+ # To make sure there's always one-and-only-one channel building payloads,
157
+ # let the listener belonging to the first event on the list be
158
+ # the one to build and publish payloads.
159
+ #
160
+ def setup_stream(channel, initial_event)
161
+ topic = initial_event.topic
162
+ channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
163
+ events_by_fingerprint = @events[topic]
164
+ object = nil
165
+ events_by_fingerprint.each do |_fingerprint, events|
166
+ if events.any? && events.first == initial_event
167
+ # The fingerprint has told us that this response should be shared by all subscribers,
168
+ # so just run it once, then deliver the result to every subscriber
169
+ first_event = events.first
170
+ first_subscription_id = first_event.context.fetch(:subscription_id)
171
+ object ||= load_action_cable_message(message, first_event.context)
172
+ result = execute_update(first_subscription_id, first_event, object)
173
+ if !result.nil?
174
+ # Having calculated the result _once_, send the same payload to all subscribers
175
+ events.each do |event|
176
+ subscription_id = event.context.fetch(:subscription_id)
177
+ deliver(subscription_id, result)
178
+ end
179
+ end
180
+ end
181
+ end
182
+ nil
183
+ end
184
+ end
185
+
186
+ # This is called to turn an ActionCable-broadcasted string (JSON)
187
+ # into a query-ready application object.
188
+ # @param message [String] n ActionCable-broadcasted string (JSON)
189
+ # @param context [GraphQL::Query::Context] the context of the first event for a given subscription fingerprint
190
+ def load_action_cable_message(message, context)
191
+ if @serialize_with_context
192
+ @serializer.load(message, context)
193
+ else
194
+ @serializer.load(message)
195
+ end
196
+ end
197
+
198
+ # Return the query from "storage" (in memory)
199
+ def read_subscription(subscription_id)
200
+ query = @subscriptions[subscription_id]
201
+ if query.nil?
202
+ # This can happen when a subscription is triggered from an unsubscribed channel,
203
+ # see https://github.com/rmosolgo/graphql-ruby/issues/2478.
204
+ # (This `nil` is handled by `#execute_update`)
205
+ nil
206
+ else
207
+ {
208
+ query_string: query.query_string,
209
+ variables: query.provided_variables,
210
+ context: query.context.to_h,
211
+ operation_name: query.operation_name,
212
+ }
213
+ end
214
+ end
215
+
216
+ # The channel was closed, forget about it.
217
+ def delete_subscription(subscription_id)
218
+ query = @subscriptions.delete(subscription_id)
219
+ # This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
220
+ # see https://github.com/rmosolgo/graphql-ruby/issues/2478
221
+ if query
222
+ events = query.context.namespace(:subscriptions)[:events]
223
+ events.each do |event|
224
+ ev_by_fingerprint = @events[event.topic]
225
+ ev_for_fingerprint = ev_by_fingerprint[event.fingerprint]
226
+ ev_for_fingerprint.delete(event)
227
+ if ev_for_fingerprint.empty?
228
+ ev_by_fingerprint.delete(event.fingerprint)
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ private
235
+
236
+ def stream_subscription_name(subscription_id)
237
+ [SUBSCRIPTION_PREFIX, @transmit_ns, subscription_id].join
238
+ end
239
+
240
+ def stream_event_name(event)
241
+ [EVENT_PREFIX, @transmit_ns, event.topic].join
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Subscriptions
5
+ # Detect whether the current operation:
6
+ # - Is a subscription operation
7
+ # - Is completely broadcastable
8
+ #
9
+ # Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
10
+ # @api private
11
+ # @see Subscriptions#broadcastable? for a public API
12
+ class BroadcastAnalyzer < GraphQL::Analysis::AST::Analyzer
13
+ def initialize(subject)
14
+ super
15
+ @default_broadcastable = subject.schema.subscriptions.default_broadcastable
16
+ # Maybe this will get set to false while analyzing
17
+ @subscription_broadcastable = true
18
+ end
19
+
20
+ # Only analyze subscription operations
21
+ def analyze?
22
+ @query.subscription?
23
+ end
24
+
25
+ def on_enter_field(node, parent, visitor)
26
+ if (@subscription_broadcastable == false) || visitor.skipping?
27
+ return
28
+ end
29
+
30
+ current_field = visitor.field_definition
31
+ apply_broadcastable(current_field)
32
+
33
+ current_type = visitor.parent_type_definition
34
+ if current_type.kind.interface?
35
+ pt = @query.possible_types(current_type)
36
+ pt.each do |object_type|
37
+ ot_field = @query.get_field(object_type, current_field.graphql_name)
38
+ # Inherited fields would be exactly the same object;
39
+ # only check fields that are overrides of the inherited one
40
+ if ot_field && ot_field != current_field
41
+ apply_broadcastable(ot_field)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # Assign the result to context.
48
+ # (This method is allowed to return an error, but we don't need to)
49
+ # @return [void]
50
+ def result
51
+ query.context.namespace(:subscriptions)[:subscription_broadcastable] = @subscription_broadcastable
52
+ nil
53
+ end
54
+
55
+ private
56
+
57
+ # Modify `@subscription_broadcastable` based on `field_defn`'s configuration (and/or the default value)
58
+ def apply_broadcastable(field_defn)
59
+ current_field_broadcastable = field_defn.introspection? || field_defn.broadcastable?
60
+ case current_field_broadcastable
61
+ when nil
62
+ # If the value wasn't set, mix in the default value:
63
+ # - If the default is false and the current value is true, make it false
64
+ # - If the default is true and the current value is true, it stays true
65
+ # - If the default is false and the current value is false, keep it false
66
+ # - If the default is true and the current value is false, keep it false
67
+ @subscription_broadcastable = @subscription_broadcastable && @default_broadcastable
68
+ when false
69
+ # One non-broadcastable field is enough to make the whole subscription non-broadcastable
70
+ @subscription_broadcastable = false
71
+ when true
72
+ # Leave `@broadcastable_query` true if it's already true,
73
+ # but don't _set_ it to true if it was set to false by something else.
74
+ # Actually, just leave it!
75
+ else
76
+ raise ArgumentError, "Unexpected `.broadcastable?` value for #{field_defn.path}: #{current_field_broadcastable}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Subscriptions
4
+ class DefaultSubscriptionResolveExtension < GraphQL::Subscriptions::SubscriptionRoot::Extension
5
+ def resolve(context:, object:, arguments:)
6
+ has_override_implementation = @field.resolver ||
7
+ object.respond_to?(@field.resolver_method)
8
+
9
+ if !has_override_implementation
10
+ if context.query.subscription_update?
11
+ object.object
12
+ else
13
+ context.skip
14
+ end
15
+ else
16
+ yield(object, arguments)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Subscriptions
4
+ # This thing can be:
5
+ # - Subscribed to by `subscription { ... }`
6
+ # - Triggered by `MySchema.subscriber.trigger(name, arguments, obj)`
7
+ #
8
+ class Event
9
+ # @return [String] Corresponds to the Subscription root field name
10
+ attr_reader :name
11
+
12
+ # @return [GraphQL::Query::Arguments]
13
+ attr_reader :arguments
14
+
15
+ # @return [GraphQL::Query::Context]
16
+ attr_reader :context
17
+
18
+ # @return [String] An opaque string which identifies this event, derived from `name` and `arguments`
19
+ attr_reader :topic
20
+
21
+ def initialize(name:, arguments:, field: nil, context: nil, scope: nil)
22
+ @name = name
23
+ @arguments = arguments
24
+ @context = context
25
+ field ||= context.field
26
+ scope_key = field.subscription_scope
27
+ scope_val = scope || (context && scope_key && context[scope_key])
28
+ if scope_key &&
29
+ (subscription = field.resolver) &&
30
+ (subscription.respond_to?(:subscription_scope_optional?)) &&
31
+ !subscription.subscription_scope_optional? &&
32
+ scope_val.nil?
33
+ raise Subscriptions::SubscriptionScopeMissingError, "#{field.path} (#{subscription}) requires a `scope:` value to trigger updates (Set `subscription_scope ..., optional: true` to disable this requirement)"
34
+ end
35
+
36
+ @topic = self.class.serialize(name, arguments, field, scope: scope_val, context: context)
37
+ end
38
+
39
+ # @return [String] an identifier for this unit of subscription
40
+ def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
41
+ subscription = field.resolver || GraphQL::Schema::Subscription
42
+ normalized_args = stringify_args(field, arguments.to_h, context)
43
+ subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
44
+ end
45
+
46
+ # @return [String] a logical identifier for this event. (Stable when the query is broadcastable.)
47
+ def fingerprint
48
+ @fingerprint ||= begin
49
+ # When this query has been flagged as broadcastable,
50
+ # use a generalized, stable fingerprint so that
51
+ # duplicate subscriptions can be evaluated and distributed in bulk.
52
+ # (`@topic` includes field, args, and subscription scope already.)
53
+ if @context.namespace(:subscriptions)[:subscription_broadcastable]
54
+ "#{@topic}/#{@context.query.fingerprint}"
55
+ else
56
+ # not broadcastable, build a unique ID for this event
57
+ @context.schema.subscriptions.build_id
58
+ end
59
+ end
60
+ end
61
+
62
+ class << self
63
+ private
64
+
65
+ # This method does not support cyclic references in the Hash,
66
+ # nor does it support Hashes whose keys are not sortable
67
+ # with respect to their peers ( cases where a <=> b might throw an error )
68
+ def deep_sort_hash_keys(hash_to_sort)
69
+ raise ArgumentError.new("Argument must be a Hash") unless hash_to_sort.is_a?(Hash)
70
+ hash_to_sort.keys.sort.map do |k|
71
+ if hash_to_sort[k].is_a?(Hash)
72
+ [k, deep_sort_hash_keys(hash_to_sort[k])]
73
+ elsif hash_to_sort[k].is_a?(Array)
74
+ [k, deep_sort_array_hashes(hash_to_sort[k])]
75
+ else
76
+ [k, hash_to_sort[k]]
77
+ end
78
+ end.to_h
79
+ end
80
+
81
+ def deep_sort_array_hashes(array_to_inspect)
82
+ raise ArgumentError.new("Argument must be an Array") unless array_to_inspect.is_a?(Array)
83
+ array_to_inspect.map do |v|
84
+ if v.is_a?(Hash)
85
+ deep_sort_hash_keys(v)
86
+ elsif v.is_a?(Array)
87
+ deep_sort_array_hashes(v)
88
+ else
89
+ v
90
+ end
91
+ end
92
+ end
93
+
94
+ def stringify_args(arg_owner, args, context)
95
+ arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
96
+ case args
97
+ when Hash
98
+ next_args = {}
99
+ args.each do |k, v|
100
+ arg_name = k.to_s
101
+ camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name)
102
+ arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context)
103
+
104
+ if arg_defn
105
+ normalized_arg_name = camelized_arg_name
106
+ else
107
+ normalized_arg_name = arg_name
108
+ arg_defn = get_arg_definition(arg_owner, normalized_arg_name, context)
109
+ end
110
+ arg_base_type = arg_defn.type.unwrap
111
+ # In the case where the value being emitted is seen as a "JSON"
112
+ # type, treat the value as one atomic unit of serialization
113
+ is_json_definition = arg_base_type && arg_base_type <= GraphQL::Types::JSON
114
+ if is_json_definition
115
+ sorted_value = if v.is_a?(Hash)
116
+ deep_sort_hash_keys(v)
117
+ elsif v.is_a?(Array)
118
+ deep_sort_array_hashes(v)
119
+ else
120
+ v
121
+ end
122
+ next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value
123
+ else
124
+ next_args[normalized_arg_name] = stringify_args(arg_base_type, v, context)
125
+ end
126
+ end
127
+ # Make sure they're deeply sorted
128
+ next_args.sort.to_h
129
+ when Array
130
+ args.map { |a| stringify_args(arg_owner, a, context) }
131
+ when GraphQL::Schema::InputObject
132
+ stringify_args(arg_owner, args.to_h, context)
133
+ else
134
+ args
135
+ end
136
+ end
137
+
138
+ def get_arg_definition(arg_owner, arg_name, context)
139
+ arg_owner.get_argument(arg_name, context) || arg_owner.arguments(context).each_value.find { |v| v.keyword.to_s == arg_name }
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Subscriptions
4
+ # Wrap the root fields of the subscription type with special logic for:
5
+ # - Registering the subscription during the first execution
6
+ # - Evaluating the triggered portion(s) of the subscription during later execution
7
+ class Instrumentation
8
+ def initialize(schema:)
9
+ @schema = schema
10
+ end
11
+
12
+ def instrument(type, field)
13
+ if type == @schema.subscription.graphql_definition
14
+ # This is a root field of `subscription`
15
+ subscribing_resolve_proc = SubscriptionRegistrationResolve.new(field.resolve_proc)
16
+ field.redefine(resolve: subscribing_resolve_proc)
17
+ else
18
+ field
19
+ end
20
+ end
21
+
22
+ # If needed, prepare to gather events which this query subscribes to
23
+ def before_query(query)
24
+ if query.subscription? && !query.subscription_update?
25
+ query.context.namespace(:subscriptions)[:events] = []
26
+ end
27
+ end
28
+
29
+ # After checking the root fields, pass the gathered events to the store
30
+ def after_query(query)
31
+ events = query.context.namespace(:subscriptions)[:events]
32
+ if events && events.any?
33
+ @schema.subscriptions.write_subscription(query, events)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ class SubscriptionRegistrationResolve
40
+ def initialize(inner_proc)
41
+ @inner_proc = inner_proc
42
+ end
43
+
44
+ # Wrap the proc with subscription registration logic
45
+ def call(obj, args, ctx)
46
+ result = nil
47
+ if @inner_proc && !@inner_proc.is_a?(GraphQL::Field::Resolve::BuiltInResolve)
48
+ result = @inner_proc.call(obj, args, ctx)
49
+ end
50
+
51
+ events = ctx.namespace(:subscriptions)[:events]
52
+
53
+ if events
54
+ # This is the first execution, so gather an Event
55
+ # for the backend to register:
56
+ events << Subscriptions::Event.new(
57
+ name: ctx.field.name,
58
+ arguments: args,
59
+ context: ctx,
60
+ )
61
+ result
62
+ elsif ctx.irep_node.subscription_topic == ctx.query.subscription_topic
63
+ if !result.nil?
64
+ result
65
+ elsif obj.is_a?(GraphQL::Schema::Object)
66
+ # The root object is _already_ the subscription update:
67
+ obj.object
68
+ else
69
+ obj
70
+ end
71
+ else
72
+ # This is a subscription update, but this event wasn't triggered.
73
+ ctx.skip
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end