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
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/execution/lazy/lazy_method_map"
3
3
  require "graphql/execution/lazy/resolve"
4
+
4
5
  module GraphQL
5
6
  module Execution
6
7
  # This wraps a value which is available, but not yet calculated, like a promise or future.
@@ -19,11 +20,17 @@ module GraphQL
19
20
  Resolve.resolve(val)
20
21
  end
21
22
 
23
+ attr_reader :path, :field
24
+
22
25
  # Create a {Lazy} which will get its inner value by calling the block
26
+ # @param path [Array<String, Integer>]
27
+ # @param field [GraphQL::Schema::Field]
23
28
  # @param get_value_func [Proc] a block to get the inner value (later)
24
- def initialize(&get_value_func)
29
+ def initialize(path: nil, field: nil, &get_value_func)
25
30
  @get_value_func = get_value_func
26
31
  @resolved = false
32
+ @path = path
33
+ @field = field
27
34
  end
28
35
 
29
36
  # @return [Object] The wrapped value, calling the lazy block if necessary
@@ -0,0 +1,351 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ # Lookahead creates a uniform interface to inspect the forthcoming selections.
5
+ #
6
+ # It assumes that the AST it's working with is valid. (So, it's safe to use
7
+ # during execution, but if you're using it directly, be sure to validate first.)
8
+ #
9
+ # A field may get access to its lookahead by adding `extras: [:lookahead]`
10
+ # to its configuration.
11
+ #
12
+ # __NOTE__: Lookahead for typed fragments (eg `node { ... on Thing { ... } }`)
13
+ # hasn't been implemented yet. It's possible, I just didn't need it yet.
14
+ # Feel free to open a PR or an issue if you want to add it.
15
+ #
16
+ # @example looking ahead in a field
17
+ # field :articles, [Types::Article], null: false,
18
+ # extras: [:lookahead]
19
+ #
20
+ # # For example, imagine a faster database call
21
+ # # may be issued when only some fields are requested.
22
+ # #
23
+ # # Imagine that _full_ fetch must be made to satisfy `fullContent`,
24
+ # # we can look ahead to see if we need that field. If we do,
25
+ # # we make the expensive database call instead of the cheap one.
26
+ # def articles(lookahead:)
27
+ # if lookahead.selects?(:full_content)
28
+ # fetch_full_articles(object)
29
+ # else
30
+ # fetch_preview_articles(object)
31
+ # end
32
+ # end
33
+ class Lookahead
34
+ # @param query [GraphQL::Query]
35
+ # @param ast_nodes [Array<GraphQL::Language::Nodes::Field>, Array<GraphQL::Language::Nodes::OperationDefinition>]
36
+ # @param field [GraphQL::Schema::Field] if `ast_nodes` are fields, this is the field definition matching those nodes
37
+ # @param root_type [Class] if `ast_nodes` are operation definition, this is the root type for that operation
38
+ def initialize(query:, ast_nodes:, field: nil, root_type: nil)
39
+ @ast_nodes = ast_nodes.freeze
40
+ @field = field
41
+ @root_type = root_type
42
+ @query = query
43
+ @selected_type = @field ? @field.type.unwrap : root_type
44
+ end
45
+
46
+ # @return [Array<GraphQL::Language::Nodes::Field>]
47
+ attr_reader :ast_nodes
48
+
49
+ # @return [GraphQL::Schema::Field]
50
+ attr_reader :field
51
+
52
+ # True if this node has a selection on `field_name`.
53
+ # If `field_name` is a String, it is treated as a GraphQL-style (camelized)
54
+ # field name and used verbatim. If `field_name` is a Symbol, it is
55
+ # treated as a Ruby-style (underscored) name and camelized before comparing.
56
+ #
57
+ # If `arguments:` is provided, each provided key/value will be matched
58
+ # against the arguments in the next selection. This method will return false
59
+ # if any of the given `arguments:` are not present and matching in the next selection.
60
+ # (But, the next selection may contain _more_ than the given arguments.)
61
+ # @param field_name [String, Symbol]
62
+ # @param arguments [Hash] Arguments which must match in the selection
63
+ # @return [Boolean]
64
+ def selects?(field_name, arguments: nil)
65
+ selection(field_name, arguments: arguments).selected?
66
+ end
67
+
68
+ # @return [Boolean] True if this lookahead represents a field that was requested
69
+ def selected?
70
+ true
71
+ end
72
+
73
+ # Like {#selects?}, but can be used for chaining.
74
+ # It returns a null object (check with {#selected?})
75
+ # @return [GraphQL::Execution::Lookahead]
76
+ def selection(field_name, selected_type: @selected_type, arguments: nil)
77
+ next_field_name = normalize_name(field_name)
78
+
79
+ next_field_defn = FieldHelpers.get_field(@query.schema, selected_type, next_field_name)
80
+ if next_field_defn
81
+ next_nodes = []
82
+ @ast_nodes.each do |ast_node|
83
+ ast_node.selections.each do |selection|
84
+ find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
85
+ end
86
+ end
87
+
88
+ if next_nodes.any?
89
+ Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn)
90
+ else
91
+ NULL_LOOKAHEAD
92
+ end
93
+ else
94
+ NULL_LOOKAHEAD
95
+ end
96
+ end
97
+
98
+ # Like {#selection}, but for all nodes.
99
+ # It returns a list of Lookaheads for all Selections
100
+ #
101
+ # If `arguments:` is provided, each provided key/value will be matched
102
+ # against the arguments in each selection. This method will filter the selections
103
+ # if any of the given `arguments:` do not match the given selection.
104
+ #
105
+ # @example getting the name of a selection
106
+ # def articles(lookahead:)
107
+ # next_lookaheads = lookahead.selections # => [#<GraphQL::Execution::Lookahead ...>, ...]
108
+ # next_lookaheads.map(&:name) #=> [:full_content, :title]
109
+ # end
110
+ #
111
+ # @param arguments [Hash] Arguments which must match in the selection
112
+ # @return [Array<GraphQL::Execution::Lookahead>]
113
+ def selections(arguments: nil)
114
+ subselections_by_name = {}
115
+ @ast_nodes.each do |node|
116
+ find_selections(subselections_by_name, @selected_type, node.selections, arguments)
117
+ end
118
+
119
+ # Items may be filtered out if `arguments` doesn't match
120
+ subselections_by_name.values.select(&:selected?)
121
+ end
122
+
123
+ # The method name of the field.
124
+ # It returns the method_sym of the Lookahead's field.
125
+ #
126
+ # @example getting the name of a selection
127
+ # def articles(lookahead:)
128
+ # article.selection(:full_content).name # => :full_content
129
+ # # ...
130
+ # end
131
+ #
132
+ # @return [Symbol]
133
+ def name
134
+ @field && @field.original_name
135
+ end
136
+
137
+ def inspect
138
+ "#<GraphQL::Execution::Lookahead #{@field ? "@field=#{@field.path.inspect}": "@root_type=#{@root_type}"} @ast_nodes.size=#{@ast_nodes.size}>"
139
+ end
140
+
141
+ # This is returned for {Lookahead#selection} when a non-existent field is passed
142
+ class NullLookahead < Lookahead
143
+ # No inputs required here.
144
+ def initialize
145
+ end
146
+
147
+ def selected?
148
+ false
149
+ end
150
+
151
+ def selects?(*)
152
+ false
153
+ end
154
+
155
+ def selection(*)
156
+ NULL_LOOKAHEAD
157
+ end
158
+
159
+ def selections(*)
160
+ []
161
+ end
162
+
163
+ def inspect
164
+ "#<GraphQL::Execution::Lookahead::NullLookahead>"
165
+ end
166
+ end
167
+
168
+ # A singleton, so that misses don't come with overhead.
169
+ NULL_LOOKAHEAD = NullLookahead.new
170
+
171
+ private
172
+
173
+ # If it's a symbol, stringify and camelize it
174
+ def normalize_name(name)
175
+ if name.is_a?(Symbol)
176
+ Schema::Member::BuildType.camelize(name.to_s)
177
+ else
178
+ name
179
+ end
180
+ end
181
+
182
+ def normalize_keyword(keyword)
183
+ if keyword.is_a?(String)
184
+ Schema::Member::BuildType.underscore(keyword).to_sym
185
+ else
186
+ keyword
187
+ end
188
+ end
189
+
190
+ def find_selections(subselections_by_name, selected_type, ast_selections, arguments)
191
+ ast_selections.each do |ast_selection|
192
+ case ast_selection
193
+ when GraphQL::Language::Nodes::Field
194
+ subselections_by_name[ast_selection.name] ||= selection(ast_selection.name, selected_type: selected_type, arguments: arguments)
195
+ when GraphQL::Language::Nodes::InlineFragment
196
+ if (t = ast_selection.type)
197
+ # Assuming this is valid, that `t` will be found.
198
+ selected_type = @query.schema.types[t.name].metadata[:type_class]
199
+ end
200
+ find_selections(subselections_by_name, selected_type, ast_selection.selections, arguments)
201
+ when GraphQL::Language::Nodes::FragmentSpread
202
+ frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
203
+ # Again, assuming a valid AST
204
+ selected_type = @query.schema.types[frag_defn.type.name].metadata[:type_class]
205
+ find_selections(subselections_by_name, selected_type, frag_defn.selections, arguments)
206
+ else
207
+ raise "Invariant: Unexpected selection type: #{ast_selection.class}"
208
+ end
209
+ end
210
+ end
211
+
212
+ # If a selection on `node` matches `field_name` (which is backed by `field_defn`)
213
+ # and matches the `arguments:` constraints, then add that node to `matches`
214
+ def find_selected_nodes(node, field_name, field_defn, arguments:, matches:)
215
+ case node
216
+ when GraphQL::Language::Nodes::Field
217
+ if node.name == field_name
218
+ if arguments.nil? || arguments.empty?
219
+ # No constraint applied
220
+ matches << node
221
+ else
222
+ query_kwargs = ArgumentHelpers.arguments(@query, nil, field_defn, node)
223
+ passes_args = arguments.all? do |arg_name, arg_value|
224
+ arg_name = normalize_keyword(arg_name)
225
+ # Make sure the constraint is present with a matching value
226
+ query_kwargs.key?(arg_name) && query_kwargs[arg_name] == arg_value
227
+ end
228
+ if passes_args
229
+ matches << node
230
+ end
231
+ end
232
+ end
233
+ when GraphQL::Language::Nodes::InlineFragment
234
+ node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
235
+ when GraphQL::Language::Nodes::FragmentSpread
236
+ frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
237
+ frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
238
+ else
239
+ raise "Unexpected selection comparison on #{node.class.name} (#{node})"
240
+ end
241
+ end
242
+
243
+ # TODO Dedup with interpreter
244
+ module ArgumentHelpers
245
+ module_function
246
+
247
+ def arguments(query, graphql_object, arg_owner, ast_node)
248
+ kwarg_arguments = {}
249
+ arg_defns = arg_owner.arguments
250
+ ast_node.arguments.each do |arg|
251
+ arg_defn = arg_defns[arg.name] || raise("Invariant: missing argument definition for #{arg.name.inspect} in #{arg_defns.keys} from #{arg_owner}")
252
+ # Need to distinguish between client-provided `nil`
253
+ # and nothing-at-all
254
+ is_present, value = arg_to_value(query, graphql_object, arg_defn.type, arg.value)
255
+ if is_present
256
+ # This doesn't apply to directives, which are legacy
257
+ # Can remove this when Skip and Include use classes or something.
258
+ if graphql_object
259
+ value = arg_defn.prepare_value(graphql_object, value)
260
+ end
261
+ kwarg_arguments[arg_defn.keyword] = value
262
+ end
263
+ end
264
+ arg_defns.each do |name, arg_defn|
265
+ if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
266
+ kwarg_arguments[arg_defn.keyword] = arg_defn.default_value
267
+ end
268
+ end
269
+ kwarg_arguments
270
+ end
271
+
272
+ # Get a Ruby-ready value from a client query.
273
+ # @param graphql_object [Object] The owner of the field whose argument this is
274
+ # @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
275
+ # @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
276
+ # @return [Array(is_present, value)]
277
+ def arg_to_value(query, graphql_object, arg_type, ast_value)
278
+ if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
279
+ # If it's not here, it will get added later
280
+ if query.variables.key?(ast_value.name)
281
+ return true, query.variables[ast_value.name]
282
+ else
283
+ return false, nil
284
+ end
285
+ elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
286
+ return true, nil
287
+ elsif arg_type.is_a?(GraphQL::Schema::NonNull)
288
+ arg_to_value(query, graphql_object, arg_type.of_type, ast_value)
289
+ elsif arg_type.is_a?(GraphQL::Schema::List)
290
+ # Treat a single value like a list
291
+ arg_value = Array(ast_value)
292
+ list = []
293
+ arg_value.map do |inner_v|
294
+ _present, value = arg_to_value(query, graphql_object, arg_type.of_type, inner_v)
295
+ list << value
296
+ end
297
+ return true, list
298
+ elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
299
+ # For these, `prepare` is applied during `#initialize`.
300
+ # Pass `nil` so it will be skipped in `#arguments`.
301
+ # What a mess.
302
+ args = arguments(query, nil, arg_type, ast_value)
303
+ # We're not tracking defaults_used, but for our purposes
304
+ # we compare the value to the default value.
305
+ return true, arg_type.new(ruby_kwargs: args, context: query.context, defaults_used: nil)
306
+ else
307
+ flat_value = flatten_ast_value(query, ast_value)
308
+ return true, arg_type.coerce_input(flat_value, query.context)
309
+ end
310
+ end
311
+
312
+ def flatten_ast_value(query, v)
313
+ case v
314
+ when GraphQL::Language::Nodes::Enum
315
+ v.name
316
+ when GraphQL::Language::Nodes::InputObject
317
+ h = {}
318
+ v.arguments.each do |arg|
319
+ h[arg.name] = flatten_ast_value(query, arg.value)
320
+ end
321
+ h
322
+ when Array
323
+ v.map { |v2| flatten_ast_value(query, v2) }
324
+ when GraphQL::Language::Nodes::VariableIdentifier
325
+ flatten_ast_value(query.variables[v.name])
326
+ else
327
+ v
328
+ end
329
+ end
330
+ end
331
+
332
+ # TODO dedup with interpreter
333
+ module FieldHelpers
334
+ module_function
335
+
336
+ def get_field(schema, owner_type, field_name)
337
+ field_defn = owner_type.get_field(field_name)
338
+ field_defn ||= if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
339
+ entry_point_field.metadata[:type_class]
340
+ elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
341
+ dynamic_field.metadata[:type_class]
342
+ else
343
+ nil
344
+ end
345
+
346
+ field_defn
347
+ end
348
+ end
349
+ end
350
+ end
351
+ end
@@ -29,8 +29,8 @@ module GraphQL
29
29
 
