Skip to content

Commit

Permalink
Merge pull request pow-auth#677 from prehnRA/phx-1.7
Browse files Browse the repository at this point in the history
Support exprs/2 for Phoenix 1.7
  • Loading branch information
danschultzer authored Oct 10, 2022
2 parents e3e8d5a + b38b1d5 commit db23786
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* [`Mix.Pow.Mix.Tasks.Pow.Phoenix.Mailer.Gen.Templates`] Now injects `config/config.exs` and `WEB_PATH/WEB_APP.ex`
* [`Mix.Pow.Mix.Tasks.Pow.Phoenix.Gen.Templates`] Now injects `config/config.exs`
* [`Mix.Tasks.Pow.Phoenix.Install`] Now injects `config/config.exs`, `WEB_PATH/endpoint.ex`, and `WEB_PATH/router.ex`
* [`Phoenix.Router.Route`] Updated to support Phoenix 1.7 breaking changes

### Documentation

Expand Down
12 changes: 6 additions & 6 deletions guides/lock_users.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ defmodule MyAppWeb.Admin.UserControllerTest do

conn = post(conn, Routes.admin_user_path(conn, :lock, user.id))

assert get_flash(conn, :info) == "User has been locked."
assert Phoenix.Flash.get(conn.assigns.flash, :info) == "User has been locked."
assert redirected_to(conn) == "/"
end

Expand All @@ -291,7 +291,7 @@ defmodule MyAppWeb.Admin.UserControllerTest do

conn = post(conn, Routes.admin_user_path(conn, :lock, user.id))

assert get_flash(conn, :error) == "User couldn't be locked."
assert Phoenix.Flash.get(conn.assigns.flash, :error) == "User couldn't be locked."
assert redirected_to(conn) == "/"
end
end
Expand Down Expand Up @@ -345,7 +345,7 @@ defmodule MyAppWeb.EnsureUserNotLockedPlugTest do
|> Pow.Plug.assign_current_user(@locked_user, @pow_config)
|> EnsureUserNotLockedPlug.call(opts)

assert get_flash(conn, :error) == "Sorry, your account is locked."
assert Phoenix.Flash.get(conn.assigns.flash, :error) == "Sorry, your account is locked."
assert redirected_to(conn) == Routes.pow_session_path(conn, :new)
end

