graphql 1.8.7 → 1.9.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 (368) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +2 -1
  3. data/lib/generators/graphql/scalar_generator.rb +20 -0
  4. data/lib/generators/graphql/templates/base_scalar.erb +4 -0
  5. data/lib/generators/graphql/templates/scalar.erb +13 -0
  6. data/lib/graphql/analysis/ast/analyzer.rb +62 -0
  7. data/lib/graphql/analysis/ast/field_usage.rb +28 -0
  8. data/lib/graphql/analysis/ast/max_query_complexity.rb +23 -0
  9. data/lib/graphql/analysis/ast/max_query_depth.rb +18 -0
  10. data/lib/graphql/analysis/ast/query_complexity.rb +114 -0
  11. data/lib/graphql/analysis/ast/query_depth.rb +66 -0
  12. data/lib/graphql/analysis/ast/visitor.rb +255 -0
  13. data/lib/graphql/analysis/ast.rb +82 -0
  14. data/lib/graphql/analysis.rb +1 -0
  15. data/lib/graphql/argument.rb +5 -0
  16. data/lib/graphql/authorization.rb +1 -0
  17. data/lib/graphql/backwards_compatibility.rb +1 -1
  18. data/lib/graphql/base_type.rb +1 -1
  19. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +1 -2
  20. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -6
  21. data/lib/graphql/dig.rb +19 -0
  22. data/lib/graphql/directive/include_directive.rb +1 -7
  23. data/lib/graphql/directive/skip_directive.rb +1 -8
  24. data/lib/graphql/directive.rb +13 -1
  25. data/lib/graphql/enum_type.rb +8 -0
  26. data/lib/graphql/execution/execute.rb +36 -17
  27. data/lib/graphql/execution/instrumentation.rb +2 -0
  28. data/lib/graphql/execution/interpreter/execution_errors.rb +29 -0
  29. data/lib/graphql/execution/interpreter/hash_response.rb +46 -0
  30. data/lib/graphql/execution/interpreter/resolve.rb +58 -0
  31. data/lib/graphql/execution/interpreter/runtime.rb +597 -0
  32. data/lib/graphql/execution/interpreter.rb +99 -0
  33. data/lib/graphql/execution/lazy.rb +8 -1
  34. data/lib/graphql/execution/lookahead.rb +351 -0
  35. data/lib/graphql/execution/multiplex.rb +37 -29
  36. data/lib/graphql/execution.rb +2 -0
  37. data/lib/graphql/execution_error.rb +1 -1
  38. data/lib/graphql/field.rb +1 -7
  39. data/lib/graphql/integer_encoding_error.rb +12 -0
  40. data/lib/graphql/internal_representation/rewrite.rb +127 -142
  41. data/lib/graphql/introspection/dynamic_fields.rb +8 -2
  42. data/lib/graphql/introspection/entry_points.rb +11 -6
  43. data/lib/graphql/introspection/enum_value_type.rb +4 -0
  44. data/lib/graphql/introspection/schema_type.rb +7 -2
  45. data/lib/graphql/introspection/type_type.rb +9 -5
  46. data/lib/graphql/invalid_null_error.rb +1 -1
  47. data/lib/graphql/language/block_string.rb +37 -0
  48. data/lib/graphql/language/document_from_schema_definition.rb +10 -7
  49. data/lib/graphql/language/lexer.rb +55 -36
  50. data/lib/graphql/language/lexer.rl +8 -3
  51. data/lib/graphql/language/nodes.rb +440 -362
  52. data/lib/graphql/language/parser.rb +56 -56
  53. data/lib/graphql/language/parser.y +12 -12
  54. data/lib/graphql/language/printer.rb +2 -2
  55. data/lib/graphql/language/visitor.rb +158 -15
  56. data/lib/graphql/language.rb +0 -1
  57. data/lib/graphql/literal_validation_error.rb +6 -0
  58. data/lib/graphql/query/arguments.rb +3 -2
  59. data/lib/graphql/query/arguments_cache.rb +1 -1
  60. data/lib/graphql/query/context.rb +14 -5
  61. data/lib/graphql/query/executor.rb +1 -1
  62. data/lib/graphql/query/result.rb +1 -1
  63. data/lib/graphql/query/validation_pipeline.rb +35 -11
  64. data/lib/graphql/query/variable_validation_error.rb +10 -1
  65. data/lib/graphql/query.rb +16 -2
  66. data/lib/graphql/relay/base_connection.rb +2 -0
  67. data/lib/graphql/relay/connection_instrumentation.rb +3 -1
  68. data/lib/graphql/relay/connection_resolve.rb +1 -1
  69. data/lib/graphql/relay/node.rb +2 -28
  70. data/lib/graphql/relay/relation_connection.rb +1 -1
  71. data/lib/graphql/schema/argument.rb +13 -5
  72. data/lib/graphql/schema/base_64_encoder.rb +4 -4
  73. data/lib/graphql/schema/build_from_definition.rb +2 -4
  74. data/lib/graphql/schema/default_type_error.rb +1 -1
  75. data/lib/graphql/schema/directive/feature.rb +66 -0
  76. data/lib/graphql/schema/directive/include.rb +25 -0
  77. data/lib/graphql/schema/directive/skip.rb +25 -0
  78. data/lib/graphql/schema/directive/transform.rb +48 -0
  79. data/lib/graphql/schema/directive.rb +103 -0
  80. data/lib/graphql/schema/enum_value.rb +3 -2
  81. data/lib/graphql/schema/field/connection_extension.rb +50 -0
  82. data/lib/graphql/schema/field/scope_extension.rb +18 -0
  83. data/lib/graphql/schema/field.rb +273 -64
  84. data/lib/graphql/schema/field_extension.rb +69 -0
  85. data/lib/graphql/schema/input_object.rb +16 -8
  86. data/lib/graphql/schema/interface.rb +1 -0
  87. data/lib/graphql/schema/loader.rb +22 -16
  88. data/lib/graphql/schema/member/base_dsl_methods.rb +8 -2
  89. data/lib/graphql/schema/member/build_type.rb +33 -1
  90. data/lib/graphql/schema/member/has_arguments.rb +6 -2
  91. data/lib/graphql/schema/member/has_fields.rb +18 -70
  92. data/lib/graphql/schema/member/has_path.rb +25 -0
  93. data/lib/graphql/schema/member/instrumentation.rb +10 -7
  94. data/lib/graphql/schema/member.rb +2 -0
  95. data/lib/graphql/schema/mutation.rb +6 -48
  96. data/lib/graphql/schema/non_null.rb +5 -1
  97. data/lib/graphql/schema/object.rb +18 -4
  98. data/lib/graphql/schema/printer.rb +1 -1
  99. data/lib/graphql/schema/relay_classic_mutation.rb +42 -9
  100. data/lib/graphql/schema/resolver/has_payload_type.rb +65 -0
  101. data/lib/graphql/schema/resolver.rb +45 -20
  102. data/lib/graphql/schema/subscription.rb +97 -0
  103. data/lib/graphql/schema/traversal.rb +11 -7
  104. data/lib/graphql/schema.rb +186 -38
  105. data/lib/graphql/static_validation/all_rules.rb +3 -2
  106. data/lib/graphql/static_validation/base_visitor.rb +199 -0
  107. data/lib/graphql/static_validation/default_visitor.rb +15 -0
  108. data/lib/graphql/static_validation/definition_dependencies.rb +62 -68
  109. data/lib/graphql/static_validation/{message.rb → error.rb} +11 -11
  110. data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
  111. data/lib/graphql/static_validation/literal_validator.rb +54 -11
  112. data/lib/graphql/static_validation/no_validate_visitor.rb +10 -0
  113. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +87 -16
  114. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +31 -0
  115. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +11 -11
  116. data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
  117. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +52 -8
  118. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +35 -0
  119. data/lib/graphql/static_validation/rules/directives_are_defined.rb +12 -15
  120. data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
  121. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +19 -14
  122. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
  123. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +17 -19
  124. data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
  125. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +30 -14
  126. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
  127. data/lib/graphql/static_validation/rules/fields_will_merge.rb +356 -29
  128. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +32 -0
  129. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +20 -13
  130. data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
  131. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +37 -29
  132. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
  133. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +25 -17
  134. data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
  135. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +12 -10
  136. data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
  137. data/lib/graphql/static_validation/rules/fragments_are_named.rb +7 -11
  138. data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
  139. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +16 -16
  140. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
  141. data/lib/graphql/static_validation/rules/fragments_are_used.rb +21 -14
  142. data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
  143. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +10 -14
  144. data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
  145. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +30 -30
  146. data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
  147. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +25 -17
  148. data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
  149. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +17 -18
  150. data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
  151. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +47 -0
  152. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
  153. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +10 -14
  154. data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
  155. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +28 -17
  156. data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
  157. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +35 -27
  158. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
  159. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +15 -14
  160. data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
  161. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +41 -30
  162. data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
  163. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +18 -14
  164. data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
  165. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +73 -65
  166. data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
  167. data/lib/graphql/static_validation/validation_context.rb +8 -51
  168. data/lib/graphql/static_validation/validator.rb +23 -15
  169. data/lib/graphql/static_validation.rb +5 -3
  170. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -2
  171. data/lib/graphql/subscriptions/event.rb +28 -5
  172. data/lib/graphql/subscriptions/subscription_root.rb +66 -0
  173. data/lib/graphql/subscriptions.rb +16 -2
  174. data/lib/graphql/tracing/active_support_notifications_tracing.rb +0 -1
  175. data/lib/graphql/tracing/appsignal_tracing.rb +1 -1
  176. data/lib/graphql/tracing/data_dog_tracing.rb +1 -1
  177. data/lib/graphql/tracing/new_relic_tracing.rb +3 -3
  178. data/lib/graphql/tracing/platform_tracing.rb +17 -1
  179. data/lib/graphql/tracing/prometheus_tracing.rb +1 -1
  180. data/lib/graphql/tracing/scout_tracing.rb +1 -1
  181. data/lib/graphql/tracing/skylight_tracing.rb +3 -3
  182. data/lib/graphql/tracing.rb +8 -8
  183. data/lib/graphql/types/float.rb +1 -1
  184. data/lib/graphql/types/int.rb +11 -2
  185. data/lib/graphql/types/iso_8601_date_time.rb +15 -1
  186. data/lib/graphql/types/relay/base_connection.rb +15 -1
  187. data/lib/graphql/types/relay/node.rb +0 -1
  188. data/lib/graphql/types/relay/node_field.rb +43 -0
  189. data/lib/graphql/types/relay/nodes_field.rb +45 -0
  190. data/lib/graphql/types/relay.rb +2 -0
  191. data/lib/graphql/unauthorized_error.rb +4 -0
  192. data/lib/graphql/unauthorized_field_error.rb +23 -0
  193. data/lib/graphql/upgrader/member.rb +5 -0
  194. data/lib/graphql/version.rb +1 -1
  195. data/lib/graphql.rb +6 -1
  196. data/readme.md +7 -7
  197. data/spec/dummy/Gemfile +1 -1
  198. data/spec/dummy/Gemfile.lock +157 -0
  199. data/spec/dummy/app/channels/graphql_channel.rb +22 -11
  200. data/spec/dummy/config/locales/en.yml +1 -1
  201. data/spec/dummy/log/test.log +199 -0
  202. data/spec/dummy/test/test_helper.rb +1 -0
  203. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4w/4wzXRZrAkwKdgYaSE0pid5eB-fer8vSfSku_NPg4rMA.cache +0 -0
  204. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/7I/7IHVBiJT06QSpgLpLoJIxboQ0B-D_tMTxsvoezBTV3Q.cache +1 -0
  205. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8w/8wY_SKagj8wHuwGNAAf6JnQ8joMbC6cEYpHrTAI8Urc.cache +1 -0
  206. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/AK/AKzz1u6bGb4auXcrObA_g5LL-oV0ejNGa448AgAi_WQ.cache +1 -0
  207. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ET/ETW4uxvaYpruL8y6_ZptUH82ZowMaHIqvg5WexBFdEM.cache +3 -0
  208. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F1/F1TWpjjyA56k9Z90n5B3xRn7DUdGjX73QCkYC6k07JQ.cache +0 -0
  209. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +2 -0
  210. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +2 -0
  211. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ms/MsKSimH_UCB-H1tLvDABDHuvGciuoW6kVqQWDrXU5FQ.cache +0 -0
  212. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mt/Mtci-Kim50aPOmeClD4AIicKn1d1WJ0n454IjSd94sk.cache +0 -0
  213. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QH/QHt3Tc1Y6M66Oo_pDuMyWrQNs4Pp3SMeZR5K1wJj2Ts.cache +1 -0
  214. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/XU/XU4k1OXnfMils5SrirorPvDSyDSqiOWLZNtmAH1HH8k.cache +0 -0
  215. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ZI/ZIof7mZxWWCnraIFOCuV6a8QRWzKJXJnx2Xd7C0ZyX0.cache +1 -0
  216. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cG/cGc_puuPS5pZKgUcy1Y_i1L6jl5UtsiIrMH59rTzR6c.cache +3 -0
  217. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/df/dfro_B6bx3KP1Go-7jEOqqZ2j4hVRseXIc3es9PKQno.cache +1 -0
  218. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/jO/jO1DfbqnG0mTULsjJJANc3fefrG2zt7DIMmcptMT628.cache +1 -0
  219. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pE/pE7gO6pQ-z187Swb4hT554wmqsq-cNzgPWLrCz-LQQQ.cache +0 -0
  220. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/r9/r9iU1l58a6rxkZSW5RSC52_tD-_UQuHxoMVnkfJ7Mhs.cache +1 -0
  221. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xi/xitPPFfPIyDMpaznV0sBBcw8eSCV8PJcLLWin78sCgE.cache +0 -0
  222. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  223. data/spec/graphql/analysis/analyze_query_spec.rb +1 -1
  224. data/spec/graphql/analysis/ast/field_usage_spec.rb +51 -0
  225. data/spec/graphql/analysis/ast/max_query_complexity_spec.rb +120 -0
  226. data/spec/graphql/analysis/ast/max_query_depth_spec.rb +114 -0
  227. data/spec/graphql/analysis/ast/query_complexity_spec.rb +299 -0
  228. data/spec/graphql/analysis/ast/query_depth_spec.rb +108 -0
  229. data/spec/graphql/analysis/ast_spec.rb +269 -0
  230. data/spec/graphql/authorization_spec.rb +120 -23
  231. data/spec/graphql/base_type_spec.rb +6 -4
  232. data/spec/graphql/enum_type_spec.rb +6 -1
  233. data/spec/graphql/execution/execute_spec.rb +9 -9
  234. data/spec/graphql/execution/instrumentation_spec.rb +19 -0
  235. data/spec/graphql/execution/interpreter_spec.rb +485 -0
  236. data/spec/graphql/execution/lazy_spec.rb +67 -1
  237. data/spec/graphql/execution/lookahead_spec.rb +363 -0
  238. data/spec/graphql/execution/multiplex_spec.rb +31 -3
  239. data/spec/graphql/execution/typecast_spec.rb +20 -20
  240. data/spec/graphql/execution_error_spec.rb +110 -96
  241. data/spec/graphql/field_spec.rb +1 -1
  242. data/spec/graphql/input_object_type_spec.rb +13 -352
  243. data/spec/graphql/int_type_spec.rb +19 -0
  244. data/spec/graphql/interface_type_spec.rb +4 -4
  245. data/spec/graphql/internal_representation/rewrite_spec.rb +2 -0
  246. data/spec/graphql/introspection/input_value_type_spec.rb +1 -1
  247. data/spec/graphql/introspection/type_type_spec.rb +1 -2
  248. data/spec/graphql/language/document_from_schema_definition_spec.rb +2 -2
  249. data/spec/graphql/language/lexer_spec.rb +72 -3
  250. data/spec/graphql/language/nodes_spec.rb +20 -0
  251. data/spec/graphql/language/printer_spec.rb +18 -6
  252. data/spec/graphql/language/visitor_spec.rb +320 -14
  253. data/spec/graphql/non_null_type_spec.rb +1 -1
  254. data/spec/graphql/object_type_spec.rb +32 -27
  255. data/spec/graphql/query/arguments_spec.rb +21 -0
  256. data/spec/graphql/query/context_spec.rb +28 -0
  257. data/spec/graphql/query/executor_spec.rb +40 -36
  258. data/spec/graphql/query_spec.rb +12 -6
  259. data/spec/graphql/schema/argument_spec.rb +35 -1
  260. data/spec/graphql/schema/build_from_definition_spec.rb +144 -29
  261. data/spec/graphql/schema/catchall_middleware_spec.rb +16 -15
  262. data/spec/graphql/schema/directive/feature_spec.rb +81 -0
  263. data/spec/graphql/schema/directive/transform_spec.rb +39 -0
  264. data/spec/graphql/schema/enum_spec.rb +12 -3
  265. data/spec/graphql/schema/enum_value_spec.rb +11 -0
  266. data/spec/graphql/schema/field_extension_spec.rb +115 -0
  267. data/spec/graphql/schema/field_spec.rb +47 -7
  268. data/spec/graphql/schema/input_object_spec.rb +95 -0
  269. data/spec/graphql/schema/instrumentation_spec.rb +3 -0
  270. data/spec/graphql/schema/interface_spec.rb +8 -2
  271. data/spec/graphql/schema/introspection_system_spec.rb +9 -1
  272. data/spec/graphql/schema/loader_spec.rb +5 -0
  273. data/spec/graphql/schema/member/accepts_definition_spec.rb +4 -0
  274. data/spec/graphql/schema/member/build_type_spec.rb +46 -0
  275. data/spec/graphql/schema/member/scoped_spec.rb +19 -3
  276. data/spec/graphql/schema/mutation_spec.rb +5 -3
  277. data/spec/graphql/schema/object_spec.rb +9 -1
  278. data/spec/graphql/schema/printer_spec.rb +255 -93
  279. data/spec/graphql/schema/relay_classic_mutation_spec.rb +133 -0
  280. data/spec/graphql/schema/resolver_spec.rb +173 -9
  281. data/spec/graphql/schema/scalar_spec.rb +6 -0
  282. data/spec/graphql/schema/subscription_spec.rb +416 -0
  283. data/spec/graphql/schema/traversal_spec.rb +10 -10
  284. data/spec/graphql/schema/type_expression_spec.rb +2 -2
  285. data/spec/graphql/schema/union_spec.rb +7 -0
  286. data/spec/graphql/schema/validation_spec.rb +1 -1
  287. data/spec/graphql/schema/warden_spec.rb +145 -88
  288. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +213 -73
  289. data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +2 -2
  290. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +72 -29
  291. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
  292. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
  293. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +10 -5
  294. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -5
  295. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +131 -5
  296. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +2 -1
  297. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
  298. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -2
  299. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
  300. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +2 -1
  301. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +6 -3
  302. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +22 -2
  303. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +2 -1
  304. data/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb +6 -3
  305. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +13 -4
  306. data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +58 -0
  307. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +2 -1
  308. data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +14 -7
  309. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +14 -7
  310. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
  311. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +8 -4
  312. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +23 -3
  313. data/spec/graphql/static_validation/type_stack_spec.rb +10 -19
  314. data/spec/graphql/static_validation/validator_spec.rb +50 -2
  315. data/spec/graphql/subscriptions_spec.rb +27 -16
  316. data/spec/graphql/tracing/new_relic_tracing_spec.rb +16 -0
  317. data/spec/graphql/tracing/platform_tracing_spec.rb +59 -37
  318. data/spec/graphql/tracing/prometheus_tracing_spec.rb +3 -0
  319. data/spec/graphql/tracing/skylight_tracing_spec.rb +16 -0
  320. data/spec/graphql/types/iso_8601_date_time_spec.rb +29 -2
  321. data/spec/graphql/union_type_spec.rb +2 -2
  322. data/spec/graphql/upgrader/member_spec.rb +67 -0
  323. data/spec/{graphql → integration/mongoid/graphql}/relay/mongo_relation_connection_spec.rb +11 -22
  324. data/spec/integration/mongoid/spec_helper.rb +2 -0
  325. data/spec/{support → integration/mongoid}/star_trek/data.rb +0 -0
  326. data/spec/{support → integration/mongoid}/star_trek/schema.rb +56 -34
  327. data/spec/{support/star_wars → integration/rails}/data.rb +1 -0
  328. data/spec/{support → integration/rails/generators}/base_generator_test.rb +0 -0
  329. data/spec/{generators → integration/rails/generators}/graphql/enum_generator_spec.rb +0 -0
  330. data/spec/{generators → integration/rails/generators}/graphql/install_generator_spec.rb +1 -1
  331. data/spec/{generators → integration/rails/generators}/graphql/interface_generator_spec.rb +0 -0
  332. data/spec/{generators → integration/rails/generators}/graphql/loader_generator_spec.rb +0 -0
  333. data/spec/{generators → integration/rails/generators}/graphql/mutation_generator_spec.rb +0 -0
  334. data/spec/{generators → integration/rails/generators}/graphql/object_generator_spec.rb +0 -0
  335. data/spec/integration/rails/generators/graphql/scalar_generator_spec.rb +28 -0
  336. data/spec/{generators → integration/rails/generators}/graphql/union_generator_spec.rb +0 -0
  337. data/spec/integration/rails/graphql/input_object_type_spec.rb +364 -0
  338. data/spec/{graphql → integration/rails/graphql}/query/variables_spec.rb +7 -7
  339. data/spec/{graphql → integration/rails/graphql}/relay/array_connection_spec.rb +9 -9
  340. data/spec/{graphql → integration/rails/graphql}/relay/base_connection_spec.rb +11 -3
  341. data/spec/{graphql → integration/rails/graphql}/relay/connection_instrumentation_spec.rb +19 -22
  342. data/spec/{graphql → integration/rails/graphql}/relay/connection_resolve_spec.rb +16 -0
  343. data/spec/{graphql → integration/rails/graphql}/relay/connection_type_spec.rb +0 -0
  344. data/spec/{graphql → integration/rails/graphql}/relay/edge_spec.rb +0 -0
  345. data/spec/{graphql → integration/rails/graphql}/relay/mutation_spec.rb +48 -0
  346. data/spec/{graphql → integration/rails/graphql}/relay/node_spec.rb +0 -0
  347. data/spec/{graphql → integration/rails/graphql}/relay/page_info_spec.rb +22 -22
  348. data/spec/{graphql → integration/rails/graphql}/relay/range_add_spec.rb +4 -4
  349. data/spec/{graphql → integration/rails/graphql}/relay/relation_connection_spec.rb +56 -27
  350. data/spec/{graphql → integration/rails/graphql}/schema_spec.rb +15 -11
  351. data/spec/{graphql → integration/rails/graphql}/tracing/active_support_notifications_tracing_spec.rb +16 -9
  352. data/spec/integration/rails/spec_helper.rb +25 -0
  353. data/spec/integration/tmp/app/graphql/types/family_type.rb +9 -0
  354. data/spec/spec_helper.rb +23 -39
  355. data/spec/support/dummy/data.rb +20 -17
  356. data/spec/support/dummy/schema.rb +315 -305
  357. data/spec/support/error_bubbling_helpers.rb +23 -0
  358. data/spec/support/jazz.rb +213 -46
  359. data/spec/support/lazy_helpers.rb +69 -27
  360. data/spec/support/new_relic.rb +3 -0
  361. data/spec/support/skylight.rb +3 -0
  362. data/spec/support/star_wars/schema.rb +131 -81
  363. data/spec/support/static_validation_helpers.rb +9 -5
  364. metadata +418 -261
  365. data/lib/graphql/language/comments.rb +0 -45
  366. data/lib/graphql/static_validation/arguments_validator.rb +0 -50
  367. data/spec/graphql/schema/member/has_fields_spec.rb +0 -129
  368. data/spec/rails_dependency_sanity_spec.rb +0 -14
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class VariablesAreInputTypesError < StaticValidation::Error
5
+ attr_reader :type_name
6
+ attr_reader :variable_name
7
+
8
+ def initialize(message, path: nil, nodes: [], type:, name:)
9
+ super(message, path: path, nodes: nodes)
10
+ @type_name = type
11
+ @variable_name = name
12
+ end
13
+
14
+ # A hash representation of this Message
15
+ def to_h
16
+ extensions = {
17
+ "code" => code,
18
+ "typeName" => type_name,
19
+ "variableName" => variable_name
20
+ }
21
+
22
+ super.merge({
23
+ "extensions" => extensions
24
+ })
25
+ end
26
+
27
+ def code
28
+ "variableRequiresValidType"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -11,9 +11,7 @@ module GraphQL
11
11
  # - re-visiting the AST for each validator
