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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8fec9f264c3f8ad38a18cb4f9f0b760341affe81
4
- data.tar.gz: b64f5cf4561c22dd6fb45e60e3e1649c298cc461
3
+ metadata.gz: 7576d86b005acda0b6db32b531eb5ee5994b46d5
4
+ data.tar.gz: 5d27e5a79e0b2ab1d8c64edd8aa4ed5bc66b72e8
5
5
  SHA512:
6
- metadata.gz: 7908c29710c195d11abe5111bcc66df3f573cc417f6b3360ef86b2ba96e7e14552981392c6e5d6e6f4237fb6ba6674d7300383a37f364e68080afebe4433f88f
7
- data.tar.gz: cd1ebc2b2ebba1ee633de058e19e7954a971394989558ce5e7882d4206eef4db8668f939071cb3158eb7c896b2d6bd617ac98a852068a36143fd4c779f9afbe4
6
+ metadata.gz: 001ec87271eaefae504d24deac1c3b6d241f1b944b2297050e3d35a8a72c6a016ec9dd6a48f3c1bdef1ab5f9eb6aac2f5f31ca745bfdf9edb077abbc6f8158d7
7
+ data.tar.gz: fafac00586652e70e096f68a92dd20bcf712e09343d721638987f3ba8d69e77250fd25de5743ca521687c1a22a8df5355b4f7b84f500ff898446c29fe08e4bed
@@ -60,13 +60,15 @@ require "graphql/type_kinds"
60
60
  require "graphql/backwards_compatibility"
61
61
  require "graphql/scalar_type"
62
62
 
63
- require "graphql/directive"
64
63
  require "graphql/name_validator"
65
64
 
66
65
  require "graphql/language"
67
66
  require "graphql/analysis"
68
67
  require "graphql/tracing"
68
+ require "graphql/execution"
69
+ require "graphql/dig"
69
70
  require "graphql/schema"
71
+ require "graphql/directive"
70
72
  require "graphql/execution"
71
73
  require "graphql/types"
72
74
  require "graphql/relay"
@@ -82,6 +84,7 @@ require "graphql/introspection"
82
84
 
83
85
  require "graphql/analysis_error"
84
86
  require "graphql/coercion_error"
87
+ require "graphql/literal_validation_error"
85
88
  require "graphql/runtime_type_error"
86
89
  require "graphql/invalid_null_error"
87
90
  require "graphql/invalid_name_error"
@@ -101,3 +104,4 @@ require "graphql/backtrace"
101
104
  require "graphql/deprecated_dsl"
102
105
  require "graphql/authorization"
103
106
  require "graphql/unauthorized_error"
107
+ require "graphql/unauthorized_field_error"
@@ -25,7 +25,7 @@ module GraphQL
25
25
  raise NotImplementedError
26
26
  end
27
27
 
28
- # Don't use make_visit_method becuase it breaks `super`
28
+ # Don't use make_visit_method because it breaks `super`
29
29
  def self.build_visitor_hooks(member_name)
30
30
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
31
31
  def on_enter_#{member_name}(node, parent, visitor)
@@ -81,7 +81,7 @@ module GraphQL
81
81
 
82
82
  def on_inline_fragment(node, parent)
83
83
  on_fragment_with_type(node) do
