if Code.ensure_loaded?(Absinthe) do defmodule Cqrs.Absinthe.Query do @moduledoc false alias Cqrs.{BoundedContext, Absinthe.Args, Absinthe.Metadata, Absinthe.Middleware, Absinthe.Query} def create_connection_query(query_module, returns, opts) do function_name = BoundedContext.__function_name__(query_module, opts) query_args = create_query_args(query_module, opts) description = query_module.__simple_moduledoc__() opts = :cqrs_tools |> Application.get_env(:absinthe_relay, []) |> Keyword.merge(opts) repo = Keyword.fetch!(opts, :repo) quote do require Middleware connection field unquote(function_name), node_type: unquote(returns) do unquote_splicing(query_args) description unquote(description) Middleware.before_resolve(unquote(query_module), unquote(opts)) resolve(fn parent, args, resolution -> alias Absinthe.Relay.Connection args = unquote(query_module) |> Query.read_filters_from_parent(parent, unquote(opts)) |> Map.merge(args) opts = Metadata.merge(resolution, unquote(opts)) case BoundedContext.__create_query__(unquote(query_module), args, opts) do {:error, error} -> {:error, error} {:ok, query} -> repo_fun = fn args -> fun = Keyword.get(unquote(opts), :repo_fun, :all) apply(unquote(repo), fun, [args]) end Connection.from_query(query, repo_fun, args) end end) Middleware.after_resolve(unquote(query_module), unquote(opts)) end end end def create_query(query_module, returns, opts) do function_name = BoundedContext.__function_name__(query_module, opts) query_args = create_query_args(query_module, opts) description = query_module.__simple_moduledoc__() quote do require Middleware field unquote(function_name), unquote(returns) do unquote_splicing(query_args) description unquote(description) Middleware.before_resolve(unquote(query_module), unquote(opts)) resolve(fn parent, args, resolution -> opts = resolution |> Metadata.merge(unquote(opts)) |> Keyword.put(:tag?, true) args = unquote(query_module) |> Query.read_filters_from_parent(parent, unquote(opts)) |> Map.merge(args) case BoundedContext.__execute_query__(unquote(query_module), args, opts) do {:ok, result} -> {:ok, result} {:error, map} when is_map(map) -> {:error, Enum.reduce(map, [], fn {key, messages}, acc -> acc ++ Enum.map(messages, fn msg -> "#{key} #{msg}" end) end)} {:error, errors} -> {:error, errors} end end) Middleware.after_resolve(unquote(query_module), unquote(opts)) end end end defp create_query_args(query_module, opts) do filters_from_parent = opts |> filters_from_parent() |> Keyword.keys() query_module.__filters__() |> Enum.reject(fn {filter, _, _} -> Enum.member?(filters_from_parent, filter) end) |> Args.extract_args(opts) |> Enum.map(fn {name, absinthe_type, required, opts} -> case required do true -> quote do: arg(unquote(name), non_null(unquote(absinthe_type)), unquote(opts)) false -> quote do: arg(unquote(name), unquote(absinthe_type), unquote(opts)) end end) end defp filters_from_parent(opts) do Keyword.get(opts, :filters_from_parent, []) end require Logger def read_filters_from_parent(_query_module, nil, _opts), do: %{} def read_filters_from_parent(query_module, parent, opts) do parent_keys = (Map.keys(parent) -- [:__meta__, :__struct__]) |> Enum.join(", ") Logger.log(:debug, [ "QUERY FILTER FROM PARENT query=", inspect(query_module), " parent keys=[", parent_keys, "]" ]) opts |> filters_from_parent() |> Enum.reduce(%{}, fn {filter, parent_field}, acc -> value = Map.get(parent, parent_field) Logger.log(:debug, [ "SETTING FILTER FROM PARENT filter=", inspect(filter), " parent=", inspect(parent_field), " value=", inspect(value) ]) Map.put(acc, filter, value) end) end end end