12
12
  # - allowing validators to say `followSpreads: true`
13
13
  #
14
- class VariablesAreUsedAndDefined
15
- include GraphQL::StaticValidation::Message::MessageHelper
16
-
14
+ module VariablesAreUsedAndDefined
17
15
  class VariableUsage
18
16
  attr_accessor :ast_node, :used_by, :declared_by, :path
19
17
  def used?
@@ -25,73 +23,67 @@ module GraphQL
25
23
  end
26
24
  end
27
25
 
28
- def variable_hash
29
- Hash.new {|h, k| h[k] = VariableUsage.new }
26
+ def initialize(*)
27
+ super
28
+ @variable_usages_for_context = Hash.new {|hash, key| hash[key] = Hash.new {|h, k| h[k] = VariableUsage.new } }
29
+ @spreads_for_context = Hash.new {|hash, key| hash[key] = [] }
30
+ @variable_context_stack = []
30
31
  end
31
32
 
32
- def validate(context)
33
- variable_usages_for_context = Hash.new {|hash, key| hash[key] = variable_hash }
34
- spreads_for_context = Hash.new {|hash, key| hash[key] = [] }
35
- variable_context_stack = []
36
-
37
- # OperationDefinitions and FragmentDefinitions
38
- # both push themselves onto the context stack (and pop themselves off)
39
- push_variable_context_stack = ->(node, parent) {
40
- # initialize the hash of vars for this context:
41
- variable_usages_for_context[node]
42
- variable_context_stack.push(node)
43
- }
44
-
45
- pop_variable_context_stack = ->(node, parent) {
46
- variable_context_stack.pop
47
- }
48
-
49
-
50
- context.visitor[GraphQL::Language::Nodes::OperationDefinition] << push_variable_context_stack
51
- context.visitor[GraphQL::Language::Nodes::OperationDefinition] << ->(node, parent) {
52
- # mark variables as defined:
53
- var_hash = variable_usages_for_context[node]
54
- node.variables.each { |var|
55
- var_usage = var_hash[var.name]
56
- var_usage.declared_by = node
57
- var_usage.path = context.path
58
- }
33
+ def on_operation_definition(node, parent)
34
+ # initialize the hash of vars for this context:
35
+ @variable_usages_for_context[node]
36
+ @variable_context_stack.push(node)
37
+ # mark variables as defined:
38
+ var_hash = @variable_usages_for_context[node]
39
+ node.variables.each { |var|
40
+ var_usage = var_hash[var.name]
41
+ var_usage.declared_by = node
42
+ var_usage.path = context.path
59
43
  }
60
- context.visitor[GraphQL::Language::Nodes::OperationDefinition].leave << pop_variable_context_stack
61
-
62
- context.visitor[GraphQL::Language::Nodes::FragmentDefinition] << push_variable_context_stack
63
- context.visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << pop_variable_context_stack
44
+ super
45
+ @variable_context_stack.pop
46
+ end
64
47
 
65
- # For FragmentSpreads:
66
- # - find the context on the stack
67
- # - mark the context as containing this spread
68
- context.visitor[GraphQL::Language::Nodes::FragmentSpread] << ->(node, parent) {
69
- variable_context = variable_context_stack.last
70
- spreads_for_context[variable_context] << node.name
71
- }
48
+ def on_fragment_definition(node, parent)
49
+ # initialize the hash of vars for this context:
50
+ @variable_usages_for_context[node]
51
+ @variable_context_stack.push(node)
52
+ super
53
+ @variable_context_stack.pop
54
+ end
72
55
 
73
- # For VariableIdentifiers:
74
- # - mark the variable as used
75
- # - assign its AST node
76
- context.visitor[GraphQL::Language::Nodes::VariableIdentifier] << ->(node, parent) {
77
- usage_context = variable_context_stack.last
78
- declared_variables = variable_usages_for_context[usage_context]
79
- usage = declared_variables[node.name]
80
- usage.used_by = usage_context
81
- usage.ast_node = node
82
- usage.path = context.path
83
- }
56
+ # For FragmentSpreads:
57
+ # - find the context on the stack
58
+ # - mark the context as containing this spread
59
+ def on_fragment_spread(node, parent)
60
+ variable_context = @variable_context_stack.last
61
+ @spreads_for_context[variable_context] << node.name
62
+ super
63
+ end
84
64
 
65
+ # For VariableIdentifiers:
66
+ # - mark the variable as used
67
+ # - assign its AST node
68
+ def on_variable_identifier(node, parent)
69
+ usage_context = @variable_context_stack.last
70
+ declared_variables = @variable_usages_for_context[usage_context]
71
+ usage = declared_variables[node.name]
72
+ usage.used_by = usage_context
73
+ usage.ast_node = node
74
+ usage.path = context.path
75
+ super
76
+ end
85
77
 
86
- context.visitor[GraphQL::Language::Nodes::Document].leave << ->(node, parent) {
87
- fragment_definitions = variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::FragmentDefinition) }
88
- operation_definitions = variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::OperationDefinition) }
78
+ def on_document(node, parent)
79
+ super
80
+ fragment_definitions = @variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::FragmentDefinition) }
81
+ operation_definitions = @variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::OperationDefinition) }
89
82
 
