defmodule PhoenixSwagger.Plug.SwaggerUI do @moduledoc """ Swagger UI in a plug. ## Examples Generate a swagger file and place it in your applications `priv/static` dir: mix phoenix.swagger.generate priv/static/myapp.json Add a swagger scope to your router, and forward all requests to SwaggerUI scope "myapp/api/swagger" do forward "/", PhoenixSwagger.Plug.SwaggerUI, otp_app: :myapp, swagger_file: "myapp.json" end Run the server with `mix phoenix.server` and browse to `localhost:8080/myapp/api/swagger/`, swagger-ui should be shown with your swagger spec loaded. """ use Plug.Router alias Plug.Conn # Serve static assets before routing plug(Plug.Static, at: "/", from: :phoenix_swagger) plug(:match) plug(:dispatch) @template """ Swagger UI
""" # Redirect / to /index.html get "/" do base_path = conn.request_path |> String.trim_trailing("/") conn |> Conn.put_resp_header("location", "#{base_path}/index.html") |> Conn.put_resp_content_type("text/html") |> Conn.send_resp(302, "Redirecting") end get "/index.html" do conn |> Conn.put_resp_content_type("text/html") |> Conn.send_resp(200, conn.assigns.index_body) end # Render the swagger.json file or 404 for any other file get "/*paths" do spec_url = conn.assigns.spec_url case conn.path_params["paths"] do [^spec_url] -> Conn.send_file(conn, 200, conn.assigns.swagger_file_path) _ -> if accept_json?(conn) do conn |> Conn.put_resp_content_type("application/json") |> Conn.send_resp(404, PhoenixSwagger.json_library().encode!(%{Error: "not found"})) |> halt else Conn.send_resp(conn, 404, "not found") end end end match "/*paths" do Conn.send_resp(conn, 405, "method not allowed") end @doc """ Plug.init callback Options: - `otp_app` (required) The name of the app has is hosting the swagger file - `swagger_file` (required) The name of the file, eg "swagger.json" - `config_object` (optional) These values are injected into the config object passed to SwaggerUI. - `config_url` (optional) Populates the `configUrl` Swagger UI parameter. A URL to fetch an external configuration document from. """ def init(opts) do app = Keyword.fetch!(opts, :otp_app) swagger_file = Keyword.fetch!(opts, :swagger_file) config_object = Keyword.get(opts, :config_object, %{}) config_url = format_config_url(opts) body = EEx.eval_string(@template, config_object: config_object, config_url: config_url, spec_url: swagger_file ) swagger_file_path = Path.join(["priv", "static", swagger_file]) [app: app, body: body, spec_url: swagger_file, swagger_file_path: swagger_file_path] end @doc """ Plug.call callback """ def call(conn, app: app, body: body, spec_url: url, swagger_file_path: swagger_file_path) do conn |> Conn.assign(:index_body, body) |> Conn.assign(:spec_url, url) |> Conn.assign(:swagger_file_path, Path.join([Application.app_dir(app), swagger_file_path])) |> super([]) end defp accept_json?(conn) do case get_req_header(conn, "accept") do ["application/json"] -> true _ -> false end end defp format_config_url(opts) do case Keyword.fetch(opts, :config_url) do :error -> :undefined {:ok, nil} -> :undefined {:ok, url} -> "\"#{url}\"" end end end