graphql 1.9.0.pre1 → 1.9.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -22,21 +22,24 @@ describe GraphQL::StaticValidation::RequiredArgumentsArePresent do
22
22
  query_root_error = {
23
23
  "message"=>"Field 'cheese' is missing required arguments: id",
24
24
  "locations"=>[{"line"=>4, "column"=>7}],
25
- "fields"=>["query getCheese", "cheese"],
25
+ "path"=>["query getCheese", "cheese"],
26
+ "extensions"=>{"code"=>"missingRequiredArguments", "className"=>"Field", "name"=>"cheese", "arguments"=>"id"}
26
27
  }
27
28
  assert_includes(errors, query_root_error)
28
29
 
29
30
  fragment_error = {
30
31
  "message"=>"Field 'similarCheese' is missing required arguments: source",
31
32
  "locations"=>[{"line"=>8, "column"=>7}],
32
- "fields"=>["fragment cheeseFields", "similarCheese"],
33
+ "path"=>["fragment cheeseFields", "similarCheese"],
34
+ "extensions"=>{"code"=>"missingRequiredArguments", "className"=>"Field", "name"=>"similarCheese", "arguments"=>"source"}
33
35
  }
34
36
  assert_includes(errors, fragment_error)
35
37
 
36
38
  directive_error = {
37
39
  "message"=>"Directive 'skip' is missing required arguments: if",
38
40
  "locations"=>[{"line"=>10, "column"=>10}],
39
- "fields"=>["fragment cheeseFields", "id"],
41
+ "path"=>["fragment cheeseFields", "id"],
42
+ "extensions"=>{"code"=>"missingRequiredArguments", "className"=>"Directive", "name"=>"skip", "arguments"=>"if"}
40
43
  }
41
44
  assert_includes(errors, directive_error)
42
45
  end
@@ -55,7 +58,13 @@ describe GraphQL::StaticValidation::RequiredArgumentsArePresent do
55
58
  "locations"=>[
56
59
  {"line"=>3, "column"=>9}
57
60
  ],
58
- "fields"=>["query", "__type"],
61
+ "path"=>["query", "__type"],
62
+ "extensions"=>{
63
+ "code"=>"missingRequiredArguments",
64
+ "className"=>"Field",
65
+ "name"=>"__type",
66
+ "arguments"=>"name"
67
+ }
59
68
  }
60
69
  ]