90
- operation_definitions.each do |node, node_variables|
91
- follow_spreads(node, node_variables, spreads_for_context, fragment_definitions, [])
92
- create_errors(node_variables, context)
93
- end
94
- }
83
+ operation_definitions.each do |node, node_variables|
84
+ follow_spreads(node, node_variables, @spreads_for_context, fragment_definitions, [])
85
+ create_errors(node_variables)
86
+ end
95
87
  end
96
88
 
97
89
  private
@@ -129,16 +121,32 @@ module GraphQL
129
121
 
130
122
  # Determine all the error messages,
131
123
  # Then push messages into the validation context
132
- def create_errors(node_variables, context)
124
+ def create_errors(node_variables)
133
125
  # Declared but not used:
134
126
  node_variables
135
127
  .select { |name, usage| usage.declared? && !usage.used? }
136
- .each { |var_name, usage| context.errors << message("Variable $#{var_name} is declared by #{usage.declared_by.name} but not used", usage.declared_by, path: usage.path) }
128
+ .each { |var_name, usage|
129
+ add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new(
130
+ "Variable $#{var_name} is declared by #{usage.declared_by.name} but not used",
131
+ nodes: usage.declared_by,
132
+ path: usage.path,
133
+ name: var_name,
134
+ error_type: VariablesAreUsedAndDefinedError::VIOLATIONS[:VARIABLE_NOT_USED]
135
+ ))
136
+ }
137
137
 
