graphql 1.9.0.pre1 → 1.9.0.pre2

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 (235) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +5 -1
  3. data/lib/graphql/analysis/ast/analyzer.rb +1 -1
  4. data/lib/graphql/analysis/ast/visitor.rb +6 -1
  5. data/lib/graphql/backwards_compatibility.rb +1 -1
  6. data/lib/graphql/dig.rb +19 -0
  7. data/lib/graphql/directive.rb +13 -1
  8. data/lib/graphql/directive/include_directive.rb +1 -7
  9. data/lib/graphql/directive/skip_directive.rb +1 -8
  10. data/lib/graphql/execution/interpreter.rb +23 -13
  11. data/lib/graphql/execution/interpreter/resolve.rb +56 -0
  12. data/lib/graphql/execution/interpreter/runtime.rb +174 -74
  13. data/lib/graphql/execution/lazy.rb +7 -1
  14. data/lib/graphql/execution/lookahead.rb +71 -6
  15. data/lib/graphql/execution_error.rb +1 -1
  16. data/lib/graphql/introspection/entry_points.rb +5 -1
  17. data/lib/graphql/introspection/type_type.rb +4 -4
  18. data/lib/graphql/language.rb +0 -1
  19. data/lib/graphql/language/block_string.rb +37 -0
  20. data/lib/graphql/language/document_from_schema_definition.rb +1 -1
  21. data/lib/graphql/language/lexer.rb +55 -36
  22. data/lib/graphql/language/lexer.rl +8 -3
  23. data/lib/graphql/language/nodes.rb +27 -4
  24. data/lib/graphql/language/parser.rb +55 -55
  25. data/lib/graphql/language/parser.y +11 -11
  26. data/lib/graphql/language/printer.rb +1 -1
  27. data/lib/graphql/language/visitor.rb +22 -13
  28. data/lib/graphql/literal_validation_error.rb +6 -0
  29. data/lib/graphql/query.rb +13 -0
  30. data/lib/graphql/query/arguments.rb +2 -1
  31. data/lib/graphql/query/context.rb +3 -10
  32. data/lib/graphql/query/executor.rb +1 -1
  33. data/lib/graphql/query/validation_pipeline.rb +1 -1
  34. data/lib/graphql/relay/connection_resolve.rb +1 -1
  35. data/lib/graphql/relay/relation_connection.rb +1 -1
  36. data/lib/graphql/schema.rb +81 -11
  37. data/lib/graphql/schema/argument.rb +1 -1
  38. data/lib/graphql/schema/build_from_definition.rb +2 -4
  39. data/lib/graphql/schema/directive.rb +103 -0
  40. data/lib/graphql/schema/directive/feature.rb +66 -0
  41. data/lib/graphql/schema/directive/include.rb +25 -0
  42. data/lib/graphql/schema/directive/skip.rb +25 -0
  43. data/lib/graphql/schema/directive/transform.rb +48 -0
  44. data/lib/graphql/schema/enum_value.rb +2 -2
  45. data/lib/graphql/schema/field.rb +63 -17
  46. data/lib/graphql/schema/input_object.rb +1 -0
  47. data/lib/graphql/schema/member/base_dsl_methods.rb +4 -2
  48. data/lib/graphql/schema/member/build_type.rb +33 -1
  49. data/lib/graphql/schema/member/has_fields.rb +8 -73
  50. data/lib/graphql/schema/relay_classic_mutation.rb +6 -1
  51. data/lib/graphql/schema/resolver.rb +1 -1
  52. data/lib/graphql/static_validation.rb +2 -1
  53. data/lib/graphql/static_validation/all_rules.rb +1 -0
  54. data/lib/graphql/static_validation/base_visitor.rb +25 -10
  55. data/lib/graphql/static_validation/definition_dependencies.rb +3 -3
  56. data/lib/graphql/static_validation/{message.rb → error.rb} +11 -11
  57. data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
  58. data/lib/graphql/static_validation/literal_validator.rb +54 -11
  59. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +34 -5
  60. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +31 -0
  61. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +2 -2
  62. data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
  63. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +7 -1
  64. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +35 -0
  65. data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -1
  66. data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
  67. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +11 -2
  68. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
  69. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +11 -2
  70. data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
  71. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -2
  72. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
  73. data/lib/graphql/static_validation/rules/fields_will_merge.rb +24 -6
  74. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +32 -0
  75. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +5 -1
  76. data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
  77. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +8 -1
  78. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
  79. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +5 -1
  80. data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
  81. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +6 -1
  82. data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
  83. data/lib/graphql/static_validation/rules/fragments_are_named.rb +4 -1
  84. data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
  85. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +5 -1
  86. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
  87. data/lib/graphql/static_validation/rules/fragments_are_used.rb +13 -3
  88. data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
  89. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +4 -1
  90. data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
  91. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +2 -2
  92. data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
  93. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +9 -2
  94. data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
  95. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +7 -1
  96. data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
  97. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +47 -0
  98. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
  99. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +4 -1
  100. data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
  101. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +4 -3
  102. data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
  103. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +20 -6
  104. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
  105. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +5 -1
  106. data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
  107. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -1
  108. data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
  109. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -2
  110. data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
  111. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +18 -2
  112. data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
  113. data/lib/graphql/static_validation/validator.rb +24 -14
  114. data/lib/graphql/tracing/new_relic_tracing.rb +2 -2
  115. data/lib/graphql/tracing/skylight_tracing.rb +2 -2
  116. data/lib/graphql/unauthorized_field_error.rb +23 -0
  117. data/lib/graphql/version.rb +1 -1
  118. data/spec/graphql/analysis/ast_spec.rb +40 -0
  119. data/spec/graphql/authorization_spec.rb +93 -20
  120. data/spec/graphql/base_type_spec.rb +3 -1
  121. data/spec/graphql/execution/interpreter_spec.rb +127 -4
  122. data/spec/graphql/execution/lazy_spec.rb +49 -0
  123. data/spec/graphql/execution/lookahead_spec.rb +113 -21
  124. data/spec/graphql/execution/multiplex_spec.rb +2 -1
  125. data/spec/graphql/introspection/type_type_spec.rb +1 -1
  126. data/spec/graphql/language/lexer_spec.rb +72 -3
  127. data/spec/graphql/language/printer_spec.rb +18 -6
  128. data/spec/graphql/query/arguments_spec.rb +21 -0
  129. data/spec/graphql/query/context_spec.rb +10 -0
  130. data/spec/graphql/schema/build_from_definition_spec.rb +144 -29
  131. data/spec/graphql/schema/directive/feature_spec.rb +81 -0
  132. data/spec/graphql/schema/directive/transform_spec.rb +39 -0
  133. data/spec/graphql/schema/enum_spec.rb +5 -3
  134. data/spec/graphql/schema/field_extension_spec.rb +3 -3
  135. data/spec/graphql/schema/field_spec.rb +19 -0
  136. data/spec/graphql/schema/input_object_spec.rb +81 -0
  137. data/spec/graphql/schema/member/build_type_spec.rb +46 -0
  138. data/spec/graphql/schema/member/scoped_spec.rb +3 -3
  139. data/spec/graphql/schema/printer_spec.rb +244 -96
  140. data/spec/graphql/schema/relay_classic_mutation_spec.rb +26 -0
  141. data/spec/graphql/schema/resolver_spec.rb +1 -1
  142. data/spec/graphql/schema/warden_spec.rb +35 -11
  143. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +212 -72
  144. data/spec/graphql/static_validation/rules/argument_names_are_unique_spec.rb +2 -2
  145. data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +72 -29
  146. data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
  147. data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
  148. data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +10 -5
  149. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -5
  150. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -1
  151. data/spec/graphql/static_validation/rules/fragment_names_are_unique_spec.rb +2 -1
  152. data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
  153. data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +4 -2
  154. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
  155. data/spec/graphql/static_validation/rules/fragments_are_named_spec.rb +2 -1
  156. data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +6 -3
  157. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +22 -2
  158. data/spec/graphql/static_validation/rules/mutation_root_exists_spec.rb +2 -1
  159. data/spec/graphql/static_validation/rules/operation_names_are_valid_spec.rb +6 -3
  160. data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +13 -4
  161. data/spec/graphql/static_validation/rules/required_input_object_attributes_are_present_spec.rb +58 -0
  162. data/spec/graphql/static_validation/rules/subscription_root_exists_spec.rb +2 -1
  163. data/spec/graphql/static_validation/rules/unique_directives_per_location_spec.rb +14 -7
  164. data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +14 -7
  165. data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
  166. data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +8 -4
  167. data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
  168. data/spec/graphql/static_validation/validator_spec.rb +6 -4
  169. data/spec/graphql/tracing/new_relic_tracing_spec.rb +10 -0
  170. data/spec/graphql/tracing/skylight_tracing_spec.rb +10 -0
  171. data/spec/graphql/types/iso_8601_date_time_spec.rb +1 -2
  172. data/spec/integration/mongoid/star_trek/schema.rb +5 -5
  173. data/spec/integration/rails/graphql/relay/relation_connection_spec.rb +37 -8
  174. data/spec/integration/rails/graphql/schema_spec.rb +2 -2
  175. data/spec/integration/rails/spec_helper.rb +10 -0
  176. data/spec/integration/tmp/app/graphql/types/bird_type.rb +7 -0
  177. data/spec/integration/tmp/dummy/Gemfile +45 -0
  178. data/spec/integration/tmp/dummy/README.rdoc +28 -0
  179. data/spec/integration/tmp/dummy/Rakefile +6 -0
  180. data/spec/integration/tmp/dummy/app/assets/javascripts/application.js +16 -0
  181. data/spec/integration/tmp/dummy/app/assets/stylesheets/application.css +15 -0
  182. data/spec/integration/tmp/dummy/app/controllers/application_controller.rb +5 -0
  183. data/spec/integration/tmp/dummy/app/controllers/graphql_controller.rb +43 -0
  184. data/spec/integration/tmp/dummy/app/graphql/dummy_schema.rb +34 -0
  185. data/spec/integration/tmp/dummy/app/graphql/types/base_enum.rb +4 -0
  186. data/spec/integration/tmp/dummy/app/graphql/types/base_input_object.rb +4 -0
  187. data/spec/integration/tmp/dummy/app/graphql/types/base_interface.rb +5 -0
  188. data/spec/integration/tmp/dummy/app/graphql/types/base_object.rb +4 -0
  189. data/spec/integration/tmp/dummy/app/graphql/types/base_scalar.rb +4 -0
  190. data/spec/integration/tmp/dummy/app/graphql/types/base_union.rb +4 -0
  191. data/spec/integration/tmp/dummy/app/graphql/types/mutation_type.rb +10 -0
  192. data/spec/integration/tmp/dummy/app/graphql/types/query_type.rb +15 -0
  193. data/spec/integration/tmp/dummy/app/helpers/application_helper.rb +2 -0
  194. data/spec/integration/tmp/dummy/app/views/layouts/application.html.erb +14 -0
  195. data/spec/integration/tmp/dummy/bin/bundle +3 -0
  196. data/spec/integration/tmp/dummy/bin/rails +4 -0
  197. data/spec/integration/tmp/dummy/bin/rake +4 -0
  198. data/spec/integration/tmp/dummy/bin/setup +29 -0
  199. data/spec/integration/tmp/dummy/config.ru +4 -0
  200. data/spec/integration/tmp/dummy/config/application.rb +32 -0
  201. data/spec/integration/tmp/dummy/config/boot.rb +3 -0
  202. data/spec/integration/tmp/dummy/config/environment.rb +5 -0
  203. data/spec/integration/tmp/dummy/config/environments/development.rb +38 -0
  204. data/spec/integration/tmp/dummy/config/environments/production.rb +76 -0
  205. data/spec/integration/tmp/dummy/config/environments/test.rb +42 -0
  206. data/spec/integration/tmp/dummy/config/initializers/assets.rb +11 -0
  207. data/spec/integration/tmp/dummy/config/initializers/backtrace_silencers.rb +7 -0
  208. data/spec/integration/tmp/dummy/config/initializers/cookies_serializer.rb +3 -0
  209. data/spec/integration/tmp/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  210. data/spec/integration/tmp/dummy/config/initializers/inflections.rb +16 -0
  211. data/spec/integration/tmp/dummy/config/initializers/mime_types.rb +4 -0
  212. data/spec/integration/tmp/dummy/config/initializers/session_store.rb +3 -0
  213. data/spec/integration/tmp/dummy/config/initializers/to_time_preserves_timezone.rb +10 -0
  214. data/spec/integration/tmp/dummy/config/initializers/wrap_parameters.rb +9 -0
  215. data/spec/integration/tmp/dummy/config/locales/en.yml +23 -0
  216. data/spec/integration/tmp/dummy/config/routes.rb +61 -0
  217. data/spec/integration/tmp/dummy/config/secrets.yml +22 -0
  218. data/spec/integration/tmp/dummy/db/seeds.rb +7 -0
  219. data/spec/integration/tmp/dummy/public/404.html +67 -0
  220. data/spec/integration/tmp/dummy/public/422.html +67 -0
  221. data/spec/integration/tmp/dummy/public/500.html +66 -0
  222. data/spec/integration/tmp/dummy/public/favicon.ico +0 -0
  223. data/spec/integration/tmp/dummy/public/robots.txt +5 -0
  224. data/spec/support/dummy/schema.rb +2 -2
  225. data/spec/support/error_bubbling_helpers.rb +23 -0
  226. data/spec/support/jazz.rb +53 -6
  227. data/spec/support/lazy_helpers.rb +26 -8
  228. data/spec/support/new_relic.rb +3 -0
  229. data/spec/support/skylight.rb +3 -0
  230. data/spec/support/star_wars/schema.rb +13 -9
  231. data/spec/support/static_validation_helpers.rb +3 -1
  232. metadata +145 -22
  233. data/lib/graphql/language/comments.rb +0 -45
  234. data/spec/graphql/schema/member/has_fields_spec.rb +0 -132
  235. data/spec/integration/tmp/app/graphql/types/family_type.rb +0 -9