30
30
  include Tracing::Traceable
31
31
 
32
- attr_reader :context, :queries, :schema
33
- def initialize(schema:, queries:, context:)
32
+ attr_reader :context, :queries, :schema, :max_complexity
33
+ def initialize(schema:, queries:, context:, max_complexity:)
34
34
  @schema = schema
35
35
  @queries = queries
36
36
  @context = context
@@ -40,6 +40,7 @@ module GraphQL
40
40
  if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
41
41
  @tracers << GraphQL::Backtrace::Tracer
42
42
  end
43
+ @max_complexity = max_complexity
43
44
  end
44
45
 
45
46
  class << self
@@ -54,20 +55,20 @@ module GraphQL
54
55
  # @param max_complexity [Integer, nil]
55
56
  # @return [Array<Hash>] One result per query
56
57
  def run_queries(schema, queries, context: {}, max_complexity: schema.max_complexity)
57
- multiplex = self.new(schema: schema, queries: queries, context: context)
58
+ multiplex = self.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
58
59
  multiplex.trace("execute_multiplex", { multiplex: multiplex }) do
59
- if has_custom_strategy?(schema)
60
+ if supports_multiplexing?(schema)
61
+ instrument_and_analyze(multiplex) do
62
+ run_as_multiplex(multiplex)
63
+ end
64
+ else
60
65
  if queries.length != 1