138
138
  # Used but not declared:
139
139
  node_variables
140
140
  .select { |name, usage| usage.used? && !usage.declared? }
141
- .each { |var_name, usage| context.errors << message("Variable $#{var_name} is used by #{usage.used_by.name} but not declared", usage.ast_node, path: usage.path) }
141
+ .each { |var_name, usage|
142
+ add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new(
143
+ "Variable $#{var_name} is used by #{usage.used_by.name} but not declared",
144
+ nodes: usage.ast_node,
145
+ path: usage.path,
146
+ name: var_name,
147
+ error_type: VariablesAreUsedAndDefinedError::VIOLATIONS[:VARIABLE_NOT_DEFINED]
148
+ ))
149
+ }
142
150
  end
143
151
  end
144
152
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class VariablesAreUsedAndDefinedError < StaticValidation::Error
5
+ attr_reader :variable_name
6
+ attr_reader :violation
7
+
8
+ VIOLATIONS = {
9
+ :VARIABLE_NOT_USED => "variableNotUsed",
10
+ :VARIABLE_NOT_DEFINED => "variableNotDefined",
11
+ }
12
+
13
+ def initialize(message, path: nil, nodes: [], name:, error_type:)
14
+ super(message, path: path, nodes: nodes)
15
+ @variable_name = name
16
+ raise("Unexpected error type: #{error_type}") if !VIOLATIONS.values.include?(error_type)
17
+ @violation = error_type
18
+ end
19
+
20
+ # A hash representation of this Message
21
+ def to_h
22
+ extensions = {
23
+ "code" => code,
24
+ "variableName" => variable_name
25
+ }
26
+
27
+ super.merge({
28
+ "extensions" => extensions
29
+ })
30
+ end
31
+
32
+ def code
33
+ @violation
34
+ end
35
+ end
36
+ end
37
+ end
@@ -14,70 +14,27 @@ module GraphQL
14
14
  class ValidationContext
