graphql 1.11.2 → 1.11.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +8 -0
  3. data/lib/generators/graphql/object_generator.rb +2 -0
  4. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  5. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  6. data/lib/generators/graphql/templates/base_field.erb +2 -0
  7. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  8. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  9. data/lib/generators/graphql/templates/base_mutation.erb +2 -0
  10. data/lib/generators/graphql/templates/base_object.erb +2 -0
  11. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  12. data/lib/generators/graphql/templates/base_union.erb +2 -0
  13. data/lib/generators/graphql/templates/enum.erb +2 -0
  14. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  15. data/lib/generators/graphql/templates/interface.erb +2 -0
  16. data/lib/generators/graphql/templates/loader.erb +2 -0
  17. data/lib/generators/graphql/templates/mutation.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  19. data/lib/generators/graphql/templates/object.erb +2 -0
  20. data/lib/generators/graphql/templates/query_type.erb +2 -0
  21. data/lib/generators/graphql/templates/scalar.erb +2 -0
  22. data/lib/generators/graphql/templates/schema.erb +2 -0
  23. data/lib/generators/graphql/templates/union.erb +3 -1
  24. data/lib/graphql.rb +17 -0
  25. data/lib/graphql/argument.rb +3 -3
  26. data/lib/graphql/backtrace/tracer.rb +2 -1
  27. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  28. data/lib/graphql/execution/interpreter.rb +10 -0
  29. data/lib/graphql/execution/interpreter/arguments.rb +21 -6
  30. data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
  31. data/lib/graphql/execution/interpreter/runtime.rb +94 -67
  32. data/lib/graphql/integer_decoding_error.rb +17 -0
  33. data/lib/graphql/introspection.rb +96 -0
  34. data/lib/graphql/introspection/field_type.rb +7 -3
  35. data/lib/graphql/introspection/input_value_type.rb +6 -0
  36. data/lib/graphql/introspection/introspection_query.rb +6 -92
  37. data/lib/graphql/introspection/type_type.rb +7 -3
  38. data/lib/graphql/invalid_null_error.rb +1 -1
  39. data/lib/graphql/language/block_string.rb +24 -5
  40. data/lib/graphql/language/lexer.rb +7 -3
  41. data/lib/graphql/language/lexer.rl +7 -3
  42. data/lib/graphql/language/nodes.rb +1 -1
  43. data/lib/graphql/language/parser.rb +107 -103
  44. data/lib/graphql/language/parser.y +4 -0
  45. data/lib/graphql/language/sanitized_printer.rb +59 -26
  46. data/lib/graphql/name_validator.rb +6 -7
  47. data/lib/graphql/pagination/connections.rb +11 -3
  48. data/lib/graphql/query.rb +6 -3
  49. data/lib/graphql/query/context.rb +34 -4
  50. data/lib/graphql/query/fingerprint.rb +2 -0
  51. data/lib/graphql/query/validation_pipeline.rb +4 -1
  52. data/lib/graphql/relay/array_connection.rb +2 -2
  53. data/lib/graphql/relay/range_add.rb +14 -5
  54. data/lib/graphql/schema.rb +49 -18
  55. data/lib/graphql/schema/argument.rb +56 -10
  56. data/lib/graphql/schema/build_from_definition.rb +67 -38
  57. data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
  58. data/lib/graphql/schema/default_type_error.rb +2 -0
  59. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  60. data/lib/graphql/schema/field.rb +32 -16
  61. data/lib/graphql/schema/field/connection_extension.rb +44 -37
  62. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  63. data/lib/graphql/schema/input_object.rb +5 -3
  64. data/lib/graphql/schema/interface.rb +1 -1
  65. data/lib/graphql/schema/late_bound_type.rb +2 -2
  66. data/lib/graphql/schema/loader.rb +1 -0
  67. data/lib/graphql/schema/member/build_type.rb +14 -4
  68. data/lib/graphql/schema/member/has_arguments.rb +54 -53
  69. data/lib/graphql/schema/member/has_fields.rb +17 -7
  70. data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
  71. data/lib/graphql/schema/mutation.rb +4 -0
  72. data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
  73. data/lib/graphql/schema/resolver.rb +6 -0
  74. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -1
  75. data/lib/graphql/schema/subscription.rb +2 -12
  76. data/lib/graphql/schema/timeout.rb +29 -15
  77. data/lib/graphql/schema/unique_within_type.rb +1 -2
  78. data/lib/graphql/schema/validation.rb +8 -0
  79. data/lib/graphql/schema/warden.rb +2 -3
  80. data/lib/graphql/static_validation.rb +1 -0
  81. data/lib/graphql/static_validation/all_rules.rb +1 -0
  82. data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
  83. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  84. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  85. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  86. data/lib/graphql/static_validation/validator.rb +29 -7
  87. data/lib/graphql/subscriptions.rb +32 -22
  88. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
  89. data/lib/graphql/tracing/appoptics_tracing.rb +10 -2
  90. data/lib/graphql/tracing/platform_tracing.rb +1 -1
  91. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  92. data/lib/graphql/types/int.rb +9 -2
  93. data/lib/graphql/types/iso_8601_date_time.rb +2 -1
  94. data/lib/graphql/types/relay/base_connection.rb +8 -6
  95. data/lib/graphql/types/relay/base_edge.rb +2 -1
  96. data/lib/graphql/types/string.rb +7 -1
  97. data/lib/graphql/unauthorized_error.rb +1 -1
  98. data/lib/graphql/version.rb +1 -1
  99. data/readme.md +1 -1
  100. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bafdf456b47766daa370e9f8559e2f525b37d24b6f5a253742eba71759428f29