@@ -19,7 +19,7 @@ describe GraphQL::StaticValidation::ArgumentNamesAreUnique do
19
19
  error = errors.first
20
20
  assert_equal 'There can be only one argument named "id"', error["message"]
21
21
  assert_equal [{ "line" => 2, "column" => 18}, { "line" => 2, "column" => 25 }], error["locations"]
22
- assert_equal ["query GetStuff", "c1"], error["fields"]
22
+ assert_equal ["query GetStuff", "c1"], error["path"]
23
23
  end
24
24
  end
25
25
 
@@ -38,7 +38,7 @@ describe GraphQL::StaticValidation::ArgumentNamesAreUnique do
38
38
  error = errors.first
39
39
  assert_equal 'There can be only one argument named "if"', error["message"]
40
40
  assert_equal [{ "line" => 2, "column" => 34}, { "line" => 2, "column" => 44 }], error["locations"]
41
- assert_equal ["query GetStuff", "c1"], error["fields"]
41
+ assert_equal ["query GetStuff", "c1"], error["path"]
42
42
  end
43
43
  end
44
44
  end
@@ -3,6 +3,7 @@ require "spec_helper"
3
3
 
4
4
  describe GraphQL::StaticValidation::ArgumentsAreDefined do
5
5
  include StaticValidationHelpers
