I have a controller code with an action that i need to set permission upon if it fail i want it to stop and send error json to user i had use plug in action but it look like halt didn’t do anything because it was in action code not in another plug.
in my action
def delete(conn, %{"id" => id,"store_id" => store_id}) do
Catastore.ValidateStoreOwnerPlug.call(conn, %{store_id: store_id})
category = Repo.get!(Category, id)
Repo.delete!(category)
send_resp(conn, :no_content, "")
end
in my plug
def call(conn, opts) do
%{current_user: current_user} = conn.assigns
if (current_user.store_id != String.to_integer(opts.store_id)) do
conn
|> render(Catastore.ErrorView, "403.json")
|> halt
end
conn
def call(%{current_user: current_user} = conn, opts) do
if (current_user.store_id != String.to_integer(opts.store_id)) do
conn
|> render(Catastore.ErrorView, "403.json")
|> halt
else
{:ok, conn}
end
end
Or the action could be (using my new block syntax I added to exceptional, not yet in released version):
def delete(conn, %{"id" => id,"store_id" => store_id}) do
Exceptional.block do
{:ok, conn} <- Catastore.ValidateStoreOwnerPlug.call(conn, %{store_id: store_id})
category = Repo.get!(Category, id)
Repo.delete!(category)
send_resp(conn, :no_content, "")
end
end
currently have to refactor a bit of code but i can’t wait for the released of the Exceptional.block it would be cool to have ability to stop another function by calling another function. thank you for you answer
My exceptional block is like with except it is purely inside a do, has a couple of options you can pass in, it matches only on <- just like with and matches outright fail if it is in one of the ‘error states’ that exceptional handles too, along with an else fallback as well. It is pretty basic and simple (It is not TCO’able as it normalizes the output of function calls as one example), but quite useful when using the exceptional error handling style.
Plugs have to return just a %Plug.Conn{} and nothing else, otherwise you will get a RuntimeError, so you won’t be able to return {:ok, conn}. I don’t think it’s a good idea to call a plug directly like that anyways, I think what you want to do is put in your controller:
plug Catastore.ValidateStoreOwnerPlug when action in [:delete]`
That will pipe requests for the :delete action through the plug, so that if the store_ids don’t match up the connection will halt, otherwise it continues as usual. One thing you will need to change though is the way you fetch the store_id parameter in the plug. The second argument to a plug’s call is intended for options about how the plug should work, not data from the request. You will need to use String.to_integer(conn.params["store_id"]) to pull it directly from conn instead.
If there are other actions in your controller that you want to restrict to the current store owner then you can add them where you register the plug: ... when action in [:update, :delete].
If you’re finding yourself needing a call a plug directly then that probably means that whatever the plug is doing should be moved into one of your contexts. Then both the plug and the original caller can both call into the context instead.
If you use it as a plug yep, but I was not using it as a plug, I was calling it straight.
I prefer to call things straight personally, I often have options I need to give it that are context/connection dependent that would end up making a lot of plugs otherwise.