4
- data.tar.gz: f2dc55cb9c2777104f23358a27e01328ed4b4df5334ce237e9af1cae731c3ab7
3
+ metadata.gz: af3eaf2a85e27ab7a366e27a98d42e4372b93c7590c99fb6fb12fccb48ef37f9
4
+ data.tar.gz: 62be23c2f74539be298db79081b148db5d004325ef6ae6f1b007711659110a01
5
5
  SHA512:
6
- metadata.gz: f7120485f538e60577a55b9674cb21e89785d10becfb5545e8412c284547b4c066c3c7abff15b53edca990cb49fd659eeb0df16e84f6bfb511aca89dd41d2834
7
- data.tar.gz: a48a2c59655e72c44d711f69cd94c7649a0ada18e1dba50cabadaa8a2748b12021ed7f03a6accca78bfba4aab93de2d37cf0b5e119f3f08a12a7d9784667af15
6
+ metadata.gz: f12858fd3c28da572082443e022ed2a5281bda7eda01a35b4c6bee65273b5744ae43904c5bf6202a5d461d67de6ee1ec9d1210a121181d5876a3aee3f37b4f1e
7
+ data.tar.gz: ed4f7ba1b52e9526d4f4a630f2ca06234816bd6be7522e29a98015a8bfa94a8bccdf98a03139ef9f45b266a8f62cb9d922c93ddebf797125d91d04a2203a4aec
@@ -41,6 +41,14 @@ module Graphql
41
41
  end
42
42
  end
43
43
 
44
+ def module_namespacing_when_supported
45
+ if defined?(module_namespacing)
46
+ module_namespacing { yield }
47
+ else
48
+ yield
49
+ end
50
+ end
51
+
44
52
  private
45
53
 
46
54
  def schema_name
@@ -40,6 +40,8 @@ module Graphql
40
40
  case type_expression
41
41
  when "Text"
42
42
  ["String", null]
43
+ when "Decimal"
44
+ ["Float", null]
43
45
  when "DateTime", "Datetime"
44
46
  ["GraphQL::Types::ISO8601DateTime", null]
45
47
  when "Date"
@@ -1,4 +1,6 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class BaseArgument < GraphQL::Schema::Argument
3
4
  end
4
5
  end
6
+ <% end -%>
@@ -1,4 +1,6 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class BaseEnum < GraphQL::Schema::Enum
3
4
  end
4
5
  end
6
+ <% end -%>
@@ -1,5 +1,7 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class BaseField < GraphQL::Schema::Field
3
4
  argument_class Types::BaseArgument
4
5
  end
5
6
  end