6
+ include ErrorBubblingHelpers
6
7
 
7
8
  let(:query_string) {"
8
9
  query getCheese {
@@ -17,38 +18,79 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
17
18
  }
18
19
  "}
19
20
 
20
- it "finds undefined arguments to fields and directives" do
21
- # There's an extra error here, the unexpected argument on "DairyProductInput"
22
- # triggers _another_ error that the field expected a different type
23
- assert_equal(5, errors.length)
21
+ describe "finds undefined arguments to fields and directives" do
22
+ it "works with error bubbling" do
23
+ with_error_bubbling(Dummy::Schema) do
24
+ # There's an extra error here, the unexpected argument on "DairyProductInput"
25
+ # triggers _another_ error that the field expected a different type
26
+ assert_equal(6, errors.length)
24
27
 
25
- query_root_error = {
26
- "message"=>"Field 'cheese' doesn't accept argument 'silly'",
27
- "locations"=>[{"line"=>4, "column"=>14}],
28
- "fields"=>["query getCheese", "cheese", "silly"],
29
- }
30
- assert_includes(errors, query_root_error)
28
+ query_root_error = {
29
+ "message"=>"Field 'cheese' doesn't accept argument 'silly'",
30
+ "locations"=>[{"line"=>4, "column"=>14}],
31
+ "path"=>["query getCheese", "cheese", "silly"],
32
+ "extensions"=>{
33
+ "code"=>"argumentNotAccepted",
34
+ "name"=>"cheese",
35
+ "typeName"=>"Field",
36
+ "argumentName"=>"silly"
37
+ },
38
+ }
39
+ assert_includes(errors, query_root_error)
31
40
 
32
- input_obj_record = {
33
- "message"=>"InputObject 'DairyProductInput' doesn't accept argument 'wacky'",
34
- "locations"=>[{"line"=>5, "column"=>30}],
35
- "fields"=>["query getCheese", "searchDairy", "product", "wacky"],
36
- }
37
- assert_includes(errors, input_obj_record)
41
+ input_obj_record = {
42
+ "message"=>"InputObject 'DairyProductInput' doesn't accept argument 'wacky'",
43
+ "locations"=>[{"line"=>5, "column"=>30}],
44
+ "path"=>["query getCheese", "searchDairy", "product", "wacky"],
45
+ "extensions"=>{
46
+ "code"=>"argumentNotAccepted",
47
+ "name"=>"DairyProductInput",
48
+ "typeName"=>"InputObject",
49
+ "argumentName"=>"wacky"
50
+ },
51
+ }
52
+ assert_includes(errors, input_obj_record)
38
53
 
39
- fragment_error = {
40
- "message"=>"Field 'similarCheese' doesn't accept argument 'nonsense'",
41
- "locations"=>[{"line"=>9, "column"=>36}],
42
- "fields"=>["fragment cheeseFields", "similarCheese", "nonsense"],
43
- }
44
- assert_includes(errors, fragment_error)
54
+ fragment_error = {
55
+ "message"=>"Field 'similarCheese' doesn't accept argument 'nonsense'",
56
+ "locations"=>[{"line"=>9, "column"=>36}],
57
+ "path"=>["fragment cheeseFields", "similarCheese", "nonsense"],
58
+ "extensions"=>{
59
+ "code"=>"argumentNotAccepted",
60
+ "name"=>"similarCheese",
61
+ "typeName"=>"Field",
62
+ "argumentName"=>"nonsense",
63
+ },
64
+ }
65
+ assert_includes(errors, fragment_error)
45
66
 
46
- directive_error = {
47
- "message"=>"Directive 'skip' doesn't accept argument 'something'",
48
- "locations"=>[{"line"=>10, "column"=>16}],
49
- "fields"=>["fragment cheeseFields", "id", "something"],
50
- }
51
- assert_includes(errors, directive_error)
67
+ directive_error = {
68
+ "message"=>"Directive 'skip' doesn't accept argument 'something'",
69
+ "locations"=>[{"line"=>10, "column"=>16}],
70
+ "path"=>["fragment cheeseFields", "id", "something"],
71
+ "extensions"=>{
72
+ "code"=>"argumentNotAccepted",
73
+ "name"=>"skip",
74
+ "typeName"=>"Directive",
75
+ "argumentName"=>"something",
76
+ },
77
+ }
78
+ assert_includes(errors, directive_error)
79
+ end
80
+ end
81
+
82
+ it "works without error bubbling" do
83
+ without_error_bubbling(Dummy::Schema) do
84
+ assert_equal(5, errors.length)
85
+
86
+ extra_error = {
87
+ "message"=>"Argument 'product' on Field 'searchDairy' has an invalid value. Expected type '[DairyProductInput]'.",
88
+ "locations"=>[{"line"=>5, "column"=>7}],
89
+ "path"=>["query getCheese", "searchDairy", "product"]
90
+ }
91
+ refute_includes(errors, extra_error)
92
+ end
93
+ end
52
94
  end
53
95
 
54
96
  describe "dynamic fields" do
@@ -62,7 +104,8 @@ describe GraphQL::StaticValidation::ArgumentsAreDefined do
62
104
  assert_includes(errors, {
63
105
  "message"=>"Field '__type' doesn't accept argument 'somethingInvalid'",
64
106
  "locations"=>[{"line"=>3, "column"=>16}],
65
- "fields"=>["query", "__type", "somethingInvalid"],
107
+ "path"=>["query", "__type", "somethingInvalid"],
108
+ "extensions"=>{"code"=>"argumentNotAccepted", "name"=>"__type", "typeName"=>"Field", "argumentName"=>"somethingInvalid"}
66
109
  })
67
110
  end
68
111
  end
@@ -20,11 +20,13 @@ describe GraphQL::StaticValidation::DirectivesAreDefined do
20
20
  {
21
21
  "message"=>"Directive @nonsense is not defined",
22
22
  "locations"=>[{"line"=>5, "column"=>16}],
23
- "fields"=>["query getCheese", "okCheese", "source"],
23
+ "path"=>["query getCheese", "okCheese", "source"],
24
+ "extensions"=>{"code"=>"undefinedDirective", "directiveName"=>"nonsense"}
24
25
  }, {
25
26
  "message"=>"Directive @moreNonsense is not defined",
26
27
  "locations"=>[{"line"=>7, "column"=>18}],
27
- "fields"=>["query getCheese", "okCheese", "... on Cheese", "flavor"],
28
+ "path"=>["query getCheese", "okCheese", "... on Cheese", "flavor"],
29
+ "extensions"=>{"code"=>"undefinedDirective", "directiveName"=>"moreNonsense"}
28
30
  }
29
31
  ]