61
70
  assert_equal(expected_errors, errors)
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent do
5
+ include StaticValidationHelpers
6
+ include ErrorBubblingHelpers
7
+
8
+ let(:query_string) {%|
9
+ query getCheese {
10
+ stringCheese: cheese(id: "aasdlkfj") { ...cheeseFields }
11
+ cheese(id: 1) { source @skip(if: "whatever") }
12
+ yakSource: searchDairy(product: [{source: COW, fatContent: 1.1}]) { __typename }
13
+ badSource: searchDairy(product: [{source: 1.1}]) { __typename }
14
+ missingSource: searchDairy(product: [{fatContent: 1.1}]) { __typename }
15
+ listCoerce: cheese(id: 1) { similarCheese(source: YAK) { __typename } }
16
+ missingInputField: searchDairy(product: [{source: YAK, wacky: 1}]) { __typename }
17
+ }
18
+
19
+ fragment cheeseFields on Cheese {
20
+ similarCheese(source: 4.5) { __typename }
21
+ }
22
+ |}
23
+ describe "with error bubbling disabled" do
24
+ missing_required_field_error = {
25
+ "message"=>"Argument 'product' on Field 'missingSource' has an invalid value. Expected type '[DairyProductInput]'.",
26
+ "locations"=>[{"line"=>7, "column"=>7}],
27
+ "path"=>["query getCheese", "missingSource", "product"],
28
+ "extensions"=>{
29
+ "code"=>"argumentLiteralsIncompatible",
30
+ "typeName"=>"Field",
31
+ "argumentName"=>"product",
32
+ },
33
+ }
34
+ missing_source_error = {
35
+ "message"=>"Argument 'source' on InputObject 'DairyProductInput' is required. Expected type DairyAnimal!",
36
+ "locations"=>[{"line"=>7, "column"=>44}],
37
+ "path"=>["query getCheese", "missingSource", "product", "source"],
38
+ "extensions"=>{
39
+ "code"=>"missingRequiredInputObjectAttribute",
40
+ "argumentName"=>"source",
41
+ "argumentType"=>"DairyAnimal!",
42
+ "inputObjectType"=>"DairyProductInput"
43
+ }
44
+ }
45
+ it "finds undefined or missing-required arguments to fields and directives" do
46
+ without_error_bubbling(schema) do
47
+ assert_includes(errors, missing_source_error)
48
+ refute_includes(errors, missing_required_field_error)
49
+ end
50
+ end
51
+ it 'works with error bubbling enabled' do
52
+ with_error_bubbling(schema) do
53
+ assert_includes(errors, missing_required_field_error)
54
+ assert_includes(errors, missing_source_error)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -26,7 +26,8 @@ describe GraphQL::StaticValidation::SubscriptionRootExists do
26
26
  missing_subscription_root_error = {
27
27
  "message"=>"Schema is not configured for subscriptions",
28
28
  "locations"=>[{"line"=>2, "column"=>5}],
29
- "fields"=>["subscription"],
29
+ "path"=>["subscription"],
30
+ "extensions"=>{"code"=>"missingSubscriptionConfiguration"}
30
31
  }
31
32
  assert_includes(errors, missing_subscription_root_error)
32
33
  end
@@ -101,7 +101,8 @@ describe GraphQL::StaticValidation::UniqueDirectivesPerLocation do
101
101
  assert_includes errors, {
102
102
  "message" => 'The directive "A" can only be used once at this location.',
103
103
  "locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 20 }],
104
- "fields" => ["query", "type", "field"],
104
+ "path" => ["query", "type", "field"],
105
+ "extensions" => {"code"=>"directiveNotUniqueForLocation", "directiveName"=>"A"}
105
106
  }
106
107
  end
107
108
  end
@@ -120,13 +121,15 @@ describe GraphQL::StaticValidation::UniqueDirectivesPerLocation do
120
121
  assert_includes errors, {
121
122
  "message" => 'The directive "A" can only be used once at this location.',
122
123
  "locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 20 }],
123
- "fields" => ["query", "type", "field"],
124
+ "path" => ["query", "type", "field"],
125
+ "extensions" => {"code"=>"directiveNotUniqueForLocation", "directiveName"=>"A"}
124
126
  }
125
127
 
126
128
  assert_includes errors, {
127
129
  "message" => 'The directive "A" can only be used once at this location.',
128
130
  "locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 23 }],
129
- "fields" => ["query", "type", "field"],
131
+ "path" => ["query", "type", "field"],
132
+ "extensions" => {"code"=>"directiveNotUniqueForLocation", "directiveName"=>"A"}
130
133
  }
131
134
  end
132
135
  end
@@ -144,13 +147,15 @@ describe GraphQL::StaticValidation::UniqueDirectivesPerLocation do
144
147
  assert_includes errors, {
145
148
  "message" => 'The directive "A" can only be used once at this location.',
146
149
  "locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 23 }],
147
- "fields" => ["query", "type", "field"],
150
+ "path" => ["query", "type", "field"],
151
+ "extensions" => {"code"=>"directiveNotUniqueForLocation", "directiveName"=>"A"}
148
152
  }
149
153
 