7
+ <% end -%>
@@ -1,5 +1,7 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class BaseInputObject < GraphQL::Schema::InputObject
3
4
  argument_class Types::BaseArgument
4
5
  end
5
6
  end
7
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  module BaseInterface
3
4
  include GraphQL::Schema::Interface
@@ -5,3 +6,4 @@ module Types
5
6
  field_class Types::BaseField
6
7
  end
7
8
  end
9
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Mutations
2
3
  class BaseMutation < GraphQL::Schema::RelayClassicMutation
3
4
  argument_class Types::BaseArgument
@@ -6,3 +7,4 @@ module Mutations
6
7
  object_class Types::BaseObject
7
8
  end
8
9
  end
10
+ <% end -%>
@@ -1,5 +1,7 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class BaseObject < GraphQL::Schema::Object
3
4
  field_class Types::BaseField
4
5
  end
5
6
  end
7
+ <% end -%>
@@ -1,4 +1,6 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class BaseScalar < GraphQL::Schema::Scalar
3
4
  end
4
5
  end
6
+ <% end -%>
@@ -1,4 +1,6 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class BaseUnion < GraphQL::Schema::Union
3
4
  end
4
5
  end
6
+ <% end -%>
@@ -1,5 +1,7 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class <%= type_ruby_name.split('::')[-1] %> < Types::BaseEnum
3
4
  <% prepared_values.each do |v| %> value "<%= v[0] %>"<%= v.length > 1 ? ", value: #{v[1]}" : "" %>
4
5
  <% end %> end
5
6
  end
7
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  class GraphqlController < ApplicationController
2
3
  # If accessing from outside this domain, nullify the session
3
4
  # This allows for outside API access while preventing CSRF attacks,
@@ -48,3 +49,4 @@ class GraphqlController < ApplicationController
48
49
  render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
49
50
  end
50
51
  end
52
+ <% end -%>
@@ -1,6 +1,8 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  module <%= type_ruby_name.split('::')[-1] %>
3
4
  include Types::BaseInterface
4
5
  <% normalized_fields.each do |f| %> <%= f.to_ruby %>
5
6
  <% end %> end
6
7
  end
8
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Loaders
2
3
  class <%= class_name %> < GraphQL::Batch::Loader
3
4
  # Define `initialize` to store grouping arguments, eg
@@ -15,3 +16,4 @@ module Loaders
15
16
  end
16
17
  end
17
18
  end
19
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Mutations
2
3
  class <%= mutation_name %> < BaseMutation
3
4
  # TODO: define return fields
@@ -12,3 +13,4 @@ module Mutations
12
13
  # end
13
14
  end
14
15
  end
16
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class MutationType < Types::BaseObject
3
4
  # TODO: remove me
@@ -8,3 +9,4 @@ module Types
8
9
  end
9
10
  end
10
11
  end
12
+ <% end -%>
@@ -1,6 +1,8 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class <%= type_ruby_name.split('::')[-1] %> < Types::BaseObject
3
4
  <% if options.node %> implements GraphQL::Relay::Node.interface
4
5
  <% end %><% normalized_fields.each do |f| %> <%= f.to_ruby %>
5
6
  <% end %> end
6
7
  end
8
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class QueryType < Types::BaseObject
3
4
  # Add root-level fields here.
@@ -13,3 +14,4 @@ module Types
13
14
  field :node, field: GraphQL::Relay::Node.field
14
15
  <% end %> end
15
16
  end
17
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class <%= type_ruby_name.split('::')[-1] %> < Types::BaseScalar
3
4
  def self.coerce_input(input_value, context)
@@ -11,3 +12,4 @@ module Types
11
12
  end
12
13
  end
13
14
  end
15
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  class <%= schema_name %> < GraphQL::Schema
2
3
  query(Types::QueryType)
3
4
 
@@ -38,3 +39,4 @@ class <%= schema_name %> < GraphQL::Schema
38
39
  # GraphQL::Batch setup:
39
40
  use GraphQL::Batch
40
41
  <% end %>end
42
+ <% end -%>
@@ -1,5 +1,7 @@
1
+ <% module_namespacing_when_supported do -%>
1
2
  module Types
2
3
  class <%= type_ruby_name.split('::')[-1] %> < Types::BaseUnion