84
- @path.push("...#{node.type ? " on #{node.type.to_query_string}" : ""}")
84
+ @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
85
85
  call_analyzers(:on_enter_inline_fragment, node, parent)
86
86
  super
87
87
  call_analyzers(:on_leave_inline_fragment, node, parent)
@@ -203,6 +203,11 @@ module GraphQL
203
203
  @field_definitions.last
204
204
  end
205
205
 
206
+ # @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
207
+ def previous_field_definition
208
+ @field_definitions[-2]
209
+ end
210
+
206
211
  # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
207
212
  def directive_definition
208
213
  @directive_definitions.last
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- # Helpers for migrating in a backwards-compatibile way
3
+ # Helpers for migrating in a backwards-compatible way
4
4
  # @api private
5
5
  module BackwardsCompatibility
6
6
  module_function
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Dig
4
+ # implemented using the old activesupport #dig instead of the ruby built-in
5
+ # so we can use some of the magic in Schema::InputObject and Query::Arguments
6
+ # to handle stringified/symbolized keys.
7
+ #
8
+ # @param args [Array<[String, Symbol>] Retrieves the value object corresponding to the each key objects repeatedly
9
+ # @return [Object]
10
+ def dig(own_key, *rest_keys)
11
+ val = self[own_key]
12
+ if val.nil? || rest_keys.empty?
13
+ val
14
+ else
15
+ val.dig(*rest_keys)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -14,7 +14,15 @@ module GraphQL
14
14
  attr_accessor :ast_node
15
15
  # @api private
16
16
  attr_writer :default_directive
17
- ensure_defined(:locations, :arguments, :name, :description, :default_directive?)
17
+ ensure_defined(:locations, :arguments, :graphql_name, :name, :description, :default_directive?)
18
+
19
+ # Future-compatible alias
20
+ # @see {GraphQL::SchemaMember}
21
+ alias :graphql_name :name
22
+
23
+ # Future-compatible alias
24
+ # @see {GraphQL::SchemaMember}
25
+ alias :graphql_definition :itself
18
26
 
19
27
  LOCATIONS = [
20
28
  QUERY = :QUERY,
@@ -84,6 +92,10 @@ module GraphQL
84
92
  def default_directive?
85
93
  @default_directive
86
94
  end
95
+
96
+ def inspect
97
+ "#<GraphQL::Directive #{name}>"
98
+ end
87
99
  end
88
100
  end
89
101
 
@@ -1,8 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::Directive::IncludeDirective = GraphQL::Directive.define do
3
- name "include"
4
- description "Directs the executor to include this field or fragment only when the `if` argument is true."
5
- locations([GraphQL::Directive::FIELD, GraphQL::Directive::FRAGMENT_SPREAD, GraphQL::Directive::INLINE_FRAGMENT])
6
- argument :if, !GraphQL::BOOLEAN_TYPE, 'Included when true.'
7
- default_directive true
8
- end
2
+ GraphQL::Directive::IncludeDirective = GraphQL::Schema::Directive::Include.graphql_definition
@@ -1,9 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::Directive::SkipDirective = GraphQL::Directive.define do
3
- name "skip"
4
- description "Directs the executor to skip this field or fragment when the `if` argument is true."
5
- locations([GraphQL::Directive::FIELD, GraphQL::Directive::FRAGMENT_SPREAD, GraphQL::Directive::INLINE_FRAGMENT])
6
-
7
- argument :if, !GraphQL::BOOLEAN_TYPE, 'Skipped when true.'
8
- default_directive true
9
- end
2
+ GraphQL::Directive::SkipDirective = GraphQL::Schema::Directive::Skip.graphql_definition
@@ -2,13 +2,12 @@
2
2
  require "graphql/execution/interpreter/execution_errors"
3
3
  require "graphql/execution/interpreter/hash_response"
4
4
  require "graphql/execution/interpreter/runtime"
5
+ require "graphql/execution/interpreter/resolve"
5
6
 
6
7
  module GraphQL
7
8
  module Execution
8
9
  class Interpreter
9
10
  def initialize
10
- # A buffer shared by all queries running in this interpreter
11
- @lazies = []
12
11
  end
13
12
 
14
13
  # Support `Executor` :S
@@ -19,9 +18,16 @@ module GraphQL
19
18
  end
20
19
 
21
20
  def self.use(schema_defn)
22
- schema_defn.query_execution_strategy(GraphQL::Execution::Interpreter)
23
- schema_defn.mutation_execution_strategy(GraphQL::Execution::Interpreter)
24
- schema_defn.subscription_execution_strategy(GraphQL::Execution::Interpreter)
21
+ schema_defn.target.interpreter = true
22
+ # Reach through the legacy objects for the actual class defn
23
+ schema_class = schema_defn.target.class
24
+ # This is not good, since both of these are holding state now,
25
+ # we have to update both :(
26
+ [schema_class, schema_defn].each do |schema_config|
27
+ schema_config.query_execution_strategy(GraphQL::Execution::Interpreter)
28
+ schema_config.mutation_execution_strategy(GraphQL::Execution::Interpreter)
29
+ schema_config.subscription_execution_strategy(GraphQL::Execution::Interpreter)
30
+ end
25
31
  end
26
32
 
27
33
  def self.begin_multiplex(multiplex)
@@ -51,14 +57,14 @@ module GraphQL
51
57
  }
52
58
  end
53
59
 
60
+ # Run the eager part of `query`
61
+ # @return {Interpreter::Runtime}
54
62
  def evaluate(query)
55
- query.context.interpreter = true
56
63
  # Although queries in a multiplex _share_ an Interpreter instance,
57
64
  # they also have another item of state, which is private to that query
58
65
  # in particular, assign it here:
59
66
  runtime = Runtime.new(
60
67
  query: query,
61
- lazies: @lazies,
62
68
  response: HashResponse.new,
63
69
  )
64
70
  query.context.namespace(:interpreter)[:runtime] = runtime
@@ -70,18 +76,22 @@ module GraphQL
70
76
  runtime
71
77
  end
72
78
 
79
+ # Run the lazy part of `query` or `multiplex`.
80
+ # @return [void]
73
81
  def sync_lazies(query: nil, multiplex: nil)
74
82
  tracer = query || multiplex
75
83
  if query.nil? && multiplex.queries.length == 1
76
84
  query = multiplex.queries[0]
77
85
  end
86
+ queries = multiplex ? multiplex.queries : [query]
87
+ final_values = queries.map do |query|
88
+ runtime = query.context.namespace(:interpreter)[:runtime]
89
+ # it might not be present if the query has an error
90
+ runtime ? runtime.final_value : nil
91
+ end
92
+ final_values.compact!
78
93
  tracer.trace("execute_query_lazy", {multiplex: multiplex, query: query}) do
79
- while @lazies.any?
80
- next_wave = @lazies.dup
81
- @lazies.clear
82
- # This will cause a side-effect with `.write(...)`
83
- next_wave.each(&:value)
84
- end
94
+ Interpreter::Resolve.resolve_all(final_values)
85
95
  end
86
96
  end
87
97
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Execution
5
+ class Interpreter
6
+ module Resolve
7
+ def self.resolve_all(results)
8
+ while results.any?
9
+ results = resolve(results)
10
+ end
11
+ end
12
+
13
+ # After getting `results` back from an interpreter evaluation,
14
+ # continue it until you get a response-ready Ruby value.
15
+ #
16
+ # `results` is one level of _depth_ of a query or multiplex.
17
+ #
18
+ # Resolve all lazy values in that depth before moving on
19
+ # to the next level.
20
+ #
21
+ # It's assumed that the lazies will
22
+ # return {Lazy} instances if there's more work to be done,
23
+ # or return {Hash}/{Array} if the query should be continued.
24
+ #
25
+ # @param results [Array]
26
+ # @return [Array] Same size, filled with finished values
27
+ def self.resolve(results)
28
+ next_results = []
29
+
30
+ # Work through the queue until it's empty
31
+ while results.size > 0
32
+ result_value = results.shift
33
+
34
+ if result_value.is_a?(Lazy)
35
+ result_value = result_value.value
36
+ end
37
+
38
+ if result_value.is_a?(Lazy)
39
+ # Since this field returned another lazy,
40
+ # add it to the same queue
41
+ results << result_value
42
+ elsif result_value.is_a?(Hash)
43
+ # This is part of the next level, add it
44
+ next_results.concat(result_value.values)
45
+ elsif result_value.is_a?(Array)
46
+ # This is part of the next level, add it
47
+ next_results.concat(result_value)
48
+ end
49
+ end
50
+
51
+ next_results
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -5,6 +5,8 @@ module GraphQL
5
5
  class Interpreter
6
6
  # I think it would be even better if we could somehow make
7
7
  # `continue_field` not recursive. "Trampolining" it somehow.
8
+ #
9
+ # @api private
8
10
  class Runtime
9
11
  # @return [GraphQL::Query]
10
12
  attr_reader :query
@@ -15,14 +17,18 @@ module GraphQL
15
17
  # @return [GraphQL::Query::Context]
16
18
  attr_reader :context
17
19
 
18
- def initialize(query:, lazies:, response:)
20
+ def initialize(query:, response:)
19
21
  @query = query
20
22
  @schema = query.schema
21
23
  @context = query.context
22
- @lazies = lazies
24
+ @interpreter_context = @context.namespace(:interpreter)
23
25
  @response = response
24
26
  @dead_paths = {}
25
27
  @types_at_paths = {}
28
+ # A cache of { Class => { String => Schema::Field } }
29
+ # Which assumes that MyObject.get_field("myField") will return the same field
30
+ # during the lifetime of a query
31
+ @fields_cache = Hash.new { |h, k| h[k] = {} }
26
32
  end
27
33
 
28
34
  def final_value
@@ -34,7 +40,7 @@ module GraphQL
34
40
  end
35
41
 
36
42
  # This _begins_ the execution. Some deferred work
37
- # might be stored up in {@lazies}.
43
+ # might be stored up in lazies.
38
44
  # @return [void]
39
45
  def run_eager
40
46
  root_operation = query.selected_operation
@@ -42,44 +48,59 @@ module GraphQL
42
48
  legacy_root_type = schema.root_type_for_operation(root_op_type)
43
49
  root_type = legacy_root_type.metadata[:type_class] || raise("Invariant: type must be class-based: #{legacy_root_type}")
44
50
  object_proxy = root_type.authorized_new(query.root_value, context)
45
-
51
+ object_proxy = schema.sync_lazy(object_proxy)
46
52
  path = []
47
53
  evaluate_selections(path, object_proxy, root_type, root_operation.selections, root_operation_type: root_op_type)
54
+ nil
48
55
  end
49
56
 
50
- private
51
-
52
- def gather_selections(owner_type, selections, selections_by_name)
57
+ def gather_selections(owner_object, owner_type, selections, selections_by_name)
53
58
  selections.each do |node|
59
+ # Skip gathering this if the directive says so
60
+ if !directives_include?(node, owner_object, owner_type)
61
+ next
62
+ end
63
+
54
64
  case node
55
65
  when GraphQL::Language::Nodes::Field
56
- if passes_skip_and_include?(node)
57
- response_key = node.alias || node.name
58
- s = selections_by_name[response_key] ||= []
66
+ response_key = node.alias || node.name
67
+ selections = selections_by_name[response_key]
68
+ # if there was already a selection of this field,
69
+ # use an array to hold all selections,
70
+ # otherise, use the single node to represent the selection
71
+ if selections
72
+ # This field was already selected at least once,
73
+ # add this node to the list of selections
74
+ s = Array(selections)
59
75
  s << node
76
+ selections_by_name[response_key] = s
77
+ else
78
+ # No selection was found for this field yet
79
+ selections_by_name[response_key] = node
60
80
  end
61
81
  when GraphQL::Language::Nodes::InlineFragment
62
- if passes_skip_and_include?(node)
63
- include_fragmment = if node.type
64
- type_defn = schema.types[node.type.name]
65
- type_defn = type_defn.metadata[:type_class]
66
- possible_types = query.warden.possible_types(type_defn).map { |t| t.metadata[:type_class] }
67
- possible_types.include?(owner_type)
68
- else
69
- true
70
- end
71
- if include_fragmment
72
- gather_selections(owner_type, node.selections, selections_by_name)
82
+ if node.type
83
+ type_defn = schema.types[node.type.name]
84
+ type_defn = type_defn.metadata[:type_class]
85
+ # Faster than .map{}.include?()
86
+ query.warden.possible_types(type_defn).each do |t|
87
+ if t.metadata[:type_class] == owner_type
88
+ gather_selections(owner_object, owner_type, node.selections, selections_by_name)
89
+ break
90
+ end
73
91
  end
92
+ else
93
+ # it's an untyped fragment, definitely continue
94
+ gather_selections(owner_object, owner_type, node.selections, selections_by_name)
74
95
  end
75
96
  when GraphQL::Language::Nodes::FragmentSpread
76
- if passes_skip_and_include?(node)
77
- fragment_def = query.fragments[node.name]
78
- type_defn = schema.types[fragment_def.type.name]
79
- type_defn = type_defn.metadata[:type_class]
80
- possible_types = schema.possible_types(type_defn).map { |t| t.metadata[:type_class] }
81
- if possible_types.include?(owner_type)
82
- gather_selections(owner_type, fragment_def.selections, selections_by_name)
97
+ fragment_def = query.fragments[node.name]
98
+ type_defn = schema.types[fragment_def.type.name]
99
+ type_defn = type_defn.metadata[:type_class]
100
+ schema.possible_types(type_defn).each do |t|
101
+ if t.metadata[:type_class] == owner_type
102
+ gather_selections(owner_object, owner_type, fragment_def.selections, selections_by_name)
103
+ break
83
104
  end
84
105
  end
85
106
  else
@@ -90,11 +111,20 @@ module GraphQL
90
111
 
91
112
  def evaluate_selections(path, owner_object, owner_type, selections, root_operation_type: nil)
92
113
  selections_by_name = {}
93
- gather_selections(owner_type, selections, selections_by_name)
94
- selections_by_name.each do |result_name, fields|
95
- ast_node = fields.first
114
+ gather_selections(owner_object, owner_type, selections, selections_by_name)
115
+ selections_by_name.each do |result_name, field_ast_nodes_or_ast_node|
116
+ # As a performance optimization, the hash key will be a `Node` if
117
+ # there's only one selection of the field. But if there are multiple
118
+ # selections of the field, it will be an Array of nodes
119
+ if field_ast_nodes_or_ast_node.is_a?(Array)
120
+ field_ast_nodes = field_ast_nodes_or_ast_node
121
+ ast_node = field_ast_nodes.first
122
+ else
123
+ field_ast_nodes = nil
124
+ ast_node = field_ast_nodes_or_ast_node
125
+ end
96
126
  field_name = ast_node.name
97
- field_defn = owner_type.get_field(field_name)
127
+ field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
98
128
  is_introspection = false
99
129
  if field_defn.nil?
100
130
  field_defn = if owner_type == schema.query.metadata[:type_class] && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
@@ -118,6 +148,9 @@ module GraphQL
118
148
  # the field's return type at this path in order
119
149
  # to propagate `null`
120
150
  set_type_at_path(next_path, return_type)
151
+ # Set this before calling `run_with_directives`, so that the directive can have the latest path
152
+ @interpreter_context[:current_path] = next_path
153
+ @interpreter_context[:current_field] = field_defn
121
154
 
122
155
  object = owner_object
123
156
 
@@ -125,6 +158,7 @@ module GraphQL
125
158
  object = field_defn.owner.authorized_new(object, context)
126
159
  end
127
160
 
161
+
128
162
  kwarg_arguments = arguments(object, field_defn, ast_node)
129
163
  # It might turn out that making arguments for every field is slow.
130
164
  # If we have to cache them, we'll need a more subtle approach here.
@@ -136,30 +170,47 @@ module GraphQL
136
170
  kwarg_arguments[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
137
171
  when :path
138
172
  kwarg_arguments[:path] = next_path
173
+ when :lookahead
174
+ if !field_ast_nodes
175
+ field_ast_nodes = [ast_node]
176
+ end
177
+ kwarg_arguments[:lookahead] = Execution::Lookahead.new(
178
+ query: query,
179
+ ast_nodes: field_ast_nodes,
180
+ field: field_defn,
181
+ )
139
182
  else
140
183
  kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
141
184
  end
142
185
  end
143
186
 
144
- next_selections = fields.inject([]) { |memo, f| memo.concat(f.selections) }
145
-
146
- app_result = query.trace("execute_field", {field: field_defn, path: next_path}) do
147
- field_defn.resolve(object, kwarg_arguments, context)
187
+ # Optimize for the case that field is selected only once
188
+ if field_ast_nodes.nil? || field_ast_nodes.size == 1
189
+ next_selections = ast_node.selections
190
+ else
191
+ next_selections = []
192
+ field_ast_nodes.each { |f| next_selections.concat(f.selections) }
148
193
  end
149
194
 
150
- after_lazy(app_result, field: field_defn, path: next_path, eager: root_operation_type == "mutation") do |inner_result|
151
- continue_value = continue_value(next_path, inner_result, field_defn, return_type, ast_node)
152
- if HALT != continue_value
153
- continue_field(next_path, continue_value, field_defn, return_type, ast_node, next_selections)
195
+ resolve_with_directives(object, ast_node) do
196
+ # Actually call the field resolver and capture the result
197
+ app_result = query.trace("execute_field", {field: field_defn, path: next_path}) do
198
+ field_defn.resolve(object, kwarg_arguments, context)
199
+ end
200
+ after_lazy(app_result, field: field_defn, path: next_path, eager: root_operation_type == "mutation") do |inner_result|
201
+ continue_value = continue_value(next_path, inner_result, field_defn, return_type.non_null?, ast_node)
202
+ if HALT != continue_value
203
+ continue_field(next_path, continue_value, field_defn, return_type, ast_node, next_selections, false)
204
+ end
154
205
  end
155
206
  end
156
207
  end
157
208
  end
158
209
 
159
210
  HALT = Object.new
160
- def continue_value(path, value, field, as_type, ast_node)
211
+ def continue_value(path, value, field, is_non_null, ast_node)
161
212
  if value.nil?
162
- if as_type.non_null?
213
+ if is_non_null
163
214
  err = GraphQL::InvalidNullError.new(field.owner, field, value)
164
215
  write_invalid_null_in_response(path, err)
165
216
  else
@@ -187,7 +238,7 @@ module GraphQL
187
238
  err
188
239
  end
189
240
 
190
- continue_value(path, next_value, field, as_type, ast_node)
241
+ continue_value(path, next_value, field, is_non_null, ast_node)
191
242
  elsif GraphQL::Execution::Execute::SKIP == value
192
243
  HALT
193
244
  else
@@ -195,11 +246,20 @@ module GraphQL
195
246
  end
196
247
  end
197
248
 
198
- def continue_field(path, value, field, type, ast_node, next_selections)
249
+ # The resolver for `field` returned `value`. Continue to execute the query,
250
+ # treating `value` as `type` (probably the return type of the field).
251
+ #
252
+ # Use `next_selections` to resolve object fields, if there are any.
253
+ #
254
+ # Location information from `path` and `ast_node`.
255
+ #
256
+ # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
257
+ def continue_field(path, value, field, type, ast_node, next_selections, is_non_null)
199
258
  case type.kind.name
200
259
  when "SCALAR", "ENUM"
201
260
  r = type.coerce_result(value, context)
202
261
  write_in_response(path, r)
262
+ r
203
263
  when "UNION", "INTERFACE"
204
264
  resolved_type = query.resolve_type(type, value)
205
265
  possible_types = query.possible_types(type)
@@ -209,9 +269,10 @@ module GraphQL
209
269
  type_error = GraphQL::UnresolvedTypeError.new(value, field, parent_type, resolved_type, possible_types)
210
270
  schema.type_error(type_error, context)
211
271
  write_in_response(path, nil)
272
+ nil
212
273
  else
213
274
  resolved_type = resolved_type.metadata[:type_class]
214
- continue_field(path, value, field, resolved_type, ast_node, next_selections)
275
+ continue_field(path, value, field, resolved_type, ast_node, next_selections, is_non_null)
215
276
  end
216
277
  when "OBJECT"
217
278
  object_proxy = begin
@@ -220,49 +281,73 @@ module GraphQL
220
281
  err
221
282
  end
222
283
  after_lazy(object_proxy, path: path, field: field) do |inner_object|
223
- continue_value = continue_value(path, inner_object, field, type, ast_node)
284
+ continue_value = continue_value(path, inner_object, field, is_non_null, ast_node)
224
285
  if HALT != continue_value
225
- write_in_response(path, {})
286
+ response_hash = {}
287
+ write_in_response(path, response_hash)
226
288
  evaluate_selections(path, continue_value, type, next_selections)
289
+ response_hash
227
290
  end
228
291
  end
229
292
  when "LIST"
230
- write_in_response(path, [])
293
+ response_list = []
294
+ write_in_response(path, response_list)
231
295
  inner_type = type.of_type
232
296
  idx = 0
233
297
  value.each do |inner_value|
234
298
  next_path = path.dup
235
299
  next_path << idx
236
300
  next_path.freeze
301
+ idx += 1
237
302
  set_type_at_path(next_path, inner_type)
303
+ # This will update `response_list` with the lazy
238
304
  after_lazy(inner_value, path: next_path, field: field) do |inner_inner_value|
239
- continue_value = continue_value(next_path, inner_inner_value, field, inner_type, ast_node)
305
+ # reset `is_non_null` here and below, because the inner type will have its own nullability constraint
306
+ continue_value = continue_value(next_path, inner_inner_value, field, false, ast_node)
240
307
  if HALT != continue_value
241
- continue_field(next_path, continue_value, field, inner_type, ast_node, next_selections)
308
+ continue_field(next_path, continue_value, field, inner_type, ast_node, next_selections, false)
242
309
  end
243
310
  end
244
- idx += 1
245
311
  end
312
+ response_list
246
313
  when "NON_NULL"
247
314
  inner_type = type.of_type
248
315
  # For fields like `__schema: __Schema!`
249
316
  inner_type = resolve_if_late_bound_type(inner_type)
250
317
  # Don't `set_type_at_path` because we want the static type,
251
318
  # we're going to use that to determine whether a `nil` should be propagated or not.
252
- continue_field(path, value, field, inner_type, ast_node, next_selections)
319
+ continue_field(path, value, field, inner_type, ast_node, next_selections, true)
253
320
  else
254
321
  raise "Invariant: Unhandled type kind #{type.kind} (#{type})"
255
322
  end
256
323
  end
257
324
 
258
- def passes_skip_and_include?(node)
259
- # Eventually this should actually call out to the directives
260
- # instead of having magical hard-coded behavior.
261
- node.directives.each do |dir|
262
- dir_defn = schema.directives.fetch(dir.name)
263
- if dir.name == "skip" && arguments(nil, dir_defn, dir)[:if] == true
264
- return false
265
- elsif dir.name == "include" && arguments(nil, dir_defn, dir)[:if] == false
325
+ def resolve_with_directives(object, ast_node)
326
+ run_directive(object, ast_node, 0) { yield }
327
+ end
328
+
329
+ def run_directive(object, ast_node, idx)
330
+ dir_node = ast_node.directives[idx]
331
+ if !dir_node
332
+ yield
333
+ else
334
+ dir_defn = schema.directives.fetch(dir_node.name)
335
+ if !dir_defn.is_a?(Class)
336
+ dir_defn = dir_defn.metadata[:type_class] || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
337
+ end
338
+ dir_args = arguments(nil, dir_defn, dir_node)
339
+ dir_defn.resolve(object, dir_args, context) do
340
+ run_directive(object, ast_node, idx + 1) { yield }
341
+ end
342
+ end
343
+ end
344
+
345
+ # Check {Schema::Directive.include?} for each directive that's present
346
+ def directives_include?(node, graphql_object, parent_type)
347
+ node.directives.each do |dir_node|
348
+ dir_defn = schema.directives.fetch(dir_node.name).metadata[:type_class] || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
349
+ args = arguments(graphql_object, dir_defn, dir_node)
350
+ if !dir_defn.include?(graphql_object, args, context)
266
351
  return false
267
352
  end
268
353
  end
@@ -283,8 +368,12 @@ module GraphQL
283
368
  # @param eager [Boolean] Set to `true` for mutation root fields only
284
369
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
285
370
  def after_lazy(obj, field:, path:, eager: false)
371
+ @interpreter_context[:current_path] = path
372
+ @interpreter_context[:current_field] = field
286
373
  if schema.lazy?(obj)
287
- lazy = GraphQL::Execution::Lazy.new do
374
+ lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
375
+ @interpreter_context[:current_path] = path
376
+ @interpreter_context[:current_field] = field
288
377
  # Wrap the execution of _this_ method with tracing,
289
378
  # but don't wrap the continuation below
290
379
  inner_obj = query.trace("execute_field_lazy", {field: field, path: path}) do
@@ -302,21 +391,38 @@ module GraphQL
302
391
  if eager
303
392
  lazy.value
304
393
  else
305
- @lazies << lazy
394
+ write_in_response(path, lazy)
395
+ lazy
306
396
  end
307
397
  else
308
398
  yield(obj)
309
399
  end
310
400
  end
311
401
 
312
- def arguments(graphql_object, arg_owner, ast_node)
402
+ def each_argument_pair(ast_args_or_hash)
403
+ case ast_args_or_hash
404
+ when GraphQL::Language::Nodes::Field, GraphQL::Language::Nodes::InputObject, GraphQL::Language::Nodes::Directive
405
+ ast_args_or_hash.arguments.each do |arg|
406
+ yield(arg.name, arg.value)
407
+ end
408
+ when Hash
409
+ ast_args_or_hash.each do |key, value|
410
+ normalized_name = GraphQL::Schema::Member::BuildType.camelize(key.to_s)
411
+ yield(normalized_name, value)
412
+ end
413
+ else
414
+ raise "Invariant, unexpected #{ast_args_or_hash.inspect}"
415
+ end
416
+ end
417
+
418
+ def arguments(graphql_object, arg_owner, ast_node_or_hash)
313
419
  kwarg_arguments = {}
314
420
  arg_defns = arg_owner.arguments
315
- ast_node.arguments.each do |arg|
316
- arg_defn = arg_defns[arg.name]
421
+ each_argument_pair(ast_node_or_hash) do |arg_name, arg_value|
422
+ arg_defn = arg_defns[arg_name]
317
423
  # Need to distinguish between client-provided `nil`
318
424
  # and nothing-at-all
319
- is_present, value = arg_to_value(graphql_object, arg_defn.type, arg.value)
425
+ is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_value)
320
426
  if is_present
321
427
  # This doesn't apply to directives, which are legacy
322
428
  # Can remove this when Skip and Include use classes or something.
@@ -328,7 +434,8 @@ module GraphQL
328
434
  end
329
435
  arg_defns.each do |name, arg_defn|
330
436
  if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
331
- kwarg_arguments[arg_defn.keyword] = arg_defn.default_value
437
+ _is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_defn.default_value)
438
+ kwarg_arguments[arg_defn.keyword] = value
332
439
  end
333
440
  end
334
441
  kwarg_arguments
@@ -432,9 +539,6 @@ module GraphQL
432
539
  def type_at(path)
433
540
  t = @types_at_paths
434
541
  path.each do |part|
435
- if part.is_a?(Integer)
436
- part = 0
437
- end
438
542
  t = t[part] || (raise("Invariant: #{part.inspect} not found in #{t}"))
439
543
  end
440
544
  t = t[:__type]
@@ -444,10 +548,6 @@ module GraphQL
444
548
  def set_type_at_path(path, type)
445
549
  types = @types_at_paths
446
550
  path.each do |part|
447
- if part.is_a?(Integer)
448
- part = 0
449
- end
450
-
451
551
  types = types[part] ||= {}
452
552
  end
453
553
  # Use this magic key so that the hash contains: