batch_resolver.ex (3054B)
1 defmodule Absinthe.Pipeline.BatchResolver do 2 alias Absinthe.Phase.Document.Execution 3 4 require Logger 5 6 @moduledoc false 7 8 def run([], _), do: [] 9 10 def run([bp | _] = blueprints, options) do 11 {initial_phases, options} = Keyword.pop(options, :initial_phases, []) 12 schema = Keyword.fetch!(options, :schema) 13 plugins = schema.plugins() 14 15 acc = init(blueprints, :acc) 16 ctx = init(blueprints, :context) 17 18 # This will serve as a generic cross document execution struct 19 exec = %{ 20 bp.execution 21 | acc: acc, 22 context: ctx, 23 fragments: %{}, 24 validation_errors: [], 25 result: nil 26 } 27 28 resolution_phase = {Execution.Resolution, [plugin_callbacks: false] ++ options} 29 phases = initial_phases ++ [resolution_phase] 30 31 do_resolve(blueprints, phases, exec, plugins, resolution_phase, options) 32 end 33 34 defp init(blueprints, attr) do 35 Enum.reduce(blueprints, %{}, &Map.merge(Map.fetch!(&1.execution, attr), &2)) 36 end 37 38 defp do_resolve(blueprints, phases, exec, plugins, resolution_phase_template, options) do 39 exec = 40 Enum.reduce(plugins, exec, fn plugin, exec -> 41 plugin.before_resolution(exec) 42 end) 43 44 abort_on_error? = Keyword.get(options, :abort_on_error, true) 45 46 {blueprints, exec} = execute(blueprints, phases, abort_on_error?, [], exec) 47 48 exec = 49 Enum.reduce(plugins, exec, fn plugin, exec -> 50 plugin.after_resolution(exec) 51 end) 52 53 plugins 54 |> Absinthe.Plugin.pipeline(exec) 55 |> case do 56 [] -> 57 blueprints 58 59 pipeline -> 60 pipeline = 61 Absinthe.Pipeline.replace(pipeline, Execution.Resolution, resolution_phase_template) 62 63 do_resolve(blueprints, pipeline, exec, plugins, resolution_phase_template, options) 64 end 65 end 66 67 defp execute([], _phases, _abort_on_error?, results, exec) do 68 {:lists.reverse(results), exec} 69 end 70 71 defp execute([bp | rest], phases, abort_on_error?, results, exec) do 72 bp 73 |> update_exec(exec) 74 |> run_pipeline(phases, abort_on_error?) 75 |> case do 76 {:ok, bp} -> 77 %{acc: acc, context: ctx} = bp.execution 78 exec = %{exec | acc: acc, context: ctx} 79 execute(rest, phases, abort_on_error?, [bp | results], exec) 80 81 :error -> 82 execute(rest, phases, abort_on_error?, [:error | results], exec) 83 end 84 end 85 86 defp run_pipeline(bp, phases, _abort_on_error? = true) do 87 {:ok, blueprint, _} = Absinthe.Pipeline.run(bp, phases) 88 {:ok, blueprint} 89 end 90 91 defp run_pipeline(bp, phases, _) do 92 {:ok, blueprint, _} = Absinthe.Pipeline.run(bp, phases) 93 {:ok, blueprint} 94 rescue 95 e -> 96 pipeline_error(e, __STACKTRACE__) 97 :error 98 end 99 100 defp update_exec(%{execution: execution} = bp, %{acc: acc, context: ctx}) do 101 %{bp | execution: %{execution | acc: acc, context: ctx}} 102 end 103 104 def pipeline_error(exception, trace) do 105 message = Exception.message(exception) 106 stacktrace = trace |> Exception.format_stacktrace() 107 108 Logger.error(""" 109 #{message} 110 111 #{stacktrace} 112 """) 113 end 114 end