Skip to content

Commit

Permalink
Add route_info lookup (#3419)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismccord committed Jun 3, 2019
1 parent fb3c923 commit 3f157c3
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 20 deletions.
35 changes: 35 additions & 0 deletions lib/phoenix/router.ex
Expand Up @@ -845,4 +845,39 @@ defmodule Phoenix.Router do
unquote(add_route(:forward, :*, path, plug, plug_opts, router_opts))
end
end

@doc """
Returns the compile-time route info and runtime path params for a request.
A map of metadata is returned with the following keys:
* `:log` - the configured log level. For example `:debug`
* `:path_params` - the map of runtime path params
* `:pipe_through` - the list of pipelines for the route's scope, for example `[:browser]`
* `:plug` - the plug to dipatch the route to, for example `AppWeb.PostController`
* `:plug_opts` - the options to pass when calling the plug, for example: `:index`
* `:route` - the string route pattern, such as `"/posts/:id"`
## Examples
iex> Phoenix.Router.route_info(AppWeb.Router, "GET", "/posts/123", "myhost")
%{
log: :debug,
path_params: %{"id" => "123"},
pipe_through: [:browser],
plug: AppWeb.PostController,
plug_opts: :show,
route: "/posts/:id",
}
iex> Phoenix.Router.route_info(MyRouter, "GET", "/not-exists", "myhost")
:error
"""
def route_info(router, method, path, host) do
split_path = for segment <- String.split(path, "/"), segment != "", do: segment
case router.__match_route__(method, split_path, host) do
{%{} = metadata, _prepare, _pipeline, {_plug, _opts}} -> Map.delete(metadata, :conn)
:error -> :error
end
end
end
8 changes: 2 additions & 6 deletions lib/phoenix/test/conn_test.ex
Expand Up @@ -575,18 +575,14 @@ defmodule Phoenix.ConnTest do
def redirected_params(%Plug.Conn{} = conn) do
router = Phoenix.Controller.router_module(conn)
%URI{path: path, host: host} = conn |> redirected_to() |> URI.parse()
path_info = split_path(path)

case router.__match_route__("GET", path_info, host || conn.host) do
case Phoenix.Router.route_info(router, "GET", path, host || conn.host) do
:error ->
raise Phoenix.Router.NoRouteError, conn: conn, router: router
{%{path_params: path_params}, _prepare, _pipes, _dispatch} ->
%{path_params: path_params} ->
Enum.into(path_params, %{}, fn {key, val} -> {String.to_atom(key), val} end)
end
end
defp split_path(path) do
for segment <- String.split(path, "/"), segment != "", do: segment
end

@doc """
Asserts an error was wrapped and sent with the given status.
Expand Down
57 changes: 44 additions & 13 deletions test/phoenix/router/routing_test.exs
Expand Up @@ -36,7 +36,7 @@ defmodule Phoenix.Router.RoutingTest do
get "/backups/*path", UserController, :image
get "/static/images/icons/*image", UserController, :image

trace "/trace", UserController, :trace
trace("/trace", UserController, :trace)
options "/options", UserController, :options
connect "/connect", UserController, :connect
match :move, "/move", UserController, :move
Expand Down Expand Up @@ -189,31 +189,62 @@ defmodule Phoenix.Router.RoutingTest do

test "logs controller and action with (path) parameters" do
assert capture_log(fn -> call(Router, :get, "/users/1", foo: "bar") end) =~ """
[debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
Parameters: %{"foo" => "bar", "id" => "1"}
Pipelines: []
"""
[debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
Parameters: %{"foo" => "bar", "id" => "1"}
Pipelines: []
"""
end

test "logs controller and action with filtered parameters" do
assert capture_log(fn -> call(Router, :get, "/users/1", password: "bar") end) =~ """
[debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
Parameters: %{"id" => "1", "password" => "[FILTERED]"}
Pipelines: []
"""
[debug] Processing with Phoenix.Router.RoutingTest.UserController.show/2
Parameters: %{"id" => "1", "password" => "[FILTERED]"}
Pipelines: []
"""
end

test "logs plug with pipeline and custom level" do
assert capture_log(fn -> call(Router, :get, "/plug") end) =~ """
[info] Processing with Phoenix.Router.RoutingTest.SomePlug
Parameters: %{}
Pipelines: [:noop]
"""
[info] Processing with Phoenix.Router.RoutingTest.SomePlug
Parameters: %{}
Pipelines: [:noop]
"""
end

test "does not log when log is set to false" do
refute capture_log(fn -> call(Router, :get, "/no_log", foo: "bar") end) =~
"Processing with Phoenix.Router.RoutingTest.SomePlug"
end
end

test "route_info returns route string and path params" do
assert Phoenix.Router.route_info(Router, "GET", "foo/bar/baz", nil) == %{
log: :debug,
path_params: %{"path" => ["foo", "bar", "baz"]},
pipe_through: [],
plug: Phoenix.Router.RoutingTest.UserController,
plug_opts: :not_found,
route: "/*path"
}

assert Phoenix.Router.route_info(Router, "GET", "users/1", nil) == %{
log: :debug,
path_params: %{"id" => "1"},
pipe_through: [],
plug: Phoenix.Router.RoutingTest.UserController,
plug_opts: :show,
route: "/users/:id",
}

assert Phoenix.Router.route_info(Router, "GET", "/", "host") == %{
log: :debug,
path_params: %{},
pipe_through: [],
plug: Phoenix.Router.RoutingTest.UserController,
plug_opts: :index,
route: "/",
}

assert Phoenix.Router.route_info(Router, "POST", "/not-exists", "host") == :error
end
end
4 changes: 3 additions & 1 deletion test/phoenix/test/conn_test.exs
Expand Up @@ -40,6 +40,8 @@ defmodule Phoenix.Test.ConnTest do
use Phoenix.ConnTest
alias Phoenix.Test.ConnTest.{Router, RedirRouter}

@moduletag :capture_log

defmodule ConnError do
defexception [message: nil, plug_status: 500]
end
Expand Down Expand Up @@ -188,7 +190,7 @@ defmodule Phoenix.Test.ConnTest do
port: 111317,
ssl_cert: <<1, 2, 3, 4>>
}
conn =
conn =
build_conn()
|> Plug.Test.put_peer_data(peer_data)

Expand Down

0 comments on commit 3f157c3

Please sign in to comment.