3
- <% if possible_types.any? %> possible_types [<%= normalized_possible_types.join(", ") %>]
4
+ <% if possible_types.any? %> possible_types <%= normalized_possible_types.join(", ") %>
4
5
  <% end %> end
5
6
  end
7
+ <% end -%>
@@ -21,6 +21,14 @@ module GraphQL
21
21
  class RequiredImplementationMissingError < Error
22
22
  end
23
23
 
24
+ class << self
25
+ def default_parser
26
+ @default_parser ||= GraphQL::Language::Parser
27
+ end
28
+
29
+ attr_writer :default_parser
30
+ end
31
+
24
32
  # Turn a query string or schema definition into an AST
25
33
  # @param graphql_string [String] a GraphQL query string or schema definition
26
34
  # @return [GraphQL::Language::Nodes::Document]
@@ -61,6 +69,14 @@ module GraphQL
61
69
  end
62
70
  end
63
71
  end
72
+
73
+ module StringMatchBackport
74
+ refine String do
75
+ def match?(pattern)
76
+ self =~ pattern
77
+ end
78
+ end
79
+ end
64
80
  end
65
81
 
66
82
  # Order matters for these:
@@ -113,6 +129,7 @@ require "graphql/introspection"
113
129
  require "graphql/analysis_error"
114
130
  require "graphql/coercion_error"
115
131
  require "graphql/invalid_name_error"
132
+ require "graphql/integer_decoding_error"
116
133
  require "graphql/integer_encoding_error"
117
134
  require "graphql/string_encoding_error"
118
135
  require "graphql/internal_representation"
@@ -3,14 +3,14 @@ module GraphQL
3
3
  # @api deprecated
4
4
  class Argument
5
5
  include GraphQL::Define::InstanceDefinable
6
- accepts_definitions :name, :type, :description, :default_value, :as, :prepare, :method_access
6
+ accepts_definitions :name, :type, :description, :default_value, :as, :prepare, :method_access, :deprecation_reason
7
7
  attr_reader :default_value
8
- attr_accessor :description, :name, :as
8
+ attr_accessor :description, :name, :as, :deprecation_reason
9
9
  attr_accessor :ast_node
10
10
  attr_accessor :method_access
11
11
  alias :graphql_name :name
12
12
 
13
- ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as, :prepare, :method_access)
13
+ ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as, :prepare, :method_access, :deprecation_reason)
14
14
 
15
15
  # @api private
16
16
  module DefaultPrepare
@@ -15,7 +15,8 @@ module GraphQL
15
15
  when "validate", "analyze_query", "execute_query", "execute_query_lazy"
16
16
  metadata[:query] || metadata[:queries]
17
17
  when "execute_field", "execute_field_lazy"
18
- metadata[:context]
18
+ # The interpreter passes `query:`, legacy passes `context:`
19
+ metadata[:context] || ((q = metadata[:query]) && q.context)
19
20
  else
20
21
  # Custom key, no backtrace data for this
21
22
  nil
@@ -2,9 +2,9 @@
2
2
  module GraphQL
3
3
  module Define
4
4
  module AssignGlobalIdField
5
- def self.call(type_defn, field_name)
5
+ def self.call(type_defn, field_name, **field_kwargs)
6
6
  resolve = GraphQL::Relay::GlobalIdResolve.new(type: type_defn)
7
- GraphQL::Define::AssignObjectField.call(type_defn, field_name, type: GraphQL::ID_TYPE.to_non_null_type, resolve: resolve)
7
+ GraphQL::Define::AssignObjectField.call(type_defn, field_name, **field_kwargs, type: GraphQL::ID_TYPE.to_non_null_type, resolve: resolve)
8
8
  end
9
9
  end
10
10
  end
@@ -93,6 +93,16 @@ module GraphQL
93
93
  tracer.trace("execute_query_lazy", {multiplex: multiplex, query: query}) do
94
94
  Interpreter::Resolve.resolve_all(final_values)
95
95
  end
