Current section

Files

Jump to
rivet lib rivet graphql.ex
Raw

lib/rivet/graphql.ex

defmodule Rivet.Graphql do
@moduledoc """
Helper functions for Absinthe resolvers.
"""
require Logger
import Rivet.Utils.Ecto.Errors, only: [convert_error_changeset: 1]
@doc """
iex> current_hostname(%{context: %{hostname: "narf"}})
{:ok, "narf"}
iex> current_hostname(:nope)
{:error, "No Host on session"}
"""
def current_hostname(%{context: %{hostname: h}}) when not is_nil(h), do: {:ok, h}
def current_hostname(_), do: {:error, "No Host on session"}
@doc """
iex> optional_arg(%{}, :narf)
[]
iex> optional_arg(%{narf: :something}, :narf)
[narf: :something]
"""
def optional_arg(map, arg) do
case Map.get(map, arg) do
nil -> []
value -> [{arg, value}]
end
end
@doc """
iex> ok_as_list({:error, "Nothing found"})
{:ok, []}
iex> ok_as_list({:ok, :narf})
{:ok, [:narf]}
iex> ok_as_list({:error, :narf})
{:error, :narf}
"""
def ok_as_list({:error, "Nothing found"}), do: {:ok, []}
def ok_as_list({:ok, result}), do: {:ok, [result]}
def ok_as_list(pass), do: pass
def parse_enum(%{value: value}, enum) do
enum.cast(value)
end
def parse_enum(_, _), do: :error
@doc """
iex> parse_atom(%{value: "narf"})
{:ok, :narf}
iex> parse_atom(%{value: 10})
:error
iex> parse_atom(:narf)
:error
"""
def parse_atom(%{value: value}) do
{:ok, String.to_existing_atom(value)}
rescue
_ ->
:error
end
def parse_atom(_), do: :error
##############################################################################
@doc """
iex> error_string([{:error, :authz}])
"Unauthorized"
iex> error_string("narf")
"narf"
iex> error_string(:authn)
"Unauthenticated"
iex> error_string(:authn)
"Unauthenticated"
iex> error_string(:narf)
"narf"
iex> error_string({:nobody, :expects})
"unexpected error, see logs"
"""
@std_errors %{authn: "Unauthenticated", authz: "Unauthorized", args: "Invalid Arguments"}
def error_string(errs) when is_list(errs) do
Enum.map(errs, &error_string/1)
|> Enum.join(",")
end
def error_string({:error, err}) when is_map_key(@std_errors, err), do: @std_errors[err]
def error_string({:error, str}) when is_binary(str), do: str
def error_string(%Ecto.Changeset{} = chgset), do: convert_error_changeset(chgset)
def error_string({:error, %Ecto.Changeset{} = chgset}), do: convert_error_changeset(chgset)
def error_string(reason) when is_binary(reason), do: reason
def error_string(reason) when is_atom(reason), do: @std_errors[reason] || "#{reason}"
def error_string(unexpected) do
Logger.error("unexpected graphql error", error: inspect(unexpected))
"unexpected error, see logs"
end
##############################################################################
@doc """
iex> graphql_result({:ok, :narf})
{:ok, :narf}
iex> graphql_result({:error, :args}, :narf)
{:error, "Invalid Arguments"}
"""
def graphql_result(x, method \\ nil)
def graphql_result({:ok, _} = pass, _), do: pass
def graphql_result({:error, reason}, method), do: graphql_error(method, reason)
##############################################################################
@doc """
iex> graphql_status_result({:error, "Unauthenticated"})
{:error, "Unauthenticated"}
iex> graphql_status_result({:ok, %{success: true, result: :narf}})
{:ok, %{success: true, result: :narf}}
iex> graphql_status_result({:ok, %{success: true, result: :narf}}, :narf)
{:ok, %{success: true, narf: :narf}}
iex> graphql_status_result({:ok, %{success: false, reason: :narf}})
{:ok, %{success: false, reason: :narf}}
iex> graphql_status_result({:ok, 100})
{:ok, %{success: true, result: 100}}
iex> graphql_status_result({:ok, 100}, :narf)
{:ok, %{narf: 100, success: true}}
iex> graphql_status_result({:error, :authz})
{:ok, %{reason: "Unauthorized", success: false}}
"""
def graphql_status_result(state, key \\ nil)
def graphql_status_result({:error, "Unauthenticated"} = pass, _), do: pass
def graphql_status_result({:ok, %{success: true}} = pass, nil), do: pass
def graphql_status_result({:ok, %{success: true, result: result} = r}, key),
do: {:ok, Map.delete(r, :result) |> Map.put(key, result)}
def graphql_status_result({:ok, %{success: _}} = pass, _), do: pass
def graphql_status_result({:ok, result}, nil), do: {:ok, %{success: true, result: result}}
def graphql_status_result({:ok, result}, key),
do: {:ok, Map.new([{:success, true}, {key, result}])}
def graphql_status_result({:error, err}, _),
do: {:ok, %{success: false, reason: error_string(err)}}
def graphql_status_result({:error, err, _opts}, _),
do: {:ok, %{success: false, reason: error_string(err)}}
##############################################################################
@doc """
Handle multi-results with total/matching tallys
iex> graphql_status_results({:ok, %{success: true, results: :narf}})
{:ok, %{success: true, results: :narf}}
iex> graphql_status_results({:ok, %{success: true, results: :narf}, :narf}, :key)
{:ok, %{success: true, results: :narf, key: :narf}}
iex> graphql_status_results({:ok, %{success: true, results: :narf}}, :results)
{:ok, %{success: true, results: :narf}}
iex> graphql_status_results({:ok, %{success: true, results: :narf}}, :narf)
{:ok, %{success: true, narf: :narf}}
iex> graphql_status_results({:ok, [:narf]}, :narf)
{:ok, %{success: true, narf: [:narf], total: 1, matching: 1}}
iex> graphql_status_results({:ok, :narf}, :narf)
{:ok, %{success: true, narf: [:narf], total: 1, matching: 1}}
iex> graphql_status_results({:error, :authn})
{:ok, %{success: false, reason: "Unauthenticated"}}
"""
def graphql_status_results(x, key \\ :results)
def graphql_status_results({:ok, meta, r}, key),
do: {:ok, Map.put(meta, :success, true) |> Map.put(key, r)}
def graphql_status_results({:ok, %{results: _, success: _}} = pass, :results), do: pass
def graphql_status_results({:ok, %{results: r, success: _} = m}, key),
do: {:ok, Map.delete(m, :results) |> Map.put(key, r)}
def graphql_status_results({:ok, r}, key) when is_list(r),
do: {:ok, %{success: true, total: length(r), matching: length(r)} |> Map.put(key, r)}
def graphql_status_results({:ok, r}, key),
do: {:ok, %{success: true, total: 1, matching: 1} |> Map.put(key, [r])}
def graphql_status_results(other, key), do: graphql_status_result(other, key)
##############################################################################
def graphql_error(method, err, logargs \\ []) do
reason = error_string(err)
graphql_log(method, [failure: reason] ++ logargs)
{:error, reason}
end
##############################################################################
@doc """
iex> graphql_log("narf")
:ok
iex> graphql_log(nil)
:ok
"""
def graphql_log(method, args \\ [])
def graphql_log(nil, _), do: :ok
def graphql_log(method, args), do: Logger.info("graphql #{method}", args)
end