30
32
  assert_equal(expected, errors)
@@ -26,12 +26,14 @@ describe GraphQL::StaticValidation::DirectivesAreInValidLocations do
26
26
  {
27
27
  "message"=> "'@skip' can't be applied to queries (allowed: fields, fragment spreads, inline fragments)",
28
28
  "locations"=>[{"line"=>2, "column"=>21}],
29
- "fields"=>["query getCheese"],
29
+ "path"=>["query getCheese"],
30
+ "extensions"=>{"code"=>"directiveCannotBeApplied", "targetName"=>"queries", "name"=>"skip"}
30
31
  },
31
32
  {
32
33
  "message"=>"'@skip' can't be applied to fragment definitions (allowed: fields, fragment spreads, inline fragments)",
33
34
  "locations"=>[{"line"=>13, "column"=>33}],
34
- "fields"=>["fragment whatever"],
35
+ "path"=>["fragment whatever"],
36
+ "extensions"=>{"code"=>"directiveCannotBeApplied", "targetName"=>"fragment definitions", "name"=>"skip"}
35
37
  },
36
38
  ]
37
39
  assert_equal(expected, errors)
@@ -31,7 +31,8 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
31
31
  {
32
32
  "message"=>"Field 'notDefinedField' doesn't exist on type 'Query'",
33
33
  "locations"=>[{"line"=>1, "column"=>18}],
34
- "fields"=>["query getStuff", "notDefinedField"],
34
+ "path"=>["query getStuff", "notDefinedField"],
35
+ "extensions"=>{"code"=>"undefinedField", "typeName"=>"Query", "fieldName"=>"notDefinedField"}
35
36
  }