96
+ queries.each do |query|
97
+ runtime = query.context.namespace(:interpreter)[:runtime]
98
+ if runtime
99
+ runtime.delete_interpreter_context(:current_path)
100
+ runtime.delete_interpreter_context(:current_field)
101
+ runtime.delete_interpreter_context(:current_object)
102
+ runtime.delete_interpreter_context(:current_arguments)
103
+ end
104
+ end
105
+ nil
96
106
  end
97
107
 
98
108
  class ListResultFailedError < GraphQL::Error
@@ -14,18 +14,33 @@ module GraphQL
14
14
  # This hash is the one used at runtime.
15
15
  #
16
16
  # @return [Hash<Symbol, Object>]
17
- attr_reader :keyword_arguments
17
+ def keyword_arguments
18
+ @keyword_arguments ||= begin
19
+ kwargs = {}
20
+ argument_values.each do |name, arg_val|
21
+ kwargs[name] = arg_val.value
22
+ end
23
+ kwargs
24
+ end
25
+ end
18
26
 
19
- def initialize(keyword_arguments:, argument_values:)
20
- @keyword_arguments = keyword_arguments
27
+ # @param argument_values [nil, Hash{Symbol => ArgumentValue}]
28
+ def initialize(argument_values:)
21
29
  @argument_values = argument_values
30
+ @empty = argument_values.nil? || argument_values.empty?
22
31
  end
23
32
 
24
33
  # @return [Hash{Symbol => ArgumentValue}]
25
- attr_reader :argument_values
34
+ def argument_values
35
+ @argument_values ||= {}
36
+ end
37
+
38
+ def empty?
39
+ @empty
40
+ end
26
41
 
27
- def_delegators :@keyword_arguments, :key?, :[], :keys, :each, :values
28
- def_delegators :@argument_values, :each_value
42
+ def_delegators :keyword_arguments, :key?, :[], :fetch, :keys, :each, :values
43
+ def_delegators :argument_values, :each_value
29
44
 
30
45
  def inspect
31
46
  "#<#{self.class} @keyword_arguments=#{keyword_arguments.inspect}>"
@@ -29,11 +29,16 @@ module GraphQL
29
29
 
30
30
  private
31
31
 
32
+ NO_ARGUMENTS = {}.freeze
33
+
32
34
  NO_VALUE_GIVEN = Object.new
33
35
 
34
36
  def prepare_args_hash(ast_arg_or_hash_or_value)
35
37
  case ast_arg_or_hash_or_value
36
38
  when Hash
39
+ if ast_arg_or_hash_or_value.empty?
40
+ return NO_ARGUMENTS
41
+ end
37
42
  args_hash = {}
38
43
  ast_arg_or_hash_or_value.each do |k, v|
39
44
  args_hash[k] = prepare_args_hash(v)
@@ -42,6 +47,9 @@ module GraphQL
42
47
  when Array
43
48
  ast_arg_or_hash_or_value.map { |v| prepare_args_hash(v) }
44
49
  when GraphQL::Language::Nodes::Field, GraphQL::Language::Nodes::InputObject, GraphQL::Language::Nodes::Directive
50
+ if ast_arg_or_hash_or_value.arguments.empty?
51
+ return NO_ARGUMENTS
52
+ end
45
53
  args_hash = {}
46
54
  ast_arg_or_hash_or_value.arguments.each do |arg|
47
55
  v = prepare_args_hash(arg.value)
@@ -43,23 +43,24 @@ module GraphQL
43
43
  # might be stored up in lazies.
44
44
  # @return [void]
45
45
  def run_eager
46
-
47
46
  root_operation = query.selected_operation
48
47
  root_op_type = root_operation.operation_type || "query"
49
48
  root_type = schema.root_type_for_operation(root_op_type)
50
49
  path = []
51
- @interpreter_context[:current_object] = query.root_value
52
- @interpreter_context[:current_path] = path
50
+ set_all_interpreter_context(query.root_value, nil, nil, path)
53
51
  object_proxy = authorized_new(root_type, query.root_value, context, path)
54
52
  object_proxy = schema.sync_lazy(object_proxy)
55
53
  if object_proxy.nil?
56
54
  # Root .authorized? returned false.
57
55
  write_in_response(path, nil)
58
- nil
59
56
  else
60
57
  evaluate_selections(path, context.scoped_context, object_proxy, root_type, root_operation.selections, root_operation_type: root_op_type)
