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 hidden or 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Used in the Absinthe schema
field
block likeresolve &Grav.Resolver.resolve(AccountClient.User, &1, &2)