Code Monkey home page Code Monkey logo

Comments (3)

rmosolgo avatar rmosolgo commented on June 15, 2024

Hey, thanks for reporting this and sorry for the trouble. I don't know of a better fix than the guideline-tech#1, because as you noticed, the un-prepared value isn't available.

It might be possible to add an original_value method to ArgumentValue. We could retain a reference to it from these two places that prepare_value is called:

# Weirdly, procs are applied during coercion, but not methods.
# Probably because these methods require a `self`.
if arg_defn.prepare.is_a?(Symbol) || context.nil?
prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
overwrite_argument(ruby_kwargs_key, prepared_value)
end

prepared_value = begin
prepare_value(parent_object, resolved_coerced_value, context: context)
rescue StandardError => err
context.schema.handle_or_reraise(context, err)
end

from graphql-ruby.

jrichardlai avatar jrichardlai commented on June 15, 2024

Hello, I probably misunderstood what you meant here, but when I tried getting the value from prepare_value it seems the value was already prepared ( converted to the Date ) and I was not able to use it as an argument, were you thinking of using the Hash value as original_value and convert it to ArgumentValue in FieldUsage?

Otherwise the value is already prepared as Date from

input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
input_obj_instance.prepare

Which is called in

coerced_value = begin
type.coerce_input(value, context)

I was able to get it working, but the solution seems dirty, not sure if I should add a method to get the unprepared value from the raw not-coerced value.

guideline-tech@29bf573

diff --git a/lib/graphql/analysis/ast/field_usage.rb b/lib/graphql/analysis/ast/field_usage.rb
index dc83577dbd..185b9a8f9b 100644
--- a/lib/graphql/analysis/ast/field_usage.rb
+++ b/lib/graphql/analysis/ast/field_usage.rb
@@ -48,8 +48,8 @@ module GraphQL
               argument_type = argument_type.of_type
             end

-            if argument_type.kind.input_object? && argument.value.respond_to?(:arguments) # Skip if value is not a GraphQL::Schema::InputObject
-              extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
+            if argument_type.kind.input_object?
+              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)
             elsif argument_type.list?
diff --git a/lib/graphql/execution/interpreter/argument_value.rb b/lib/graphql/execution/interpreter/argument_value.rb
index 4ca37977c0..82d31f3804 100644
--- a/lib/graphql/execution/interpreter/argument_value.rb
+++ b/lib/graphql/execution/interpreter/argument_value.rb
@@ -6,15 +6,19 @@ module GraphQL
       # 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 original Ruby-ready value for this Argument
+        attr_reader :original_value
+
         # @return [GraphQL::Schema::Argument] The definition instance for this argument
         attr_reader :definition

diff --git a/lib/graphql/schema/argument.rb b/lib/graphql/schema/argument.rb
index 633f329b65..dfa85a69a2 100644
--- a/lib/graphql/schema/argument.rb
+++ b/lib/graphql/schema/argument.rb
@@ -261,8 +261,11 @@ module GraphQL
         end

         loaded_value = nil
+        original_value = nil
         coerced_value = begin
-          type.coerce_input(value, context)
+          type.coerce_input(value, context) do |input_obj_instance|
+            original_value = input_obj_instance
+          end
         rescue StandardError => err
           context.schema.handle_or_reraise(context, err)
         end
@@ -290,6 +293,7 @@ module GraphQL
             # 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: original_value,
               definition: self,
               default_used: default_used,
             )
diff --git a/lib/graphql/schema/input_object.rb b/lib/graphql/schema/input_object.rb
index 46c69c1826..856eae5ef5 100644
--- a/lib/graphql/schema/input_object.rb
+++ b/lib/graphql/schema/input_object.rb
@@ -216,6 +216,7 @@ module GraphQL
               raise resolved_arguments
             else
               input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
+              yield input_obj_instance if block_given?
               input_obj_instance.prepare
             end
           end
diff --git a/spec/graphql/analysis/ast/field_usage_spec.rb b/spec/graphql/analysis/ast/field_usage_spec.rb
index 53cafb4851..56924b3904 100644
--- a/spec/graphql/analysis/ast/field_usage_spec.rb
+++ b/spec/graphql/analysis/ast/field_usage_spec.rb
@@ -254,18 +254,30 @@ describe GraphQL::Analysis::AST::FieldUsage do
     end
   end

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

-    it "does not keeps track of nested deprecated arguments" do
+    it "does not return non-deprecated arguments" do
       assert_equal [], result[:used_deprecated_arguments]
     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
       class FieldUsage < GraphQL::Analysis::AST::FieldUsage

from graphql-ruby.

rmosolgo avatar rmosolgo commented on June 15, 2024

Hey! Sorry it took me a while to come back to this. Thanks for sharing what you found when you investigated that fix. I agree, it didn't turn out as nice as I thought it might...

I reviewed this part of the gem and noticed that input_object.prepare was actually being called twice. First, when incoming GraphQL arguments are being turned into an InputObject instance:

input_obj_instance = self.new(resolved_arguments, ruby_kwargs: resolved_arguments.keyword_arguments, context: ctx, defaults_used: nil)
input_obj_instance.prepare

Then, later, the Argument code calls .prepare again:

def prepare_value(obj, value, context: nil)
if value.is_a?(GraphQL::Schema::InputObject)
value = value.prepare
end

For normal uses of .prepare, that second call would never happen because the value would no longer be an InputObject instance. But the default implementation of InputObject#prepare is to return itself, and in that case, it was being called twice.

I found that if I removed that first call to .prepare, then it was much easier to capture original_value and pass it along to the ArgumentValue. I took that approach in #4902.

from graphql-ruby.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.