150
154
  assert_includes errors, {
151
155
  "message" => 'The directive "B" can only be used once at this location.',
152
156
  "locations" => [{ "line" => 4, "column" => 20 }, { "line" => 4, "column" => 26 }],
153
- "fields" => ["query", "type", "field"],
157
+ "path" => ["query", "type", "field"],
158
+ "extensions" => {"code"=>"directiveNotUniqueForLocation", "directiveName"=>"B"}
154
159
  }
155
160
  end
156
161
  end
@@ -168,13 +173,15 @@ describe GraphQL::StaticValidation::UniqueDirectivesPerLocation do
168
173
  assert_includes errors, {
169
174
  "message" => 'The directive "A" can only be used once at this location.',
170
175
  "locations" => [{ "line" => 3, "column" => 14 }, { "line" => 3, "column" => 17 }],
171
- "fields" => ["query", "type"],
176
+ "path" => ["query", "type"],
177
+ "extensions" => {"code"=>"directiveNotUniqueForLocation", "directiveName"=>"A"}
172
178
  }
173
179
 
174
180
  assert_includes errors, {
175
181
  "message" => 'The directive "A" can only be used once at this location.',
176
182
  "locations" => [{ "line" => 4, "column" => 17 }, { "line" => 4, "column" => 20 }],
177
- "fields" => ["query", "type", "field"],
183
+ "path" => ["query", "type", "field"],
184
+ "extensions" => {"code"=>"directiveNotUniqueForLocation", "directiveName"=>"A"}
178
185
  }
179
186
  end
180
187
  end
@@ -29,17 +29,20 @@ describe GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped do
29
29
  {
30
30
  "message"=>"Default value for $badInt doesn't match type Int",
31
31
  "locations"=>[{"line"=>6, "column"=>7}],
32
- "fields"=>["query getCheese"],
32
+ "path"=>["query getCheese"],
33
+ "extensions"=>{"code"=>"defaultValueInvalidType", "variableName"=>"badInt", "typeName"=>"Int"}
33
34
  },
34
35
  {
35
36
  "message"=>"Default value for $badInput doesn't match type DairyProductInput",
36
37
  "locations"=>[{"line"=>8, "column"=>7}],
37
- "fields"=>["query getCheese"],
38
+ "path"=>["query getCheese"],
39
+ "extensions"=>{"code"=>"defaultValueInvalidType", "variableName"=>"badInput", "typeName"=>"DairyProductInput"}
38
40
  },
39
41
  {
40
42
  "message"=>"Non-null variable $nonNull can't have a default value",
41
43
  "locations"=>[{"line"=>9, "column"=>7}],
42
- "fields"=>["query getCheese"],
44
+ "path"=>["query getCheese"],
45
+ "extensions"=>{"code"=>"defaultValueInvalidOnNonNullVariable", "variableName"=>"nonNull"}
43
46
  }
44
47
  ]
45
48
  assert_equal(expected, errors)
@@ -116,17 +119,20 @@ describe GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped do
116
119
  {
117
120
  "message"=>"Non-null variable $a can't have a default value",
118
121
  "locations"=>[{"line"=>3, "column"=>11}],
119
- "fields"=>["query getCheese"]
122
+ "path"=>["query getCheese"],
123
+ "extensions"=>{"code"=>"defaultValueInvalidOnNonNullVariable", "variableName"=>"a"}
120
124
  },
121
125
  {
122
126
  "message"=>"Non-null variable $b can't have a default value",
123
127
  "locations"=>[{"line"=>4, "column"=>11}],
124
- "fields"=>["query getCheese"]
128
+ "path"=>["query getCheese"],
129
+ "extensions"=>{"code"=>"defaultValueInvalidOnNonNullVariable", "variableName"=>"b"}
125
130
  },
126
131
  {
127
132
  "message"=>"Default value for $c doesn't match type ComplexInput",
128
133
  "locations"=>[{"line"=>5, "column"=>11}],
129
- "fields"=>["query getCheese"]
134
+ "path"=>["query getCheese"],
135
+ "extensions"=>{"code"=>"defaultValueInvalidType", "variableName"=>"c", "typeName"=>"ComplexInput"}
130
136
  }