15
15
  extend Forwardable
16
16
 
17
- attr_reader :query, :errors, :visitor, :dependencies, :each_irep_node_handlers
17
+ attr_reader :query, :errors, :visitor,
18
+ :on_dependency_resolve_handlers
18
19
 
19
20
  def_delegators :@query, :schema, :document, :fragments, :operations, :warden
20
21
 
21
- def initialize(query)
22
+ def initialize(query, visitor_class)
22
23
  @query = query
23
24
  @literal_validator = LiteralValidator.new(context: query.context)
24
25
  @errors = []
25
- @visitor = GraphQL::Language::Visitor.new(document)
26
- @type_stack = GraphQL::StaticValidation::TypeStack.new(schema, visitor)
27
- definition_dependencies = DefinitionDependencies.mount(self)
28
26
  @on_dependency_resolve_handlers = []
29
- @each_irep_node_handlers = []
30
- visitor[GraphQL::Language::Nodes::Document].leave << ->(_n, _p) {
31
- @dependencies = definition_dependencies.dependency_map { |defn, spreads, frag|
32
- @on_dependency_resolve_handlers.each { |h| h.call(defn, spreads, frag) }
33
- }
34
- }
27
+ @visitor = visitor_class.new(document, self)
35
28
  end
36
29
 
30
+ def_delegators :@visitor,
31
+ :path, :type_definition, :field_definition, :argument_definition,
32
+ :parent_type_definition, :directive_definition, :object_types, :dependencies
33
+
37
34
  def on_dependency_resolve(&handler)
