graphql 1.8.7 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
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