131
137
  ]
132
138
 
@@ -182,7 +188,8 @@ describe GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped do
182
188
  assert_includes errors, {
183
189
  "message"=> "cannot coerce to Float",
184
190
  "locations"=>[{"line"=>3, "column"=>9}],
185
- "fields"=>["query"]
191
+ "path"=>["query"],
192
+ "extensions"=>{"code"=>"defaultValueInvalidType", "variableName"=>"value", "typeName"=>"Time"}
186
193
  }
187
194
  end
188
195
  end
@@ -43,22 +43,26 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
43
43
  {
44
44
  "message"=>"Nullability mismatch on variable $badInt and argument id (Int / Int!)",
45
45
  "locations"=>[{"line"=>14, "column"=>28}],
46
- "fields"=>["query getCheese", "badCheese", "id"],
46
+ "path"=>["query getCheese", "badCheese", "id"],
47
+ "extensions"=>{"code"=>"variableMismatch", "variableName"=>"badInt", "typeName"=>"Int", "argumentName"=>"id", "errorMessage"=>"Nullability mismatch"}
47
48
  },
48
49
  {
49
50
  "message"=>"Type mismatch on variable $badStr and argument id (String! / Int!)",
50
51
  "locations"=>[{"line"=>15, "column"=>28}],
51
- "fields"=>["query getCheese", "badStrCheese", "id"],
52
+ "path"=>["query getCheese", "badStrCheese", "id"],
53
+ "extensions"=>{"code"=>"variableMismatch", "variableName"=>"badStr", "typeName"=>"String!", "argumentName"=>"id", "errorMessage"=>"Type mismatch"}
52
54
  },
53
55
  {
54
56
  "message"=>"Nullability mismatch on variable $badAnimals and argument source ([DairyAnimal]! / [DairyAnimal!]!)",
55
57
  "locations"=>[{"line"=>18, "column"=>30}],
56
- "fields"=>["query getCheese", "cheese", "other", "source"],
58
+ "path"=>["query getCheese", "cheese", "other", "source"],
59
+ "extensions"=>{"code"=>"variableMismatch", "variableName"=>"badAnimals", "typeName"=>"[DairyAnimal]!", "argumentName"=>"source", "errorMessage"=>"Nullability mismatch"}
57
60
  },
58
61
  {
59
62
  "message"=>"List dimension mismatch on variable $deepAnimals and argument source ([[DairyAnimal!]!]! / [DairyAnimal!]!)",
60
63
  "locations"=>[{"line"=>19, "column"=>32}],
61
- "fields"=>["query getCheese", "cheese", "tooDeep", "source"],
64
+ "path"=>["query getCheese", "cheese", "tooDeep", "source"],
65
+ "extensions"=>{"code"=>"variableMismatch", "variableName"=>"deepAnimals", "typeName"=>"[[DairyAnimal!]!]!", "argumentName"=>"source", "errorMessage"=>"List dimension mismatch"}
62
66
  }
63
67
  ]
64
68
  assert_equal(expected, errors)
@@ -22,25 +22,29 @@ describe GraphQL::StaticValidation::VariablesAreInputTypes do
22
22
  assert_includes(errors, {
23
23
  "message"=>"AnimalProduct isn't a valid input type (on $interface)",
24
24
  "locations"=>[{"line"=>5, "column"=>7}],
25
- "fields"=>["query getCheese"],
25
+ "path"=>["query getCheese"],
26
+ "extensions"=> {"code"=>"variableRequiresValidType", "typeName"=>"AnimalProduct", "variableName"=>"interface"}
26
27
  })
27
28
 