61
- nil
62
58
  end
59
+ delete_interpreter_context(:current_path)
60
+ delete_interpreter_context(:current_field)
61
+ delete_interpreter_context(:current_object)
62
+ delete_interpreter_context(:current_arguments)
63
+ nil
63
64
  end
64
65
 
65
66
  def gather_selections(owner_object, owner_type, selections, selections_by_name)
@@ -116,9 +117,10 @@ module GraphQL
116
117
  end
117
118
  end
118
119
 
120
+ NO_ARGS = {}.freeze
121
+
119
122
  def evaluate_selections(path, scoped_context, owner_object, owner_type, selections, root_operation_type: nil)
120
- @interpreter_context[:current_object] = owner_object
121
- @interpreter_context[:current_path] = path
123
+ set_all_interpreter_context(owner_object, nil, nil, path)
122
124
  selections_by_name = {}
123
125
  gather_selections(owner_object, owner_type, selections, selections_by_name)
124
126
  selections_by_name.each do |result_name, field_ast_nodes_or_ast_node|
@@ -157,8 +159,7 @@ module GraphQL
157
159
  # to propagate `null`
158
160
  set_type_at_path(next_path, return_type)
159
161
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
160
- @interpreter_context[:current_path] = next_path
161
- @interpreter_context[:current_field] = field_defn
162
+ set_all_interpreter_context(nil, field_defn, nil, next_path)
162
163
 
163
164
  context.scoped_context = scoped_context
164
165
  object = owner_object
@@ -169,44 +170,50 @@ module GraphQL
169
170
 
170
171
  begin
171
172
  kwarg_arguments = arguments(object, field_defn, ast_node)
172
- rescue GraphQL::ExecutionError => e
173
- continue_value(next_path, e, field_defn, return_type.non_null?, ast_node)
173
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => e
174
+ continue_value(next_path, e, owner_type, field_defn, return_type.non_null?, ast_node)
174
175
  next
175
176
  end
176
177
 
177
178
  after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |resolved_arguments|
178
- if resolved_arguments.is_a? GraphQL::ExecutionError
179
- continue_value(next_path, resolved_arguments, field_defn, return_type.non_null?, ast_node)
179
+ case resolved_arguments
180
+ when GraphQL::ExecutionError, GraphQL::UnauthorizedError
181
+ continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node)
180
182
  next
181
183
  end
182
184
 
183
- kwarg_arguments = resolved_arguments.keyword_arguments
184
-
185
- field_defn.extras.each do |extra|
186
- case extra
187
- when :ast_node
188
- kwarg_arguments[:ast_node] = ast_node
189
- when :execution_errors
190
- kwarg_arguments[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
191
- when :path
192
- kwarg_arguments[:path] = next_path
193
- when :lookahead
194
- if !field_ast_nodes
195
- field_ast_nodes = [ast_node]
185
+ if resolved_arguments.empty? && field_defn.extras.empty?
186
+ # We can avoid allocating the `{ Symbol => Object }` hash in this case
187
+ kwarg_arguments = NO_ARGS
188
+ else
189
+ kwarg_arguments = resolved_arguments.keyword_arguments
190
+
191
+ field_defn.extras.each do |extra|
192
+ case extra
193
+ when :ast_node
194
+ kwarg_arguments[:ast_node] = ast_node
195
+ when :execution_errors
196
+ kwarg_arguments[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
197
+ when :path
198
+ kwarg_arguments[:path] = next_path
199
+ when :lookahead
200
+ if !field_ast_nodes
201
+ field_ast_nodes = [ast_node]
202
+ end
203
+ kwarg_arguments[:lookahead] = Execution::Lookahead.new(
204
+ query: query,
205
+ ast_nodes: field_ast_nodes,
206
+ field: field_defn,
207
+ )
208
+ when :argument_details
209
+ kwarg_arguments[:argument_details] = resolved_arguments
210
+ else
211
+ kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
196
212
  end
197
- kwarg_arguments[:lookahead] = Execution::Lookahead.new(
198
- query: query,
199
- ast_nodes: field_ast_nodes,
200
- field: field_defn,
201
- )
202
- when :argument_details
203
- kwarg_arguments[:argument_details] = resolved_arguments
204
- else
205
- kwarg_arguments[extra] = field_defn.fetch_extra(extra, context)
206
213
  end
207
214
  end
208
215
 
209
- @interpreter_context[:current_arguments] = kwarg_arguments
216
+ set_all_interpreter_context(nil, nil, kwarg_arguments, nil)
210
217
 
211
218
  # Optimize for the case that field is selected only once
212
219
  if field_ast_nodes.nil? || field_ast_nodes.size == 1
@@ -228,12 +235,12 @@ module GraphQL
228
235
  err
229
236
  end
230
237
  after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments) do |inner_result|
231
- continue_value = continue_value(next_path, inner_result, field_defn, return_type.non_null?, ast_node)
238
+ continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node)
232
239
  if RawValue === continue_value
