diff --git a/lib/open_api_spex/plug/swagger_ui.ex b/lib/open_api_spex/plug/swagger_ui.ex index 0ffdbabf..a24239ad 100644 --- a/lib/open_api_spex/plug/swagger_ui.ex +++ b/lib/open_api_spex/plug/swagger_ui.ex @@ -125,7 +125,7 @@ defmodule OpenApiSpex.Plug.SwaggerUI do } return request; } - <%= for {k, v} <- Map.drop(config, [:path, :oauth, :csp_nonce_assign_key]) do %> + <%= for {k, v} <- Map.drop(config, [:path, :paths, :oauth, :csp_nonce_assign_key]) do %> , <%= camelize(k) %>: <%= encode_config(camelize(k), v) %> <% end %> }; @@ -259,15 +259,48 @@ defmodule OpenApiSpex.Plug.SwaggerUI do end if Code.ensure_loaded?(Phoenix.Controller) do - defp supplement_config(%{oauth2_redirect_url: {:endpoint_url, path}} = config, conn) do - endpoint_module = Phoenix.Controller.endpoint_module(conn) - url = Path.join(endpoint_module.url(), path) - Map.put(config, :oauth2_redirect_url, url) + defp supplement_config(config, conn) do + case conn.private[:phoenix_endpoint] do + nil -> + config + + endpoint_module -> + config + |> expand_oauth2_redirect_url(endpoint_module) + |> expand_spec_path(endpoint_module) + end + end + + defp expand_oauth2_redirect_url( + %{oauth2_redirect_url: {:endpoint_url, path}} = config, + endpoint_module + ) do + url = Path.join([endpoint_module.url(), endpoint_module.path("/"), path]) + %{config | oauth2_redirect_url: url} + end + + defp expand_oauth2_redirect_url(config, _endpoint_module), do: config + + defp expand_spec_path(%{paths: paths} = config, endpoint_module) when is_list(paths) do + expanded_paths = + Enum.map(paths, fn {name, path} -> {name, maybe_expand_path(endpoint_module, path)} end) + + %{config | paths: expanded_paths} + end + + defp expand_spec_path(%{path: path} = config, endpoint_module) when is_binary(path) do + %{config | path: maybe_expand_path(endpoint_module, path)} + end + + defp expand_spec_path(config, _endpoint_module), do: config + + defp maybe_expand_path(endpoint_module, "/" <> _ = path) do + endpoint_module.path(path) end - end - defp supplement_config(config, _conn) do - config + defp maybe_expand_path(_endpoint_module, path), do: path + else + defp supplement_config(config, _conn), do: config end def get_nonce(conn, config, type) do diff --git a/test/plug/swagger_ui_test.exs b/test/plug/swagger_ui_test.exs index 4506e7bc..4fc38286 100644 --- a/test/plug/swagger_ui_test.exs +++ b/test/plug/swagger_ui_test.exs @@ -1,6 +1,10 @@ -defmodule OpenApiSpec.Plug.SwaggerUITest do +defmodule OpenApiSpex.Plug.SwaggerUITest do use ExUnit.Case + defmodule Endpoint do + use Phoenix.Endpoint, otp_app: :open_api_spex_test + end + alias OpenApiSpex.Plug.SwaggerUI @opts SwaggerUI.init(path: "/ui") @@ -46,4 +50,60 @@ defmodule OpenApiSpec.Plug.SwaggerUITest do assert String.match?(conn.resp_body, ~r/ SwaggerUI.call(@opts) + assert conn.resp_body =~ ~r[pathname.+?/my_app/ui] + end + + test "expands paths with endpoint path prefix" do + opts = SwaggerUI.init(paths: [latest: "/api/openapi", legacy: "/legacy/openapi"]) + conn = conn_with_endpoint() |> SwaggerUI.call(opts) + assert conn.resp_body =~ "/my_app/api/openapi" + assert conn.resp_body =~ "/my_app/legacy/openapi" + end + + test "does not expand absolute URLs in paths" do + opts = + SwaggerUI.init(paths: [local: "/api/openapi", remote: "http://other.host/api/openapi"]) + + conn = conn_with_endpoint() |> SwaggerUI.call(opts) + assert conn.resp_body =~ "/my_app/api/openapi" + assert conn.resp_body =~ "http://other.host/api/openapi" + end + + test "expands oauth2_redirect_url with endpoint_url tuple" do + opts = + SwaggerUI.init( + path: "/ui", + oauth2_redirect_url: {:endpoint_url, "/oauth2-redirect.html"} + ) + + conn = conn_with_endpoint() |> SwaggerUI.call(opts) + assert conn.resp_body =~ "https://example.com:1234/my_app/oauth2-redirect.html" + end + + test "does not expand path without phoenix endpoint" do + conn = Plug.Test.conn(:get, "/ui") |> SwaggerUI.call(@opts) + assert conn.resp_body =~ ~r[pathname.+?/ui] + refute conn.resp_body =~ "/my_app" + end + end + + defp conn_with_endpoint(path \\ "/ui") do + Plug.Test.conn(:get, path) + |> Plug.Conn.put_private(:phoenix_endpoint, Endpoint) + end end