28
29
  assert_includes(errors, {
29
30
  "message"=>"Milk isn't a valid input type (on $object)",
30
31
  "locations"=>[{"line"=>6, "column"=>7}],
31
- "fields"=>["query getCheese"],
32
+ "path"=>["query getCheese"],
33
+ "extensions"=>{"code"=>"variableRequiresValidType", "typeName"=>"Milk", "variableName"=>"object"}
32
34
  })
33
35
 
34
36
  assert_includes(errors, {
35
37
  "message"=>"Cheese isn't a valid input type (on $objects)",
36
38
  "locations"=>[{"line"=>7, "column"=>7}],
37
- "fields"=>["query getCheese"],
39
+ "path"=>["query getCheese"],
40
+ "extensions"=>{"code"=>"variableRequiresValidType", "typeName"=>"Cheese", "variableName"=>"objects"}
38
41
  })
39
42
 
40
43
  assert_includes(errors, {
41
44
  "message"=>"Nonsense isn't a defined input type (on $unknownType)",
42
45
  "locations"=>[{"line"=>8, "column"=>7}],
43
- "fields"=>["query getCheese"],
46
+ "path"=>["query getCheese"],
47
+ "extensions"=>{"code"=>"variableRequiresValidType", "typeName"=>"Nonsense", "variableName"=>"unknownType"}
44
48
  })
45
49
  end
46
50
 
@@ -42,17 +42,20 @@ describe GraphQL::StaticValidation::VariablesAreUsedAndDefined do
42
42
  {
43
43
  "message"=>"Variable $notUsedVar is declared by getCheese but not used",
44
44
  "locations"=>[{"line"=>2, "column"=>5}],
45
- "fields"=>["query getCheese"],
45
+ "path"=>["query getCheese"],
46
+ "extensions"=>{"code"=>"variableNotUsed", "variableName"=>"notUsedVar"}
46
47
  },
47
48
  {
48
49
  "message"=>"Variable $undefinedVar is used by getCheese but not declared",
49
50
  "locations"=>[{"line"=>19, "column"=>22}],
50
- "fields"=>["query getCheese", "c3", "id"],
51
+ "path"=>["query getCheese", "c3", "id"],
52
+ "extensions"=>{"code"=>"variableNotDefined", "variableName"=>"undefinedVar"}
51
53
  },
52
54
  {
53
55
  "message"=>"Variable $undefinedFragmentVar is used by innerCheeseFields but not declared",
54
56
  "locations"=>[{"line"=>29, "column"=>22}],
55
- "fields"=>["fragment innerCheeseFields", "c4", "id"],
57
+ "path"=>["fragment innerCheeseFields", "c4", "id"],
58
+ "extensions"=>{"code"=>"variableNotDefined", "variableName"=>"undefinedFragmentVar"}
56
59
  },
57
60
  ]
58
61
 
@@ -34,7 +34,8 @@ describe GraphQL::StaticValidation::Validator do
34
34
  expected_errors = [{
35
35
  "message" => "Variable $undefinedVar is used by but not declared",
36
36
  "locations" => [{"line" => 1, "column" => 14, "filename" => "not_a_real.graphql"}],
37
- "fields" => ["query", "cheese", "id"]
37
+ "path" => ["query", "cheese", "id"],
38
+ "extensions"=>{"code"=>"variableNotDefined", "variableName"=>"undefinedVar"}
38
39
  }]
39
40
  assert_equal expected_errors, errors
40
41
  end
@@ -115,7 +116,8 @@ describe GraphQL::StaticValidation::Validator do
115
116
  {
116
117
  "message"=>"Fragment cheeseFields contains an infinite loop",
117
118
  "locations"=>[{"line"=>10, "column"=>9}],
118
- "fields"=>["fragment cheeseFields"]
119
+ "path"=>["fragment cheeseFields"],
120
+ "extensions"=>{"code"=>"infiniteLoop", "fragmentName"=>"cheeseFields"}
119
121
  }
120
122
  ]
121
123
  assert_equal(expected, errors)
