Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 additions & 8 deletions lib/graphql/execution/batching.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,52 @@ module Execution
module Batching
module SchemaExtension
def execute_batching(query_str = nil, context: nil, document: nil, variables: nil, root_value: nil, validate: true, visibility_profile: nil)
GraphQL::Execution::Batching.run(
schema: self,
query_string: query_str,
multiplex_context = if context
{
backtrace: context[:backtrace],
tracers: context[:tracers],
trace: context[:trace],
dataloader: context[:dataloader],
trace_mode: context[:trace_mode],
}
else
{}
end
query_opts = {
query: query_str,
document: document,
context: context,
validate: validate,
variables: variables,
root_object: root_value,
root_value: root_value,
visibility_profile: visibility_profile,
)
}
m_results = multiplex_batching([query_opts], context: multiplex_context, max_complexity: nil)
m_results[0]
end

def multiplex_batching(query_options, context: {}, max_complexity: self.max_complexity)
Batching.run_all(self, query_options, context: context, max_complexity: max_complexity)
end
end

def self.use(schema)
schema.extend(SchemaExtension)
end

def self.run(schema:, query_string: nil, document: nil, context: {}, validate: true, variables: {}, root_object: nil, visibility_profile: nil)
query = GraphQL::Query.new(schema, query_string, document: document, validate: validate, context: context, variables: variables, root_value: root_object, visibility_profile: visibility_profile)
runner = Runner.new(query)
def self.run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
queries = query_options.map do |opts|
case opts
when Hash
schema.query_class.new(schema, nil, **opts)
when GraphQL::Query, GraphQL::Query::Partial
opts
else
raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
end
end
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
runner = Runner.new(multiplex)
runner.execute
end
end
Expand Down
55 changes: 32 additions & 23 deletions lib/graphql/execution/batching/field_resolve_step.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ def initialize(parent_type:, runner:, key:, selections_step:)
@key = key
@parent_type = parent_type
@ast_node = @ast_nodes = nil
@objects = nil
@results = nil
@runner = runner
@field_definition = nil
@field_results = nil
Expand All @@ -22,8 +20,6 @@ def initialize(parent_type:, runner:, key:, selections_step:)
@next_selections = nil
end

attr_writer :objects, :results

attr_reader :ast_node, :key, :parent_type, :selections_step, :runner, :field_definition

def path
Expand All @@ -46,7 +42,7 @@ def append_selection(ast_node)
end

def coerce_arguments(argument_owner, ast_arguments_or_hash)
arg_defns = argument_owner.arguments(@runner.context)
arg_defns = argument_owner.arguments(@selections_step.query.context)
if arg_defns.empty?
return EmptyObjects::EMPTY_HASH
end
Expand Down Expand Up @@ -84,10 +80,11 @@ def coerce_argument_value(arg_t, arg_value)
end

if arg_value.is_a?(Language::Nodes::VariableIdentifier)
arg_value = if @runner.variables.key?(arg_value.name)
@runner.variables[arg_value.name]
elsif @runner.variables.key?(arg_value.name.to_sym)
@runner.variables[arg_value.name.to_sym]
vars = @selections_step.query.variables
arg_value = if vars.key?(arg_value.name)
vars[arg_value.name]
elsif vars.key?(arg_value.name.to_sym)
vars[arg_value.name.to_sym]
end
elsif arg_value.is_a?(Language::Nodes::NullValue)
arg_value = nil
Expand All @@ -106,7 +103,7 @@ def coerce_argument_value(arg_t, arg_value)
arg_value.map { |v| coerce_argument_value(inner_t, v) }
end
elsif arg_t.kind.leaf?
arg_t.coerce_input(arg_value, @runner.context)
arg_t.coerce_input(arg_value, @selections_step.query.context)
elsif arg_t.kind.input_object?
coerce_arguments(arg_t, arg_value)
else
Expand Down Expand Up @@ -147,17 +144,17 @@ def call

def execute_field
field_name = @ast_node.name
@field_definition = @runner.query.get_field(@parent_type, field_name) || raise("Invariant: no field found for #{@parent_type.to_type_signature}.#{ast_node.name}")

@field_definition = @selections_step.query.get_field(@parent_type, field_name) || raise("Invariant: no field found for #{@parent_type.to_type_signature}.#{ast_node.name}")
objects = @selections_step.objects
if field_name == "__typename"
@field_results = Array.new(@objects.size, @parent_type.graphql_name)
@field_results = Array.new(objects.size, @parent_type.graphql_name)
build_results
return
end

arguments = coerce_arguments(@field_definition, @ast_node.arguments) # rubocop:disable Development/ContextIsPassedCop

@field_results = @field_definition.resolve_batch(self, @objects, @runner.context, arguments)
@field_results = @field_definition.resolve_batch(self, objects, @selections_step.query.context, arguments)

if @runner.resolves_lazies # TODO extract this
lazies = false
Expand Down Expand Up @@ -207,8 +204,9 @@ def build_results