233
240
  # Write raw value directly to the response without resolving nested objects
234
241
  write_in_response(next_path, continue_value.resolve)
235
242
  elsif HALT != continue_value
236
- continue_field(next_path, continue_value, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments)
243
+ continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments)
237
244
  end
238
245
  end
239
246
  end
@@ -251,10 +258,9 @@ module GraphQL
251
258
  end
252
259
 
253
260
  HALT = Object.new
254
- def continue_value(path, value, field, is_non_null, ast_node)
261
+ def continue_value(path, value, parent_type, field, is_non_null, ast_node)
255
262
  if value.nil?
256
263
  if is_non_null
257
- parent_type = field.owner_type
258
264
  err = parent_type::InvalidNullError.new(parent_type, field, value)
259
265
  write_invalid_null_in_response(path, err)
260
266
  else
@@ -282,7 +288,7 @@ module GraphQL
282
288
  err
283
289
  end
284
290
 
285
- continue_value(path, next_value, field, is_non_null, ast_node)
291
+ continue_value(path, next_value, parent_type, field, is_non_null, ast_node)
286
292
  elsif GraphQL::Execution::Execute::SKIP == value
287
293
  HALT
288
294
  else
@@ -298,49 +304,49 @@ module GraphQL
298
304
  # Location information from `path` and `ast_node`.
299
305
  #
300
306
  # @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
301
- def continue_field(path, value, field, type, ast_node, next_selections, is_non_null, owner_object, arguments) # rubocop:disable Metrics/ParameterLists
302
- case type.kind.name
307
+ def continue_field(path, value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments) # rubocop:disable Metrics/ParameterLists
308
+ case current_type.kind.name
303
309
  when "SCALAR", "ENUM"
304
- r = type.coerce_result(value, context)
310
+ r = current_type.coerce_result(value, context)
305
311
  write_in_response(path, r)
306
312
  r
307
313
  when "UNION", "INTERFACE"
308
- resolved_type_or_lazy, resolved_value = resolve_type(type, value, path)
314
+ resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
309
315
  resolved_value ||= value
310
316
 
