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
@@ -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