61
66
  raise ArgumentError, "Multiplexing doesn't support custom execution strategies, run one query at a time instead"
62
67
  else
63
- instrument_and_analyze(multiplex, max_complexity: max_complexity) do
68
+ instrument_and_analyze(multiplex) do
64
69
  [run_one_legacy(schema, queries.first)]
65
70
  end
66
71
  end
67
- else
68
- instrument_and_analyze(multiplex, max_complexity: max_complexity) do
69
- run_as_multiplex(multiplex)
70
- end
71
72
  end
72
73
  end
73
74
  end
@@ -75,23 +76,25 @@ module GraphQL
75
76
  private
76
77
 
77
78
  def run_as_multiplex(multiplex)
79
+
80
+ multiplex.schema.query_execution_strategy.begin_multiplex(multiplex)
78
81
  queries = multiplex.queries
79
82
  # Do as much eager evaluation of the query as possible
80
83
  results = queries.map do |query|
81
- begin_query(query)
84
+ begin_query(query, multiplex)
82
85
  end
83
86
 
84
87
  # Then, work through lazy results in a breadth-first way
85
- GraphQL::Execution::Execute::ExecutionFunctions.lazy_resolve_root_selection(results, { multiplex: multiplex })
88
+ multiplex.schema.query_execution_strategy.finish_multiplex(results, multiplex)
86
89
 
