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
@@ -4,81 +4,75 @@ module GraphQL
4
4
  # Track fragment dependencies for operations
5
5
  # and expose the fragment definitions which
6
6
  # are used by a given operation
7
- class DefinitionDependencies
8
- def self.mount(visitor)
9
- deps = self.new
10
- deps.mount(visitor)
11
- deps
12
- end
7
+ module DefinitionDependencies
8
+ attr_reader :dependencies
13
9
 
14
- def initialize
15
- @node_paths = {}
10
+ def initialize(*)
11
+ super
12
+ @defdep_node_paths = {}
16
13
 
17
14
  # { name => node } pairs for fragments
18
- @fragment_definitions = {}
15
+ @defdep_fragment_definitions = {}
19
16
 
20
17
  # This tracks dependencies from fragment to Node where it was used
21
18
  # { fragment_definition_node => [dependent_node, dependent_node]}
22
- @dependent_definitions = Hash.new { |h, k| h[k] = Set.new }
19
+ @defdep_dependent_definitions = Hash.new { |h, k| h[k] = Set.new }
23
20
 
24
21
  # First-level usages of spreads within definitions
25
22
  # (When a key has an empty list as its value,
26
- # we can resolve that key's depenedents)
23
+ # we can resolve that key's dependents)
27
24
  # { definition_node => [node, node ...] }
28
- @immediate_dependencies = Hash.new { |h, k| h[k] = Set.new }
29
- end
30
-
31
- # A map of operation definitions to an array of that operation's dependencies
32
- # @return [DependencyMap]
33
- def dependency_map(&block)
34
- @dependency_map ||= resolve_dependencies(&block)
35
- end
25
+ @defdep_immediate_dependencies = Hash.new { |h, k| h[k] = Set.new }
36
26
 
37
- def mount(context)
38
- visitor = context.visitor
39
27
  # When we encounter a spread,
40
28
  # this node is the one who depends on it
41
- current_parent = nil
42
-
43
- visitor[GraphQL::Language::Nodes::Document] << ->(node, prev_node) {
44
- node.definitions.each do |definition|
45
- case definition
46
- when GraphQL::Language::Nodes::OperationDefinition
47
- when GraphQL::Language::Nodes::FragmentDefinition
48
- @fragment_definitions[definition.name] = definition
49
- end
50
- end
51
- }
29
+ @defdep_current_parent = nil
30
+ end
52
31
 