36
37
  ]
37
38
  assert_equal(expected_errors, errors)
@@ -46,7 +47,8 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
46
47
  {
47
48
  "message"=>"Field 'amountThatILikeIt' doesn't exist on type 'Edible'",
48
49
  "locations"=>[{"line"=>1, "column"=>35}],
49
- "fields"=>["query getStuff", "favoriteEdible", "amountThatILikeIt"],
50
+ "path"=>["query getStuff", "favoriteEdible", "amountThatILikeIt"],
51
+ "extensions"=>{"code"=>"undefinedField", "typeName"=>"Edible", "fieldName"=>"amountThatILikeIt"}
50
52
  }
51
53
  ]
52
54
  assert_equal(expected_errors, errors)
@@ -67,7 +69,8 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
67
69
  "locations"=>[
68
70
  {"line"=>3, "column"=>7}
69
71
  ],
70
- "fields"=>["fragment dpFields", "source"],
72
+ "path"=>["fragment dpFields", "source"],
73
+ "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"DairyProduct"}
71
74
  }
72
75
  ]
73
76
  assert_equal(expected_errors, errors)
@@ -120,7 +123,8 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
120
123
  "locations"=>[
121
124
  {"line"=>2, "column"=>33}
122
125
  ],
123
- "fields"=>["query", "cheese", "__schema"],
126
+ "path"=>["query", "cheese", "__schema"],
127
+ "extensions"=>{"code"=>"undefinedField", "typeName"=>"Cheese", "fieldName"=>"__schema"}
124
128
  }
