mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 21:43:02 +12:00
256 lines
8 KiB
Elixir
256 lines
8 KiB
Elixir
if Code.ensure_loaded?(Plug.Conn) do
|
|
defmodule Ash.PlugHelpers do
|
|
@moduledoc """
|
|
Helpers for working with the Plug connection.
|
|
"""
|
|
|
|
alias Plug.Conn
|
|
require Logger
|
|
|
|
defmacrop emit_assign_warning(type) do
|
|
quote do
|
|
Logger.warning(fn ->
|
|
{fun, arity} = __ENV__.function
|
|
file = Path.relative_to_cwd(__ENV__.file)
|
|
|
|
"""
|
|
Storing the #{unquote(type)} in conn assigns is deprecated.
|
|
#{file}:#{__ENV__.line}: #{inspect(__ENV__.module)}.#{fun}/#{arity}
|
|
"""
|
|
end)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Sets the actor inside the Plug connection.
|
|
|
|
The actor is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Example
|
|
|
|
iex> actor = build_actor(%{email: "marty@1985.retro"})
|
|
...> conn = build_conn() |> set_actor(actor)
|
|
%Plug.Conn{private: %{ash: %{actor: %{email: "marty@1985.retro"}}}} = conn
|
|
|
|
"""
|
|
@spec set_actor(Conn.t(), Ash.Resource.record()) :: Conn.t()
|
|
def set_actor(conn, actor) do
|
|
ash_private =
|
|
conn.private
|
|
|> Map.get(:ash, %{})
|
|
|> Map.put(:actor, actor)
|
|
|
|
conn
|
|
|> Conn.put_private(:ash, ash_private)
|
|
end
|
|
|
|
@doc """
|
|
Retrieves the actor from the Plug connection.
|
|
|
|
The actor is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Deprecation warning
|
|
|
|
This function checks to see if the actor is already set in the `@actor`
|
|
assign, and if so will emit a deprecation warning.
|
|
|
|
This is to allow apps using the previous method a chance to update.
|
|
|
|
Rather than setting the actor in the assigns, please use the `set_actor/2`
|
|
method.
|
|
|
|
## Example
|
|
|
|
iex> actor = build_actor(%{email: "marty@1985.retro"})
|
|
...> conn = build_conn() |> put_private(:ash, %{actor: actor})
|
|
...> actor = get_actor(conn)
|
|
%{email: "marty@1985.retro"} = actor
|
|
|
|
iex> actor = build_actor(%{email: "marty@1985.retro"})
|
|
...> conn = build_conn() |> assign(:actor, actor)
|
|
...> actor = get_actor(conn)
|
|
%{email: "marty@1985.retro"} = actor
|
|
"""
|
|
@spec get_actor(Conn.t()) :: nil | Ash.Resource.record()
|
|
def get_actor(%{assigns: %{actor: actor}}) when not is_nil(actor) do
|
|
emit_assign_warning(:actor)
|
|
|
|
actor
|
|
end
|
|
|
|
def get_actor(%{private: %{ash: %{actor: actor}}}), do: actor
|
|
def get_actor(_), do: nil
|
|
|
|
@doc """
|
|
Updates the actor inside the Plug connection.
|
|
|
|
The actor is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Example
|
|
|
|
iex> actor = build_actor(%{email: "marty@1985.retro"})
|
|
...> conn = build_conn() |> put_private(:ash, %{actor: actor})
|
|
...> actor = get_actor(conn)
|
|
%{email: "marty@1985.retro"} = actor
|
|
...> conn = update_actor(conn, fn actor -> Map.put(actor, :name, "Marty Retro") end)
|
|
...> actor = get_actor(conn)
|
|
%{email: "marty@1985.retro", name: "Marty Retro"} = actor
|
|
...> conn = update_actor(conn, fn actor -> Map.delete(actor, :email) end)
|
|
...> actor = get_actor(conn)
|
|
%{name: "Marty Retro"} = actor
|
|
"""
|
|
@spec update_actor(Conn.t(), (nil | Ash.Resource.record() -> nil | Ash.Resource.record())) ::
|
|
Conn.t()
|
|
def update_actor(conn, callback) do
|
|
case get_actor(conn) do
|
|
nil ->
|
|
conn
|
|
|
|
actor ->
|
|
conn
|
|
|> set_actor(callback.(actor))
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Sets the tenant inside the Plug connection.
|
|
|
|
The tenant is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Example
|
|
|
|
iex> tenant = build_tenant(%{name: "Deliver-yesterday"})
|
|
...> conn = build_conn() |> set_tenant(tenant)
|
|
%Plug.Conn{private: %{ash: %{tenant: %{name: "Deliver-yesterday"}}}} = conn
|
|
"""
|
|
@spec set_tenant(Conn.t(), Ash.Resource.record()) :: Conn.t()
|
|
def set_tenant(conn, tenant) do
|
|
ash_private =
|
|
conn.private
|
|
|> Map.get(:ash, %{})
|
|
|> Map.put(:tenant, tenant)
|
|
|
|
conn
|
|
|> Conn.put_private(:ash, ash_private)
|
|
end
|
|
|
|
@doc """
|
|
Retrieves the tenant from the Plug connection.
|
|
|
|
The tenant is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Deprecation warning
|
|
|
|
This function checks to see if the tenant is already set in the `@tenant`
|
|
assign, and if so will emit a deprecation warning.
|
|
|
|
This is to allow apps using the previous method a chance to update.
|
|
|
|
Rather than setting the tenant in the assigns, please use the `set_tenant/2`
|
|
method.
|
|
|
|
|
|
## Example
|
|
|
|
iex> tenant = build_tenant(%{name: "Deliver-yesterday"})
|
|
...> conn = build_conn() |> put_private(:ash, %{tenant: tenant})
|
|
...> tenant = get_tenant(conn)
|
|
%{name: "Deliver-yesterday"} = tenant
|
|
|
|
iex> tenant = build_tenant(%{name: "Deliver-yesterday"})
|
|
...> conn = build_conn() |> assign(:tenant, tenant)
|
|
...> tenant = get_tenant(conn)
|
|
%{name: "Deliver-yesterday"} = tenant
|
|
"""
|
|
@spec get_tenant(Conn.t()) :: nil | Ash.Resource.record()
|
|
def get_tenant(%{assigns: %{tenant: tenant}}) when not is_nil(tenant) do
|
|
emit_assign_warning(:tenant)
|
|
|
|
tenant
|
|
end
|
|
|
|
def get_tenant(%{private: %{ash: %{tenant: tenant}}}), do: tenant
|
|
def get_tenant(_), do: nil
|
|
|
|
@doc """
|
|
Sets the context inside the Plug connection.
|
|
|
|
Context can be used to store abitrary data about the user, connection, or
|
|
anything else you like that doesn't belong as part of the actor or tenant.
|
|
|
|
The context is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Example
|
|
|
|
iex> context = %{fraud_score: 0.427}
|
|
...> conn = build_conn() |> set_context(context)
|
|
%Plug.Conn{private: %{ash: %{context: %{fraud_score: 0.427}}}}
|
|
|
|
"""
|
|
@spec set_context(Conn.t(), Ash.Resource.record()) :: Conn.t()
|
|
def set_context(conn, context) do
|
|
ash_private =
|
|
conn.private
|
|
|> Map.get(:ash, %{})
|
|
|> Map.put(:context, context)
|
|
|
|
conn
|
|
|> Conn.put_private(:ash, ash_private)
|
|
end
|
|
|
|
@doc """
|
|
Retrieves the context from the Plug connection.
|
|
|
|
The context is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Example
|
|
|
|
iex> context = %{fraud_score: 0.427}
|
|
...> conn = build_conn() |> put_private(:ash, %{context: context})
|
|
...> context = get_context(conn)
|
|
%{fraud_score: 0.427}
|
|
"""
|
|
@spec get_context(Conn.t()) :: nil | Ash.Resource.record()
|
|
def get_context(%{private: %{ash: %{context: context}}}), do: context
|
|
def get_context(_), do: nil
|
|
|
|
@doc """
|
|
Updates the context inside the Plug connection.
|
|
|
|
The context is stored inside the [connection's private
|
|
fields](https://hexdocs.pm/plug/Plug.Conn.html#module-private-fields).
|
|
|
|
## Example
|
|
|
|
iex> context = %{species: "Fythetropozoat"}
|
|
...> conn = build_conn() |> put_private(:ash, %{context: context})
|
|
...> context = get_context(conn)
|
|
%{fraud_score: 0.427}
|
|
...> conn = update_context(conn, fn context -> Map.put(context, :location, "Barnard's Loop") end)
|
|
...> context = get_context(conn)
|
|
%{species: "Fythetropozoat", location: "Barnard's Loop"}
|
|
...> conn = update_context(conn, fn context -> Map.delete(context, :fraud_score) end)
|
|
...> context = get_context(conn)
|
|
%{location: "Barnard's Loop"}
|
|
"""
|
|
@spec update_context(Conn.t(), (nil | Ash.Resource.record() -> nil | Ash.Resource.record())) ::
|
|
Conn.t()
|
|
def update_context(conn, callback) do
|
|
case get_context(conn) do
|
|
nil ->
|
|
conn
|
|
|
|
context ->
|
|
conn
|
|
|> set_context(callback.(context))
|
|
end
|
|
end
|
|
end
|
|
end
|