87
90
  # Then, find all errors and assign the result to the query object
88
91
  results.each_with_index.map do |data_result, idx|
89
92
  query = queries[idx]
90
- finish_query(data_result, query)
93
+ finish_query(data_result, query, multiplex)
91
94
  # Get the Query::Result, not the Hash
92
95
  query.result
93
96
  end
94
- rescue StandardError
97
+ rescue Exception
95
98
  # Assign values here so that the query's `@executed` becomes true
96
99
  queries.map { |q| q.result_values ||= {} }
97
100
  raise
@@ -99,13 +102,14 @@ module GraphQL
99
102
 
100
103
  # @param query [GraphQL::Query]
101
104
  # @return [Hash] The initial result (may not be finished if there are lazy values)
102
- def begin_query(query)
105
+ def begin_query(query, multiplex)
103
106
  operation = query.selected_operation
104
- if operation.nil? || !query.valid?
107
+ if operation.nil? || !query.valid? || query.context.errors.any?
105
108
  NO_OPERATION
106
109
  else
107
110
  begin
108
- GraphQL::Execution::Execute::ExecutionFunctions.resolve_root_selection(query)
111
+ # These were checked to be the same in `#supports_multiplexing?`
112
+ query.schema.query_execution_strategy.begin_query(query, multiplex)
109
113
  rescue GraphQL::ExecutionError => err