125
129
  ]
126
130
  assert_equal(expected_errors, errors)
@@ -151,7 +155,8 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
151
155
  "locations"=>[
152
156
  {"line"=>2, "column"=>33}
153
157
  ],
154
- "fields"=>["query", "cheese", "__type"],
158
+ "path"=>["query", "cheese", "__type"],
159
+ "extensions"=>{"code"=>"undefinedField", "typeName"=>"Cheese", "fieldName"=>"__type"}
155
160
  }
156
161
  ]
157
162
  assert_equal(expected_errors, errors)
@@ -19,28 +19,32 @@ describe GraphQL::StaticValidation::FieldsHaveAppropriateSelections do
19
19
  illegal_selection_error = {
20
20
  "message"=>"Selections can't be made on scalars (field 'id' returns Int but has selections [something, someFields])",
21
21
  "locations"=>[{"line"=>6, "column"=>47}],
22
- "fields"=>["query getCheese", "illegalSelectionCheese", "id"],
22
+ "path"=>["query getCheese", "illegalSelectionCheese", "id"],
23
+ "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"field 'id'", "typeName"=>"Int"}
23
24
  }
24
25
  assert_includes(errors, illegal_selection_error, "finds illegal selections on scalars")
25
26
 
26
27
  objects_selection_required_error = {
27
28
  "message"=>"Field must have selections (field 'cheese' returns Cheese but has no selections. Did you mean 'cheese { ... }'?)",
28
29
  "locations"=>[{"line"=>4, "column"=>7}],
29
- "fields"=>["query getCheese", "missingFieldsObject"],
30
+ "path"=>["query getCheese", "missingFieldsObject"],
31
+ "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"field 'cheese'", "typeName"=>"Cheese"}
30
32
  }
31
33
  assert_includes(errors, objects_selection_required_error, "finds objects without selections")
32
34
 
33
35
  interfaces_selection_required_error = {
34
36
  "message"=>"Field must have selections (field 'selfAsEdible' returns Edible but has no selections. Did you mean 'selfAsEdible { ... }'?)",
35
37
  "locations"=>[{"line"=>5, "column"=>47}],
36
- "fields"=>["query getCheese", "missingFieldsInterface", "selfAsEdible"],
38
+ "path"=>["query getCheese", "missingFieldsInterface", "selfAsEdible"],
39
+ "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"field 'selfAsEdible'", "typeName"=>"Edible"}
37
40
  }
38
41
  assert_includes(errors, interfaces_selection_required_error, "finds interfaces without selections")
39
42
 
40
43
  incorrect_fragment_error = {
41
44
  "message"=>"Selections can't be made on scalars (field 'flavor' returns String but has inline fragments [String])",
42
45
  "locations"=>[{"line"=>7, "column"=>48}],
43
- "fields"=>["query getCheese", "incorrectFragmentSpread", "flavor"],
46
+ "path"=>["query getCheese", "incorrectFragmentSpread", "flavor"],
47
+ "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"field 'flavor'", "typeName"=>"String"}
44
48
  }
45
49
  assert_includes(errors, incorrect_fragment_error, "finds scalar fields with selections")
46
50
  end
@@ -53,7 +57,8 @@ describe GraphQL::StaticValidation::FieldsHaveAppropriateSelections do
53
57
  selections_required_error = {
54
58
  "message"=> "Field must have selections (anonymous query returns Query but has no selections. Did you mean ' { ... }'?)",
55
59
  "locations"=>[{"line"=>1, "column"=>1}],
56
- "fields"=>["query"]
60
+ "path"=>["query"],
61
+ "extensions"=>{"code"=>"selectionMismatch", "nodeName"=>"anonymous query", "typeName"=>"Query"}
57
62
  }
58
63
  assert_includes(errors, selections_required_error)
59
64
  end
@@ -381,7 +381,8 @@ describe GraphQL::StaticValidation::FieldsWillMerge do
381
381
  {"line"=>4, "column"=>11},
382
382
  {"line"=>8, "column"=>11}
383
383
  ],
384
- "fields"=>[]
384
+ "path"=>[],
385
+ "extensions"=>{"code"=>"fieldConflict", "fieldName"=>"x", "conflicts"=>"name or nickname"}
385
386
  }
386
387
  ]
387
388
  assert_equal expected_errors, errors
@@ -20,7 +20,8 @@ describe GraphQL::StaticValidation::FragmentNamesAreUnique do
20
20
  fragment_def_error = {
21
21
  "message"=>"Fragment name \"frag1\" must be unique",
22
22
  "locations"=>[{"line"=>8, "column"=>5}, {"line"=>9, "column"=>5}],
23
- "fields"=>[],
23
+ "path"=>[],
24
+ "extensions"=>{"code"=>"fragmentNotUnique", "fragmentName"=>"frag1"}
24
25
  }
25
26
  assert_includes(errors, fragment_def_error)
26
27
  end