53
- visitor[GraphQL::Language::Nodes::OperationDefinition] << ->(node, prev_node) {
54
- @node_paths[node] = NodeWithPath.new(node, context.path)
55
- current_parent = node
32
+ def on_document(node, parent)
33
+ node.definitions.each do |definition|
34
+ if definition.is_a? GraphQL::Language::Nodes::FragmentDefinition
35
+ @defdep_fragment_definitions[definition.name] = definition
36
+ end
37
+ end
38
+ super
39
+ @dependencies = dependency_map { |defn, spreads, frag|
40
+ context.on_dependency_resolve_handlers.each { |h| h.call(defn, spreads, frag) }
56
41
  }
42
+ end
57
43
 
58
- visitor[GraphQL::Language::Nodes::OperationDefinition].leave << ->(node, prev_node) {
59
- current_parent = nil
60
- }
44
+ def on_operation_definition(node, prev_node)
45
+ @defdep_node_paths[node] = NodeWithPath.new(node, context.path)
46
+ @defdep_current_parent = node
47
+ super
48
+ @defdep_current_parent = nil
49
+ end
61
50
 
62
- visitor[GraphQL::Language::Nodes::FragmentDefinition] << ->(node, prev_node) {
63
- @node_paths[node] = NodeWithPath.new(node, context.path)
64
- current_parent = node
65
- }
51
+ def on_fragment_definition(node, parent)
52
+ @defdep_node_paths[node] = NodeWithPath.new(node, context.path)
53
+ @defdep_current_parent = node
54
+ super
55
+ @defdep_current_parent = nil
56
+ end
66
57
 
67
- visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << ->(node, prev_node) {
68
- current_parent = nil
69
- }
58
+ def on_fragment_spread(node, parent)
59
+ @defdep_node_paths[node] = NodeWithPath.new(node, context.path)
70
60
 
71
- visitor[GraphQL::Language::Nodes::FragmentSpread] << ->(node, prev_node) {
72
- @node_paths[node] = NodeWithPath.new(node, context.path)
61
+ # Track both sides of the dependency
62
+ @defdep_dependent_definitions[@defdep_fragment_definitions[node.name]] << @defdep_current_parent
63
+ @defdep_immediate_dependencies[@defdep_current_parent] << node
64
+ super
65
+ end
73
66
 
74
- # Track both sides of the dependency
75
- @dependent_definitions[@fragment_definitions[node.name]] << current_parent
76
- @immediate_dependencies[current_parent] << node
77
- }
67
+ # A map of operation definitions to an array of that operation's dependencies
68
+ # @return [DependencyMap]
69
+ def dependency_map(&block)
70
+ @dependency_map ||= resolve_dependencies(&block)
78
71
  end
79
72
 
73
+
80
74
  # Map definition AST nodes to the definition AST nodes they depend on.
81
- # Expose circular depednencies.
75
+ # Expose circular dependencies.
82
76
  class DependencyMap
83
77
  # @return [Array<GraphQL::Language::Nodes::FragmentDefinition>]
84
78
  attr_reader :cyclical_definitions
@@ -122,14 +116,14 @@ module GraphQL
122
116
  dependency_map = DependencyMap.new
123
117
  # Don't allow the loop to run more times
124
118
  # than the number of fragments in the document
125
- max_loops = @fragment_definitions.size
119
+ max_loops = @defdep_fragment_definitions.size
126
120
  loops = 0
127
121
 
128
122
  # Instead of tracking independent fragments _as you visit_,
129
123
  # determine them at the end. This way, we can treat fragments with the
130
124
  # same name as if they were the same name. If _any_ of the fragments
131
125
  # with that name has a dependency, we record it.
132
- independent_fragment_nodes = @fragment_definitions.values - @immediate_dependencies.keys
126
+ independent_fragment_nodes = @defdep_fragment_definitions.values - @defdep_immediate_dependencies.keys
133
127
 
134
128
  while fragment_node = independent_fragment_nodes.pop
135
129
  loops += 1
@@ -138,26 +132,26 @@ module GraphQL
138
132
  end
139
133
  # Since it's independent, let's remove it from here.
140
134
  # That way, we can use the remainder to identify cycles
141
- @immediate_dependencies.delete(fragment_node)
142
- fragment_usages = @dependent_definitions[fragment_node]
143
- if fragment_usages.none?
135
+ @defdep_immediate_dependencies.delete(fragment_node)
136
+ fragment_usages = @defdep_dependent_definitions[fragment_node]
137
+ if fragment_usages.empty?
144
138
  # If we didn't record any usages during the visit,
145
139
  # then this fragment is unused.
146
- dependency_map.unused_dependencies << @node_paths[fragment_node]
140
+ dependency_map.unused_dependencies << @defdep_node_paths[fragment_node]
147
141
  else
148
142
  fragment_usages.each do |definition_node|
149
143
  # Register the dependency AND second-order dependencies
150
144
  dependency_map[definition_node] << fragment_node
151
145
  dependency_map[definition_node].concat(dependency_map[fragment_node])
152
- # Since we've regestered it, remove it from our to-do list
153
- deps = @immediate_dependencies[definition_node]
146
+ # Since we've registered it, remove it from our to-do list
147
+ deps = @defdep_immediate_dependencies[definition_node]
154
148
  # Can't find a way to _just_ delete from `deps` and return the deleted entries
155
149
  removed, remaining = deps.partition { |spread| spread.name == fragment_node.name }
156
- @immediate_dependencies[definition_node] = remaining
150
+ @defdep_immediate_dependencies[definition_node] = remaining
157
151
  if block_given?
158
152
  yield(definition_node, removed, fragment_node)
159
153
  end
160
- if remaining.none? && definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
154
+ if remaining.empty? && definition_node.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
161
155
  # If all of this definition's dependencies have
162
156
  # been resolved, we can now resolve its
163
157
  # own dependents.
@@ -170,20 +164,20 @@ module GraphQL
170
164
  # If any dependencies were _unmet_
171
165
  # (eg, spreads with no corresponding definition)
172
166
  # then they're still in there
173
- @immediate_dependencies.each do |defn_node, deps|
167
+ @defdep_immediate_dependencies.each do |defn_node, deps|
174
168
  deps.each do |spread|
175
- if @fragment_definitions[spread.name].nil?
176
- dependency_map.unmet_dependencies[@node_paths[defn_node]] << @node_paths[spread]
169
+ if @defdep_fragment_definitions[spread.name].nil?
170
+ dependency_map.unmet_dependencies[@defdep_node_paths[defn_node]] << @defdep_node_paths[spread]
177
171
  deps.delete(spread)
178
172
  end
179
173
  end
180
- if deps.none?
181
- @immediate_dependencies.delete(defn_node)
174
+ if deps.empty?
175
+ @defdep_immediate_dependencies.delete(defn_node)
182
176
  end
183
177
  end
184
178
 
185
179
  # Anything left in @immediate_dependencies is cyclical
186
- cyclical_nodes = @immediate_dependencies.keys.map { |n| @node_paths[n] }
180
+ cyclical_nodes = @defdep_immediate_dependencies.keys.map { |n| @defdep_node_paths[n] }
187
181
  # @immediate_dependencies also includes operation names, but we don't care about
188
182
  # those. They became nil when we looked them up on `@fragment_definitions`, so remove them.
189
183
  cyclical_nodes.compact!
@@ -2,22 +2,23 @@
2
2
  module GraphQL
3
3
  module StaticValidation
4
4
  # Generates GraphQL-compliant validation message.
5
- class Message
5
+ class Error
6
6
  # Convenience for validators
7
- module MessageHelper
8
- # Error `message` is located at `node`
9
- def message(message, nodes, context: nil, path: nil)
7
+ module ErrorHelper
8
+ # Error `error_message` is located at `node`
9
+ def error(error_message, nodes, context: nil, path: nil, extensions: {})
10
10
  path ||= context.path
11
11
  nodes = Array(nodes)
12
- GraphQL::StaticValidation::Message.new(message, nodes: nodes, path: path)
12
+ GraphQL::StaticValidation::Error.new(error_message, nodes: nodes, path: path)
13
13
  end
14
14
  end
15
15
 
16
- attr_reader :message, :path
16
+ attr_reader :message
17
+ attr_accessor :path
17
18
 
18
- def initialize(message, path: [], nodes: [])
19
+ def initialize(message, path: nil, nodes: [])
19
20
  @message = message
20
- @nodes = nodes
21
+ @nodes = Array(nodes)
21
22
  @path = path
22
23
  end
23
24
 
@@ -25,9 +26,8 @@ module GraphQL
25
26
  def to_h
26
27
  {
27
28
  "message" => message,
28
- "locations" => locations,
29
- "fields" => path,
30
- }
29
+ "locations" => locations
30
+ }.tap { |h| h["path"] = path unless path.nil? }
31
31
  end