@@ -180,10 +182,10 @@ describe GraphQL::StaticValidation::Validator do
180
182
  describe "With a legacy-style rule" do
181
183
  # GraphQL-Pro's operation store uses this
182
184
  class ValidatorSpecLegacyRule
183
- include GraphQL::StaticValidation::Message::MessageHelper
185
+ include GraphQL::StaticValidation::Error::ErrorHelper
184
186
  def validate(ctx)
185
187
  ctx.visitor[GraphQL::Language::Nodes::OperationDefinition] << ->(n, _p) {
186
- ctx.errors << message("Busted!", n, context: ctx)
188
+ ctx.errors << error("Busted!", n, context: ctx)
187
189
  }
188
190
  end
189
191
  end
@@ -26,6 +26,11 @@ describe GraphQL::Tracing::NewRelicTracing do
26
26
  use GraphQL::Execution::Interpreter
27
27
  end
28
28
  end
29
+
30
+ class SchemaWithScalarTrace < GraphQL::Schema
31
+ query(Query)
32
+ use(GraphQL::Tracing::NewRelicTracing, trace_scalars: true)
33
+ end
29
34
  end
30
35
 
31
36
  before do
@@ -50,4 +55,9 @@ describe GraphQL::Tracing::NewRelicTracing do
50
55
  NewRelicTest::SchemaWithoutTransactionName.execute "{ int }", context: { set_new_relic_transaction_name: true }
51
56
  assert_equal ["GraphQL/query.anonymous"], NewRelic::TRANSACTION_NAMES
52
57
  end
58
+
59
+ it "traces scalars when trace_scalars is true" do
60
+ NewRelicTest::SchemaWithScalarTrace.execute "query X { int }"
61
+ assert_includes NewRelic::EXECUTION_SCOPES, "GraphQL/Query/int"
62
+ end
53
63
  end
@@ -26,6 +26,11 @@ describe GraphQL::Tracing::SkylightTracing do
26
26
  use GraphQL::Execution::Interpreter
27
27
  end
28
28
  end
29
+
30
+ class SchemaWithScalarTrace < GraphQL::Schema
31
+ query(Query)
32
+ use(GraphQL::Tracing::SkylightTracing, trace_scalars: true)
33
+ end
29
34
  end
30
35
 
31
36
  before do
@@ -50,4 +55,9 @@ describe GraphQL::Tracing::SkylightTracing do
50
55
  SkylightTest::SchemaWithoutTransactionName.execute "{ int }", context: { set_skylight_endpoint_name: true }
51
56
  assert_equal ["GraphQL/query.<anonymous>"], Skylight::ENDPOINT_NAMES
52
57
  end
58
+
59
+ it "traces scalars when trace_scalars is true" do
60
+ SkylightTest::SchemaWithScalarTrace.execute "query X { int }"
61
+ assert_includes Skylight::TITLE_NAMES, "graphql.Query.int"
62
+ end
53
63
  end
@@ -11,8 +11,7 @@ describe GraphQL::Types::ISO8601DateTime do
11
11
  field :minute, Integer, null: false
12
12
  field :second, Integer, null: false
13
13
  field :zone, String, null: false
14
- # Use method: :object so that the DateTime instance is passed to the scalar
15
- field :iso8601, GraphQL::Types::ISO8601DateTime, null: false, method: :object
14
+ field :iso8601, GraphQL::Types::ISO8601DateTime, null: false, method: :itself
16
15
  end
17
16
 
18
17
  class Query < GraphQL::Schema::Object
@@ -169,11 +169,11 @@ module StarTrek
169
169
  all_bases.to_a
170
170
  end
171
171
 