@@ -31,17 +31,20 @@ describe GraphQL::StaticValidation::FragmentSpreadsArePossible do
31
31
  {
32
32
  "message"=>"Fragment on Milk can't be spread inside Cheese",
33
33
  "locations"=>[{"line"=>6, "column"=>9}],
34
- "fields"=>["query getCheese", "cheese", "... on Milk"],
34
+ "path"=>["query getCheese", "cheese", "... on Milk"],
35
+ "extensions"=>{"code"=>"cannotSpreadFragment", "typeName"=>"Milk", "fragmentName"=>"unknown", "parentName"=>"Cheese"}
35
36
  },
36
37
  {
37
38
  "message"=>"Fragment milkFields on Milk can't be spread inside Cheese",
38
39
  "locations"=>[{"line"=>4, "column"=>9}],
39
- "fields"=>["query getCheese", "cheese", "... milkFields"],
40
+ "path"=>["query getCheese", "cheese", "... milkFields"],
41
+ "extensions"=>{"code"=>"cannotSpreadFragment", "typeName"=>"Milk", "fragmentName"=>" milkFields", "parentName"=>"Cheese"}
40
42
  },
41
43
  {
42
44
  "message"=>"Fragment milkFields on Milk can't be spread inside Cheese",
43
45
  "locations"=>[{"line"=>18, "column"=>7}],
44
- "fields"=>["fragment cheeseFields", "... milkFields"],
46
+ "path"=>["fragment cheeseFields", "... milkFields"],
47
+ "extensions"=>{"code"=>"cannotSpreadFragment", "typeName"=>"Milk", "fragmentName"=>" milkFields", "parentName"=>"Cheese"}
45
48
  }
46
49
  ]
47
50
  assert_equal(expected, errors)
@@ -27,13 +27,15 @@ describe GraphQL::StaticValidation::FragmentTypesExist do
27
27
  inline_fragment_error = {
28
28
  "message"=>"No such type Something, so it can't be a fragment condition",
29
29
  "locations"=>[{"line"=>11, "column"=>5}],
30
- "fields"=>["fragment somethingFields"],
30
+ "path"=>["fragment somethingFields"],
31
+ "extensions"=>{"code"=>"undefinedType", "typeName"=>"Something"}
31
32
  }
32
33
  assert_includes(errors, inline_fragment_error, "on inline fragments")
33
34
  fragment_def_error = {
34
35
  "message"=>"No such type Nothing, so it can't be a fragment condition",
35
36
  "locations"=>[{"line"=>5, "column"=>9}],
36
- "fields"=>["query getCheese", "cheese", "... on Nothing"],
37
+ "path"=>["query getCheese", "cheese", "... on Nothing"],
38
+ "extensions"=>{"code"=>"undefinedType", "typeName"=>"Nothing"}
37
39
  }
38
40
  assert_includes(errors, fragment_def_error, "on fragment definitions")
39
41
  end
@@ -38,12 +38,14 @@ describe GraphQL::StaticValidation::FragmentsAreFinite do
38
38
  {
39
39
  "message"=>"Fragment sourceField contains an infinite loop",
40
40
  "locations"=>[{"line"=>12, "column"=>5}],
41
- "fields"=>["fragment sourceField"],
41
+ "path"=>["fragment sourceField"],
42
+ "extensions"=>{"code"=>"infiniteLoop", "fragmentName"=>"sourceField"}
42
43
  },
43
44
  {
44
45
  "message"=>"Fragment flavorField contains an infinite loop",
45
46
  "locations"=>[{"line"=>17, "column"=>5}],
46
- "fields"=>["fragment flavorField"],
47
+ "path"=>["fragment flavorField"],
48
+ "extensions"=>{"code"=>"infiniteLoop", "fragmentName"=>"flavorField"}
47
49
  }
48
50
  ]
49
51
  assert_equal(expected, errors)
@@ -16,7 +16,8 @@ describe GraphQL::StaticValidation::FragmentTypesExist do
16
16
  fragment_def_error = {
17
17
  "message"=>"Fragment definition has no name",
18
18
  "locations"=>[{"line"=>2, "column"=>5}],
19
- "fields"=>["fragment "],
19
+ "path"=>["fragment "],
20
+ "extensions"=>{"code"=>"anonymousFragment"}
20
21
  }
21
22
  assert_includes(errors, fragment_def_error, "on fragment definitions")
22
23
  end
@@ -35,17 +35,20 @@ describe GraphQL::StaticValidation::FragmentsAreOnCompositeTypes do
35
35
  {
36
36
  "message"=>"Invalid fragment on type Boolean (must be Union, Interface or Object)",
37
37
  "locations"=>[{"line"=>6, "column"=>11}],
38
- "fields"=>["query getCheese", "cheese", "... on Cheese", "... on Boolean"],
38
+ "path"=>["query getCheese", "cheese", "... on Cheese", "... on Boolean"],
39
+ "extensions"=>{"code"=>"fragmentOnNonCompositeType", "typeName"=>"Boolean"}
39
40
  },