32
32
 
33
33
  private
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class InterpreterVisitor < BaseVisitor
5
+ include(GraphQL::StaticValidation::DefinitionDependencies)
6
+
7
+ StaticValidation::ALL_RULES.reverse_each do |r|
8
+ include(r)
9
+ end
10
+
11
+ include(ContextMethods)
12
+ end
13
+ end
14
+ end
@@ -9,29 +9,61 @@ module GraphQL
9
9
  end
10
10
 
11
11
  def validate(ast_value, type)
12
- if ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
13
- !type.kind.non_null?
12
+ if type.nil?
13
+ # this means we're an undefined argument, see #present_input_field_values_are_valid
14
+ return maybe_raise_if_invalid(ast_value) do
15
+ false
16
+ end
17
+ elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
18
+ maybe_raise_if_invalid(ast_value) do
19
+ !type.kind.non_null?
20
+ end
14
21
  elsif type.kind.non_null?
15
- (!ast_value.nil?) && validate(ast_value, type.of_type)
22
+ maybe_raise_if_invalid(ast_value) do
23
+ (!ast_value.nil?)
24
+ end && validate(ast_value, type.of_type)
16
25
  elsif type.kind.list?
17
26
  item_type = type.of_type
18
27
  ensure_array(ast_value).all? { |val| validate(val, item_type) }
19
28
  elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
20
29
  true
21
30
  elsif type.kind.scalar? && constant_scalar?(ast_value)
