Skip to content

Commit

Permalink
Add hooks for _lazy events
Browse files Browse the repository at this point in the history
  • Loading branch information
rmosolgo committed Feb 18, 2025
1 parent 9246b56 commit 5451eba
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 14 deletions.
11 changes: 9 additions & 2 deletions lib/graphql/execution/interpreter/runtime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -789,8 +789,10 @@ def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:,
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
# Wrap the execution of _this_ method with tracing,
# but don't wrap the continuation below
result = nil
inner_obj = begin
if trace
result = if trace
@current_trace.begin_execute_field(field, owner_object, arguments, query)
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
schema.sync_lazy(lazy_obj)
end
Expand All @@ -805,6 +807,10 @@ def after_lazy(lazy_obj, field:, owner_object:, arguments:, ast_node:, result:,
rescue GraphQL::ExecutionError => ex_err
ex_err
end
ensure
if trace
@current_trace.end_execute_field(field, owner_object, arguments, query, result)
end
end
yield(inner_obj, runtime_state)
end
Expand Down Expand Up @@ -852,17 +858,18 @@ def resolve_type(type, value)
resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
query.resolve_type(type, value)
end
@current_trace.end_resolve_type(type, value, context, resolved_type)

if lazy?(resolved_type)
GraphQL::Execution::Lazy.new do
@current_trace.begin_resolve_type(type, value, context)
@current_trace.resolve_type_lazy(query: query, type: type, object: value) do
rt = schema.sync_lazy(resolved_type)
@current_trace.end_resolve_type(type, value, context, rt)
rt
end
end
else
@current_trace.end_resolve_type(type, value, context, resolved_type)
[resolved_type, resolved_value]
end
end
Expand Down
26 changes: 16 additions & 10 deletions lib/graphql/schema/object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,29 +66,35 @@ def wrap(object, context)
# @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
# @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
def authorized_new(object, context)
maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do
begin
context.query.current_trace.begin_authorized(self, object, context)
authorized?(object, context)
rescue GraphQL::UnauthorizedError => err
context.schema.unauthorized_object(err)
rescue StandardError => err
context.query.handle_or_reraise(err)
context.query.current_trace.begin_authorized(self, object, context)
begin
maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do
begin
authorized?(object, context)
rescue GraphQL::UnauthorizedError => err
context.schema.unauthorized_object(err)
rescue StandardError => err
context.query.handle_or_reraise(err)
end
end
ensure
context.query.current_trace.end_authorized(self, object, context, maybe_lazy_auth_val)
end

auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
GraphQL::Execution::Lazy.new do
context.query.current_trace.begin_authorized(self, object, context)
context.query.current_trace.authorized_lazy(query: context.query, type: self, object: object) do
context.schema.sync_lazy(maybe_lazy_auth_val)
res = context.schema.sync_lazy(maybe_lazy_auth_val)
context.query.current_trace.end_authorized(self, object, context, res)
res
end
end
else
maybe_lazy_auth_val
end

context.query.after_lazy(auth_val) do |is_authorized|
context.query.current_trace.end_authorized(self, object, context, is_authorized)
if is_authorized
self.new(object, context)
else
Expand Down
48 changes: 46 additions & 2 deletions spec/graphql/tracing/new_relic_trace_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ module Nameable
field :name, String

def self.resolve_type(abs_type, ctx)
Other
ctx[:lazy] ? -> { Other } : Other
end
end
class Other < GraphQL::Schema::Object
implements Nameable
def self.authorized?(obj, ctx)
ctx[:lazy] ? -> { true } : true
end
field :name, String, fallback_value: "other"
end

Expand All @@ -47,6 +50,8 @@ def other
end

field :nameable, Nameable, fallback_value: :nameable

field :lazy_nameable, Nameable, fallback_value: -> { :nameable }
end

class SchemaWithoutTransactionName < GraphQL::Schema
Expand All @@ -58,7 +63,7 @@ def self.object_from_id(_id, _ctx)
:thing
end

def self.resolve_type(_type, _obj, _ctx)
def self.resolve_type(_type, _obj, ctx)
Thing
end
end
Expand All @@ -67,6 +72,7 @@ class SchemaWithTransactionName < GraphQL::Schema
query(Query)
trace_with(GraphQL::Tracing::NewRelicTrace, set_transaction_name: true)
use GraphQL::Dataloader
lazy_resolve(Proc, :call)
end

class SchemaWithScalarTrace < GraphQL::Schema
Expand Down Expand Up @@ -197,4 +203,42 @@ class SchemaWithoutAuthorizedOrResolveType < GraphQL::Schema
assert_equal ["An operation name is required"], res["errors"].map { |e| e["message"] }
assert_equal ["GraphQL/query.anonymous"], NewRelic::TRANSACTION_NAMES
end

it "handles lazies" do
NewRelicTraceTest::SchemaWithTransactionName.execute("{ lazyNameable { name } }", context: { lazy: true })
expected_steps = [
"GraphQL/parse",
"FINISH GraphQL/parse",
"GraphQL/execute",
"GraphQL/analyze",
"GraphQL/validate",
"FINISH GraphQL/validate",
"FINISH GraphQL/analyze",
"GraphQL/Authorized/Query",
"FINISH GraphQL/Authorized/Query",
# Eager:
"GraphQL/Query/lazyNameable",
"FINISH GraphQL/Query/lazyNameable",
# Lazy:
"GraphQL/Query/lazyNameable",
"FINISH GraphQL/Query/lazyNameable",

# Eager/lazy:
"GraphQL/ResolveType/Nameable",
"FINISH GraphQL/ResolveType/Nameable",
"GraphQL/ResolveType/Nameable",
"FINISH GraphQL/ResolveType/Nameable",

# Eager/lazy:
"GraphQL/Authorized/Other",
"FINISH GraphQL/Authorized/Other",
"GraphQL/Authorized/Other",
"FINISH GraphQL/Authorized/Other",

"GraphQL/Other/name",
"FINISH GraphQL/Other/name",
"FINISH GraphQL/execute",
]
assert_equal expected_steps, NewRelic::EXECUTION_SCOPES
end
end

0 comments on commit 5451eba

Please sign in to comment.