Created
October 5, 2016 17:27
-
-
Save dplummer/e1d21a26c370f8fc4a14b02ab034cd79 to your computer and use it in GitHub Desktop.
recursive batch resolver for Absinthe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Grav.Resolver do | |
def resolve(protocol_module, args, field) do | |
{:ok, %{meta: meta} = root} = apply(protocol_module, :where, [args]) | |
records_name = root |> Map.keys |> Enum.reject(& &1 in [:meta, :links, :linked]) |> hd | |
records = preload_associations(root[records_name], field |> derive_preloads) | |
{:ok, root |> Map.put(records_name, records) |> Map.merge(meta)} | |
end | |
@spec derive_preloads(Absinthe.Execution.Field.t) :: [atom] | |
def derive_preloads(%Absinthe.Execution.Field{ast_node: ast_node}), do: derive_preloads(ast_node) | |
@spec derive_preloads(Absinthe.Language.Field.t) :: [atom] | |
def derive_preloads(%Absinthe.Language.Field{selection_set: selection_set}), do: derive_preloads(selection_set) | |
@spec derive_preloads(Absinthe.Language.SelectionSet.t) :: [atom] | |
def derive_preloads(%Absinthe.Language.SelectionSet{selections: selections}), do: derive_preloads(selections) | |
@spec derive_preloads([Absinthe.Language.Field.t]) :: [atom] | |
def derive_preloads(selections) when is_list(selections) do | |
Enum.reduce(selections, [], fn(field, acc) -> acc ++ derive_preload(field.name, field) end) | |
end | |
def derive_preloads(nil), do: [] | |
def derive_preload(field_name, field) do | |
case Inflex.pluralize(field_name) do | |
^field_name -> derive_preloads(field) | |
_ -> | |
case find_client(field_name |> Macro.camelize) do | |
nil -> [] | |
_ -> [{field_name |> String.to_atom, derive_preloads(field)}] | |
end | |
end | |
end | |
def preload_associations(records, []), do: records | |
def preload_associations(records, [{assoc, deps} | tail]) do | |
name = assoc |> Atom.to_string | |
foreign_key = "#{name}_id" |> String.to_atom | |
pluralized = name |> Inflex.pluralize |> String.to_atom | |
case records |> hd |> Map.fetch(assoc) do | |
{:ok, _} -> inlined(records, assoc, deps, name, foreign_key, pluralized) | |
_ -> external(records, assoc, deps, name, foreign_key, pluralized) | |
end | |
|> preload_associations(tail) | |
end | |
def inlined(records, assoc, deps, name, foreign_key, pluralized) do | |
associated_records = records | |
|> Enum.map(& Map.fetch!(&1, assoc)) | |
|> preload_associations(deps) | |
case associated_records |> hd do | |
%{} -> | |
records | |
|> Enum.map(fn record -> | |
associated_record = associated_records |> Enum.find(& &1.id == Map.get(record, assoc).id) | |
record |> Map.put(assoc, associated_record) | |
end) | |
_ -> records | |
end | |
end | |
def external(records, assoc, deps, name, foreign_key, pluralized) do | |
ids = records |> Enum.map(& Map.get(&1, foreign_key)) | |
client_module = find_client(name |> Macro.camelize()) |> String.to_atom() | |
{:ok, %{^pluralized => associated_records}} = apply(client_module, :where, [ %{ids: ids |> Enum.uniq |> Enum.join(",")} ]) | |
associated_records = associated_records |> preload_associations(deps) | |
records | |
|> Enum.map(fn record -> | |
associated_record = associated_records |> Enum.find(& &1.id == Map.get(record, foreign_key)) | |
record |> Map.put(assoc, associated_record) | |
end) | |
end | |
def find_client(record_type) do | |
[ | |
AccountClient, | |
GnomonClient, | |
LedgerClient, | |
QuasiClient, | |
SolicitorClient, | |
] | |
|> Enum.map(fn client_module -> "#{client_module}.#{record_type}" end) | |
|> Enum.find(fn potential_name -> | |
case Code.ensure_loaded(potential_name |> String.to_atom()) do | |
{:error, _} -> | |
false | |
_ -> | |
true | |
end | |
end) | |
end | |
end |
Used in the Absinthe schema field
block like resolve &Grav.Resolver.resolve(AccountClient.User, &1, &2)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FYI this code was written for a hackathon. The
find_client
function in particular is a total hack.