Skip to content

Commit

Permalink
Merge pull request #4902 from rmosolgo/fix-field-usage-on-prepared-in…
Browse files Browse the repository at this point in the history
…put-types-with-original-value

Fix field usage on prepared input types with original value
  • Loading branch information
rmosolgo authored Apr 5, 2024
2 parents ec1ae00 + 7fc0a5d commit b0738a0
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 8 deletions.
12 changes: 7 additions & 5 deletions lib/graphql/analysis/ast/field_usage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,28 @@ def extract_deprecated_arguments(argument_values)
@used_deprecated_arguments << argument.definition.path
end

next if argument.value.nil?
arg_val = argument.value

next if arg_val.nil?

argument_type = argument.definition.type
if argument_type.non_null?
argument_type = argument_type.of_type
end

if argument_type.kind.input_object?
extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
extract_deprecated_arguments(argument.original_value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
elsif argument_type.kind.enum?
extract_deprecated_enum_value(argument_type, argument.value)
extract_deprecated_enum_value(argument_type, arg_val)
elsif argument_type.list?
inner_type = argument_type.unwrap
case inner_type.kind
when TypeKinds::INPUT_OBJECT
argument.value.each do |value|
argument.original_value.each do |value|
extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
end
when TypeKinds::ENUM
argument.value.each do |value|
arg_val.each do |value|
extract_deprecated_enum_value(inner_type, value)
end
else
Expand Down
6 changes: 5 additions & 1 deletion lib/graphql/execution/interpreter/argument_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ class Interpreter
# A container for metadata regarding arguments present in a GraphQL query.
# @see Interpreter::Arguments#argument_values for a hash of these objects.
class ArgumentValue
def initialize(definition:, value:, default_used:)
def initialize(definition:, value:, original_value:, default_used:)
@definition = definition
@value = value
@original_value = original_value
@default_used = default_used
end

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

# @return [Object] The value of this argument _before_ `prepare` is applied.
attr_reader :original_value

# @return [GraphQL::Schema::Argument] The definition instance for this argument
attr_reader :definition

Expand Down
1 change: 1 addition & 0 deletions lib/graphql/schema/argument.rb
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ def coerce_into_values(parent_object, values, context, argument_values)
# TODO code smell to access such a deeply-nested constant in a distant module
argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
value: resolved_loaded_value,
original_value: resolved_coerced_value,
definition: self,
default_used: default_used,
)
Expand Down
3 changes: 1 addition & 2 deletions lib/graphql/schema/input_object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,7 @@ def coerce_input(value, ctx)
if resolved_arguments.is_a?(GraphQL::Error)
raise resolved_arguments
else
input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
input_obj_instance.prepare
self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions spec/graphql/analysis/ast/field_usage_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,17 @@
end
end

describe "mutation with deprecated arguments with prepared values" do
let(:query_string) {%|
mutation {
pushValue(preparedTestInput: { deprecatedDate: "2020-10-10" })
}
|}

it "keeps track of nested deprecated arguments" do
assert_equal ['PreparedDateInput.deprecatedDate'], result[:used_deprecated_arguments]
end
end

describe "when an argument prepare raises a GraphQL::ExecutionError" do
class ArgumentErrorFieldUsageSchema < GraphQL::Schema
Expand Down
24 changes: 24 additions & 0 deletions spec/graphql/schema/input_object_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,17 @@ def prepare
end
end

class OnlyOnePrepareInputObject < GraphQL::Schema::InputObject
argument :i, Int

attr_reader :prepared_count
def prepare
@prepared_count ||= 0
@prepared_count += 1
super
end
end

class SmallIntegerArgument < GraphQL::Schema::Argument
def authorized?(obj, val, ctx)
if val > 100
Expand Down Expand Up @@ -388,6 +399,14 @@ def inputs(input:)
end

field :hash_input, resolver: HashInputResolver

field :prepare_once, Int do
argument :input, OnlyOnePrepareInputObject
end

def prepare_once(input:)
input.prepared_count
end
end

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

it "only prepares once" do
res = InputObjectPrepareObjectTest::Schema.execute("{ prepareOnce( input: { i: 1 } ) }")
assert_equal 1, res["data"]["prepareOnce"]
end

it "calls prepare on the input object (variable)" do
query_str = <<-GRAPHQL
query ($input: RangeInput!){ inputs(input: $input) }
Expand Down
13 changes: 13 additions & 0 deletions spec/support/dummy/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,18 @@ class DairyProductInput < BaseInputObject
argument :old_source, String, required: false, deprecation_reason: "No longer supported"
end

class PreparedDateInput < BaseInputObject
description "Input with prepared value"
argument :date, String, description: "date as a string", required: false
argument :deprecated_date, String, description: "date as a string", required: false, deprecation_reason: "Use date"

def prepare
return nil unless date || deprecated_date

Date.parse(date || deprecated_date)
end
end

class DeepNonNull < BaseObject
field :non_null_int, Integer, null: false do
argument :returning, Integer, required: false
Expand Down Expand Up @@ -492,6 +504,7 @@ class DairyAppMutation < BaseObject
field :push_value, [Integer], null: false, description: "Push a value onto a global array :D" do
argument :value, Integer, as: :val
argument :deprecated_test_input, DairyProductInput, required: false
argument :prepared_test_input, PreparedDateInput, required: false
end
def push_value(val:)
GLOBAL_VALUES << val
Expand Down

0 comments on commit b0738a0

Please sign in to comment.