22
- type.valid_input?(ast_value, @context)
23
- elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
24
- type.valid_input?(ast_value.name, @context)
31
+ maybe_raise_if_invalid(ast_value) do
32
+ type.valid_input?(ast_value, @context)
33
+ end
34
+ elsif type.kind.enum?
35
+ maybe_raise_if_invalid(ast_value) do
36
+ if ast_value.is_a?(GraphQL::Language::Nodes::Enum)
37
+ type.valid_input?(ast_value.name, @context)
38
+ else
39
+ # if our ast_value isn't an Enum it's going to be invalid so return false
40
+ false
41
+ end
42
+ end
25
43
  elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
26
- required_input_fields_are_present(type, ast_value) &&
27
- present_input_field_values_are_valid(type, ast_value)
44
+ maybe_raise_if_invalid(ast_value) do
45
+ required_input_fields_are_present(type, ast_value) && present_input_field_values_are_valid(type, ast_value)
46
+ end
28
47
  else
29
- false
48
+ maybe_raise_if_invalid(ast_value) do
49
+ false
50
+ end
30
51
  end
31
52
  end
32
53
 
33
54
  private
34
55
 
56
+ def maybe_raise_if_invalid(ast_value)
57
+ ret = yield
58
+ if !@context.schema.error_bubbling && !ret
59
+ e = LiteralValidationError.new
60
+ e.ast_value = ast_value
61
+ raise e
62
+ else
63
+ ret
64
+ end
65
+ end
66
+
35
67
  # The GraphQL grammar supports variables embedded within scalars but graphql.js
36
68
  # doesn't support it so we won't either for simplicity
37
69
  def constant_scalar?(ast_value)
@@ -47,19 +79,30 @@ module GraphQL
47
79
  end
48
80
 
49
81
  def required_input_fields_are_present(type, ast_node)
82
+ # TODO - would be nice to use these to create an error message so the caller knows
83
+ # that required fields are missing
50
84
  required_field_names = @warden.arguments(type)
51
85
  .select { |f| f.type.kind.non_null? }
52
86
  .map(&:name)
53
87
  present_field_names = ast_node.arguments.map(&:name)
54
88
  missing_required_field_names = required_field_names - present_field_names
55
- missing_required_field_names.none?
89
+ if @context.schema.error_bubbling
90
+ missing_required_field_names.empty?
91
+ else
92
+ missing_required_field_names.all? do |name|
93
+ validate(GraphQL::Language::Nodes::NullValue.new(name: name), @warden.arguments(type).find { |f| f.name == name }.type )
94
+ end
95
+ end
56
96
  end
57
97
 
58
98
  def present_input_field_values_are_valid(type, ast_node)
59
99
  field_map = @warden.arguments(type).reduce({}) { |m, f| m[f.name] = f; m}
60
100
  ast_node.arguments.all? do |value|
61
101
  field = field_map[value.name]
62
- field && validate(value.value, field.type)
102
+ # we want to call validate on an argument even if it's an invalid one
103
+ # so that our raise exception is on it instead of the entire InputObject
104
+ type = field && field.type
105
+ validate(value.value, type)
63
106
  end
64
107
  end
65
108
 
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class NoValidateVisitor < StaticValidation::BaseVisitor
5
+ include(GraphQL::InternalRepresentation::Rewrite)
6
+ include(GraphQL::StaticValidation::DefinitionDependencies)
7
+ include(ContextMethods)
8
+ end
9
+ end
10
+ end
@@ -1,27 +1,98 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module StaticValidation
4
- class ArgumentLiteralsAreCompatible < GraphQL::StaticValidation::ArgumentsValidator
5
- def validate_node(parent, node, defn, context)
6
- return if node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
7
- arg_defn = defn.arguments[node.name]
8
- return unless arg_defn
9
-
10
- begin
11
- valid = context.valid_literal?(node.value, arg_defn.type)
12
- rescue GraphQL::CoercionError => err
13
- error_message = err.message
4
+ module ArgumentLiteralsAreCompatible
5
+ # TODO dedup with ArgumentsAreDefined
6
+ def on_argument(node, parent)
7
+ parent_defn = case parent
8
+ when GraphQL::Language::Nodes::InputObject
9
+ arg_defn = context.argument_definition
10
+ if arg_defn.nil?
11
+ nil
12
+ else
13
+ arg_ret_type = arg_defn.type.unwrap
14
+ if !arg_ret_type.is_a?(GraphQL::InputObjectType)
15
+ nil
16
+ else
17
+ arg_ret_type
18
+ end
19
+ end
20
+ when GraphQL::Language::Nodes::Directive
21
+ context.schema.directives[parent.name]
22
+ when GraphQL::Language::Nodes::Field
23
+ context.field_definition
24
+ else
25
+ raise "Unexpected argument parent: #{parent.class} (##{parent})"
14
26
  end