Expand All @@ -356,7 +356,7 @@ defmodule MyAppWeb.EnsureUserNotLockedPlugTest do
|> Plug.Test.conn("/")
|> Plug.Test.init_test_session(%{})
|> Pow.Plug.put_config(pow_config)
|> Phoenix.Controller.fetch_flash()
|> fetch_flash()
end
end
```
Expand All @@ -377,7 +377,7 @@ defmodule MyAppWeb.ResetPasswordControllerTest do

conn = post(conn, Routes.reset_password_path(conn, :create, @valid_params))

assert get_flash(conn, :info)
assert Phoenix.Flash.get(conn.assigns.flash, :info)
assert redirected_to(conn) == Routes.pow_session_path(conn, :new)

assert count_reset_password_tokens_for_user(conn, user) == 1
Expand All @@ -388,7 +388,7 @@ defmodule MyAppWeb.ResetPasswordControllerTest do

conn = post(conn, Routes.reset_password_path(conn, :create, @valid_params))

assert get_flash(conn, :info)
assert Phoenix.Flash.get(conn.assigns.flash, :info)
assert redirected_to(conn) == Routes.pow_session_path(conn, :new)

assert count_reset_password_tokens_for_user(conn, user) == 0
Expand Down
4 changes: 2 additions & 2 deletions guides/multitenancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ defmodule MyAppWeb.Pow.TriplexSessionPlugTest do
:get
|> Plug.Test.conn("/")
|> Plug.Test.init_test_session(%{})
|> Phoenix.Controller.fetch_flash()
|> fetch_flash()
end

defp set_triplex_tenant(conn, tenant) do
Expand Down Expand Up @@ -311,7 +311,7 @@ defmodule MyAppWeb.AccountControllerTest do
test "with valid params", %{conn: conn} do
conn = post(conn, Routes.account_path(conn, :create, @valid_params))

assert get_flash(conn, :info) == "Welcome!"
assert Phoenix.Flash.get(conn.assigns.flash, :info) == "Welcome!"
assert redirected_to(conn) == Routes.page_path(conn, :index)

assert Pow.Plug.current_user(conn)
Expand Down
4 changes: 2 additions & 2 deletions guides/user_roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ defmodule MyAppWeb.EnsureRolePlugTest do
conn = EnsureRolePlug.call(conn, opts)

assert conn.halted
assert Phoenix.ConnTest.redirected_to(conn) == Routes.page_path(conn, :index)
assert redirected_to(conn) == Routes.page_path(conn, :index)
end

test "call/2 with non-admin user", %{conn: conn} do
Expand All @@ -278,7 +278,7 @@ defmodule MyAppWeb.EnsureRolePlugTest do
|> EnsureRolePlug.call(opts)

assert conn.halted
assert Phoenix.ConnTest.redirected_to(conn) == Routes.page_path(conn, :index)
assert redirected_to(conn) == Routes.page_path(conn, :index)
end

test "call/2 with non-admin user and multiple roles", %{conn: conn} do
Expand Down
36 changes: 24 additions & 12 deletions lib/pow/phoenix/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,16 @@ defmodule Pow.Phoenix.Router do
@doc false
defmacro pow_resources(path, controller, opts) do
quote location: :keep do
opts = unquote(__MODULE__).__filter_resource_actions__(@phoenix_routes, __ENV__.line, __ENV__.module, unquote(path), unquote(controller), unquote(opts))
phoenix_routes = Module.get_attribute(__ENV__.module, :phoenix_routes)
phoenix_forwards = Module.get_attribute(__ENV__.module, :phoenix_forwards)
opts = unquote(__MODULE__).__filter_resource_actions__({phoenix_routes, phoenix_forwards}, __ENV__.line, __ENV__.module, unquote(path), unquote(controller), unquote(opts))

resources unquote(path), unquote(controller), opts
end
end

@doc false
def __filter_resource_actions__(phoenix_routes, line, module, path, controller, options) do
def __filter_resource_actions__({phoenix_routes, phoenix_forwards}, line, module, path, controller, options) do
resource = Phoenix.Router.Resource.build(path, controller, options)
param = resource.param
action_opts =
Expand Down Expand Up @@ -188,43 +190,53 @@ defmodule Pow.Phoenix.Router do
Enum.reject(resource.actions, fn plug_opts ->
{verb, path} = Keyword.fetch!(action_opts, plug_opts)

__route_defined__(phoenix_routes, line, module, verb, path, controller, plug_opts, options)
__route_defined__({phoenix_routes, phoenix_forwards}, line, module, verb, path, controller, plug_opts, options)
end)

Keyword.put(options, :only, only)
end

@doc false
def __route_defined__(phoenix_routes, line, module, verb, path, plug, plug_opts, options) do
def __route_defined__({phoenix_routes, phoenix_forwards}, line, module, verb, path, plug, plug_opts, options) do
line
|> Phoenix.Router.Scope.route(module, :match, verb, path, plug, plug_opts, options)
|> case do
%{plug_opts: _, helper: _} = route ->
any_matching_routes?(phoenix_routes, route, [:plug_opts, :helper])
any_matching_routes?(phoenix_routes, phoenix_forwards, route, [:plug_opts, :helper])

# TODO: Remove this match by 1.1.0, and up requirement for Phoenix to minimum 1.4.7
%{opts: _, helper: _} = route ->
any_matching_routes?(phoenix_routes, route, [:opts, :helper])
any_matching_routes?(phoenix_routes, phoenix_forwards, route, [:opts, :helper])

_any ->
false
end
end

defp any_matching_routes?(phoenix_routes, route, keys) do
defp any_matching_routes?(phoenix_routes, phoenix_forwards, route, keys) do
needle = Map.take(route, keys)
route_exprs = Phoenix.Router.Route.exprs(route)
route_exprs = exprs(route, phoenix_forwards)

Enum.any?(phoenix_routes, &Map.take(&1, keys) == needle && equal_binding_length?(&1, route_exprs))
Enum.any?(phoenix_routes, &Map.take(&1, keys) == needle && equal_binding_length?(&1, route_exprs, phoenix_forwards))
end

defp equal_binding_length?(route, exprs) do
length(exprs.binding) == length(Phoenix.Router.Route.exprs(route).binding)
defp equal_binding_length?(route, exprs, forwards) do
length(exprs.binding) == length(exprs(route, forwards).binding)
end

# TODO: Remove when Phoenix 1.7 is required
if Code.ensure_loaded?(Phoenix.Router.Route) and function_exported?(Phoenix.Router.Route, :exprs, 1) do
def exprs(route, _forwards), do: Phoenix.Router.Route.exprs(route)
else
defdelegate exprs(route, forwards), to: Phoenix.Router.Route
end

defmacro pow_route(verb, path, plug, plug_opts, options \\ []) do
quote location: :keep do
unless unquote(__MODULE__).__route_defined__(@phoenix_routes, __ENV__.line, __ENV__.module, unquote(verb), unquote(path), unquote(plug), unquote(plug_opts), unquote(options)) do
phoenix_routes = Module.get_attribute(__ENV__.module, :phoenix_routes)
phoenix_forwards = Module.get_attribute(__ENV__.module, :phoenix_forwards)

unless unquote(__MODULE__).__route_defined__({phoenix_routes, phoenix_forwards}, __ENV__.line, __ENV__.module, unquote(verb), unquote(path), unquote(plug), unquote(plug_opts), unquote(options)) do
unquote(verb)(unquote(path), unquote(plug), unquote(plug_opts), unquote(options))
end
end
Expand Down
33 changes: 18 additions & 15 deletions test/pow/phoenix/controllers/plug_error_handler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ defmodule Pow.Phoenix.PlugErrorHandlerTest do
use ExUnit.Case
doctest Pow.Phoenix.PlugErrorHandler

alias Phoenix.ConnTest
import Phoenix.ConnTest, except: [get_flash: 2]
import Pow.Test.Phoenix.ConnCase, only: [get_flash: 2]

alias Plug.Conn
alias Pow.Phoenix.{Messages, PlugErrorHandler}

Expand All @@ -16,22 +18,23 @@ defmodule Pow.Phoenix.PlugErrorHandlerTest do
defp prepare_conn(conn) do
conn
|> Conn.put_private(:pow_config, messages_backend: Messages)
|> Conn.put_private(:phoenix_flash, %{})
|> Conn.put_private(:phoenix_flash, %{}) # TODO: Remove when Phoenix 1.7 is required
|> Map.update(:assigns, %{}, & Map.put(&1, :flash, %{}))
|> Conn.put_private(:phoenix_router, Pow.Test.Phoenix.Router)
|> Conn.fetch_query_params()
end

setup do
conn = prepare_conn(ConnTest.build_conn())
conn = prepare_conn(build_conn())

{:ok, conn: conn}
end

test "call/2 :not_authenticated", %{conn: conn} do
conn = PlugErrorHandler.call(conn, :not_authenticated)

assert ConnTest.redirected_to(conn) == "/session/new?request_path=%2F"
assert ConnTest.get_flash(conn, :error) == :not_authenticated
assert redirected_to(conn) == "/session/new?request_path=%2F"
assert get_flash(conn, :error) == :not_authenticated
end

test "call/2 :not_authenticated doesn't override flash if message is nil", %{conn: conn} do
Expand All @@ -41,34 +44,34 @@ defmodule Pow.Phoenix.PlugErrorHandlerTest do
|> Phoenix.Controller.put_flash(:error, "Existing error")
|> PlugErrorHandler.call(:not_authenticated)

assert ConnTest.redirected_to(conn) == "/session/new?request_path=%2F"
assert ConnTest.get_flash(conn, :error) == "Existing error"
assert redirected_to(conn) == "/session/new?request_path=%2F"
assert get_flash(conn, :error) == "Existing error"
end

test "call/2 :not_authenticated doesn't set request_path if not GET request" do
conn =
:post
|> ConnTest.build_conn("/", nil)
|> build_conn("/", nil)
|> prepare_conn()
|> PlugErrorHandler.call(:not_authenticated)

assert ConnTest.redirected_to(conn) == "/session/new"
assert ConnTest.get_flash(conn, :error) == :not_authenticated
assert redirected_to(conn) == "/session/new"
assert get_flash(conn, :error) == :not_authenticated

conn =
:delete
|> ConnTest.build_conn("/", nil)
|> build_conn("/", nil)
|> prepare_conn()
|> PlugErrorHandler.call(:not_authenticated)

assert ConnTest.redirected_to(conn) == "/session/new"
assert ConnTest.get_flash(conn, :error) == :not_authenticated
assert redirected_to(conn) == "/session/new"
assert get_flash(conn, :error) == :not_authenticated
end

test "call/2 :already_authenticated", %{conn: conn} do
conn = PlugErrorHandler.call(conn, :already_authenticated)

assert ConnTest.redirected_to(conn) == "/"
assert ConnTest.get_flash(conn, :error) == :already_authenticated
assert redirected_to(conn) == "/"
assert get_flash(conn, :error) == :already_authenticated
end
end
3 changes: 2 additions & 1 deletion test/support/extensions/mocks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ defmodule Pow.Test.ExtensionMocks do
using do
quote do
import Plug.Conn
import Phoenix.ConnTest
import Phoenix.ConnTest, except: [get_flash: 2]
import Pow.Test.Phoenix.ConnCase, only: [get_flash: 2]
import ControllerAssertions

alias Router.Helpers, as: Routes
Expand Down
10 changes: 9 additions & 1 deletion test/support/phoenix/conn_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ defmodule Pow.Test.Phoenix.ConnCase do
using do
quote do
import Plug.Conn
import Phoenix.ConnTest
import Phoenix.ConnTest, except: [get_flash: 2]
import unquote(__MODULE__), only: [get_flash: 2]
import ControllerAssertions

alias Router.Helpers, as: Routes
Expand All @@ -22,4 +23,11 @@ defmodule Pow.Test.Phoenix.ConnCase do

{:ok, conn: Phoenix.ConnTest.build_conn(), ets: EtsCacheMock}
end

# TODO: Remove when Phoenix 1.7 is required
if Code.ensure_loaded?(Phoenix.Flash) do
def get_flash(conn, key), do: Phoenix.Flash.get(conn.assigns.flash, key)
else
defdelegate get_flash(conn, key), to: Phoenix.Controller
end
end
12 changes: 7 additions & 5 deletions test/support/phoenix/controller_assertions.ex
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
defmodule Pow.Test.Phoenix.ControllerAssertions do
@moduledoc false
alias Phoenix.ConnTest
import Phoenix.ConnTest, except: [get_flash: 2]
import Pow.Test.Phoenix.ConnCase, only: [get_flash: 2]

alias Pow.Phoenix.{Messages, Routes}

@spec assert_authenticated_redirect(Plug.Conn.t()) :: Macro.t()
defmacro assert_authenticated_redirect(conn) do
quote do
routes = Keyword.get(unquote(conn).private.pow_config, :routes_backend, Routes)

assert ConnTest.redirected_to(unquote(conn)) == routes.after_sign_in_path(unquote(conn))
assert ConnTest.get_flash(unquote(conn), :error) == Messages.user_already_authenticated(unquote(conn))
assert redirected_to(unquote(conn)) == routes.after_sign_in_path(unquote(conn))
assert get_flash(unquote(conn), :error) == Messages.user_already_authenticated(unquote(conn))
end
end

Expand All @@ -24,8 +26,8 @@ defmodule Pow.Test.Phoenix.ControllerAssertions do
_any -> router.pow_session_path(conn, :new)
end

assert ConnTest.redirected_to(conn) == expected_path
assert ConnTest.get_flash(conn, :error) == Messages.user_not_authenticated(conn)
assert redirected_to(conn) == expected_path
assert get_flash(conn, :error) == Messages.user_not_authenticated(conn)
end
end
end

0 comments on commit db23786

Please sign in to comment.