diff --git a/CHANGELOG.md b/CHANGELOG.md index 04182cfb..7527686e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/guides/lock_users.md b/guides/lock_users.md index 8360891b..d9df57fe 100644 --- a/guides/lock_users.md +++ b/guides/lock_users.md @@ -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 @@ -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 @@ -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 @@ -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 ``` @@ -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 @@ -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 diff --git a/guides/multitenancy.md b/guides/multitenancy.md index 67532140..fa765686 100644 --- a/guides/multitenancy.md +++ b/guides/multitenancy.md @@ -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 @@ -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) diff --git a/guides/user_roles.md b/guides/user_roles.md index 0dc0bce3..d4b449bb 100644 --- a/guides/user_roles.md +++ b/guides/user_roles.md @@ -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 @@ -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 diff --git a/lib/pow/phoenix/router.ex b/lib/pow/phoenix/router.ex index 0d4050d0..d2134f10 100644 --- a/lib/pow/phoenix/router.ex +++ b/lib/pow/phoenix/router.ex @@ -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 = @@ -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 diff --git a/test/pow/phoenix/controllers/plug_error_handler_test.exs b/test/pow/phoenix/controllers/plug_error_handler_test.exs index 2d4d7d62..29969b79 100644 --- a/test/pow/phoenix/controllers/plug_error_handler_test.exs +++ b/test/pow/phoenix/controllers/plug_error_handler_test.exs @@ -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} @@ -16,13 +18,14 @@ 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 @@ -30,8 +33,8 @@ defmodule Pow.Phoenix.PlugErrorHandlerTest do 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 @@ -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 diff --git a/test/support/extensions/mocks.ex b/test/support/extensions/mocks.ex index c9c83d72..7a126801 100644 --- a/test/support/extensions/mocks.ex +++ b/test/support/extensions/mocks.ex @@ -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 diff --git a/test/support/phoenix/conn_case.ex b/test/support/phoenix/conn_case.ex index e5db0ae5..8dca49ba 100644 --- a/test/support/phoenix/conn_case.ex +++ b/test/support/phoenix/conn_case.ex @@ -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 @@ -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 diff --git a/test/support/phoenix/controller_assertions.ex b/test/support/phoenix/controller_assertions.ex index 112a669e..d8c7c2ce 100644 --- a/test/support/phoenix/controller_assertions.ex +++ b/test/support/phoenix/controller_assertions.ex @@ -1,6 +1,8 @@ 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() @@ -8,8 +10,8 @@ defmodule Pow.Test.Phoenix.ControllerAssertions 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 @@ -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