defmodule NavEx.Adapters.ETS do
@moduledoc """
NavEx.Adapters.ETS is adapter for keeping user's navigation history
utilizing ETS. It uses NavEx.Adapters.ETS.RecordsStorage as the
module to interact with the ETS table.
## Adapter config
config NavEx.Adapters.ETS,
identity_key: "nav_ex_identity", # name of the key in cookies where the user's identity is saved
table_name: :navigation_history # name of the ETS table
"""
@behaviour NavEx.Adapter
import Plug.Conn
alias NavEx.Adapters.ETS.RecordsStorage
@key_length 128
@cookies_key Application.compile_env(NavEx.Adapters.ETS, :identity_key) || "nav_ex_identity"
@impl NavEx.Adapter
def children, do: [NavEx.Adapters.ETS.RecordsStorage]
@impl NavEx.Adapter
def insert(%Plug.Conn{request_path: request_path} = conn) do
{conn, user_identity} = maybe_insert_identity(conn)
{:ok, _result} = RecordsStorage.insert(user_identity, request_path)
{:ok, conn}
end
@impl NavEx.Adapter
def list(%Plug.Conn{} = conn) do
with {:ok, user_identity} <- get_identity(conn),
{:ok, [{_user_identity, list}]} <- RecordsStorage.list(user_identity) do
{:ok, list}
end
end
@impl NavEx.Adapter
def last_path(%Plug.Conn{} = conn) do
conn
|> get_identity()
|> case do
{:ok, user_identity} ->
RecordsStorage.last_path(user_identity)
error ->
error
end
end
@impl NavEx.Adapter
def path_at(%Plug.Conn{} = conn, n) do
conn
|> get_identity()
|> case do
{:ok, user_identity} ->
RecordsStorage.path_at(user_identity, n)
error ->
error
end
end
###
defp maybe_insert_identity(%Plug.Conn{} = conn) do
case fetch_cookies(conn) do
%{cookies: %{@cookies_key => user_identity}} ->
{conn, user_identity}
_no_identity ->
user_identity = create_user_identity()
conn = put_resp_cookie(conn, @cookies_key, user_identity)
{conn, user_identity}
end
end
defp get_identity(%Plug.Conn{} = conn) do
case fetch_cookies(conn) do
%{cookies: %{@cookies_key => user_identity}} ->
{:ok, user_identity}
_no_identity ->
{:error, :not_found}
end
end
defp create_user_identity() do
:crypto.strong_rand_bytes(@key_length) |> Base.url_encode64() |> binary_part(0, @key_length)
end
end