110
114
  query.context.errors << err
111
115
  NO_OPERATION
@@ -116,19 +120,18 @@ module GraphQL
116
120
  # @param data_result [Hash] The result for the "data" key, if any
117
121
  # @param query [GraphQL::Query] The query which was run
118
122
  # @return [Hash] final result of this query, including all values and errors
119
- def finish_query(data_result, query)
123
+ def finish_query(data_result, query, multiplex)
120
124
  # Assign the result so that it can be accessed in instrumentation
121
125
  query.result_values = if data_result.equal?(NO_OPERATION)
122
- if !query.valid?
126
+ if !query.valid? || query.context.errors.any?
127
+ # A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
123
128
  { "errors" => query.static_errors.map(&:to_h) }
124
129
  else
125
130
  data_result
126
131
  end
127
132
  else
128
133
  # Use `context.value` which was assigned during execution
129
- result = {
130
- "data" => Execution::Flatten.call(query.context)
131
- }
134
+ result = query.schema.query_execution_strategy.finish_query(query, multiplex)
132
135
 
133
136
  if query.context.errors.any?
134
137
  error_result = query.context.errors.map(&:to_h)
@@ -153,24 +156,29 @@ module GraphQL
153
156
  end
154
157
  end
155
158
 
156
- def has_custom_strategy?(schema)
157
- schema.query_execution_strategy != GraphQL::Execution::Execute ||
158
- schema.mutation_execution_strategy != GraphQL::Execution::Execute ||
159
- schema.subscription_execution_strategy != GraphQL::Execution::Execute
159
+ DEFAULT_STRATEGIES = [
160
+ GraphQL::Execution::Execute,
161
+ GraphQL::Execution::Interpreter
162
+ ]
163
+ # @return [Boolean] True if the schema is only using one strategy, and it's one that supports multiplexing.
164
+ def supports_multiplexing?(schema)
165
+ schema_strategies = [schema.query_execution_strategy, schema.mutation_execution_strategy, schema.subscription_execution_strategy]
166
+ schema_strategies.uniq!
167
+ schema_strategies.size == 1 && DEFAULT_STRATEGIES.include?(schema_strategies.first)
160
168
  end
