graphql 2.5.23 → 2.6.3

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/query_complexity.rb +29 -13
  3. data/lib/graphql/analysis.rb +20 -13
  4. data/lib/graphql/backtrace/table.rb +10 -1
  5. data/lib/graphql/current.rb +7 -1
  6. data/lib/graphql/dataloader.rb +1 -1
  7. data/lib/graphql/execution/directive_checks.rb +2 -0
  8. data/lib/graphql/execution/field_resolve_step.rb +744 -0
  9. data/lib/graphql/execution/finalize.rb +230 -0
  10. data/lib/graphql/execution/input_values.rb +333 -0
  11. data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
  12. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  13. data/lib/graphql/execution/interpreter/runtime.rb +36 -15
  14. data/lib/graphql/execution/load_argument_step.rb +102 -0
  15. data/lib/graphql/execution/next.rb +42 -16
  16. data/lib/graphql/execution/prepare_object_step.rb +147 -0
  17. data/lib/graphql/execution/resolve_type_step.rb +27 -0
  18. data/lib/graphql/execution/runner.rb +445 -0
  19. data/lib/graphql/execution/selections_step.rb +91 -0
  20. data/lib/graphql/execution.rb +10 -3
  21. data/lib/graphql/execution_error.rb +7 -13
  22. data/lib/graphql/introspection/entry_points.rb +2 -2
  23. data/lib/graphql/introspection/schema_type.rb +6 -2
  24. data/lib/graphql/language/lexer.rb +12 -8
  25. data/lib/graphql/language/parser.rb +1 -1
  26. data/lib/graphql/language.rb +8 -2
  27. data/lib/graphql/pagination/connections.rb +1 -3
  28. data/lib/graphql/query/context.rb +6 -0
  29. data/lib/graphql/query/partial.rb +18 -3
  30. data/lib/graphql/query.rb +12 -3
  31. data/lib/graphql/runtime_error.rb +6 -0
  32. data/lib/graphql/schema/argument.rb +3 -3
  33. data/lib/graphql/schema/build_from_definition.rb +10 -0
  34. data/lib/graphql/schema/directive/feature.rb +4 -0
  35. data/lib/graphql/schema/directive/transform.rb +20 -0
  36. data/lib/graphql/schema/directive.rb +23 -9
  37. data/lib/graphql/schema/field/connection_extension.rb +2 -15
  38. data/lib/graphql/schema/field/scope_extension.rb +0 -4
  39. data/lib/graphql/schema/field.rb +20 -20
  40. data/lib/graphql/schema/field_extension.rb +11 -41
  41. data/lib/graphql/schema/has_single_input_argument.rb +24 -13
  42. data/lib/graphql/schema/input_object.rb +4 -0
  43. data/lib/graphql/schema/interface.rb +26 -0
  44. data/lib/graphql/schema/introspection_system.rb +6 -21
  45. data/lib/graphql/schema/list.rb +4 -0
  46. data/lib/graphql/schema/member/base_dsl_methods.rb +0 -10
  47. data/lib/graphql/schema/printer.rb +1 -1
  48. data/lib/graphql/schema/ractor_shareable.rb +1 -0
  49. data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
  50. data/lib/graphql/schema/resolver.rb +30 -14
  51. data/lib/graphql/schema/subscription.rb +53 -8
  52. data/lib/graphql/schema/timeout.rb +2 -2
  53. data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
  54. data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
  55. data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
  56. data/lib/graphql/schema/validator/format_validator.rb +3 -3
  57. data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
  58. data/lib/graphql/schema/validator/length_validator.rb +6 -6
  59. data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
  60. data/lib/graphql/schema/validator/required_validator.rb +6 -4
  61. data/lib/graphql/schema/validator.rb +9 -0
  62. data/lib/graphql/schema/visibility/profile.rb +6 -4
  63. data/lib/graphql/schema/visibility/visit.rb +1 -1
  64. data/lib/graphql/schema/visibility.rb +30 -22
  65. data/lib/graphql/schema.rb +43 -20
  66. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +31 -25
  67. data/lib/graphql/subscriptions/event.rb +0 -1
  68. data/lib/graphql/subscriptions.rb +15 -0
  69. data/lib/graphql/tracing/perfetto_trace.rb +5 -3
  70. data/lib/graphql/tracing/trace.rb +6 -0
  71. data/lib/graphql/unauthorized_error.rb +1 -1
  72. data/lib/graphql/version.rb +1 -1
  73. data/lib/graphql.rb +1 -3
  74. metadata +11 -7
  75. data/lib/graphql/execution/next/field_resolve_step.rb +0 -743
  76. data/lib/graphql/execution/next/load_argument_step.rb +0 -64
  77. data/lib/graphql/execution/next/prepare_object_step.rb +0 -129
  78. data/lib/graphql/execution/next/runner.rb +0 -411
  79. data/lib/graphql/execution/next/selections_step.rb +0 -37
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class Finalize
5
+ def initialize(query, data, runner)
6
+ @query = query
7
+ @data = data
8
+ @static_type_at = runner.static_type_at
9
+ @runner = runner
10
+ @current_exec_path = query.path.dup
11
+ @current_result_path = query.path.dup
12
+ @finalizers = runner.finalizers ? runner.finalizers[query] : {}.compare_by_identity
13
+ @finalizers_count = 0
14
+ @finalizers.each do |key, values|
15
+ values.each do |key2, values2|
16
+ case values2
17
+ when Array
18
+ @finalizers_count += values2.size
19
+ else
20
+ @finalizers_count += 1
21
+ end
22
+ end
23
+ end
24
+
25
+ query.context.errors.each do |err|
26
+ err_path = err.path - @current_exec_path
27
+ key = err_path.pop
28
+ targets = [data]
29
+ while (part = err_path.shift)
30
+ targets.map! { |t| t[part] }
31
+ targets.flatten!
32
+ end
33
+
34
+ targets.each_with_index do |target, idx|
35
+ if target.is_a?(Hash)
36
+ if target[key].equal?(err)
37
+ tf = @finalizers[target] ||= {}.compare_by_identity
38
+ tf[key] = err
39
+ @finalizers_count += 1
40
+ elsif (arr = target[key]).is_a?(Array)
41
+ arr.each_with_index do |el, idx|
42
+ if el.equal?(err)
43
+ tf = @finalizers[arr] ||= {}.compare_by_identity
44
+ tf[idx] = err
45
+ @finalizers_count += 1
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def run
55
+ if (selected_operation = @query.selected_operation) && @data
56
+ if @data.is_a?(Hash)
57
+ check_object_result(@data, @query.root_type, selected_operation.selections)
58
+ elsif @data.is_a?(Array)
59
+ check_list_result(@data, @query.root_type, selected_operation.selections)
60
+ elsif @data.is_a?(Finalizer)
61
+ dummy_data = {}
62
+ dummy_key = "__dummy"
63
+ @data.path = @query.path
64
+ @data.finalize_graphql_result(@query, dummy_data, dummy_key)
65
+ dummy_data[dummy_key]
66
+ else
67
+ raise ArgumentError, "Unexpected @data: #{@data.inspect}"
68
+ end
69
+ else
70
+ @data
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def run_finalizers(result_path, finalizer_or_finalizers, result_data, result_key)
77
+ if finalizer_or_finalizers.is_a?(Array)
78
+ finalizer_or_finalizers.each { |f|
79
+ f.path = result_path
80
+ f.finalize_graphql_result(@query, result_data, result_key)
81
+ }
82
+ @finalizers_count -= finalizer_or_finalizers.size
83
+ else
84
+ f = finalizer_or_finalizers
85
+ f.path = result_path
86
+ f.finalize_graphql_result(@query, result_data, result_key)
87
+ @finalizers_count -= 1
88
+ end
89
+ end
90
+
91
+ def finalizers(result_value, key)
92
+ finalizers_for_value = @finalizers[result_value]
93
+ finalizers_for_value && finalizers_for_value[key]
94
+ end
95
+
96
+ def check_object_result(result_h, parent_type, ast_selections)
97
+ if (f = finalizers(result_h, nil))
98
+ run_finalizers(@current_result_path.dup, f, result_h, nil)
99
+ return result_h if @finalizers_count == 0
100
+ end
101
+
102
+ if parent_type.kind.abstract?
103
+ parent_type = @runner.runtime_type_at[result_h]
104
+ end
105
+
106
+ ast_selections.each do |ast_selection|
107
+ case ast_selection
108
+ when Language::Nodes::Field
109
+ key = ast_selection.alias || ast_selection.name
110
+ if (f = finalizers(result_h, key))
111
+ result_value = result_h[key]
112
+ run_finalizers(@current_result_path.dup << key, f, result_h, key)
113
+ new_result_value = result_h.key?(key) ? result_h[key] : :unassigned
114
+ end
115
+ next if !(f || result_h.key?(key))
116
+ begin
117
+ @current_exec_path << key
118
+ @current_result_path << key
119
+
120
+ field_defn = @query.context.types.field(parent_type, ast_selection.name) || raise("Invariant: No field found for #{parent_type.to_type_signature}.#{ast_selection.name}")
121
+ result_type = field_defn.type
122
+ if (result_type_non_null = result_type.non_null?)
123
+ result_type = result_type.of_type
124
+ end
125
+
126
+ if !f
127
+ result_value = result_h[key]
128
+ new_result_value = if result_type.list? && result_value
129
+ check_list_result(result_value, result_type.of_type, ast_selection.selections)
130
+ elsif !result_type.kind.leaf? && result_value
131
+ check_object_result(result_value, result_type, ast_selection.selections)
132
+ else
133
+ result_value
134
+ end
135
+ end
136
+
137
+ if new_result_value.nil? && result_type_non_null
138
+ return nil
139
+ elsif :unassigned.equal?(new_result_value)
140
+ # Do nothing
141
+ break if @finalizers_count == 0
142
+ elsif !new_result_value.equal?(result_value)
143
+ result_h[key] = new_result_value
144
+ break if @finalizers_count == 0
145
+ end
146
+ ensure
147
+ @current_exec_path.pop
148
+ @current_result_path.pop
149
+ end
150
+ when Language::Nodes::InlineFragment
151
+ static_type_at_result = @static_type_at[result_h]
152
+ if static_type_at_result && (
153
+ (t = ast_selection.type).nil? ||
154
+ @runner.type_condition_applies?(@query.context, static_type_at_result, t.name)
155
+ )
156
+ result_h = check_object_result(result_h, parent_type, ast_selection.selections)
157
+ return nil if result_h.nil?
158
+ end
159
+ when Language::Nodes::FragmentSpread
160
+ fragment_defn = @query.document.definitions.find { |defn| defn.is_a?(Language::Nodes::FragmentDefinition) && defn.name == ast_selection.name }
161
+ static_type_at_result = @static_type_at[result_h]
162
+ if static_type_at_result && @runner.type_condition_applies?(@query.context, static_type_at_result, fragment_defn.type.name)
163
+ result_h = check_object_result(result_h, parent_type, fragment_defn.selections)
164
+ return nil if result_h.nil?
165
+ end
166
+ end
167
+ end
168
+
169
+ result_h
170
+ end
171
+
172
+ def check_list_result(result_arr, inner_type, ast_selections)
173
+ inner_type_non_null = false
174
+ if inner_type.non_null?
175
+ inner_type_non_null = true
176
+ inner_type = inner_type.of_type
177
+ end
178
+
179
+ new_invalid_null = false
180
+
181
+ if (f = finalizers(result_arr, nil))
182
+ run_finalizers(@current_result_path.dup, f, result_arr, nil)
183
+ return result_arr if @finalizers_count == 0
184
+ end
185
+
186
+ effective_idx = -1
187
+ result_arr.each_with_index do |result_item, before_idx|
188
+ effective_idx += 1
189
+ @current_result_path << before_idx
190
+ new_result = if (f = finalizers(result_arr, before_idx))
191
+ before_size = result_arr.size
192
+ run_finalizers(@current_result_path.dup, f, result_arr, effective_idx)
193
+ after_size = result_arr.size
194
+ if after_size < before_size
195
+ effective_idx -= 1
196
+ :unassigned
197
+ else
198
+ result_arr[effective_idx]
199
+ end
200
+ elsif inner_type.list? && result_item
201
+ check_list_result(result_item, inner_type.of_type, ast_selections)
202
+ elsif !inner_type.kind.leaf? && result_item
203
+ check_object_result(result_item, inner_type, ast_selections)
204
+ else
205
+ result_item
206
+ end
207
+
208
+ if new_result.nil? && inner_type_non_null
209
+ new_invalid_null = true
210
+ result_arr[effective_idx] = nil
211
+ break if @finalizers_count == 0
212
+ elsif :unassigned.equal?(new_result)
213
+ break if @finalizers_count == 0
214
+ elsif !new_result.equal?(result_item)
215
+ result_arr[effective_idx] = new_result
216
+ break if @finalizers_count == 0
217
+ end
218
+ ensure
219
+ @current_result_path.pop
220
+ end
221
+
222
+ if new_invalid_null
223
+ nil
224
+ else
225
+ result_arr
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,333 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class InputValues
5
+ def initialize(query, runner)
6
+ @query = query
7
+ @runner = runner
8
+ @variable_values = nil
9
+ end
10
+
11
+ def variable_values
12
+ @variable_values ||= begin
13
+ variable_nodes = @query.selected_operation.variables
14
+ if variable_nodes.empty?
15
+ EmptyObjects::EMPTY_HASH
16
+ else
17
+ raw_values = @query.provided_variables
18
+ values = {}
19
+ variable_nodes.each do |var_node|
20
+ var_ast_value = if raw_values.key?(var_node.name)
21
+ raw_values[var_node.name]
22
+ elsif raw_values.key?(sym_name = var_node.name.to_sym)
23
+ raw_values[sym_name]
24
+ elsif !var_node.default_value.nil?
25
+ var_node.default_value
26
+ else
27
+ next
28
+ end
29
+
30
+ var_type = @runner.schema.type_from_ast(var_node.type, context: @query.context)
31
+ values[var_node.name] = variable_value(var_ast_value, var_type)
32
+ end
33
+ values
34
+ end
35
+ end
36
+ end
37
+
38
+ def argument_values(owner_defn, argument_nodes, field_resolve_step)
39
+ arg_defns = @query.types.arguments(owner_defn)
40
+ argument_values = {}
41
+ errors = nil
42
+
43
+ arg_defns.each do |argument_definition|
44
+ arg_ruby_key = argument_definition.keyword
45
+ arg_graphql_key = argument_definition.graphql_name
46
+ arg_node = argument_nodes.find { |a| a.name == arg_graphql_key }
47
+ if arg_node.nil? || (arg_node.value.is_a?(Language::Nodes::VariableIdentifier) && !variable_values.key?(arg_node.value.name))
48
+ if argument_definition.default_value?
49
+ arg_value = value_from_ast(argument_definition.default_value, argument_definition.type)
50
+ argument_value(argument_values, arg_ruby_key, argument_definition, arg_value, nil, field_resolve_step)
51
+ end
52
+ else
53
+ arg_value = value_from_ast(arg_node.value, argument_definition.type)
54
+ argument_value(argument_values, arg_ruby_key, argument_definition, arg_value, nil, field_resolve_step)
55
+ end
56
+ rescue GraphQL::RuntimeError => exec_err
57
+ errors ||= []
58
+ errors << exec_err
59
+ end
60
+
61
+ return argument_values, errors
62
+ end
63
+
64
+ private
65
+
66
+ def variable_value(value, type)
67
+ if type.non_null?
68
+ type = type.of_type
69
+ end
70
+
71
+ if value.is_a?(Language::Nodes::Enum)
72
+ value = value.name
73
+ end
74
+
75
+ if value.nil?
76
+ nil
77
+ elsif type.list?
78
+ inner_type = type.of_type
79
+ if value.is_a?(Array)
80
+ value.map { |v| variable_value(v, inner_type) }.freeze
81
+ else
82
+ [variable_value(value, inner_type)].freeze
83
+ end
84
+ elsif type.kind.input_object?
85
+ coerced_obj = {}
86
+
87
+ if value.is_a?(Hash)
88
+ @query.types.arguments(type).each do |arg|
89
+ arg_key = arg.keyword
90
+ if value.key?(arg.graphql_name)
91
+ arg_value = value[arg.graphql_name]
92
+ elsif value.key?(sym_name = arg.graphql_name.to_sym)
93
+ arg_value = value[sym_name]
94
+ elsif arg.default_value?
95
+ coerced_obj[arg_key] = arg.default_value
96
+ next
97
+ else
98
+ next
99
+ end
100
+
101
+ if arg_value.nil? && arg.replace_null_with_default?
102
+ arg_value = arg.default_value
103
+ end
104
+
105
+ coerced_obj[arg_key] = variable_value(arg_value, arg.type)
106
+ end
107
+ else
108
+ @query.types.arguments(type).each do |arg|
109
+ arg_key = arg.keyword
110
+ arg_name = arg.graphql_name
111
+ if (v_node = value.arguments.find { |a| a.name == arg_name }) # rubocop:disable Development/ContextIsPassedCop
112
+ arg_value = v_node.value
113
+ coerced_obj[arg_key] = if arg_value.nil? && arg.replace_null_with_default?
114
+ arg.default_value
115
+ else
116
+ variable_value(arg_value, arg.type)
117
+ end
118
+ elsif arg.default_value?
119
+ coerced_obj[arg_key] = arg.default_value
120
+ else
121
+ # Nothing
122
+ end
123
+ end
124
+ end
125
+
126
+ coerced_obj
127
+ elsif type.kind.leaf?
128
+ type.coerce_input(value, @query.context)
129
+ else
130
+ raise GraphQL::Error, "Unexpected input type: #{type.graphql_name}."
131
+ end
132
+ end
133
+
134
+ def argument_value(argument_values, argument_key, argument_definition, arg_value, override_type, field_resolve_step)
135
+ treat_as_type = override_type || argument_definition.type
136
+ if treat_as_type.non_null?
137
+ if arg_value.nil?
138
+ treat_as_type.coerce_input(arg_value, @query.context)
139
+ end
140
+ treat_as_type = treat_as_type.of_type
141
+ end
142
+
143
+ if arg_value.nil? && argument_definition.replace_null_with_default?
144
+ arg_value = argument_definition.default_value
145
+ end
146
+
147
+ if treat_as_type.kind.list? && !arg_value.nil?
148
+ inner_t = treat_as_type.of_type
149
+ arg_value = if arg_value.is_a?(Array)
150
+ values = Array.new(arg_value.size)
151
+ arg_value.each_with_index { |inner_v, idx| argument_value(values, idx, argument_definition, inner_v, inner_t, field_resolve_step)}
152
+ values
153
+ else
154
+ values = [nil]
155
+ argument_value(values, 0, argument_definition, arg_value, inner_t, field_resolve_step)
156
+ values
157
+ end
158
+ end
159
+
160
+ if arg_value && treat_as_type.kind.input_object?
161
+ arg_defns = @query.types.arguments(treat_as_type)
162
+ new_arg_value = {}
163
+ arg_defns.each do |inner_arg_defn|
164
+ inner_arg_key = inner_arg_defn.keyword
165
+ if arg_value.is_a?(Hash)
166
+ if arg_value.key?(inner_arg_key)
167
+ inner_arg_value = arg_value[inner_arg_key]
168
+ argument_value(new_arg_value, inner_arg_key, inner_arg_defn, inner_arg_value, nil, field_resolve_step)
169
+ end
170
+ else
171
+ inner_arg_name = inner_arg_defn.graphql_name
172
+ inner_arg_value = arg_value.arguments.find { |a| a.name == inner_arg_name } # rubocop:disable Development/ContextIsPassedCop
173
+ if inner_arg_value
174
+ argument_value(new_arg_value, inner_arg_key, inner_arg_defn, inner_arg_value, nil, field_resolve_step)
175
+ end
176
+ end
177
+ end
178
+ arg_value = treat_as_type.new(nil, ruby_kwargs: new_arg_value, context: @query.context, defaults_used: nil)
179
+ end
180
+
181
+ if override_type.nil? # only on root arguments, not list elements
182
+ arg_value = begin
183
+ argument_definition.prepare_value(nil, arg_value, context: @query.context)
184
+ rescue StandardError => err
185
+ @runner.schema.handle_or_reraise(@query.context, err, object: nil, arguments: argument_values, field: field_resolve_step&.field_definition)
186
+ end
187
+ end
188
+
189
+ if field_resolve_step && arg_value && override_type.nil? && argument_definition.loads
190
+ field_defn = field_resolve_step.field_definition
191
+ load_receiver = if (r = field_defn.resolver)
192
+ r.new(field: field_defn, context: @query.context, object: nil)
193
+ else
194
+ field_defn
195
+ end
196
+ ps = field_resolve_step.pending_steps ||= []
197
+
198
+ if argument_definition.type.list?
199
+ results = Array.new(arg_value.size, nil)
200
+ argument_values[argument_key] = results
201
+ arg_value.each_with_index do |inner_v, idx|
202
+ loads_step = LoadArgumentStep.new(
203
+ field_resolve_step: field_resolve_step,
204
+ load_receiver: load_receiver,
205
+ argument_value: inner_v,
206
+ argument_definition: argument_definition,
207
+ arguments: results,
208
+ argument_key: idx,
209
+ )
210
+ ps.push(loads_step)
211
+ @runner.add_step(loads_step)
212
+ end
213
+ else
214
+ loads_step = LoadArgumentStep.new(
215
+ field_resolve_step: field_resolve_step,
216
+ load_receiver: load_receiver,
217
+ argument_value: arg_value,
218
+ argument_definition: argument_definition,
219
+ arguments: argument_values,
220
+ argument_key: argument_key,
221
+ )
222
+ ps.push(loads_step)
223
+ @runner.add_step(loads_step)
224
+ end
225
+ else
226
+ argument_values[argument_key] = arg_value
227
+ end
228
+ nil
229
+ end
230
+
231
+ def value_from_ast(value_node, type)
232
+ if type.non_null?
233
+ type = type.of_type
234
+ end
235
+
236
+ if value_node.nil?
237
+ nil
238
+ elsif value_node.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
239
+ variable_values[value_node.name]
240
+ elsif type.list?
241
+ inner_type = type.of_type
242
+ if value_node.is_a?(Array)
243
+ coerced_items = value_node.map do |inner_value_node|
244
+ value_from_ast(inner_value_node, inner_type)
245
+ end
246
+ coerced_items.freeze
247
+ elsif value_node.is_a?(Language::Nodes::NullValue)
248
+ nil
249
+ else
250
+ item_value = value_from_ast(value_node, inner_type)
251
+ [item_value].freeze
252
+ end
253
+ elsif type.kind.input_object?
254
+ coerced_obj = {}
255
+ # TODO manually handle NullValue here?
256
+ if value_node.is_a?(Hash)
257
+ @query.types.arguments(type).each do |arg|
258
+ arg_value = value_node[arg.keyword]
259
+ arg_key = arg.keyword
260
+ if arg_value.nil?
261
+ if arg.default_value?
262
+ coerced_obj[arg_key] = arg.default_value
263
+ end
264
+ next
265
+ end
266
+
267
+ coerced_obj[arg_key] = value_from_ast(arg_value, arg.type)
268
+ end
269
+ else
270
+ arg_nodes_by_name = value_node.arguments.each_with_object({}) do |arg_node, acc| # rubocop:disable Development/ContextIsPassedCop
271
+ acc[arg_node.name] = arg_node
272
+ end
273
+
274
+ @query.types.arguments(type).each do |arg|
275
+ arg_node = arg_nodes_by_name[arg.graphql_name]
276
+ arg_key = arg.keyword
277
+ if arg_node.nil? || (arg_node.value.is_a?(Language::Nodes::VariableIdentifier) && !variable_values.key?(arg_node.value.name))
278
+ if arg.default_value?
279
+ coerced_obj[arg_key] = arg.default_value
280
+ end
281
+ next
282
+ end
283
+
284
+ arg_value = value_from_ast(arg_node.value, arg.type)
285
+ coerced_obj[arg_key] = arg_value
286
+ end
287
+ end
288
+
289
+
290
+ coerced_obj
291
+ elsif type.kind.leaf?
292
+ if value_node.is_a?(Language::Nodes::AbstractNode) || value_node.is_a?(Array)
293
+ value_node = coerce_untyped_input(value_node)
294
+ end
295
+
296
+ begin
297
+ type.coerce_input(value_node, @query.context)
298
+ rescue GraphQL::UnauthorizedEnumValueError => enum_err
299
+ @runner.schema.unauthorized_object(enum_err)
300
+ end
301
+ else
302
+ raise "Unexpected input type: #{type.to_type_signature}."
303
+ end
304
+ end
305
+
306
+ private
307
+
308
+ def coerce_untyped_input(input_value)
309
+ case input_value
310
+ when Language::Nodes::AbstractNode
311
+ case input_value
312
+ when Language::Nodes::NullValue
313
+ nil
314
+ when Language::Nodes::Enum
315
+ input_value.name
316
+ when Language::Nodes::InputObject
317
+ value_h = {}
318
+ input_value.arguments.each do |arg| # rubocop:disable Development/ContextIsPassedCop
319
+ value_h[arg.name] = coerce_untyped_input(arg.value)
320
+ end
321
+ value_h
322
+ else
323
+ raise "Unhandled untyped input AST node: #{input_value.class}"
324
+ end
325
+ when Array
326
+ input_value.map { |v| coerce_untyped_input(v) }
327
+ else
328
+ input_value
329
+ end
330
+ end
331
+ end
332
+ end
333
+ end
@@ -30,7 +30,10 @@ module GraphQL
30
30
  @storage[argument_owner][parent_object][ast_node] = resolved_args
31
31
  end
32
32
  end
33
+ end
33
34
 
35
+ def cached_arguments_for(ast_node, argument_owner)
36
+ @storage[argument_owner][nil][ast_node]
34
37
  end
35
38
 
36
39
  # @yield [Interpreter::Arguments, Lazy<Interpreter::Arguments>] The finally-loaded arguments
@@ -5,6 +5,12 @@ module GraphQL
5
5
  class Interpreter
6
6
  # Wrapper for raw values
7
7
  class RawValue
8
+ include GraphQL::Execution::Finalizer
9
+
10
+ def finalize_graphql_result(query, result_data, result_key)
11
+ result_data[result_key] = @object
12
+ end
13
+
8
14
  def initialize(obj = nil)
9
15
  @object = obj
10
16
  end