38
35
  @on_dependency_resolve_handlers << handler
39
36
  end
40
37
 
41
- def object_types
42
- @type_stack.object_types
43
- end
44
-
45
- def each_irep_node(&handler)
46
- @each_irep_node_handlers << handler
47
- end
48
-
49
- # @return [GraphQL::BaseType] The current object type
50
- def type_definition
51
- object_types.last
52
- end
53
-
54
- # @return [GraphQL::BaseType] The type which the current type came from
55
- def parent_type_definition
56
- object_types[-2]
57
- end
58
-
59
- # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
60
- def field_definition
61
- @type_stack.field_definitions.last
62
- end
63
-
64
- # @return [Array<String>] Field names to get to the current field
65
- def path
66
- @type_stack.path.dup
67
- end
68
-
69
- # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
70
- def directive_definition
71
- @type_stack.directive_definitions.last
72
- end
73
-
74
- # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
75
- def argument_definition
76
- # Don't get the _last_ one because that's the current one.
77
- # Get the second-to-last one, which is the parent of the current one.
78
- @type_stack.argument_definitions[-2]
79
- end
80
-
81
38
  def valid_literal?(ast_value, type)
82
39
  @literal_validator.validate(ast_value, type)
83
40
  end
@@ -23,29 +23,37 @@ module GraphQL
23
23
  # @return [Array<Hash>]
24
24
  def validate(query, validate: true)
25
25
  query.trace("validate", { validate: validate, query: query }) do
