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