Skip to content

Commit

Permalink
Merge pull request #1784 from rmosolgo/schema-lazy-resolve-hook
Browse files Browse the repository at this point in the history
Add  a schema-level hook for lazy/batched resolution
  • Loading branch information
Robert Mosolgo authored Sep 14, 2018
2 parents 191725e + 232e827 commit 5f29d74
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 17 deletions.
4 changes: 2 additions & 2 deletions lib/graphql/execution/execute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ def resolve_field(object, field_ctx)
# If the returned object is finished, continue to coerce
# and resolve child fields
def continue_or_wait(raw_value, field_type, field_ctx)
if (lazy_method = field_ctx.schema.lazy_method_name(raw_value))
if field_ctx.schema.lazy?(raw_value)
field_ctx.value = Execution::Lazy.new {
inner_value = begin
begin
raw_value.public_send(lazy_method)
field_ctx.schema.sync_lazy(raw_value)
rescue GraphQL::UnauthorizedError => err
field_ctx.schema.unauthorized_object(err)
end
Expand Down
8 changes: 1 addition & 7 deletions lib/graphql/field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -323,13 +323,7 @@ def build_default_resolver

module DefaultLazyResolve
def self.call(obj, args, ctx)
method_name = ctx.schema.lazy_method_name(obj)
next_obj = obj.public_send(method_name)
if ctx.schema.lazy?(next_obj)
call(next_obj, args, ctx)
else
next_obj
end
ctx.schema.sync_lazy(obj)
end
end
end
Expand Down
28 changes: 25 additions & 3 deletions lib/graphql/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ class << self
:static_validator, :introspection_system,
:query_analyzers, :tracers, :instrumenters,
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
:validate, :multiplex_analyzers, :lazy?, :lazy_method_name, :after_lazy,
:validate, :multiplex_analyzers, :lazy?, :lazy_method_name, :after_lazy, :sync_lazy,
# Configuration
:max_complexity=, :max_depth=,
:metadata,
Expand Down Expand Up @@ -1001,9 +1001,9 @@ def resolve_type(type, obj, ctx = :__undefined__)
# - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
# @api private
def after_lazy(value)
if (lazy_method = lazy_method_name(value))
if lazy?(value)
GraphQL::Execution::Lazy.new do
result = value.public_send(lazy_method)
result = sync_lazy(value)
# The returned result might also be lazy, so check it, too
after_lazy(result) do |final_result|
yield(final_result) if block_given?
Expand All @@ -1014,6 +1014,28 @@ def after_lazy(value)
end
end

# Override this method to handle lazy objects in a custom way.
# @param value [Object] an instance of a class registered with {.lazy_resolve}
# @param ctx [GraphQL::Query::Context] the context for this query
# @return [Object] A GraphQL-ready (non-lazy) object
def self.sync_lazy(value)
yield(value)
end

# @see Schema.sync_lazy for a hook to override
# @api private
def sync_lazy(value)
self.class.sync_lazy(value) { |v|
lazy_method = lazy_method_name(v)
if lazy_method
synced_value = value.public_send(lazy_method)
sync_lazy(synced_value)
else
v
end
}
end

protected

def rescues?
Expand Down
19 changes: 18 additions & 1 deletion spec/graphql/execution/lazy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,23 @@
end
end

describe "Schema#sync_lazy(object)" do
it "Passes objects to that hook at runtime" do
res = run_query <<-GRAPHQL
{
a: nullableNestedSum(value: 1001) { value }
b: nullableNestedSum(value: 1013) { value }
c: nullableNestedSum(value: 1002) { value }
}
GRAPHQL

# This odd, non-adding behavior is hacked into `#sync_lazy`
assert_equal 101, res["data"]["a"]["value"]
assert_equal 113, res["data"]["b"]["value"]
assert_equal 102, res["data"]["c"]["value"]
end
end

describe "LazyMethodMap" do
class SubWrapper < LazyHelpers::Wrapper; end

Expand All @@ -165,7 +182,7 @@ class SubWrapper < LazyHelpers::Wrapper; end
map.set(LazyHelpers::SumAll, :value)
b = LazyHelpers::Wrapper.new(1)
sub_b = LazyHelpers::Wrapper.new(2)
s = LazyHelpers::SumAll.new({}, 3)
s = LazyHelpers::SumAll.new(3)
assert_equal(:item, map.get(b))
assert_equal(:item, map.get(sub_b))
assert_equal(:value, map.get(s))
Expand Down
17 changes: 13 additions & 4 deletions spec/support/lazy_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class SumAll
attr_reader :own_value
attr_writer :value

def initialize(ctx, own_value)
def initialize(own_value)
@own_value = own_value
all << self
end
Expand Down Expand Up @@ -56,7 +56,7 @@ def nested_sum(value:)
if value == 13
Wrapper.new(nil)
else
SumAll.new(@context, @object + value)
SumAll.new(@object + value)
end
end

Expand All @@ -82,7 +82,7 @@ def nested_sum(value:)

field :nestedSum, !LazySum do
argument :value, !types.Int
resolve ->(o, args, c) { SumAll.new(c, args[:value]) }
resolve ->(o, args, c) { SumAll.new(args[:value]) }
end

field :nullableNestedSum, LazySum do
Expand All @@ -91,7 +91,7 @@ def nested_sum(value:)
if args[:value] == 13
Wrapper.new { raise GraphQL::ExecutionError.new("13 is unlucky") }
else
SumAll.new(c, args[:value])
SumAll.new(args[:value])
end
}
end
Expand Down Expand Up @@ -142,6 +142,15 @@ class LazySchema < GraphQL::Schema
instrument(:query, SumAllInstrumentation.new(counter: nil))
instrument(:multiplex, SumAllInstrumentation.new(counter: 1))
instrument(:multiplex, SumAllInstrumentation.new(counter: 2))

def self.sync_lazy(lazy)
if lazy.is_a?(SumAll) && lazy.own_value > 1000
lazy.value # clear the previous set
lazy.own_value - 900
else
super
end
end
end

def run_query(query_str)
Expand Down

0 comments on commit 5f29d74

Please sign in to comment.