26
- context = GraphQL::StaticValidation::ValidationContext.new(query)
27
- rewrite = GraphQL::InternalRepresentation::Rewrite.new
26
+ can_skip_rewrite = query.context.interpreter? && query.schema.using_ast_analysis?
27
+ errors = if validate == false && can_skip_rewrite
28
+ []
29
+ else
30
+ rules_to_use = validate ? @rules : []
31
+ visitor_class = BaseVisitor.including_rules(rules_to_use, rewrite: !can_skip_rewrite)
28
32
 
29
- # Put this first so its enters and exits are always called
30
- rewrite.validate(context)
33
+ context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
31
34
 
32
- # If the caller opted out of validation, don't attach these
33
- if validate
34
- @rules.each do |rules|
35
- rules.new.validate(context)
35
+ # Attach legacy-style rules
36
+ rules_to_use.each do |rule_class_or_module|
37
+ if rule_class_or_module.method_defined?(:validate)
38
+ rule_class_or_module.new.validate(context)
39
+ end
36
40
  end
41
+
42
+ context.visitor.visit
43
+ context.errors
37
44
  end
38
45
 
39
- context.visitor.visit
40
- rewrite_result = rewrite.document
41
46
 
42
- # Post-validation: allow validators to register handlers on rewritten query nodes
43
- GraphQL::InternalRepresentation::Visit.visit_each_node(rewrite_result.operation_definitions, context.each_irep_node_handlers)
47
+ irep = if errors.empty? && context
48
+ # Only return this if there are no errors and validation was actually run
49
+ context.visitor.rewrite_document
50
+ else
51
+ nil
52
+ end
44
53
 
45
54
  {
46
- errors: context.errors,
47
- # If there were errors, the irep is garbage
48
- irep: context.errors.any? ? nil : rewrite_result,
55
+ errors: errors,
56
+ irep: irep,
49
57
  }
50
58
  end
51
59
  end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/static_validation/message"
3
- require "graphql/static_validation/arguments_validator"
2
+ require "graphql/static_validation/error"
4
3
  require "graphql/static_validation/definition_dependencies"
5
4
  require "graphql/static_validation/type_stack"
6
5
  require "graphql/static_validation/validator"
7
6
  require "graphql/static_validation/validation_context"
8
7
  require "graphql/static_validation/literal_validator"
9
-
8
+ require "graphql/static_validation/base_visitor"
9
+ require "graphql/static_validation/no_validate_visitor"
10
10
 
11
11
  rules_glob = File.expand_path("../static_validation/rules/*.rb", __FILE__)
12
12
  Dir.glob(rules_glob).each do |file|
@@ -14,3 +14,5 @@ Dir.glob(rules_glob).each do |file|
14
14
  end
15
15
 
16
16
  require "graphql/static_validation/all_rules"
17
+ require "graphql/static_validation/default_visitor"
18
+ require "graphql/static_validation/interpreter_visitor"
@@ -39,7 +39,7 @@ module GraphQL
39
39
  # })
40
40
  #
41
41
  # payload = {
42
- # result: result.subscription? ? nil : result.to_h,
42
+ # result: result.subscription? ? { data: nil } : result.to_h,
43
43
  # more: result.subscription?,
44
44
  # }
45
45
  #
@@ -54,9 +54,28 @@ module GraphQL
54
54
  #
55
55
  # def unsubscribed
56
56
  # @subscription_ids.each { |sid|
57
- # CardsSchema.subscriptions.delete_subscription(sid)
57
+ # MySchema.subscriptions.delete_subscription(sid)
58
58
  # }
59
59
  # end
60
+ #
61
+ # private
62
+ #
63
+ # def ensure_hash(ambiguous_param)
64
+ # case ambiguous_param
65
+ # when String
66
+ # if ambiguous_param.present?
67
+ # ensure_hash(JSON.parse(ambiguous_param))
68
+ # else
69
+ # {}
70
+ # end
71
+ # when Hash, ActionController::Parameters
72
+ # ambiguous_param
73
+ # when nil
74
+ # {}
75
+ # else
76
+ # raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
77
+ # end
78
+ # end
60
79
  # end
61
80
  #
62
81
  class ActionCableSubscriptions < GraphQL::Subscriptions
@@ -36,11 +36,15 @@ module GraphQL
36
36
  when GraphQL::Query::Arguments
37
37
  arguments
38
38
  when Hash
39
- GraphQL::Query::LiteralInput.from_arguments(
40
- arguments,
41
- field,
42
- nil,
43
- )
39
+ if field.is_a?(GraphQL::Schema::Field)
40
+ stringify_args(arguments)
41
+ else
42
+ GraphQL::Query::LiteralInput.from_arguments(
43
+ arguments,
44
+ field,
45
+ nil,
46
+ )
47
+ end
44
48
  else
45
49
  raise ArgumentError, "Unexpected arguments: #{arguments}, must be Hash or GraphQL::Arguments"
46
50
  end
@@ -48,6 +52,25 @@ module GraphQL
48
52
  sorted_h = normalized_args.to_h.sort.to_h
49
53
  Serialize.dump_recursive([scope, name, sorted_h])
50
54
  end
55
+
56
+ class << self
57
+ private
58
+ def stringify_args(args)
59
+ case args
60
+ when Hash
61
+ next_args = {}
62
+ args.each do |k, v|
63
+ str_k = GraphQL::Schema::Member::BuildType.camelize(k.to_s)
64
+ next_args[str_k] = stringify_args(v)
65
+ end
66
+ next_args
67
+ when Array
68
+ args.map { |a| stringify_args(a) }
69
+ else
70
+ args
71
+ end
72
+ end
73
+ end
51
74
  end
52
75
  end