is_list = return_type.list?
is_non_null = return_type.non_null?
results = @selections_step.results
@field_results.each_with_index do |result, i|
result_h = @results[i]
result_h = results[i]
build_graphql_result(result_h, @key, result, return_type, is_non_null, is_list, false)
end
@enqueued_authorization = true
Expand All @@ -219,22 +217,24 @@ def build_results
# Do nothing -- it will enqueue itself later
end
else
results = @selections_step.results
ctx = @selections_step.query.context
@field_results.each_with_index do |result, i|
result_h = @results[i]
result_h = results[i]
result_h[@key] = if result.nil?
if return_type.non_null?
@runner.add_non_null_error(@parent_type, @field_definition, ast_nodes, false, path)
add_non_null_error(false)
else
nil
end
elsif result.is_a?(GraphQL::Error)
result.path = path
result.ast_nodes = ast_nodes
@runner.context.add_error(result)
ctx.add_error(result)
result
else
# TODO `nil`s in [T!] types aren't handled
return_type.coerce_result(result, @runner.context)
return_type.coerce_result(result, ctx)
end
end
end
Expand All @@ -248,12 +248,14 @@ def enqueue_next_steps
next_objects_by_type = Hash.new { |h, obj_t| h[obj_t] = [] }.compare_by_identity
next_results_by_type = Hash.new { |h, obj_t| h[obj_t] = [] }.compare_by_identity

ctx = nil
@all_next_objects.each_with_index do |next_object, i|
result = @all_next_results[i]
if (object_type = @runner.runtime_types_at_result[result])
# OK
else
object_type, _unused_new_value = @runner.schema.resolve_type(@static_type, next_object, @runner.context)
ctx ||= @selections_step.query.context
object_type, _unused_new_value = @runner.schema.resolve_type(@static_type, next_object, ctx)
end
next_objects_by_type[object_type] << next_object
next_results_by_type[object_type] << result
Expand All @@ -267,6 +269,7 @@ def enqueue_next_steps
objects: next_objects,
results: next_results_by_type[obj_type],
runner: @runner,
query: @selections_step.query,
))
end
else
Expand All @@ -277,6 +280,7 @@ def enqueue_next_steps
objects: @all_next_objects,
results: @all_next_results,
runner: @runner,
query: @selections_step.query,
))
end
end
Expand All @@ -289,19 +293,24 @@ def authorized_finished
end
end

def add_non_null_error(is_from_array)
err = InvalidNullError.new(@parent_type, @field_definition, ast_nodes, is_from_array: is_from_array, path: path)
@runner.schema.type_error(err, @selections_step.query.context)
end

private

def build_graphql_result(graphql_result, key, field_result, return_type, is_nn, is_list, is_from_array) # rubocop:disable Metrics/ParameterLists
if field_result.nil?
if is_nn
graphql_result[key] = @runner.add_non_null_error(@parent_type, @field_definition, ast_nodes, is_from_array, path)
graphql_result[key] = add_non_null_error(is_from_array)
else
graphql_result[key] = nil
end
elsif field_result.is_a?(GraphQL::Error)
field_result.path = path
field_result.ast_nodes = ast_nodes
@runner.context.add_error(field_result)
@selections_step.query.context.add_error(field_result)
graphql_result[key] = field_result
elsif is_list
if is_nn
Expand All @@ -314,7 +323,7 @@ def build_graphql_result(graphql_result, key, field_result, return_type, is_nn,
field_result.each_with_index do |inner_f_r, i|
build_graphql_result(list_result, i, inner_f_r, inner_type, inner_type_nn, inner_type_l, true)
end
elsif @runner.authorizes
elsif @runner.resolves_lazies # Handle possible lazy resolve_type response
@pending_authorize_steps_count += 1
@runner.add_step(Batching::PrepareObjectStep.new(
static_type: @static_type,
Expand Down
12 changes: 8 additions & 4 deletions lib/graphql/execution/batching/prepare_object_step.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ def value
def call
case @next_step
when :resolve_type
@resolved_type, _ignored_value = @static_type.kind.abstract? ? @runner.schema.resolve_type(@static_type, @object, @runner.context) : @static_type
if @static_type.kind.abstract?
@resolved_type, _ignored_value = @runner.schema.resolve_type(@static_type, @object, @field_resolve_step.selections_step.query.context)
else
@resolved_type = @static_type
end
if @runner.resolves_lazies && @runner.schema.lazy?(@resolved_type)
@next_step = :authorize
@runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
Expand All @@ -48,7 +52,7 @@ def call
end

def authorize
@authorized_value = @resolved_type.authorized?(@object, @runner.context)
@authorized_value = @resolved_type.authorized?(@object, @field_resolve_step.selections_step.query.context)
if @runner.resolves_lazies && @runner.schema.lazy?(@authorized_value)
@runner.dataloader.lazy_at_depth(@field_resolve_step.path.size, self)
@next_step = :create_result
Expand All @@ -58,7 +62,7 @@ def authorize
rescue GraphQL::Error => err
err.path = @field_resolve_step.path
err.ast_nodes = @field_resolve_step.ast_nodes
@runner.context.errors << err
@field_resolve_step.selections_step.query.context.errors << err
@graphql_result[@key] = err
end

Expand All @@ -71,7 +75,7 @@ def create_result
@runner.runtime_types_at_result[next_result_h] = @resolved_type
@runner.static_types_at_result[next_result_h] = @static_type
elsif @is_non_null
@graphql_result[@key] = @runner.add_non_null_error(@field_resolve_step.parent_type, @field_resolve_step.field_definition, @field_resolve_step.ast_nodes, @is_from_array, @field_resolve_step.path)
@graphql_result[@key] = @field_resolve_step.add_non_null_error(@is_from_array)
else
@graphql_result[@key] = nil
end
Expand Down
Loading
Loading