161
169
 
162
170
  # Apply multiplex & query instrumentation to `queries`.
163
171
  #
164
172
  # It yields when the queries should be executed, then runs teardown.
165
- def instrument_and_analyze(multiplex, max_complexity:)
173
+ def instrument_and_analyze(multiplex)
166
174
  GraphQL::Execution::Instrumentation.apply_instrumenters(multiplex) do
167
175
  schema = multiplex.schema
168
176
  multiplex_analyzers = schema.multiplex_analyzers
169
- if max_complexity
170
- multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity.new(max_complexity)]
177
+ if multiplex.max_complexity
178
+ multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity.new(multiplex.max_complexity)]
171
179
  end
172
180
 
173
- GraphQL::Analysis.analyze_multiplex(multiplex, multiplex_analyzers)
181
+ schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
174
182
  yield
175
183
  end
176
184
  end
@@ -3,6 +3,8 @@ require "graphql/execution/directive_checks"
3
3
  require "graphql/execution/execute"
4
4
  require "graphql/execution/flatten"
5
5
  require "graphql/execution/instrumentation"
6
+ require "graphql/execution/interpreter"
6
7
  require "graphql/execution/lazy"
8
+ require "graphql/execution/lookahead"
7
9
  require "graphql/execution/multiplex"
8
10
  require "graphql/execution/typecast"
@@ -4,7 +4,7 @@ module GraphQL
4
4
  # the error will be inserted into the response's `"errors"` key
5
5
  # and the field will resolve to `nil`.
6
6
  class ExecutionError < GraphQL::Error
7
- # @return [GraphQL::Language::Nodes::Field] the field where the error occured
7
+ # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
8
8
  attr_accessor :ast_node
9
9
 
10
10
  # @return [String] an array describing the JSON-path into the execution
data/lib/graphql/field.rb CHANGED
@@ -323,13 +323,7 @@ module GraphQL
323
323
 
324
324
  module DefaultLazyResolve
325
325
  def self.call(obj, args, ctx)
326
- method_name = ctx.schema.lazy_method_name(obj)
327
- next_obj = obj.public_send(method_name)
328
- if ctx.schema.lazy?(next_obj)
329
- call(next_obj, args, ctx)
330
- else
331
- next_obj
332
- end
326
+ ctx.schema.sync_lazy(obj)
333
327
  end
334
328
  end
335
329
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class IntegerEncodingError < GraphQL::RuntimeTypeError
4
+ # The value which couldn't be encoded
5
+ attr_reader :integer_value
6
+
7
+ def initialize(value)
8
+ @integer_value = value
9
+ super('Integer out of bounds.')
10
+ end
11
+ end
12
+ end