15
27
 
16
- return if valid
28
+ if parent_defn && !node.value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
29
+ arg_defn = parent_defn.arguments[node.name]
30
+ if arg_defn
31
+ begin
32
+ valid = context.valid_literal?(node.value, arg_defn.type)
33
+ rescue GraphQL::CoercionError => err
34
+ context.schema.error_bubbling
35
+ if !context.schema.error_bubbling && !arg_defn.type.unwrap.kind.scalar?
36
+ # if error bubbling is disabled and the arg that caused this error isn't a scalar then
37
+ # short-circuit here so we avoid bubbling this up to whatever input_object / array contains us
38
+ return super
39
+ end
40
+ error = GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new(err.message, nodes: parent, type: "CoercionError")
41
+ rescue GraphQL::LiteralValidationError => err
42
+ # check to see if the ast node that caused the error to be raised is
43
+ # the same as the node we were checking here.
44
+ matched = if arg_defn.type.kind.list?
45
+ # for a list we claim an error if the node is contained in our list
46
+ Array(node.value).include?(err.ast_value)
47
+ elsif arg_defn.type.kind.input_object? && node.value.is_a?(GraphQL::Language::Nodes::InputObject)
48
+ # for an input object we check the arguments
49
+ node.value.arguments.include?(err.ast_value)
50
+ else
51
+ # otherwise we just check equality
52
+ node.value == (err.ast_value)
53
+ end
54
+ if !matched
55
+ # This node isn't the node that caused the error,
56
+ # So halt this visit but continue visiting the rest of the tree
57
+ return super
58
+ end
59
+ end
17
60
 
18
- error_message ||= begin
19
- kind_of_node = node_type(parent)
20
- error_arg_name = parent_name(parent, defn)
21
- "Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value. Expected type '#{arg_defn.type}'."
61
+ if !valid
62
+ error ||= begin
63
+ kind_of_node = node_type(parent)
64
+ error_arg_name = parent_name(parent, parent_defn)
65
+
66
+ GraphQL::StaticValidation::ArgumentLiteralsAreCompatibleError.new(
67
+ "Argument '#{node.name}' on #{kind_of_node} '#{error_arg_name}' has an invalid value. Expected type '#{arg_defn.type}'.",
68
+ nodes: parent,
69
+ type: kind_of_node,
70
+ argument: node.name
71
+ )
72
+ end
73
+ add_error(error)
74
+ end
75
+ end
76
+ end
77
+
78
+ super
79
+ end
80
+
81
+
82
+ private
83
+
84
+ def parent_name(parent, type_defn)
85
+ if parent.is_a?(GraphQL::Language::Nodes::Field)
86
+ parent.alias || parent.name
87
+ elsif parent.is_a?(GraphQL::Language::Nodes::InputObject)
88
+ type_defn.name
89
+ else
90
+ parent.name
22
91
  end
92
+ end
23
93
 
24
- context.errors << message(error_message, parent, context: context)
94
+ def node_type(parent)
95
+ parent.class.name.split("::").last
25
96
  end
26
97
  end
27
98
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class ArgumentLiteralsAreCompatibleError < StaticValidation::Error
5
+ attr_reader :type_name
6
+ attr_reader :argument_name
7
+
8
+ def initialize(message, path: nil, nodes: [], type:, argument: nil)
9
+ super(message, path: path, nodes: nodes)
10
+ @type_name = type
11
+ @argument_name = argument
12
+ end
13
+
14
+ # A hash representation of this Message
15
+ def to_h
16
+ extensions = {
17
+ "code" => code,
18
+ "typeName" => type_name
19
+ }.tap { |h| h["argumentName"] = argument_name unless argument_name.nil? }
20
+
21
+ super.merge({
22
+ "extensions" => extensions
23
+ })
24
+ end
25
+
26
+ def code
27
+ "argumentLiteralsIncompatible"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,27 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module StaticValidation
4
- class ArgumentNamesAreUnique
5
- include GraphQL::StaticValidation::Message::MessageHelper
4
+ module ArgumentNamesAreUnique
5
+ include GraphQL::StaticValidation::Error::ErrorHelper
6
6
 
