graphql 0.16.0 → 2.0.15

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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (490) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +5 -0
  3. data/lib/generators/graphql/core.rb +69 -0
  4. data/lib/generators/graphql/enum_generator.rb +27 -0
  5. data/lib/generators/graphql/field_extractor.rb +31 -0
  6. data/lib/generators/graphql/input_generator.rb +50 -0
  7. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  8. data/lib/generators/graphql/install/templates/base_mutation.erb +10 -0
  9. data/lib/generators/graphql/install/templates/mutation_type.erb +12 -0
  10. data/lib/generators/graphql/install_generator.rb +197 -0
  11. data/lib/generators/graphql/interface_generator.rb +27 -0
  12. data/lib/generators/graphql/loader_generator.rb +21 -0
  13. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  14. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  15. data/lib/generators/graphql/mutation_generator.rb +30 -0
  16. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  17. data/lib/generators/graphql/object_generator.rb +50 -0
  18. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  19. data/lib/generators/graphql/relay.rb +49 -0
  20. data/lib/generators/graphql/relay_generator.rb +21 -0
  21. data/lib/generators/graphql/scalar_generator.rb +22 -0
  22. data/lib/generators/graphql/templates/base_argument.erb +6 -0
  23. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  24. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  25. data/lib/generators/graphql/templates/base_enum.erb +6 -0
  26. data/lib/generators/graphql/templates/base_field.erb +7 -0
  27. data/lib/generators/graphql/templates/base_input_object.erb +7 -0
  28. data/lib/generators/graphql/templates/base_interface.erb +9 -0
  29. data/lib/generators/graphql/templates/base_object.erb +7 -0
  30. data/lib/generators/graphql/templates/base_scalar.erb +6 -0
  31. data/lib/generators/graphql/templates/base_union.erb +6 -0
  32. data/lib/generators/graphql/templates/enum.erb +11 -0
  33. data/lib/generators/graphql/templates/graphql_controller.erb +52 -0
  34. data/lib/generators/graphql/templates/input.erb +9 -0
  35. data/lib/generators/graphql/templates/interface.erb +10 -0
  36. data/lib/generators/graphql/templates/loader.erb +19 -0
  37. data/lib/generators/graphql/templates/mutation.erb +16 -0
  38. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  39. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  40. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  41. data/lib/generators/graphql/templates/node_type.erb +9 -0
  42. data/lib/generators/graphql/templates/object.erb +10 -0
  43. data/lib/generators/graphql/templates/query_type.erb +15 -0
  44. data/lib/generators/graphql/templates/scalar.erb +17 -0
  45. data/lib/generators/graphql/templates/schema.erb +30 -0
  46. data/lib/generators/graphql/templates/union.erb +9 -0
  47. data/lib/generators/graphql/type_generator.rb +135 -0
  48. data/lib/generators/graphql/union_generator.rb +33 -0
  49. data/lib/graphql/analysis/ast/analyzer.rb +84 -0
  50. data/lib/graphql/analysis/ast/field_usage.rb +57 -0
  51. data/lib/graphql/analysis/ast/max_query_complexity.rb +22 -0
  52. data/lib/graphql/analysis/ast/max_query_depth.rb +22 -0
  53. data/lib/graphql/analysis/ast/query_complexity.rb +230 -0
  54. data/lib/graphql/analysis/ast/query_depth.rb +55 -0
  55. data/lib/graphql/analysis/ast/visitor.rb +269 -0
  56. data/lib/graphql/analysis/ast.rb +81 -0
  57. data/lib/graphql/analysis.rb +2 -5
  58. data/lib/graphql/analysis_error.rb +1 -0
  59. data/lib/graphql/backtrace/inspect_result.rb +50 -0
  60. data/lib/graphql/backtrace/table.rb +141 -0
  61. data/lib/graphql/backtrace/traced_error.rb +54 -0
  62. data/lib/graphql/backtrace/tracer.rb +80 -0
  63. data/lib/graphql/backtrace.rb +58 -0
  64. data/lib/graphql/coercion_error.rb +13 -0
  65. data/lib/graphql/dataloader/null_dataloader.rb +24 -0
  66. data/lib/graphql/dataloader/request.rb +19 -0
  67. data/lib/graphql/dataloader/request_all.rb +19 -0
  68. data/lib/graphql/dataloader/source.rb +164 -0
  69. data/lib/graphql/dataloader.rb +311 -0
  70. data/lib/graphql/date_encoding_error.rb +16 -0
  71. data/lib/graphql/deprecation.rb +9 -0
  72. data/lib/graphql/dig.rb +19 -0
  73. data/lib/graphql/execution/directive_checks.rb +37 -0
  74. data/lib/graphql/execution/errors.rb +93 -0
  75. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  76. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  77. data/lib/graphql/execution/interpreter/arguments_cache.rb +105 -0
  78. data/lib/graphql/execution/interpreter/execution_errors.rb +29 -0
  79. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  80. data/lib/graphql/execution/interpreter/resolve.rb +77 -0
  81. data/lib/graphql/execution/interpreter/runtime.rb +994 -0
  82. data/lib/graphql/execution/interpreter.rb +226 -0
  83. data/lib/graphql/execution/lazy/lazy_method_map.rb +98 -0
  84. data/lib/graphql/execution/lazy.rb +75 -0
  85. data/lib/graphql/execution/lookahead.rb +311 -0
  86. data/lib/graphql/execution/multiplex.rb +45 -0
  87. data/lib/graphql/execution.rb +18 -0
  88. data/lib/graphql/execution_error.rb +34 -1
  89. data/lib/graphql/filter.rb +53 -0
  90. data/lib/graphql/integer_decoding_error.rb +17 -0
  91. data/lib/graphql/integer_encoding_error.rb +36 -0
  92. data/lib/graphql/introspection/base_object.rb +13 -0
  93. data/lib/graphql/introspection/directive_location_enum.rb +12 -5
  94. data/lib/graphql/introspection/directive_type.rb +30 -10
  95. data/lib/graphql/introspection/dynamic_fields.rb +12 -0
  96. data/lib/graphql/introspection/entry_points.rb +22 -0
  97. data/lib/graphql/introspection/enum_value_type.rb +21 -8
  98. data/lib/graphql/introspection/field_type.rb +26 -10
  99. data/lib/graphql/introspection/input_value_type.rb +64 -14
  100. data/lib/graphql/introspection/introspection_query.rb +7 -76
  101. data/lib/graphql/introspection/schema_type.rb +42 -17
  102. data/lib/graphql/introspection/type_kind_enum.rb +11 -5
  103. data/lib/graphql/introspection/type_type.rb +104 -16
  104. data/lib/graphql/introspection.rb +104 -13
  105. data/lib/graphql/invalid_name_error.rb +11 -0
  106. data/lib/graphql/invalid_null_error.rb +36 -8
  107. data/lib/graphql/language/block_string.rb +99 -0
  108. data/lib/graphql/language/cache.rb +37 -0
  109. data/lib/graphql/language/definition_slice.rb +41 -0
  110. data/lib/graphql/language/document_from_schema_definition.rb +335 -0
  111. data/lib/graphql/language/generation.rb +16 -86
  112. data/lib/graphql/language/lexer.rb +1436 -705
  113. data/lib/graphql/language/lexer.rl +172 -64
  114. data/lib/graphql/language/nodes.rb +617 -105
  115. data/lib/graphql/language/parser.rb +1524 -430
  116. data/lib/graphql/language/parser.y +348 -73
  117. data/lib/graphql/language/printer.rb +386 -0
  118. data/lib/graphql/language/sanitized_printer.rb +222 -0
  119. data/lib/graphql/language/token.rb +16 -3
  120. data/lib/graphql/language/visitor.rb +169 -25
  121. data/lib/graphql/language.rb +30 -0
  122. data/lib/graphql/load_application_object_failed_error.rb +22 -0
  123. data/lib/graphql/name_validator.rb +11 -0
  124. data/lib/graphql/pagination/active_record_relation_connection.rb +85 -0
  125. data/lib/graphql/pagination/array_connection.rb +79 -0
  126. data/lib/graphql/pagination/connection.rb +253 -0
  127. data/lib/graphql/pagination/connections.rb +135 -0
  128. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  129. data/lib/graphql/pagination/relation_connection.rb +228 -0
  130. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  131. data/lib/graphql/pagination.rb +6 -0
  132. data/lib/graphql/parse_error.rb +24 -0
  133. data/lib/graphql/query/context.rb +266 -12
  134. data/lib/graphql/query/fingerprint.rb +26 -0
  135. data/lib/graphql/query/input_validation_result.rb +34 -7
  136. data/lib/graphql/query/null_context.rb +52 -0
  137. data/lib/graphql/query/result.rb +63 -0
  138. data/lib/graphql/query/validation_pipeline.rb +114 -0
  139. data/lib/graphql/query/variable_validation_error.rb +27 -3
  140. data/lib/graphql/query/variables.rb +75 -24
  141. data/lib/graphql/query.rb +359 -92
  142. data/lib/graphql/railtie.rb +13 -0
  143. data/lib/graphql/rake_task/validate.rb +63 -0
  144. data/lib/graphql/rake_task.rb +146 -0
  145. data/lib/graphql/relay/range_add.rb +52 -0
  146. data/lib/graphql/relay.rb +3 -0
  147. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  148. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  149. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  150. data/lib/graphql/rubocop.rb +4 -0
  151. data/lib/graphql/runtime_type_error.rb +5 -0
  152. data/lib/graphql/schema/addition.rb +245 -0
  153. data/lib/graphql/schema/argument.rb +395 -0
  154. data/lib/graphql/schema/base_64_bp.rb +26 -0
  155. data/lib/graphql/schema/base_64_encoder.rb +21 -0
  156. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
  157. data/lib/graphql/schema/build_from_definition/resolve_map.rb +78 -0
  158. data/lib/graphql/schema/build_from_definition.rb +492 -0
  159. data/lib/graphql/schema/built_in_types.rb +12 -0
  160. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  161. data/lib/graphql/schema/directive/feature.rb +66 -0
  162. data/lib/graphql/schema/directive/flagged.rb +57 -0
  163. data/lib/graphql/schema/directive/include.rb +25 -0
  164. data/lib/graphql/schema/directive/one_of.rb +12 -0
  165. data/lib/graphql/schema/directive/skip.rb +25 -0
  166. data/lib/graphql/schema/directive/transform.rb +60 -0
  167. data/lib/graphql/schema/directive.rb +212 -0
  168. data/lib/graphql/schema/enum.rb +176 -0
  169. data/lib/graphql/schema/enum_value.rb +77 -0
  170. data/lib/graphql/schema/field/connection_extension.rb +80 -0
  171. data/lib/graphql/schema/field/scope_extension.rb +22 -0
  172. data/lib/graphql/schema/field.rb +862 -0
  173. data/lib/graphql/schema/field_extension.rb +156 -0
  174. data/lib/graphql/schema/find_inherited_value.rb +36 -0
  175. data/lib/graphql/schema/finder.rb +155 -0
  176. data/lib/graphql/schema/input_object.rb +258 -0
  177. data/lib/graphql/schema/interface.rb +113 -0
  178. data/lib/graphql/schema/introspection_system.rb +164 -0
  179. data/lib/graphql/schema/invalid_type_error.rb +1 -0
  180. data/lib/graphql/schema/late_bound_type.rb +37 -0
  181. data/lib/graphql/schema/list.rb +86 -0
  182. data/lib/graphql/schema/loader.rb +228 -0
  183. data/lib/graphql/schema/member/base_dsl_methods.rb +124 -0
  184. data/lib/graphql/schema/member/build_type.rb +178 -0
  185. data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
  186. data/lib/graphql/schema/member/has_arguments.rb +376 -0
  187. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  188. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  189. data/lib/graphql/schema/member/has_directives.rb +113 -0
  190. data/lib/graphql/schema/member/has_fields.rb +163 -0
  191. data/lib/graphql/schema/member/has_interfaces.rb +88 -0
  192. data/lib/graphql/schema/member/has_path.rb +25 -0
  193. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  194. data/lib/graphql/schema/member/has_validators.rb +31 -0
  195. data/lib/graphql/schema/member/relay_shortcuts.rb +73 -0
  196. data/lib/graphql/schema/member/scoped.rb +21 -0
  197. data/lib/graphql/schema/member/type_system_helpers.rb +38 -0
  198. data/lib/graphql/schema/member/validates_input.rb +33 -0
  199. data/lib/graphql/schema/member.rb +39 -0
  200. data/lib/graphql/schema/mutation.rb +85 -0
  201. data/lib/graphql/schema/non_null.rb +67 -0
  202. data/lib/graphql/schema/null_mask.rb +11 -0
  203. data/lib/graphql/schema/object.rb +117 -0
  204. data/lib/graphql/schema/printer.rb +72 -128
  205. data/lib/graphql/schema/relay_classic_mutation.rb +179 -0
  206. data/lib/graphql/schema/resolver/has_payload_type.rb +106 -0
  207. data/lib/graphql/schema/resolver.rb +402 -0
  208. data/lib/graphql/schema/scalar.rb +68 -0
  209. data/lib/graphql/schema/subscription.rb +148 -0
  210. data/lib/graphql/schema/timeout.rb +123 -0
  211. data/lib/graphql/schema/type_expression.rb +29 -5
  212. data/lib/graphql/schema/type_membership.rb +51 -0
  213. data/lib/graphql/schema/union.rb +81 -0
  214. data/lib/graphql/schema/unique_within_type.rb +34 -0
  215. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  216. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  217. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  218. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  219. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  220. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  221. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  222. data/lib/graphql/schema/validator/required_validator.rb +82 -0
  223. data/lib/graphql/schema/validator.rb +171 -0
  224. data/lib/graphql/schema/warden.rb +413 -0
  225. data/lib/graphql/schema/wrapper.rb +24 -0
  226. data/lib/graphql/schema.rb +1179 -104
  227. data/lib/graphql/static_validation/all_rules.rb +14 -0
  228. data/lib/graphql/static_validation/base_visitor.rb +200 -0
  229. data/lib/graphql/static_validation/definition_dependencies.rb +198 -0
  230. data/lib/graphql/static_validation/error.rb +46 -0
  231. data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
  232. data/lib/graphql/static_validation/literal_validator.rb +113 -22
  233. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +59 -11
  234. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +48 -0
  235. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +31 -0
  236. data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
  237. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +62 -8
  238. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +37 -0
  239. data/lib/graphql/static_validation/rules/directives_are_defined.rb +20 -13
  240. data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
  241. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +32 -26
  242. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
  243. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +21 -23
  244. data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
  245. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +55 -18
  246. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
  247. data/lib/graphql/static_validation/rules/fields_will_merge.rb +390 -70
  248. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +53 -0
  249. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +30 -0
  250. data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
  251. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +54 -37
  252. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
  253. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +26 -16
  254. data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
  255. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +13 -19
  256. data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
  257. data/lib/graphql/static_validation/rules/fragments_are_named.rb +16 -0
  258. data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
  259. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +25 -20
  260. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
  261. data/lib/graphql/static_validation/rules/fragments_are_used.rb +22 -33
  262. data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
  263. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  264. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  265. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +17 -0
  266. data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
  267. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +41 -0
  268. data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
  269. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  270. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  271. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +36 -0
  272. data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
  273. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  274. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  275. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +22 -21
  276. data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
  277. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +59 -0
  278. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
  279. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +17 -0
  280. data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
  281. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +56 -0
  282. data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
  283. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +36 -18
  284. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
  285. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +24 -0
  286. data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
  287. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +103 -39
  288. data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
  289. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +22 -14
  290. data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
  291. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +92 -70
  292. data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
  293. data/lib/graphql/static_validation/type_stack.rb +85 -24
  294. data/lib/graphql/static_validation/validation_context.rb +25 -46
  295. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  296. data/lib/graphql/static_validation/validator.rb +46 -15
  297. data/lib/graphql/static_validation.rb +6 -3
  298. data/lib/graphql/string_encoding_error.rb +20 -0
  299. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +247 -0
  300. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  301. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +58 -0
  302. data/lib/graphql/subscriptions/event.rb +144 -0
  303. data/lib/graphql/subscriptions/instrumentation.rb +28 -0
  304. data/lib/graphql/subscriptions/serialize.rb +158 -0
  305. data/lib/graphql/subscriptions.rb +306 -0
  306. data/lib/graphql/tracing/active_support_notifications_tracing.rb +21 -0
  307. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  308. data/lib/graphql/tracing/appsignal_tracing.rb +51 -0
  309. data/lib/graphql/tracing/data_dog_tracing.rb +100 -0
  310. data/lib/graphql/tracing/instrumentation_tracing.rb +83 -0
  311. data/lib/graphql/tracing/new_relic_tracing.rb +51 -0
  312. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  313. data/lib/graphql/tracing/platform_tracing.rb +122 -0
  314. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +32 -0
  315. data/lib/graphql/tracing/prometheus_tracing.rb +67 -0
  316. data/lib/graphql/tracing/scout_tracing.rb +54 -0
  317. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  318. data/lib/graphql/tracing.rb +94 -0
  319. data/lib/graphql/type_kinds.rb +50 -22
  320. data/lib/graphql/types/big_int.rb +23 -0
  321. data/lib/graphql/types/boolean.rb +18 -0
  322. data/lib/graphql/types/float.rb +19 -0
  323. data/lib/graphql/types/id.rb +24 -0
  324. data/lib/graphql/types/int.rb +36 -0
  325. data/lib/graphql/types/iso_8601_date.rb +45 -0
  326. data/lib/graphql/types/iso_8601_date_time.rb +76 -0
  327. data/lib/graphql/types/json.rb +25 -0
  328. data/lib/graphql/types/relay/base_connection.rb +49 -0
  329. data/lib/graphql/types/relay/base_edge.rb +29 -0
  330. data/lib/graphql/types/relay/connection_behaviors.rb +154 -0
  331. data/lib/graphql/types/relay/default_relay.rb +21 -0
  332. data/lib/graphql/types/relay/edge_behaviors.rb +64 -0
  333. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  334. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  335. data/lib/graphql/types/relay/node.rb +15 -0
  336. data/lib/graphql/types/relay/node_behaviors.rb +19 -0
  337. data/lib/graphql/types/relay/page_info.rb +11 -0
  338. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  339. data/lib/graphql/types/relay.rb +39 -0
  340. data/lib/graphql/types/string.rb +29 -0
  341. data/lib/graphql/types.rb +11 -0
  342. data/lib/graphql/unauthorized_error.rb +29 -0
  343. data/lib/graphql/unauthorized_field_error.rb +23 -0
  344. data/lib/graphql/unresolved_type_error.rb +35 -0
  345. data/lib/graphql/version.rb +2 -1
  346. data/lib/graphql.rb +86 -41
  347. data/readme.md +15 -101
  348. metadata +394 -279
  349. data/lib/graphql/analysis/analyze_query.rb +0 -73
  350. data/lib/graphql/analysis/max_query_complexity.rb +0 -25
  351. data/lib/graphql/analysis/max_query_depth.rb +0 -25
  352. data/lib/graphql/analysis/query_complexity.rb +0 -122
  353. data/lib/graphql/analysis/query_depth.rb +0 -54
  354. data/lib/graphql/argument.rb +0 -25
  355. data/lib/graphql/base_type.rb +0 -115
  356. data/lib/graphql/boolean_type.rb +0 -9
  357. data/lib/graphql/define/assign_argument.rb +0 -20
  358. data/lib/graphql/define/assign_enum_value.rb +0 -16
  359. data/lib/graphql/define/assign_object_field.rb +0 -21
  360. data/lib/graphql/define/assignment_dictionary.rb +0 -26
  361. data/lib/graphql/define/defined_object_proxy.rb +0 -32
  362. data/lib/graphql/define/instance_definable.rb +0 -79
  363. data/lib/graphql/define/non_null_with_bang.rb +0 -15
  364. data/lib/graphql/define/type_definer.rb +0 -30
  365. data/lib/graphql/define.rb +0 -8
  366. data/lib/graphql/directive/include_directive.rb +0 -10
  367. data/lib/graphql/directive/skip_directive.rb +0 -11
  368. data/lib/graphql/directive.rb +0 -49
  369. data/lib/graphql/enum_type.rb +0 -95
  370. data/lib/graphql/field.rb +0 -131
  371. data/lib/graphql/float_type.rb +0 -5
  372. data/lib/graphql/id_type.rb +0 -12
  373. data/lib/graphql/input_object_type.rb +0 -71
  374. data/lib/graphql/int_type.rb +0 -5
  375. data/lib/graphql/interface_type.rb +0 -38
  376. data/lib/graphql/internal_representation/node.rb +0 -81
  377. data/lib/graphql/internal_representation/rewrite.rb +0 -177
  378. data/lib/graphql/internal_representation.rb +0 -2
  379. data/lib/graphql/introspection/arguments_field.rb +0 -5
  380. data/lib/graphql/introspection/enum_values_field.rb +0 -13
  381. data/lib/graphql/introspection/fields_field.rb +0 -13
  382. data/lib/graphql/introspection/input_fields_field.rb +0 -12
  383. data/lib/graphql/introspection/interfaces_field.rb +0 -5
  384. data/lib/graphql/introspection/of_type_field.rb +0 -6
  385. data/lib/graphql/introspection/possible_types_field.rb +0 -11
  386. data/lib/graphql/introspection/schema_field.rb +0 -15
  387. data/lib/graphql/introspection/type_by_name_field.rb +0 -16
  388. data/lib/graphql/introspection/typename_field.rb +0 -15
  389. data/lib/graphql/list_type.rb +0 -46
  390. data/lib/graphql/non_null_type.rb +0 -43
  391. data/lib/graphql/object_type.rb +0 -93
  392. data/lib/graphql/query/arguments.rb +0 -76
  393. data/lib/graphql/query/directive_resolution.rb +0 -16
  394. data/lib/graphql/query/executor.rb +0 -45
  395. data/lib/graphql/query/literal_input.rb +0 -90
  396. data/lib/graphql/query/serial_execution/execution_context.rb +0 -31
  397. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -82
  398. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -27
  399. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -42
  400. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -107
  401. data/lib/graphql/query/serial_execution.rb +0 -41
  402. data/lib/graphql/query/type_resolver.rb +0 -25
  403. data/lib/graphql/scalar_type.rb +0 -53
  404. data/lib/graphql/schema/catchall_middleware.rb +0 -34
  405. data/lib/graphql/schema/middleware_chain.rb +0 -28
  406. data/lib/graphql/schema/possible_types.rb +0 -34
  407. data/lib/graphql/schema/reduce_types.rb +0 -68
  408. data/lib/graphql/schema/rescue_middleware.rb +0 -53
  409. data/lib/graphql/schema/timeout_middleware.rb +0 -67
  410. data/lib/graphql/schema/type_map.rb +0 -30
  411. data/lib/graphql/schema/validation.rb +0 -164
  412. data/lib/graphql/static_validation/arguments_validator.rb +0 -48
  413. data/lib/graphql/static_validation/message.rb +0 -36
  414. data/lib/graphql/string_type.rb +0 -5
  415. data/lib/graphql/union_type.rb +0 -38
  416. data/spec/graphql/analysis/analyze_query_spec.rb +0 -50
  417. data/spec/graphql/analysis/max_query_complexity_spec.rb +0 -62
  418. data/spec/graphql/analysis/max_query_depth_spec.rb +0 -100
  419. data/spec/graphql/analysis/query_complexity_spec.rb +0 -235
  420. data/spec/graphql/analysis/query_depth_spec.rb +0 -80
  421. data/spec/graphql/argument_spec.rb +0 -20
  422. data/spec/graphql/base_type_spec.rb +0 -24
  423. data/spec/graphql/boolean_type_spec.rb +0 -20
  424. data/spec/graphql/define/instance_definable_spec.rb +0 -55
  425. data/spec/graphql/directive_spec.rb +0 -77
  426. data/spec/graphql/enum_type_spec.rb +0 -31
  427. data/spec/graphql/execution_error_spec.rb +0 -61
  428. data/spec/graphql/field_spec.rb +0 -92
  429. data/spec/graphql/float_type_spec.rb +0 -15
  430. data/spec/graphql/id_type_spec.rb +0 -32
  431. data/spec/graphql/input_object_type_spec.rb +0 -162
  432. data/spec/graphql/int_type_spec.rb +0 -15
  433. data/spec/graphql/interface_type_spec.rb +0 -56
  434. data/spec/graphql/internal_representation/rewrite_spec.rb +0 -120
  435. data/spec/graphql/introspection/directive_type_spec.rb +0 -50
  436. data/spec/graphql/introspection/input_value_type_spec.rb +0 -42
  437. data/spec/graphql/introspection/introspection_query_spec.rb +0 -10
  438. data/spec/graphql/introspection/schema_type_spec.rb +0 -45
  439. data/spec/graphql/introspection/type_type_spec.rb +0 -122
  440. data/spec/graphql/language/generation_spec.rb +0 -42
  441. data/spec/graphql/language/parser_spec.rb +0 -442
  442. data/spec/graphql/language/visitor_spec.rb +0 -49
  443. data/spec/graphql/list_type_spec.rb +0 -32
  444. data/spec/graphql/non_null_type_spec.rb +0 -31
  445. data/spec/graphql/object_type_spec.rb +0 -42
  446. data/spec/graphql/query/arguments_spec.rb +0 -25
  447. data/spec/graphql/query/context_spec.rb +0 -83
  448. data/spec/graphql/query/executor_spec.rb +0 -273
  449. data/spec/graphql/query/serial_execution/execution_context_spec.rb +0 -53
  450. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +0 -66
  451. data/spec/graphql/query/type_resolver_spec.rb +0 -8
  452. data/spec/graphql/query/variables_spec.rb +0 -28
  453. data/spec/graphql/query_spec.rb +0 -363
  454. data/spec/graphql/scalar_type_spec.rb +0 -61
  455. data/spec/graphql/schema/catchall_middleware_spec.rb +0 -32
  456. data/spec/graphql/schema/middleware_chain_spec.rb +0 -42
  457. data/spec/graphql/schema/printer_spec.rb +0 -190
  458. data/spec/graphql/schema/reduce_types_spec.rb +0 -102
  459. data/spec/graphql/schema/rescue_middleware_spec.rb +0 -33
  460. data/spec/graphql/schema/timeout_middleware_spec.rb +0 -180
  461. data/spec/graphql/schema/type_expression_spec.rb +0 -38
  462. data/spec/graphql/schema/validation_spec.rb +0 -219
  463. data/spec/graphql/schema_spec.rb +0 -23
  464. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +0 -63
  465. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +0 -48
  466. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +0 -34
  467. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +0 -39
  468. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +0 -60
  469. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +0 -31
  470. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +0 -48
  471. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +0 -47
  472. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +0 -39
  473. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +0 -44
  474. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +0 -49
  475. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +0 -25
  476. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +0 -42
  477. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +0 -44
  478. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +0 -63
  479. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +0 -37
  480. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +0 -53
  481. data/spec/graphql/static_validation/type_stack_spec.rb +0 -37
  482. data/spec/graphql/static_validation/validator_spec.rb +0 -69
  483. data/spec/graphql/string_type_spec.rb +0 -15
  484. data/spec/graphql/union_type_spec.rb +0 -31
  485. data/spec/spec_helper.rb +0 -18
  486. data/spec/support/dairy_app.rb +0 -309
  487. data/spec/support/dairy_data.rb +0 -23
  488. data/spec/support/minimum_input_object.rb +0 -16
  489. data/spec/support/star_wars_data.rb +0 -71
  490. data/spec/support/star_wars_schema.rb +0 -76
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+ require "set"
3
+ module GraphQL
4
+ class Subscriptions
5
+ # Serialization helpers for passing subscription data around.
6
+ # @api private
7
+ module Serialize
8
+ GLOBALID_KEY = "__gid__"
9
+ SYMBOL_KEY = "__sym__"
10
+ SYMBOL_KEYS_KEY = "__sym_keys__"
11
+ TIMESTAMP_KEY = "__timestamp__"
12
+ TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%z" # eg '2020-01-01 23:59:59.123456789+05:00'
13
+ OPEN_STRUCT_KEY = "__ostruct__"
14
+
15
+ module_function
16
+
17
+ # @param str [String] A serialized object from {.dump}
18
+ # @return [Object] An object equivalent to the one passed to {.dump}
19
+ def load(str)
20
+ parsed_obj = JSON.parse(str)
21
+ load_value(parsed_obj)
22
+ end
23
+
24
+ # @param obj [Object] Some subscription-related data to dump
25
+ # @return [String] The stringified object
26
+ def dump(obj)
27
+ JSON.generate(dump_value(obj), quirks_mode: true)
28
+ end
29
+
30
+ # This is for turning objects into subscription scopes.
31
+ # It's a one-way transformation, can't reload this :'(
32
+ # @param obj [Object]
33
+ # @return [String]
34
+ def dump_recursive(obj)
35
+ case
36
+ when obj.is_a?(Array)
37
+ obj.map { |i| dump_recursive(i) }.join(':')
38
+ when obj.is_a?(Hash)
39
+ obj.map { |k, v| "#{dump_recursive(k)}:#{dump_recursive(v)}" }.join(":")
40
+ when obj.is_a?(GraphQL::Schema::InputObject)
41
+ dump_recursive(obj.to_h)
42
+ when obj.respond_to?(:to_gid_param)
43
+ obj.to_gid_param
44
+ when obj.respond_to?(:to_param)
45
+ obj.to_param
46
+ else
47
+ obj.to_s
48
+ end
49
+ end
50
+
51
+ class << self
52
+ private
53
+
54
+ # @param value [Object] A parsed JSON object
55
+ # @return [Object] An object that load Global::Identification recursive
56
+ def load_value(value)
57
+ if value.is_a?(Array)
58
+ is_gids = (v1 = value[0]).is_a?(Hash) && v1.size == 1 && v1[GLOBALID_KEY]
59
+ if is_gids
60
+ # Assume it's an array of global IDs
61
+ ids = value.map { |v| v[GLOBALID_KEY] }
62
+ GlobalID::Locator.locate_many(ids)
63
+ else
64
+ value.map { |item| load_value(item) }
65
+ end
66
+ elsif value.is_a?(Hash)
67
+ if value.size == 1
68
+ case value.keys.first # there's only 1 key
69
+ when GLOBALID_KEY
70
+ GlobalID::Locator.locate(value[GLOBALID_KEY])
71
+ when SYMBOL_KEY
72
+ value[SYMBOL_KEY].to_sym
73
+ when TIMESTAMP_KEY
74
+ timestamp_class_name, *timestamp_args = value[TIMESTAMP_KEY]
75
+ timestamp_class = Object.const_get(timestamp_class_name)
76
+ if defined?(ActiveSupport::TimeWithZone) && timestamp_class <= ActiveSupport::TimeWithZone
77
+ zone_name, timestamp_s = timestamp_args
78
+ zone = ActiveSupport::TimeZone[zone_name]
79
+ raise "Zone #{zone_name} not found, unable to deserialize" unless zone
80
+ zone.strptime(timestamp_s, TIMESTAMP_FORMAT)
81
+ else
82
+ timestamp_s = timestamp_args.first
83
+ timestamp_class.strptime(timestamp_s, TIMESTAMP_FORMAT)
84
+ end
85
+ when OPEN_STRUCT_KEY
86
+ ostruct_values = load_value(value[OPEN_STRUCT_KEY])
87
+ OpenStruct.new(ostruct_values)
88
+ else
89
+ key = value.keys.first
90
+ { key => load_value(value[key]) }
91
+ end
92
+ else
93
+ loaded_h = {}
94
+ sym_keys = value.fetch(SYMBOL_KEYS_KEY, [])
95
+ value.each do |k, v|
96
+ if k == SYMBOL_KEYS_KEY
97
+ next
98
+ end
99
+ if sym_keys.include?(k)
100
+ k = k.to_sym
101
+ end
102
+ loaded_h[k] = load_value(v)
103
+ end
104
+ loaded_h
105
+ end
106
+ else
107
+ value
108
+ end
109
+ end
110
+
111
+ # @param obj [Object] Some subscription-related data to dump
112
+ # @return [Object] The object that converted Global::Identification
113
+ def dump_value(obj)
114
+ if obj.is_a?(Array)
115
+ obj.map{|item| dump_value(item)}
116
+ elsif obj.is_a?(Hash)
117
+ symbol_keys = nil
118
+ dumped_h = {}
119
+ obj.each do |k, v|
120
+ dumped_h[k.to_s] = dump_value(v)
121
+ if k.is_a?(Symbol)
122
+ symbol_keys ||= Set.new
123
+ symbol_keys << k.to_s
124
+ end
125
+ end
126
+ if symbol_keys
127
+ dumped_h[SYMBOL_KEYS_KEY] = symbol_keys.to_a
128
+ end
129
+ dumped_h
130
+ elsif obj.is_a?(Symbol)
131
+ { SYMBOL_KEY => obj.to_s }
132
+ elsif obj.respond_to?(:to_gid_param)
133
+ {GLOBALID_KEY => obj.to_gid_param}
134
+ elsif defined?(ActiveSupport::TimeWithZone) && obj.is_a?(ActiveSupport::TimeWithZone) && obj.class.name != Time.name
135
+ # This handles a case where Rails prior to 7 would
136
+ # make the class ActiveSupport::TimeWithZone return "Time" for
137
+ # its name. In Rails 7, it will now return "ActiveSupport::TimeWithZone",
138
+ # which happens to be incompatible with expectations we have
139
+ # with what a Time class supports ( notably, strptime in `load_value` ).
140
+ #
141
+ # This now passes along the name of the zone, such that a future deserialization
142
+ # of this string will use the correct time zone from the ActiveSupport TimeZone
143
+ # list to produce the time.
144
+ #
145
+ { TIMESTAMP_KEY => [obj.class.name, obj.time_zone.name, obj.strftime(TIMESTAMP_FORMAT)] }
146
+ elsif obj.is_a?(Date) || obj.is_a?(Time)
147
+ # DateTime extends Date; for TimeWithZone, call `.utc` first.
148
+ { TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
149
+ elsif obj.is_a?(OpenStruct)
150
+ { OPEN_STRUCT_KEY => dump_value(obj.to_h) }
151
+ else
152
+ obj
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,306 @@
1
+ # frozen_string_literal: true
2
+ require "securerandom"
3
+ require "graphql/subscriptions/broadcast_analyzer"
4
+ require "graphql/subscriptions/event"
5
+ require "graphql/subscriptions/instrumentation"
6
+ require "graphql/subscriptions/serialize"
7
+ require "graphql/subscriptions/action_cable_subscriptions"
8
+ require "graphql/subscriptions/default_subscription_resolve_extension"
9
+
10
+ module GraphQL
11
+ class Subscriptions
12
+ # Raised when either:
13
+ # - the triggered `event_name` doesn't match a field in the schema; or
14
+ # - one or more arguments don't match the field arguments
15
+ class InvalidTriggerError < GraphQL::Error
16
+ end
17
+
18
+ # Raised when either:
19
+ # - An initial subscription didn't have a value for `context[subscription_scope]`
20
+ # - Or, an update didn't pass `.trigger(..., scope:)`
21
+ # When raised, the initial subscription or update fails completely.
22
+ class SubscriptionScopeMissingError < GraphQL::Error
23
+ end
24
+
25
+ # @see {Subscriptions#initialize} for options, concrete implementations may add options.
26
+ def self.use(defn, options = {})
27
+ schema = defn.is_a?(Class) ? defn : defn.target
28
+
29
+ if schema.subscriptions(inherited: false)
30
+ raise ArgumentError, "Can't reinstall subscriptions. #{schema} is using #{schema.subscriptions}, can't also add #{self}"
31
+ end
32
+
33
+ instrumentation = Subscriptions::Instrumentation.new(schema: schema)
34
+ defn.instrument(:query, instrumentation)
35
+ options[:schema] = schema
36
+ schema.subscriptions = self.new(**options)
37
+ schema.add_subscription_extension_if_necessary
38
+ nil
39
+ end
40
+
41
+ # @param schema [Class] the GraphQL schema this manager belongs to
42
+ # @param validate_update [Boolean] If false, then validation is skipped when executing updates
43
+ def initialize(schema:, validate_update: true, broadcast: false, default_broadcastable: false, **rest)
44
+ if broadcast
45
+ schema.query_analyzer(Subscriptions::BroadcastAnalyzer)
46
+ end
47
+ @default_broadcastable = default_broadcastable
48
+ @schema = schema
49
+ @validate_update = validate_update
50
+ end
51
+
52
+ # @return [Boolean] Used when fields don't have `broadcastable:` explicitly set
53
+ attr_reader :default_broadcastable
54
+
55
+ # Fetch subscriptions matching this field + arguments pair
56
+ # And pass them off to the queue.
57
+ # @param event_name [String]
58
+ # @param args [Hash<String, Symbol => Object]
59
+ # @param object [Object]
60
+ # @param scope [Symbol, String]
61
+ # @param context [Hash]
62
+ # @return [void]
63
+ def trigger(event_name, args, object, scope: nil, context: {})
64
+ # Make something as context-like as possible, even though there isn't a current query:
65
+ dummy_query = GraphQL::Query.new(@schema, "", validate: false, context: context)
66
+ context = dummy_query.context
67
+ event_name = event_name.to_s
68
+
69
+ # Try with the verbatim input first:
70
+ field = @schema.get_field(@schema.subscription, event_name, context)
71
+
72
+ if field.nil?
73
+ # And if it wasn't found, normalize it:
74
+ normalized_event_name = normalize_name(event_name)
75
+ field = @schema.get_field(@schema.subscription, normalized_event_name, context)
76
+ if field.nil?
77
+ raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
78
+ end
79
+ else
80
+ # Since we found a field, the original input was already normalized
81
+ normalized_event_name = event_name
82
+ end
83
+
84
+ # Normalize symbol-keyed args to strings, try camelizing them
85
+ # Should this accept a real context somehow?
86
+ normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext)
87
+
88
+ event = Subscriptions::Event.new(
89
+ name: normalized_event_name,
90
+ arguments: normalized_args,
91
+ field: field,
92
+ scope: scope,
93
+ )
94
+ execute_all(event, object)
95
+ end
96
+
97
+ # `event` was triggered on `object`, and `subscription_id` was subscribed,
98
+ # so it should be updated.
99
+ #
100
+ # Load `subscription_id`'s GraphQL data, re-evaluate the query and return the result.
101
+ #
102
+ # @param subscription_id [String]
103
+ # @param event [GraphQL::Subscriptions::Event] The event which was triggered
104
+ # @param object [Object] The value for the subscription field
105
+ # @return [GraphQL::Query::Result]
106
+ def execute_update(subscription_id, event, object)
107
+ # Lookup the saved data for this subscription
108
+ query_data = read_subscription(subscription_id)
109
+ if query_data.nil?
110
+ delete_subscription(subscription_id)
111
+ return nil
112
+ end
113
+
114
+ # Fetch the required keys from the saved data
115
+ query_string = query_data.fetch(:query_string)
116
+ variables = query_data.fetch(:variables)
117
+ context = query_data.fetch(:context)
118
+ operation_name = query_data.fetch(:operation_name)
119
+ execute_options = {
120
+ query: query_string,
121
+ context: context,
122
+ subscription_topic: event.topic,
123
+ operation_name: operation_name,
124
+ variables: variables,
125
+ root_value: object,
126
+ }
127
+ execute_options[:validate] = validate_update?(**execute_options)
128
+ result = @schema.execute(**execute_options)
129
+ subscriptions_context = result.context.namespace(:subscriptions)
130
+ if subscriptions_context[:no_update]
131
+ result = nil
132
+ end
133
+
134
+ unsubscribed = subscriptions_context[:unsubscribed]
135
+
136
+ if unsubscribed
137
+ # `unsubscribe` was called, clean up on our side
138
+ # TODO also send `{more: false}` to client?
139
+ delete_subscription(subscription_id)
140
+ result = nil
141
+ end
142
+
143
+ result
144
+ end
145
+
146
+ # Define this method to customize whether to validate
147
+ # this subscription when executing an update.
148
+ #
149
+ # @return [Boolean] defaults to `true`, or false if `validate: false` is provided.
150
+ def validate_update?(query:, context:, root_value:, subscription_topic:, operation_name:, variables:)
151
+ @validate_update
152
+ end
153
+
154
+ # Run the update query for this subscription and deliver it
155
+ # @see {#execute_update}
156
+ # @see {#deliver}
157
+ # @return [void]
158
+ def execute(subscription_id, event, object)
159
+ res = execute_update(subscription_id, event, object)
160
+ if !res.nil?
161
+ deliver(subscription_id, res)
162
+ end
163
+ end
164
+
165
+ # Event `event` occurred on `object`,
166
+ # Update all subscribers.
167
+ # @param event [Subscriptions::Event]
168
+ # @param object [Object]
169
+ # @return [void]
170
+ def execute_all(event, object)
171
+ raise GraphQL::RequiredImplementationMissingError
172
+ end
173
+
174
+ # The system wants to send an update to this subscription.
175
+ # Read its data and return it.
176
+ # @param subscription_id [String]
177
+ # @return [Hash] Containing required keys
178
+ def read_subscription(subscription_id)
179
+ raise GraphQL::RequiredImplementationMissingError
180
+ end
181
+
182
+ # A subscription query was re-evaluated, returning `result`.
183
+ # The result should be send to `subscription_id`.
184
+ # @param subscription_id [String]
185
+ # @param result [Hash]
186
+ # @return [void]
187
+ def deliver(subscription_id, result)
188
+ raise GraphQL::RequiredImplementationMissingError
189
+ end
190
+
191
+ # `query` was executed and found subscriptions to `events`.
192
+ # Update the database to reflect this new state.
193
+ # @param query [GraphQL::Query]
194
+ # @param events [Array<GraphQL::Subscriptions::Event>]
195
+ # @return [void]
196
+ def write_subscription(query, events)
197
+ raise GraphQL::RequiredImplementationMissingError
198
+ end
199
+
200
+ # A subscription was terminated server-side.
201
+ # Clean up the database.
202
+ # @param subscription_id [String]
203
+ # @return void.
204
+ def delete_subscription(subscription_id)
205
+ raise GraphQL::RequiredImplementationMissingError
206
+ end
207
+
208
+ # @return [String] A new unique identifier for a subscription
209
+ def build_id
210
+ SecureRandom.uuid
211
+ end
212
+
213
+ # Convert a user-provided event name or argument
214
+ # to the equivalent in GraphQL.
215
+ #
216
+ # By default, it converts the identifier to camelcase.
217
+ # Override this in a subclass to change the transformation.
218
+ #
219
+ # @param event_or_arg_name [String, Symbol]
220
+ # @return [String]
221
+ def normalize_name(event_or_arg_name)
222
+ Schema::Member::BuildType.camelize(event_or_arg_name.to_s)
223
+ end
224
+
225
+ # @return [Boolean] if true, then a query like this one would be broadcasted
226
+ def broadcastable?(query_str, **query_options)
227
+ query = GraphQL::Query.new(@schema, query_str, **query_options)
228
+ if !query.valid?
229
+ raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
230
+ end
231
+ GraphQL::Analysis::AST.analyze_query(query, @schema.query_analyzers)
232
+ query.context.namespace(:subscriptions)[:subscription_broadcastable]
233
+ end
234
+
235
+ private
236
+
237
+ # Recursively normalize `args` as belonging to `arg_owner`:
238
+ # - convert symbols to strings,
239
+ # - if needed, camelize the string (using {#normalize_name})
240
+ # @param arg_owner [GraphQL::Field, GraphQL::BaseType]
241
+ # @param args [Hash, Array, Any] some GraphQL input value to coerce as `arg_owner`
242
+ # @return [Any] normalized arguments value
243
+ def normalize_arguments(event_name, arg_owner, args, context)
244
+ case arg_owner
245
+ when GraphQL::Schema::Field, Class
246
+ if arg_owner.is_a?(Class) && !arg_owner.kind.input_object?
247
+ # it's a type, but not an input object
248
+ return args
249
+ end
250
+ normalized_args = {}
251
+ missing_arg_names = []
252
+ args.each do |k, v|
253
+ arg_name = k.to_s
254
+ arg_defn = arg_owner.get_argument(arg_name, context)
255
+ if arg_defn
256
+ normalized_arg_name = arg_name
257
+ else
258
+ normalized_arg_name = normalize_name(arg_name)
259
+ arg_defn = arg_owner.get_argument(normalized_arg_name, context)
260
+ end
261
+
262
+ if arg_defn
263
+ if arg_defn.loads
264
+ normalized_arg_name = arg_defn.keyword.to_s
265
+ end
266
+ normalized = normalize_arguments(event_name, arg_defn.type, v, context)
267
+ normalized_args[normalized_arg_name] = normalized
268
+ else
269
+ # Couldn't find a matching argument definition
270
+ missing_arg_names << arg_name
271
+ end
272
+ end
273
+
274
+ # Backfill default values so that trigger arguments
275
+ # match query arguments.
276
+ arg_owner.arguments(context).each do |_name, arg_defn|
277
+ if arg_defn.default_value? && !normalized_args.key?(arg_defn.name)
278
+ default_value = arg_defn.default_value
279
+ # We don't have an underlying "object" here, so it can't call methods.
280
+ # This is broken.
281
+ normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context: context)
282
+ end
283
+ end
284
+
285
+ if missing_arg_names.any?
286
+ arg_owner_name = if arg_owner.is_a?(GraphQL::Schema::Field)
287
+ arg_owner.path
288
+ elsif arg_owner.is_a?(Class)
289
+ arg_owner.graphql_name
290
+ else
291
+ arg_owner.to_s
292
+ end
293
+ raise InvalidTriggerError, "Can't trigger Subscription.#{event_name}, received undefined arguments: #{missing_arg_names.join(", ")}. (Should match arguments of #{arg_owner_name}.)"
294
+ end
295
+
296
+ normalized_args
297
+ when GraphQL::Schema::List
298
+ args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
299
+ when GraphQL::Schema::NonNull
300
+ normalize_arguments(event_name, arg_owner.of_type, args, context)
301
+ else
302
+ args
303
+ end
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphql/tracing/notifications_tracing'
4
+
5
+ module GraphQL
6
+ module Tracing
7
+ # This implementation forwards events to ActiveSupport::Notifications
8
+ # with a `graphql` suffix.
9
+ #
10
+ # @see KEYS for event names
11
+ module ActiveSupportNotificationsTracing
12
+ # A cache of frequently-used keys to avoid needless string allocations
13
+ KEYS = NotificationsTracing::KEYS
14
+ NOTIFICATIONS_ENGINE = NotificationsTracing.new(ActiveSupport::Notifications) if defined?(ActiveSupport::Notifications)
15
+
16
+ def self.trace(key, metadata, &blk)
17
+ NOTIFICATIONS_ENGINE.trace(key, metadata, &blk)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Tracing
5
+
6
+ # This class uses the AppopticsAPM SDK from the appoptics_apm gem to create
7
+ # traces for GraphQL.
8
+ #
9
+ # There are 4 configurations available. They can be set in the
10
+ # appoptics_apm config file or in code. Please see:
11
+ # {https://docs.appoptics.com/kb/apm_tracing/ruby/configure}
12
+ #
13
+ # AppOpticsAPM::Config[:graphql][:enabled] = true|false
14
+ # AppOpticsAPM::Config[:graphql][:transaction_name] = true|false
15
+ # AppOpticsAPM::Config[:graphql][:sanitize_query] = true|false
16
+ # AppOpticsAPM::Config[:graphql][:remove_comments] = true|false
17
+ class AppOpticsTracing < GraphQL::Tracing::PlatformTracing
18
+ # These GraphQL events will show up as 'graphql.prep' spans
19
+ PREP_KEYS = ['lex', 'parse', 'validate', 'analyze_query', 'analyze_multiplex'].freeze
20
+ # These GraphQL events will show up as 'graphql.execute' spans
21
+ EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
22
+
23
+ # During auto-instrumentation this version of AppOpticsTracing is compared
24
+ # with the version provided in the appoptics_apm gem, so that the newer
25
+ # version of the class can be used
26
+
27
+ def self.version
28
+ Gem::Version.new('1.0.0')
29
+ end
30
+
31
+ self.platform_keys = {
32
+ 'lex' => 'lex',
33
+ 'parse' => 'parse',
34
+ 'validate' => 'validate',
35
+ 'analyze_query' => 'analyze_query',
36
+ 'analyze_multiplex' => 'analyze_multiplex',
37
+ 'execute_multiplex' => 'execute_multiplex',
38
+ 'execute_query' => 'execute_query',
39
+ 'execute_query_lazy' => 'execute_query_lazy'
40
+ }
41
+
42
+ def platform_trace(platform_key, _key, data)
43
+ return yield if !defined?(AppOpticsAPM) || gql_config[:enabled] == false
44
+
45
+ layer = span_name(platform_key)
46
+ kvs = metadata(data, layer)
47
+ kvs[:Key] = platform_key if (PREP_KEYS + EXEC_KEYS).include?(platform_key)
48
+
49
+ transaction_name(kvs[:InboundQuery]) if kvs[:InboundQuery] && layer == 'graphql.execute'
50
+
51
+ ::AppOpticsAPM::SDK.trace(layer, kvs) do
52
+ kvs.clear # we don't have to send them twice
53
+ yield
54
+ end
55
+ end
56
+
57
+ def platform_field_key(type, field)
58
+ "graphql.#{type.graphql_name}.#{field.graphql_name}"
59
+ end
60
+
61
+ def platform_authorized_key(type)
62
+ "graphql.authorized.#{type.graphql_name}"
63
+ end
64
+
65
+ def platform_resolve_type_key(type)
66
+ "graphql.resolve_type.#{type.graphql_name}"
67
+ end
68
+
69
+ private
70
+
71
+ def gql_config
72
+ ::AppOpticsAPM::Config[:graphql] ||= {}
73
+ end
74
+
75
+ def transaction_name(query)
76
+ return if gql_config[:transaction_name] == false ||
77
+ ::AppOpticsAPM::SDK.get_transaction_name
78
+
79
+ split_query = query.strip.split(/\W+/, 3)
80
+ split_query[0] = 'query' if split_query[0].empty?
81
+ name = "graphql.#{split_query[0..1].join('.')}"
82
+
83
+ ::AppOpticsAPM::SDK.set_transaction_name(name)
84
+ end
85
+
86
+ def multiplex_transaction_name(names)
87
+ return if gql_config[:transaction_name] == false ||
88
+ ::AppOpticsAPM::SDK.get_transaction_name
89
+
90
+ name = "graphql.multiplex.#{names.join('.')}"
91
+ name = "#{name[0..251]}..." if name.length > 254
92
+
93
+ ::AppOpticsAPM::SDK.set_transaction_name(name)
94
+ end
95
+
96
+ def span_name(key)
97
+ return 'graphql.prep' if PREP_KEYS.include?(key)
98
+ return 'graphql.execute' if EXEC_KEYS.include?(key)
99
+
100
+ key[/^graphql\./] ? key : "graphql.#{key}"
101
+ end
102
+
103
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
104
+ def metadata(data, layer)
105
+ data.keys.map do |key|
106
+ case key
107
+ when :context
108
+ graphql_context(data[key], layer)
109
+ when :query
110
+ graphql_query(data[key])
111
+ when :query_string
112
+ graphql_query_string(data[key])
113
+ when :multiplex
114
+ graphql_multiplex(data[key])
115
+ when :path
116
+ [key, data[key].join(".")]
117
+ else
118
+ [key, data[key]]
119
+ end
120
+ end.flatten(2).each_slice(2).to_h.merge(Spec: 'graphql')
121
+ end
122
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
123
+
124
+ def graphql_context(context, layer)
125
+ context.errors && context.errors.each do |err|
126
+ AppOpticsAPM::API.log_exception(layer, err)
127
+ end
128
+
129
+ [[:Path, context.path.join('.')]]
130
+ end
131
+
132
+ def graphql_query(query)
133
+ return [] unless query
134
+
135
+ query_string = query.query_string
136
+ query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
137
+ query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
138
+
139
+ [[:InboundQuery, query_string],
140
+ [:Operation, query.selected_operation_name]]
141
+ end
142
+
143
+ def graphql_query_string(query_string)
144
+ query_string = remove_comments(query_string) if gql_config[:remove_comments] != false
145
+ query_string = sanitize(query_string) if gql_config[:sanitize_query] != false
146
+
147
+ [:InboundQuery, query_string]
148
+ end
149
+
150
+ def graphql_multiplex(data)
151
+ names = data.queries.map(&:operations).map(&:keys).flatten.compact
152
+ multiplex_transaction_name(names) if names.size > 1
153
+
154
+ [:Operations, names.join(', ')]
155
+ end
156
+
157
+ def sanitize(query)
158
+ return unless query
159
+
160
+ # remove arguments
161
+ query.gsub(/"[^"]*"/, '"?"') # strings
162
+ .gsub(/-?[0-9]*\.?[0-9]+e?[0-9]*/, '?') # ints + floats
163
+ .gsub(/\[[^\]]*\]/, '[?]') # arrays
164
+ end
165
+
166
+ def remove_comments(query)
167
+ return unless query
168
+
169
+ query.gsub(/#[^\n\r]*/, '')
170
+ end
171
+ end
172
+ end
173
+ end