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,299 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::Analysis::AST::QueryComplexity do
5
+ let(:reduce_result) { GraphQL::Analysis::AST.analyze_query(query, [GraphQL::Analysis::AST::QueryComplexity]) }
6
+ let(:variables) { {} }
7
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables) }
8
+
9
+ describe "simple queries" do
10
+ let(:query_string) {%|
11
+ query cheeses($isSkipped: Boolean = false){
12
+ # complexity of 3
13
+ cheese1: cheese(id: 1) {
14
+ id
15
+ flavor
16
+ }
17
+
18
+ # complexity of 4
19
+ cheese2: cheese(id: 2) @skip(if: $isSkipped) {
20
+ similarCheese(source: SHEEP) {
21
+ ... on Cheese {
22
+ similarCheese(source: SHEEP) {
23
+ id
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ |}
30
+
31
+ it "sums the complexity" do
32
+ complexities = reduce_result.first
33
+ assert_equal 7, complexities
34
+ end
35
+
36
+ describe "when skipped by directives" do
37
+ let(:variables) { { "isSkipped" => true } }
38
+ it "doesn't include skipped fields" do
39
+ complexity = reduce_result.first
40
+ assert_equal 3, complexity
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "query with fragments" do
46
+ let(:query_string) {%|
47
+ {
48
+ # complexity of 3
49
+ cheese1: cheese(id: 1) {
50
+ id
51
+ flavor
52
+ }
53
+
54
+ # complexity of 7
55
+ cheese2: cheese(id: 2) {
56
+ ... cheeseFields1
57
+ ... cheeseFields2
58
+ }
59
+ }
60
+
61
+ fragment cheeseFields1 on Cheese {
62
+ similarCow: similarCheese(source: COW) {
63
+ id
64
+ ... cheeseFields2
65
+ }
66
+ }
67
+
68
+ fragment cheeseFields2 on Cheese {
69
+ similarSheep: similarCheese(source: SHEEP) {
70
+ id
71
+ }
72
+ }
73
+ |}
74
+
75
+ it "counts all fragment usages, not the definitions" do
76
+ complexity = reduce_result.first
77
+ assert_equal 10, complexity
78
+ end
79
+
80
+ describe "mutually exclusive types" do
81
+ let(:query_string) {%|
82
+ {
83
+ favoriteEdible {
84
+ # 1 for everybody
85
+ fatContent
86
+
87
+ # 1 for everybody
88
+ ... on Edible {
89
+ origin
90
+ }
91
+
92
+ # 1 for honey
93
+ ... on Sweetener {
94
+ sweetness
95
+ }
96
+
97
+ # 2 for milk
98
+ ... milkFields
99
+ # 1 for cheese
100
+ ... cheeseFields
101
+ # 1 for honey
102
+ ... honeyFields
103
+ # 1 for milk + cheese
104
+ ... dairyProductFields
105
+ }
106
+ }
107
+
108
+ fragment milkFields on Milk {
109
+ id
110
+ source
111
+ }
112
+
113
+ fragment cheeseFields on Cheese {
114
+ source
115
+ }
116
+
117
+ fragment honeyFields on Honey {
118
+ flowerType
119
+ }
120
+
121
+ fragment dairyProductFields on DairyProduct {
122
+ ... on Cheese {
123
+ flavor
124
+ }
125
+
126
+ ... on Milk {
127
+ flavors
128
+ }
129
+ }
130
+ |}
131
+
132
+ it "gets the max among options" do
133
+ complexity = reduce_result.first
134
+ assert_equal 6, complexity
135
+ end
136
+ end
137
+
138
+
139
+ describe "when there are no selections on any object types" do
140
+ let(:query_string) {%|
141
+ {
142
+ # 1 for everybody
143
+ favoriteEdible {
144
+ # 1 for everybody
145
+ fatContent
146
+
147
+ # 1 for everybody
148
+ ... on Edible { origin }
149
+
150
+ # 1 for honey
151
+ ... on Sweetener { sweetness }
152
+ }
153
+ }
154
+ |}
155
+
156
+ it "gets the max among interface types" do
157
+ complexity = reduce_result.first
158
+ assert_equal 4, complexity
159
+ end
160
+ end
161
+
162
+ describe "redundant fields" do
163
+ let(:query_string) {%|
164
+ {
165
+ favoriteEdible {
166
+ fatContent
167
+ # this is executed separately and counts separately:
168
+ aliasedFatContent: fatContent
169
+
170
+ ... on Edible {
171
+ fatContent
172
+ }
173
+
174
+ ... edibleFields
175
+ }
176
+ }
177
+
178
+ fragment edibleFields on Edible {
179
+ fatContent
180
+ }
181
+ |}
182
+
183
+ it "only counts them once" do
184
+ complexity = reduce_result.first
185
+ assert_equal 3, complexity
186
+ end
187
+ end
188
+ end
189
+
190
+ describe "relay types" do
191
+ let(:query) { GraphQL::Query.new(StarWars::Schema, query_string) }
192
+ let(:query_string) {%|
193
+ {
194
+ rebels {
195
+ ships {
196
+ edges {
197
+ node {
198
+ id
199
+ }
200
+ }
201
+ pageInfo {
202
+ hasNextPage
203
+ }
204
+ }
205
+ }
206
+ }
207
+ |}
208
+
209
+ it "gets the complexity" do
210
+ complexity = reduce_result.first
211
+ assert_equal 7, complexity
212
+ end
213
+ end
214
+
215
+ describe "custom complexities" do
216
+ let(:query) { GraphQL::Query.new(complexity_schema, query_string) }
217
+ let(:complexity_schema) {
218
+ complexity_interface = GraphQL::InterfaceType.define do
219
+ name "ComplexityInterface"
220
+ field :value, types.Int
221
+ end
222
+
223
+ single_complexity_type = GraphQL::ObjectType.define do
224
+ name "SingleComplexity"
225
+ field :value, types.Int, complexity: 0.1 do
226
+ resolve ->(obj, args, ctx) { obj }
227
+ end
228
+ field :complexity, single_complexity_type do
229
+ argument :value, types.Int
230
+ complexity ->(ctx, args, child_complexity) { args[:value] + child_complexity }
231
+ resolve ->(obj, args, ctx) { args[:value] }
232
+ end
233
+ interfaces [complexity_interface]
234
+ end
235
+
236
+ double_complexity_type = GraphQL::ObjectType.define do
237
+ name "DoubleComplexity"
238
+ field :value, types.Int, complexity: 4 do
239
+ resolve ->(obj, args, ctx) { obj }
240
+ end
241
+ interfaces [complexity_interface]
242
+ end
243
+
244
+ query_type = GraphQL::ObjectType.define do
245
+ name "Query"
246
+ field :complexity, single_complexity_type do
247
+ argument :value, types.Int
248
+ complexity ->(ctx, args, child_complexity) { args[:value] + child_complexity }
249
+ resolve ->(obj, args, ctx) { args[:value] }
250
+ end
251
+
252
+ field :innerComplexity, complexity_interface do
253
+ argument :value, types.Int
254
+ resolve ->(obj, args, ctx) { args[:value] }
255
+ end
256
+ end
257
+
258
+ GraphQL::Schema.define(
259
+ query: query_type,
260
+ orphan_types: [double_complexity_type],
261
+ resolve_type: ->(a,b,c) { :pass }
262
+ )
263
+ }
264
+ let(:query_string) {%|
265
+ {
266
+ a: complexity(value: 3) { value }
267
+ b: complexity(value: 6) {
268
+ value
269
+ complexity(value: 1) {
270
+ value
271
+ }
272
+ }
273
+ }
274
+ |}
275
+
276
+ it "sums the complexity" do
277
+ complexity = reduce_result.first
278
+ # 10 from `complexity`, `0.3` from `value`
279
+ assert_equal 10.3, complexity
280
+ end
281
+
282
+ describe "same field on multiple types" do
283
+ let(:query_string) {%|
284
+ {
285
+ innerComplexity(value: 2) {
286
+ ... on SingleComplexity { value }
287
+ ... on DoubleComplexity { value }
288
+ }
289
+ }
290
+ |}
291
+
292
+ it "picks them max for those fields" do
293
+ complexity = reduce_result.first
294
+ # 1 for innerComplexity + 4 for DoubleComplexity.value
295
+ assert_equal 5, complexity
296
+ end
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::Analysis::AST::QueryDepth do
5
+ let(:result) { GraphQL::Analysis::AST.analyze_query(query, [GraphQL::Analysis::AST::QueryDepth]) }
6
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables) }
7
+ let(:variables) { {} }
8
+
9
+ describe "multiple operations" do
10
+ let(:query_string) {%|
11
+ query Cheese1 {
12
+ cheese1: cheese(id: 1) {
13
+ id
14
+ flavor
15
+ }
16
+ }
17
+
18
+ query Cheese2 {
19
+ cheese(id: 2) {
20
+ similarCheese(source: SHEEP) {
21
+ ... on Cheese {
22
+ similarCheese(source: SHEEP) {
23
+ id
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ |}
30
+
31
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, operation_name: "Cheese1") }
32
+
33
+ it "analyzes the selected operation only" do
34
+ depth = result.first
35
+ assert_equal 2, depth
36
+ end
37
+ end
38
+
39
+ describe "simple queries" do
40
+ let(:query_string) {%|
41
+ query cheeses($isIncluded: Boolean = true){
42
+ # depth of 2
43
+ cheese1: cheese(id: 1) {
44
+ id
45
+ flavor
46
+ }
47
+
48
+ # depth of 4
49
+ cheese2: cheese(id: 2) @include(if: $isIncluded) {
50
+ similarCheese(source: SHEEP) {
51
+ ... on Cheese {
52
+ similarCheese(source: SHEEP) {
53
+ id
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ |}
60
+
61
+ it "finds the max depth" do
62
+ depth = result.first
63
+ assert_equal 4, depth
64
+ end
65
+
66
+ describe "with directives" do
67
+ let(:variables) { { "isIncluded" => false } }
68
+
69
+ it "doesn't count skipped fields" do
70
+ assert_equal 2, result.first
71
+ end
72
+ end
73
+ end
74
+
75
+ describe "query with fragments" do
76
+ let(:query_string) {%|
77
+ {
78
+ # depth of 2
79
+ cheese1: cheese(id: 1) {
80
+ id
81
+ flavor
82
+ }
83
+
84
+ # depth of 4
85
+ cheese2: cheese(id: 2) {
86
+ ... cheeseFields1
87
+ }
88
+ }
89
+
90
+ fragment cheeseFields1 on Cheese {
91
+ similarCheese(source: COW) {
92
+ id
93
+ ... cheeseFields2
94
+ }
95
+ }
96
+
97
+ fragment cheeseFields2 on Cheese {
98
+ similarCheese(source: SHEEP) {
99
+ id
100
+ }
101
+ }
102
+ |}
103
+
104
+ it "finds the max depth" do
105
+ assert_equal 4, result.first
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,269 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::Analysis::AST do
5
+ class AstTypeCollector < GraphQL::Analysis::AST::Analyzer
6
+ def initialize(query)
7
+ super
8
+ @types = []
9
+ end
10
+
11
+ def on_enter_operation_definition(node, parent, visitor)
12
+ @types << visitor.type_definition
13
+ end
14
+
15
+ def on_enter_field(memo, node, visitor)
16
+ @types << visitor.field_definition.type.unwrap
17
+ end
18
+
19
+ def result
20
+ @types
21
+ end
22
+ end
23
+
24
+ class AstNodeCounter < GraphQL::Analysis::AST::Analyzer
25
+ def initialize(query)
26
+ super
27
+ @nodes = Hash.new { |h,k| h[k] = 0 }
28
+ end
29
+
30
+ def on_enter_abstract_node(node, parent, _visitor)
31
+ @nodes[node.class] += 1
32
+ end
33
+
34
+ def result
35
+ @nodes
36
+ end
37
+ end
38
+
39
+ class AstConditionalAnalyzer < GraphQL::Analysis::AST::Analyzer
40
+ def initialize(query)
41
+ super
42
+ @i_have_been_called = false
43
+ end
44
+
45
+ def analyze?
46
+ !!query.context[:analyze]
47
+ end
48
+
49
+ def on_operation_definition(node, parent, visitor)
50
+ @i_have_been_called = true
51
+ end
52
+
53
+ def result
54
+ @i_have_been_called
55
+ end
56
+ end
57
+
58
+ class AstErrorAnalyzer < GraphQL::Analysis::AST::Analyzer
59
+ def result
60
+ GraphQL::AnalysisError.new("An Error!")
61
+ end
62
+ end
63
+
64
+ class AstPreviousField < GraphQL::Analysis::AST::Analyzer
65
+ def on_enter_field(node, parent, visitor)
66
+ @previous_field = visitor.previous_field_definition
67
+ end
68
+
69
+ def result
70
+ @previous_field
71
+ end
72
+ end
73
+
74
+ describe "using the AST analysis engine" do
75
+ let(:schema) do
76
+ query_type = Class.new(GraphQL::Schema::Object) do
77
+ graphql_name 'Query'
78
+
79
+ field :foobar, Integer, null: false
80
+
81
+ def foobar
82
+ 1337
83
+ end
84
+ end
85
+
86
+ Class.new(GraphQL::Schema) do
87
+ query query_type
88
+ use GraphQL::Analysis::AST
89
+ query_analyzer AstErrorAnalyzer
90
+ use GraphQL::Execution::Interpreter
91
+ end
92
+ end
93
+
94
+ let(:query_string) {%|
95
+ query {
96
+ foobar
97
+ }
98
+ |}
99
+
100
+ let(:query) { GraphQL::Query.new(schema, query_string, variables: {}) }
101
+
102
+ it "runs the AST analyzers correctly" do
103
+ res = query.result
104
+ refute res.key?("data")
105
+ assert_equal ["An Error!"], res["errors"].map { |e| e["message"] }
106
+ end
107
+
108
+ it "skips rewrite" do
109
+ # Try running the query:
110
+ query.result
111
+ # But the validation step doesn't build an irep_node tree
112
+ assert_nil query.irep_selection
113
+ end
114
+
115
+ describe "when validate: false" do
116
+ let(:query) { GraphQL::Query.new(schema, query_string, validate: false) }
117
+ it "Skips rewrite" do
118
+ # Try running the query:
119
+ query.result
120
+ # But the validation step doesn't build an irep_node tree
121
+ assert_nil query.irep_selection
122
+ end
123
+ end
124
+ end
125
+
126
+ describe ".analyze_query" do
127
+ let(:analyzers) { [AstTypeCollector, AstNodeCounter] }
128
+ let(:reduce_result) { GraphQL::Analysis::AST.analyze_query(query, analyzers) }
129
+ let(:variables) { {} }
130
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables) }
131
+ let(:query_string) {%|
132
+ {
133
+ cheese(id: 1) {
134
+ id
135
+ flavor
136
+ }
137
+ }
138
+ |}
139
+
140
+ describe "without a selected operation" do
141
+ let(:query_string) {%|
142
+ # A comment
143
+ # And nothing else
144
+ # Should not break
145
+ |}
146
+
147
+ it "bails early when there is no selected operation to be executed" do
148
+ assert_equal 2, reduce_result.size
149
+ end
150
+ end
151
+
152
+ describe "conditional analysis" do
153
+ let(:analyzers) { [AstTypeCollector, AstConditionalAnalyzer] }
154
+
155
+ describe "when analyze? returns false" do
156
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, context: { analyze: false }) }
157
+
158
+ it "does not run the analyzer" do
159
+ # Only type_collector ran
160
+ assert_equal 1, reduce_result.size
161
+ end
162
+ end
163
+
164
+ describe "when analyze? returns true" do
165
+ let(:query) { GraphQL::Query.new(Dummy::Schema, query_string, variables: variables, context: { analyze: true }) }
166
+
167
+ it "it runs the analyzer" do
168
+ # Both analyzers ran
169
+ assert_equal 2, reduce_result.size
170
+ end
171
+ end
172
+
173
+ describe "Visitor#previous_field_definition" do
174
+ let(:analyzers) { [AstPreviousField] }
175
+ let(:query) { GraphQL::Query.new(Dummy::Schema, "{ __schema { types { name } } }") }
176
+
177
+ it "it runs the analyzer" do
178
+ prev_field = reduce_result.first
179
+ assert_equal "__Schema.types", prev_field.metadata[:type_class].path
180
+ end
181
+ end
182
+ end
183
+
184
+ it "calls the defined analyzers" do
185
+ collected_types, node_counts = reduce_result
186
+ expected_visited_types = [
187
+ Dummy::DairyAppQuery.graphql_definition,
188
+ Dummy::Cheese.graphql_definition,
189
+ GraphQL::INT_TYPE,
190
+ GraphQL::STRING_TYPE
191
+ ]
192
+ assert_equal expected_visited_types, collected_types
193
+
194
+ expected_node_counts = {
195
+ GraphQL::Language::Nodes::OperationDefinition => 1,
196
+ GraphQL::Language::Nodes::Field => 3,
197
+ GraphQL::Language::Nodes::Argument => 1
198
+ }
199
+
200
+ assert_equal expected_node_counts, node_counts
201
+ end
202
+
203
+ describe "tracing" do
204
+ let(:query_string) { "{ t: __typename }"}
205
+
206
+ it "emits traces" do
207
+ traces = TestTracing.with_trace do
208
+ ctx = { tracers: [TestTracing] }
209
+ Dummy::Schema.execute(query_string, context: ctx)
210
+ end
211
+
212
+ # The query_trace is on the list _first_ because it finished first
213
+ _lex, _parse, _validate, query_trace, multiplex_trace, *_rest = traces
214
+
215
+ assert_equal "analyze_multiplex", multiplex_trace[:key]
216
+ assert_instance_of GraphQL::Execution::Multiplex, multiplex_trace[:multiplex]
217
+
218
+ assert_equal "analyze_query", query_trace[:key]
219
+ assert_instance_of GraphQL::Query, query_trace[:query]
220
+ end
221
+ end
222
+
223
+ class AstConnectionCounter < GraphQL::Analysis::AST::Analyzer
224
+ def initialize(query)
225
+ super
226
+ @fields = 0
227
+ @connections = 0
228
+ end
229
+
230
+ def on_enter_field(node, parent, visitor)
231
+ if visitor.field_definition.connection?
232
+ @connections += 1
233
+ else
234
+ @fields += 1
235
+ end
236
+ end
237
+
238
+ def result
239
+ {
240
+ fields: @fields,
241
+ connections: @connections
242
+ }
243
+ end
244
+ end
245
+
246
+ describe "when processing fields" do
247
+ let(:analyzers) { [AstConnectionCounter] }
248
+ let(:reduce_result) { GraphQL::Analysis::AST.analyze_query(query, analyzers) }
249
+ let(:query) { GraphQL::Query.new(StarWars::Schema, query_string, variables: variables) }
250
+ let(:query_string) {%|
251
+ query getBases {
252
+ empire {
253
+ basesByName(first: 30) { edges { cursor } }
254
+ bases(first: 30) { edges { cursor } }
255
+ }
256
+ }
257
+ |}
258
+
259
+ it "knows which fields are connections" do
260
+ connection_counts = reduce_result.first
261
+ expected_connection_counts = {
262
+ :fields => 5,
263
+ :connections => 2
264
+ }
265
+ assert_equal expected_connection_counts, connection_counts
266
+ end
267
+ end
268
+ end
269
+ end