7
- def validate(context)
8
- context.visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) {
9
- validate_arguments(node, context)
10
- }
7
+ def on_field(node, parent)
8
+ validate_arguments(node)
9
+ super
10
+ end
11
11
 
12
- context.visitor[GraphQL::Language::Nodes::Directive] << ->(node, parent) {
13
- validate_arguments(node, context)
14
- }
12
+ def on_directive(node, parent)
13
+ validate_arguments(node)
14
+ super
15
15
  end
16
16
 
17
- def validate_arguments(node, context)
17
+ def validate_arguments(node)
18
18
  argument_defns = node.arguments
19
19
  if argument_defns.any?
20
20
  args_by_name = Hash.new { |h, k| h[k] = [] }
21
21
  argument_defns.each { |a| args_by_name[a.name] << a }
22
22
  args_by_name.each do |name, defns|
23
23
  if defns.size > 1
24
- context.errors << message("There can be only one argument named \"#{name}\"", defns, context: context)
24
+ add_error(GraphQL::StaticValidation::ArgumentNamesAreUniqueError.new("There can be only one argument named \"#{name}\"", nodes: defns, name: name))
25
25
  end
26
26
  end
27
27
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class ArgumentNamesAreUniqueError < StaticValidation::Error
5
+ attr_reader :name
6
+
7
+ def initialize(message, path: nil, nodes: [], name:)
8
+ super(message, path: path, nodes: nodes)
9
+ @name = name
10
+ end
11
+
12
+ # A hash representation of this Message
13
+ def to_h
14
+ extensions = {
15
+ "code" => code,
16
+ "name" => name
17
+ }
18
+
19
+ super.merge({
20
+ "extensions" => extensions
21
+ })
22
+ end
23
+
24
+ def code
25
+ "argumentNotUnique"
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -1,18 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  module StaticValidation
4
- class ArgumentsAreDefined < GraphQL::StaticValidation::ArgumentsValidator
5
- def validate_node(parent, node, defn, context)
6
- argument_defn = context.warden.arguments(defn).find { |arg| arg.name == node.name }
7
- if argument_defn.nil?
4
+ module ArgumentsAreDefined
5
+ def on_argument(node, parent)
6
+ parent_defn = case parent
7
+ when GraphQL::Language::Nodes::InputObject
8
+ arg_defn = context.argument_definition
9
+ if arg_defn.nil?
10
+ nil
11
+ else
12
+ arg_ret_type = arg_defn.type.unwrap
13
+ if !arg_ret_type.is_a?(GraphQL::InputObjectType)
14
+ nil
15
+ else
16
+ arg_ret_type
17
+ end
18
+ end
19
+ when GraphQL::Language::Nodes::Directive
20
+ context.schema.directives[parent.name]
21
+ when GraphQL::Language::Nodes::Field
22
+ context.field_definition
23
+ else
24
+ raise "Unexpected argument parent: #{parent.class} (##{parent})"
25
+ end
26
+
27
+ if parent_defn && context.warden.arguments(parent_defn).any? { |arg| arg.name == node.name }
28
+ super
29
+ elsif parent_defn
8
30
  kind_of_node = node_type(parent)
9
- error_arg_name = parent_name(parent, defn)
10
- context.errors << message("#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'", node, context: context)
11
- GraphQL::Language::Visitor::SKIP
31
+ error_arg_name = parent_name(parent, parent_defn)
32
+ add_error(GraphQL::StaticValidation::ArgumentsAreDefinedError.new(
33
+ "#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'",
34
+ nodes: node,
35
+ name: error_arg_name,
36
+ type: kind_of_node,
37
+ argument: node.name
38
+ ))
39
+ else
40
+ # Some other weird error
41
+ super
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def parent_name(parent, type_defn)
48
+ if parent.is_a?(GraphQL::Language::Nodes::Field)
49
+ parent.alias || parent.name
50
+ elsif parent.is_a?(GraphQL::Language::Nodes::InputObject)
51
+ type_defn.name
12
52
  else
13
- nil
53
+ parent.name
14
54
  end
15
55
  end
56
+
57
+ def node_type(parent)
58
+ parent.class.name.split("::").last
59
+ end
16
60
  end
17
61
  end
18
62
  end