Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call prepare before loads for all arguments #4128

Merged
merged 1 commit into from Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 10 additions & 10 deletions lib/graphql/schema/argument.rb
Expand Up @@ -259,26 +259,26 @@ def coerce_into_values(parent_object, values, context, argument_values)
# If this isn't lazy, then the block returns eagerly and assigns the result here
# If it _is_ lazy, then we write the lazy to the hash, then update it later
argument_values[arg_key] = context.schema.after_lazy(coerced_value) do |resolved_coerced_value|
owner.validate_directive_argument(self, resolved_coerced_value)
prepared_value = begin
prepare_value(parent_object, resolved_coerced_value, context: context)
rescue StandardError => err
context.schema.handle_or_reraise(context, err)
end

if loads && !from_resolver?
loaded_value = begin
load_and_authorize_value(owner, coerced_value, context)
load_and_authorize_value(owner, prepared_value, context)
rescue StandardError => err
context.schema.handle_or_reraise(context, err)
end
end

maybe_loaded_value = loaded_value || resolved_coerced_value
maybe_loaded_value = loaded_value || prepared_value
context.schema.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
owner.validate_directive_argument(self, resolved_loaded_value)
prepared_value = begin
prepare_value(parent_object, resolved_loaded_value, context: context)
rescue StandardError => err
context.schema.handle_or_reraise(context, err)
end

# TODO code smell to access such a deeply-nested constant in a distant module
argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
value: prepared_value,
value: resolved_loaded_value,
definition: self,
default_used: default_used,
)
Expand Down
48 changes: 47 additions & 1 deletion spec/graphql/schema/input_object_spec.rb
Expand Up @@ -126,6 +126,24 @@ def prep(val)
end
end

class Thing < GraphQL::Schema::Object
field :name, String
end

class PreparedInputObj < GraphQL::Schema::InputObject
argument :thing_id, ID, loads: Thing, prepare: ->(val, ctx) { "thing-#{val}" }
end

class ResolverPrepares < GraphQL::Schema::Resolver
argument :input_object, PreparedInputObj
argument :thing_id, ID, loads: Thing, prepare: ->(val, ctx) { "thing-#{val}" }
type [String], null: false

def resolve(thing:, input_object:)
[thing.name, input_object[:thing].name]
end
end

class Query < GraphQL::Schema::Object
field :inputs, [String], null: false do
argument :input, InputObj
Expand All @@ -134,6 +152,20 @@ class Query < GraphQL::Schema::Object
def inputs(input:)
[input.to_kwargs.inspect, input.instrument.name]
end

field :multiple_prepares, [String] do
argument :input_object, PreparedInputObj
argument :thing_id, ID, loads: Thing, prepare: ->(val, ctx) { "thing-#{val}" }
end

def multiple_prepares(thing:, input_object:)
[thing.name, input_object[:thing].name]
end

field :resolver_prepares, resolver: ResolverPrepares


field :t, Thing
end

class Mutation < GraphQL::Schema::Object
Expand Down Expand Up @@ -193,7 +225,13 @@ class Schema < GraphQL::Schema
lazy_resolve(Proc, :call)

def self.object_from_id(id, ctx)
-> { Jazz::GloballyIdentifiableType.find(id) }
-> {
if id.start_with?("thing-")
OpenStruct.new(name: id)
else
Jazz::GloballyIdentifiableType.find(id)
end
}
end

def self.resolve_type(type, obj, ctx)
Expand All @@ -205,6 +243,14 @@ def self.resolve_type(type, obj, ctx)
end
end

it "always prepares before loading" do
res = InputObjectPrepareTest::Schema.execute("{ resolverPrepares(thingId: \"abc\", inputObject: { thingId: \"def\" }) }")
assert_equal ["thing-abc", "thing-def"], res["data"]["resolverPrepares"]

res = InputObjectPrepareTest::Schema.execute("{ multiplePrepares(thingId: \"abc\", inputObject: { thingId: \"def\" }) }")
assert_equal ["thing-abc", "thing-def"], res["data"]["multiplePrepares"]
end

it "calls methods on the input object" do
query_str = <<-GRAPHQL
{ inputs(input: { a: 1, b: 2, c: 3, d: 4, e: 5, instrumentId: "Instrument/Drum Kit" }) }
Expand Down