I am building an admin tool app for our system. I want to record every action made by every user.
Here's what I did
defmodule AdminToolWeb.UserController do
use AdminToolWeb, :controller
...
def delete(conn, %{"id" => id}) do
current_user = Guardian.Plug.current_resource(conn)
with %User{} <- user = Accounts.get_user(id) do
Accounts.delete_user(user)
conn
|> put_flash(:info, "#{user.id} deleted.")
|> Activities.log(current_user)
|> redirect(to: Routes.user_path(conn, :index))
end
end
...
end
The problem is I have to pipe |> Activity.log(current_user)
in every action of every controller I have in my app.
Is there a way to implement something like this?
Controller -> (ActivityLogPlugOfSorts) -> View
using a custom plug and call it like this?
defmodule AdminToolWeb.UserController do
use AdminToolWeb, :controller
import AdminToolWeb.Plugs.Activities
plug :log
...
but It should be called between controller and view.
Or should I put a function inside a View module instead?
I hope there is a better way.
What you're looking for here are controller plugs which can indeed be inserted directly at the controller level. They will run before the controller action, so you will not have the opportunity when the plug runs to know if the attempted action will be successful. However you can use the controller plug to setup a callback that will be run after the controller action (but before the response is sent). An example might be:
defmodule HelloWeb.Plugs.ActionLogger do
import Plug.Conn
require Logger
def init(default), do: default
def call(conn, _default) do
register_before_send(conn, fn conn ->
if (response_code_2xx?(conn) do
Logger.info("action taken: #{extract_action(conn)}")
end
conn
end)
end
end
where response_code_2xx?/1
and extract_action/1
are left as exercises for the reader.