53
76
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Subscriptions
5
+ # Extend this module in your subscription root when using {GraphQL::Execution::Interpreter}.
6
+ module SubscriptionRoot
7
+ def self.extended(child_cls)
8
+ child_cls.include(InstanceMethods)
9
+ end
10
+
11
+ # This is for maintaining backwards compatibility:
12
+ # if a subscription field is created without a `subscription:` resolver class,
13
+ # then implement the method with the previous default behavior.
14
+ module InstanceMethods
15
+ def skip_subscription_root(*)
16
+ if context.query.subscription_update?
17
+ object
18
+ else
19
+ context.skip
20
+ end
21
+ end
22
+ end
23
+
24
+ def field(*args, extensions: [], **rest, &block)
25
+ extensions += [Extension]
26
+ # Backwards-compat for schemas
27
+ if !rest[:subscription]
28
+ name = args.first
29
+ alias_method(name, :skip_subscription_root)
30
+ end
31
+ super(*args, extensions: extensions, **rest, &block)
32
+ end
33
+
34
+ class Extension < GraphQL::Schema::FieldExtension
35
+ def after_resolve(value:, context:, object:, arguments:, **rest)
36
+ if value.is_a?(GraphQL::ExecutionError)
37
+ value
38
+ elsif (events = context.namespace(:subscriptions)[:events])
39
+ # This is the first execution, so gather an Event
40
+ # for the backend to register:
41
+ events << Subscriptions::Event.new(
42
+ name: field.name,
43
+ arguments: arguments,
44
+ context: context,
45
+ field: field,
46
+ )
47
+ # TODO compat with non-class-based subscriptions?
48
+ value
49
+ elsif context.query.subscription_topic == Subscriptions::Event.serialize(
50
+ field.name,
51
+ arguments,
52
+ field,
53
+ scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
54
+ )
55
+ # This is a subscription update. The resolver returned `skip` if it should be skipped,
56
+ # or else it returned an object to resolve the update.
57
+ value
58
+ else
59
+ # This is a subscription update, but this event wasn't triggered.
60
+ context.skip
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -6,6 +6,7 @@ require "graphql/subscriptions/serialize"
6
6
  if defined?(ActionCable)
7
7
  require "graphql/subscriptions/action_cable_subscriptions"
8
8
  end
9
+ require "graphql/subscriptions/subscription_root"
9
10
 
10
11
  module GraphQL
11
12
  class Subscriptions
@@ -99,6 +100,12 @@ module GraphQL
99
100
  }
100
101
  )
101
102
  deliver(subscription_id, result)
103
+ rescue GraphQL::Schema::Subscription::NoUpdateError
104
+ # This update was skipped in user code; do nothing.
105
+ rescue GraphQL::Schema::Subscription::UnsubscribedError
106
+ # `unsubscribe` was called, clean up on our side
107
+ # TODO also send `{more: false}` to client?
108
+ delete_subscription(subscription_id)
102
109
  end
103
110
 
104
111
  # Event `event` occurred on `object`,
@@ -132,9 +139,8 @@ module GraphQL
132
139
  # The result should be send to `subscription_id`.
133
140
  # @param subscription_id [String]
134
141
  # @param result [Hash]
135
- # @param context [GraphQL::Query::Context]
136
142
  # @return [void]
137
- def deliver(subscription_id, result, context)
143
+ def deliver(subscription_id, result)
138
144
  raise NotImplementedError
139
145
  end
140
146
 
@@ -147,6 +153,14 @@ module GraphQL
147
153
  raise NotImplementedError
148
154
  end
149
155
 
156
+ # A subscription was terminated server-side.
157
+ # Clean up the database.
158
+ # @param subscription_id [String]
159
+ # @return void.
160
+ def delete_subscription(subscription_id)
161
+ raise NotImplementedError
162
+ end
163
+
150
164
  # @return [String] A new unique identifier for a subscription
151
165
  def build_id
152
166
  SecureRandom.uuid
@@ -5,7 +5,6 @@ module GraphQL
5
5
  # This implementation forwards events to ActiveSupport::Notifications
6
6
  # with a `graphql.` prefix.
7
7
  #
8
- # Installed automatically when `ActiveSupport::Notifications` is discovered.
9
8
  module ActiveSupportNotificationsTracing
10
9
  # A cache of frequently-used keys to avoid needless string allocations
11
10
  KEYS = {
@@ -21,7 +21,7 @@ module GraphQL
21
21
  end
22
22
 
23
23
  def platform_field_key(type, field)
24
- "#{type.name}.#{field.name}.graphql"
24
+ "#{type.graphql_name}.#{field.graphql_name}.graphql"
25
25
  end
26
26
  end
27
27
  end
@@ -42,7 +42,7 @@ module GraphQL
42
42
  end
43
43
 
44
44
  def platform_field_key(type, field)
45
- "#{type.name}.#{field.name}"
45
+ "#{type.graphql_name}.#{field.graphql_name}"
46
46
  end
47
47
  end
48
48
  end
@@ -17,8 +17,8 @@ module GraphQL
17
17
  # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
18
18
  # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
19
19
  # It can also be specified per-query with `context[:set_new_relic_transaction_name]`.
20
- def initialize(set_transaction_name: false)
21
- @set_transaction_name = set_transaction_name
20
+ def initialize(options = {})
21
+ @set_transaction_name = options.fetch(:set_transaction_name, false)
22
22
  super
23
23
  end
24
24
 
@@ -47,7 +47,7 @@ module GraphQL
47
47
  end
48
48
 
49
49
  def platform_field_key(type, field)
50
- "GraphQL/#{type.name}/#{field.name}"
50
+ "GraphQL/#{type.graphql_name}/#{field.graphql_name}"
51
51
  end
52
52
  end
53
53
  end
@@ -26,7 +26,23 @@ module GraphQL
26
26
  yield
27
27
  end
28
28
  when "execute_field", "execute_field_lazy"
29
- if (platform_key = data[:context].field.metadata[:platform_key])
29
+ if data[:context]
30
+ field = data[:context].field
31
+ platform_key = field.metadata[:platform_key]
32
+ trace_field = true # implemented with instrumenter
33
+ else
34
+ field = data[:field]
35
+ # Lots of duplicated work here, can this be done ahead of time?
36
+ platform_key = platform_field_key(field.owner, field)
37
+ return_type = field.type.unwrap
38
+ trace_field = if return_type.kind.scalar? || return_type.kind.enum?
39
+ (field.trace.nil? && @trace_scalars) || field.trace
40
+ else
41
+ true
42
+ end
43
+ end
44
+
45
+ if platform_key && trace_field
30
46
  platform_trace(platform_key, key, data) do
31
47
  yield
32
48
  end