40
41
  {
41
42
  "message"=>"Invalid fragment on type DairyProductInput (must be Union, Interface or Object)",
42
43
  "locations"=>[{"line"=>16, "column"=>9}],
43
- "fields"=>["query getCheese", "cheese", "... on DairyProductInput"],
44
+ "path"=>["query getCheese", "cheese", "... on DairyProductInput"],
45
+ "extensions"=>{"code"=>"fragmentOnNonCompositeType", "typeName"=>"DairyProductInput"}
44
46
  },
45
47
  {
46
48
  "message"=>"Invalid fragment on type Int (must be Union, Interface or Object)",
47
49
  "locations"=>[{"line"=>22, "column"=>5}],
48
- "fields"=>["fragment intFields"],
50
+ "path"=>["fragment intFields"],
51
+ "extensions"=>{"code"=>"fragmentOnNonCompositeType", "typeName"=>"Int"}
49
52
  },
50
53
  ]
51
54
  assert_equal(expected, errors)
@@ -17,7 +17,8 @@ describe GraphQL::StaticValidation::FragmentsAreUsed do
17
17
  assert_includes(errors, {
18
18
  "message"=>"Fragment unusedFields was defined, but not used",
19
19
  "locations"=>[{"line"=>8, "column"=>5}],
20
- "fields"=>["fragment unusedFields"],
20
+ "path"=>["fragment unusedFields"],
21
+ "extensions"=>{"code"=>"useAndDefineFragment", "fragmentName"=>"unusedFields"}
21
22
  })
22
23
  end
23
24
 
@@ -25,7 +26,8 @@ describe GraphQL::StaticValidation::FragmentsAreUsed do
25
26
  assert_includes(errors, {
26
27
  "message"=>"Fragment undefinedFields was used, but not defined",
27
28
  "locations"=>[{"line"=>5, "column"=>7}],
28
- "fields"=>["query getCheese", "... undefinedFields"]
29
+ "path"=>["query getCheese", "... undefinedFields"],
30
+ "extensions"=>{"code"=>"useAndDefineFragment", "fragmentName"=>"undefinedFields"}
29
31
  })
30
32
  end
31
33
 
@@ -38,4 +40,22 @@ describe GraphQL::StaticValidation::FragmentsAreUsed do
38
40
  assert_equal({}, result)
39
41
  end
40
42
  end
43
+
44
+ describe "invalid unused fragments" do
45
+ let(:query_string) {"
46
+ query getCheese {
47
+ name
48
+ }
49
+ fragment Invalid on DoesNotExist { fatContent }
50
+ "}
51
+
52
+ it "handles them gracefully" do
53
+ assert_includes(errors, {
54
+ "message"=>"No such type DoesNotExist, so it can't be a fragment condition",
55
+ "locations"=>[{"line"=>5, "column"=>7}],
56
+ "path"=>["fragment Invalid"],
57
+ "extensions"=>{"code"=>"undefinedType", "typeName"=>"DoesNotExist"}
58
+ })
59
+ end
60
+ end
41
61
  end
@@ -31,7 +31,8 @@ describe GraphQL::StaticValidation::MutationRootExists do
31
31
  missing_mutation_root_error = {
32
32
  "message"=>"Schema is not configured for mutations",
33
33
  "locations"=>[{"line"=>2, "column"=>5}],
34
- "fields"=>["mutation addBagel"],
34
+ "path"=>["mutation addBagel"],
35
+ "extensions"=>{"code"=>"missingMutationConfiguration"}
35
36
  }
36
37
  assert_includes(errors, missing_mutation_root_error)
37
38
  end
@@ -25,7 +25,8 @@ describe GraphQL::StaticValidation::OperationNamesAreValid do
25
25
  requires_name_error = {
26
26
  "message"=>"Operation name is required when multiple operations are present",
27
27
  "locations"=>[{"line"=>5, "column"=>5}, {"line"=>9, "column"=>5}],
28
- "fields"=>[],
28
+ "path"=>[],
29
+ "extensions"=>{"code"=>"uniquelyNamedOperations"}
29
30
  }
30
31
  assert_includes(errors, requires_name_error)
31
32
  end
@@ -48,7 +49,8 @@ describe GraphQL::StaticValidation::OperationNamesAreValid do
48
49
  requires_name_error = {
49
50
  "message"=>"Operation name is required when multiple operations are present",
50
51
  "locations"=>[{"line"=>1, "column"=>5}, {"line"=>5, "column"=>5}],
51
- "fields"=>[],
52
+ "path"=>[],
53
+ "extensions"=>{"code"=>"uniquelyNamedOperations"}
52
54
  }
53
55
  assert_includes(errors, requires_name_error)
54
56
  end
@@ -71,7 +73,8 @@ describe GraphQL::StaticValidation::OperationNamesAreValid do
71
73
  name_uniqueness_error = {
72
74
  "message"=>'Operation name "getCheese" must be unique',
73
75
  "locations"=>[{"line"=>1, "column"=>5}, {"line"=>5, "column"=>5}],
74
- "fields"=>[],
76
+ "path"=>[],
77
+ "extensions"=>{"code"=>"uniquelyNamedOperations", "operationName"=>"getCheese"}
75
78
  }
76
79
  assert_includes(errors, name_uniqueness_error)
77
80
  end