311
- after_lazy(resolved_type_or_lazy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
312
- possible_types = query.possible_types(type)
317
+ after_lazy(resolved_type_or_lazy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
318
+ possible_types = query.possible_types(current_type)
313
319
 
314
320
  if !possible_types.include?(resolved_type)
315
321
  parent_type = field.owner_type
316
- err_class = type::UnresolvedTypeError
322
+ err_class = current_type::UnresolvedTypeError
317
323
  type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
318
324
  schema.type_error(type_error, context)
319
325
  write_in_response(path, nil)
320
326
  nil
321
327
  else
322
- continue_field(path, resolved_value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
328
+ continue_field(path, resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
323
329
  end
324
330
  end
325
331
  when "OBJECT"
326
332
  object_proxy = begin
327
- authorized_new(type, value, context, path)
333
+ authorized_new(current_type, value, context, path)
328
334
  rescue GraphQL::ExecutionError => err
329
335
  err
330
336
  end
331
- after_lazy(object_proxy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
332
- continue_value = continue_value(path, inner_object, field, is_non_null, ast_node)
337
+ after_lazy(object_proxy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
338
+ continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node)
333
339
  if HALT != continue_value
334
340
  response_hash = {}
335
341
  write_in_response(path, response_hash)
336
- evaluate_selections(path, context.scoped_context, continue_value, type, next_selections)
342
+ evaluate_selections(path, context.scoped_context, continue_value, current_type, next_selections)
337
343
  response_hash
338
344
  end
339
345
  end
340
346
  when "LIST"
341
347
  response_list = []
342
348
  write_in_response(path, response_list)
343
- inner_type = type.of_type
349
+ inner_type = current_type.of_type
344
350
  idx = 0
345
351
  scoped_context = context.scoped_context
346
352
  begin
@@ -352,9 +358,9 @@ module GraphQL
352
358
  set_type_at_path(next_path, inner_type)
353
359
  # This will update `response_list` with the lazy
354
360
  after_lazy(inner_value, owner: inner_type, path: next_path, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments) do |inner_inner_value|
355
- continue_value = continue_value(next_path, inner_inner_value, field, inner_type.non_null?, ast_node)
361
+ continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node)
356
362
  if HALT != continue_value
357
- continue_field(next_path, continue_value, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
363
+ continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
358
364
  end
359
365
  end
360
366
  end
@@ -371,12 +377,12 @@ module GraphQL
371
377
 
372
378
  response_list
373
379
  when "NON_NULL"
374
- inner_type = type.of_type
380
+ inner_type = current_type.of_type
375
381
  # Don't `set_type_at_path` because we want the static type,
376
382
  # we're going to use that to determine whether a `nil` should be propagated or not.
377
- continue_field(path, value, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
383
+ continue_field(path, value, owner_type, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
378
384
  else
379
- raise "Invariant: Unhandled type kind #{type.kind} (#{type})"
385
+ raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
380
386
  end
381
387
  end
382
388
 
@@ -413,6 +419,21 @@ module GraphQL
413
419
  true
414
420
  end
415
421
 
422
+ def set_all_interpreter_context(object, field, arguments, path)
423
+ if object
424
+ @context[:current_object] = @interpreter_context[:current_object] = object
425
+ end
426
+ if field
427
+ @context[:current_field] = @interpreter_context[:current_field] = field
428
+ end
429
+ if arguments
430
+ @context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
431
+ end
432
+ if path
433
+ @context[:current_path] = @interpreter_context[:current_path] = path
434
+ end
435
+ end
436
+
416
437
  # @param obj [Object] Some user-returned value that may want to be batched
417
438
  # @param path [Array<String>]
418
439
  # @param field [GraphQL::Schema::Field]
@@ -420,16 +441,10 @@ module GraphQL
420
441
  # @param trace [Boolean] If `false`, don't wrap this with field tracing
421
442
  # @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
422
443
  def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true, &block)
423
- @interpreter_context[:current_object] = owner_object
424
- @interpreter_context[:current_arguments] = arguments
425
- @interpreter_context[:current_path] = path
426
- @interpreter_context[:current_field] = field
444
+ set_all_interpreter_context(owner_object, field, arguments, path)
427
445
  if schema.lazy?(lazy_obj)
428
446
  lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
429
- @interpreter_context[:current_path] = path
430
- @interpreter_context[:current_field] = field
431
- @interpreter_context[:current_object] = owner_object
432
- @interpreter_context[:current_arguments] = arguments
447
+ set_all_interpreter_context(owner_object, field, arguments, path)
433
448
  context.scoped_context = scoped_context
434
449
  # Wrap the execution of _this_ method with tracing,
435
450
  # but don't wrap the continuation below
@@ -540,6 +555,18 @@ module GraphQL
540
555
  res && res[:__dead]
541
556
  end
542
557
 
558
+ # Set this pair in the Query context, but also in the interpeter namespace,
559
+ # for compatibility.
560
+ def set_interpreter_context(key, value)
561
+ @interpreter_context[key] = value
562
+ @context[key] = value
563
+ end
564
+
565
+ def delete_interpreter_context(key)
566
+ @interpreter_context.delete(key)
567
+ @context.delete(key)
568
+ end
569
+
543
570
  def resolve_type(type, value, path)
544
571
  trace_payload = { context: context, type: type, object: value, path: path }
545
572
  resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do