172
- field :basesWithMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 2, method: :all_bases
173
- field :basesWithMaxLimitArray, BaseType.connection_type, null: true, max_page_size: 2, method: :all_bases_array
174
- field :basesWithDefaultMaxLimitRelation, BaseType.connection_type, null: true, method: :all_bases
175
- field :basesWithDefaultMaxLimitArray, BaseType.connection_type, null: true, method: :all_bases_array
176
- field :basesWithLargeMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 1000, method: :all_bases
172
+ field :basesWithMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 2, resolver_method: :all_bases
173
+ field :basesWithMaxLimitArray, BaseType.connection_type, null: true, max_page_size: 2, resolver_method: :all_bases_array
174
+ field :basesWithDefaultMaxLimitRelation, BaseType.connection_type, null: true, resolver_method: :all_bases
175
+ field :basesWithDefaultMaxLimitArray, BaseType.connection_type, null: true, resolver_method: :all_bases_array
176
+ field :basesWithLargeMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 1000, resolver_method: :all_bases
177
177
 
178
178
  field :basesWithCustomEdge, CustomEdgeBaseConnectionType, null: true, connection: true
179
179
  def bases_with_custom_edge
@@ -21,7 +21,7 @@ describe GraphQL::Relay::RelationConnection do
21
21
 
22
22
  describe "results" do
23
23
  let(:query_string) {%|
24
- query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){
24
+ query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){
25
25
  empire {
26
26
  bases(first: $first, after: $after, last: $last, before: $before, nameIncludes: $nameIncludes) {
27
27
  ... basesConnection
@@ -66,6 +66,39 @@ describe GraphQL::Relay::RelationConnection do
66
66
  assert_equal("Mw", get_last_cursor(result))
67
67
  end
68
68
 
69
+ it "uses unscope(:order) count(*) when the relation has some complicated SQL" do
70
+ query_s = <<-GRAPHQL
71
+ query getShips($first: Int, $after: String, $complexOrder: Boolean){
72
+ empire {
73
+ bases(first: $first, after: $after, complexOrder: $complexOrder) {
74
+ edges {
75
+ node {
76
+ name
77
+ }
78
+ }
79
+ pageInfo {
80
+ hasNextPage
81
+ }
82
+ }
83
+ }
84
+ }
85
+ GRAPHQL
86
+ result = nil
87
+ log = with_active_record_log do
88
+ result = star_wars_query(query_s, "first" => 1, "after" => "MQ==", "complexOrder" => true)
89
+ end
90
+
91
+ conn = result["data"]["empire"]["bases"]
92
+ assert_equal(1, conn["edges"].size)
93
+ assert_equal(true, conn["pageInfo"]["hasNextPage"])
94
+
95
+ log_entries = log.split("\n")
96
+ assert_equal 2, log_entries.size, "It ran 2 sql queries"
97
+ edges_query, has_next_page_query = log_entries
98
+ assert_includes edges_query, "ORDER BY bases.name", "The query for edges _is_ ordered"
99
+ refute_includes has_next_page_query, "ORDER BY bases.name", "The count query **does not** have an order"
100
+ end
101
+
69
102
  it 'provides custom fields on the connection type' do
70
103
  result = star_wars_query(query_string, "first" => 2)
71
104
  assert_equal(
@@ -90,15 +123,11 @@ describe GraphQL::Relay::RelationConnection do
90
123
  }
91
124
  }
92
125
  GRAPHQL
93
- io = StringIO.new
94
- begin
95
- prev_logger = ActiveRecord::Base.logger
96
- ActiveRecord::Base.logger = Logger.new(io)
126
+ result = nil
127
+ log = with_active_record_log do
97
128
  result = star_wars_query(query_str, "first" => 2)
98
- ensure
99
- ActiveRecord::Base.logger = prev_logger
100
129
  end
101
- assert_equal 2, io.string.scan("\n").count, "Two log entries"
130
+ assert_equal 2, log.scan("\n").count, "Two log entries"
102
131
  assert_equal 3, result["data"]["empire"]["bases"]["totalCount"]
103
132
  assert_equal 2, result["data"]["empire"]["bases"]["edges"].size
104
133
  end