Skip to content

Commit 7fc0a5d

Browse files
committed
Make sure input objects are only prepared once; handle prepared input objects in FieldUsage analyzer
1 parent fad460d commit 7fc0a5d

File tree

5 files changed

+38
-8
lines changed

5 files changed

+38
-8
lines changed

lib/graphql/analysis/ast/field_usage.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,28 @@ def extract_deprecated_arguments(argument_values)
4141
@used_deprecated_arguments << argument.definition.path
4242
end
4343

44-
next if argument.value.nil?
44+
arg_val = argument.value
45+
46+
next if arg_val.nil?
4547

4648
argument_type = argument.definition.type
4749
if argument_type.non_null?
4850
argument_type = argument_type.of_type
4951
end
5052

5153
if argument_type.kind.input_object?
52-
extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
54+
extract_deprecated_arguments(argument.original_value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
5355
elsif argument_type.kind.enum?
54-
extract_deprecated_enum_value(argument_type, argument.value)
56+
extract_deprecated_enum_value(argument_type, arg_val)
5557
elsif argument_type.list?
5658
inner_type = argument_type.unwrap
5759
case inner_type.kind
5860
when TypeKinds::INPUT_OBJECT
59-
argument.value.each do |value|
61+
argument.original_value.each do |value|
6062
extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
6163
end
6264
when TypeKinds::ENUM
63-
argument.value.each do |value|
65+
arg_val.each do |value|
6466
extract_deprecated_enum_value(inner_type, value)
6567
end
6668
else

lib/graphql/execution/interpreter/argument_value.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@ class Interpreter
66
# A container for metadata regarding arguments present in a GraphQL query.
77
# @see Interpreter::Arguments#argument_values for a hash of these objects.
88
class ArgumentValue
9-
def initialize(definition:, value:, default_used:)
9+
def initialize(definition:, value:, original_value:, default_used:)
1010
@definition = definition
1111
@value = value
12+
@original_value = original_value
1213
@default_used = default_used
1314
end
1415

1516
# @return [Object] The Ruby-ready value for this Argument
1617
attr_reader :value
1718

19+
# @return [Object] The value of this argument _before_ `prepare` is applied.
20+
attr_reader :original_value
21+
1822
# @return [GraphQL::Schema::Argument] The definition instance for this argument
1923
attr_reader :definition
2024

lib/graphql/schema/argument.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ def coerce_into_values(parent_object, values, context, argument_values)
290290
# TODO code smell to access such a deeply-nested constant in a distant module
291291
argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
292292
value: resolved_loaded_value,
293+
original_value: resolved_coerced_value,
293294
definition: self,
294295
default_used: default_used,
295296
)

lib/graphql/schema/input_object.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,7 @@ def coerce_input(value, ctx)
215215
if resolved_arguments.is_a?(GraphQL::Error)
216216
raise resolved_arguments
217217
else
218-
input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
219-
input_obj_instance.prepare
218+
self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
220219
end
221220
end
222221
end

spec/graphql/schema/input_object_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,17 @@ def prepare
353353
end
354354
end
355355

356+
class OnlyOnePrepareInputObject < GraphQL::Schema::InputObject
357+
argument :i, Int
358+
359+
attr_reader :prepared_count
360+
def prepare
361+
@prepared_count ||= 0
362+
@prepared_count += 1
363+
super
364+
end
365+
end
366+
356367
class SmallIntegerArgument < GraphQL::Schema::Argument
357368
def authorized?(obj, val, ctx)
358369
if val > 100
@@ -388,6 +399,14 @@ def inputs(input:)
388399
end
389400

390401
field :hash_input, resolver: HashInputResolver
402+
403+
field :prepare_once, Int do
404+
argument :input, OnlyOnePrepareInputObject
405+
end
406+
407+
def prepare_once(input:)
408+
input.prepared_count
409+
end
391410
end
392411

393412
class Schema < GraphQL::Schema
@@ -404,6 +423,11 @@ class Schema < GraphQL::Schema
404423
assert_equal "5..10", res["data"]["inputs"]
405424
end
406425

426+
it "only prepares once" do
427+
res = InputObjectPrepareObjectTest::Schema.execute("{ prepareOnce( input: { i: 1 } ) }")
428+
assert_equal 1, res["data"]["prepareOnce"]
429+
end
430+
407431
it "calls prepare on the input object (variable)" do
408432
query_str = <<-GRAPHQL
409433
query ($input: RangeInput!){ inputs(input: $input) }

0 commit comments

Comments
 (0)