diff --git a/README.md b/README.md index 41729d3e..94c0582b 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,8 @@ serve a SwaggerUI interface. The `path:` plug option must be supplied to give th All JavaScript and CSS assets are sourced from cdnjs.cloudflare.com, rather than vendoring into this package. + + ```elixir scope "/" do pipe_through :browser # Use the default browser stack @@ -299,6 +301,27 @@ All JavaScript and CSS assets are sourced from cdnjs.cloudflare.com, rather than end ``` +## Assets can be installed into the local application + +```shell +mix openapi.spec.install 'install_path' +``` +```elixir + scope "/" do + pipe_through :browser # Use the default browser stack + + get "/", MyAppWeb.PageController, :index + get "/swaggerui", OpenApiSpex.Plug.SwaggerUI.Local, path: "/api/openapi" + end + + scope "/api" do + pipe_through :api + + resources "/users", MyAppWeb.UserController, only: [:create, :index, :show] + get "/openapi", OpenApiSpex.Plug.RenderSpec, [] + end +``` + ## Importing an existing schema file > :warning: This functionality currently converts Strings into Atoms, which makes it potentially [vulnerable to DoS attacks](https://til.hashrocket.com/posts/gkwwfy9xvw-converting-strings-to-atoms-safely). We recommend that you load Open API Schemas from _known files_ during application startup and _not dynamically from external sources at runtime_. diff --git a/lib/mix/tasks/install.ex b/lib/mix/tasks/install.ex new file mode 100644 index 00000000..2f67019d --- /dev/null +++ b/lib/mix/tasks/install.ex @@ -0,0 +1,29 @@ +defmodule Mix.Tasks.Openapi.Spec.Install do + @moduledoc """ + Fetch and install the resources locally for the interface. + + ## Example + + mix run Openapi.Install '/path_to_app' + """ + use Mix.Task + + @preset 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.32.4/swagger-ui-standalone-preset.js' + @css 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.32.4/swagger-ui.css' + @bundle 'https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.32.4/swagger-ui-bundle.js' + + @impl true + def run([path]) do + Mix.Task.run("app.start") + + :inets.start() + :ssl.start() + + {:ok, :saved_to_file} = :httpc.request(:get, {css, []}, [], [stream: path <> '/css/']) + {:ok, :saved_to_file} = :httpc.request(:get, {@preset, []}, [], [stream: path <> '/js/']) + {:ok, :saved_to_file} = :httpc.request(:get, {@bundle, []}, [], [stream: path <> '/js/']) + + end + + + end \ No newline at end of file diff --git a/lib/open_api_spex/plug/swagger_ui_local.ex b/lib/open_api_spex/plug/swagger_ui_local.ex new file mode 100644 index 00000000..7c633af5 --- /dev/null +++ b/lib/open_api_spex/plug/swagger_ui_local.ex @@ -0,0 +1,151 @@ +defmodule OpenApiSpex.Plug.SwaggerUI.Local do + @moduledoc """ + Module plug that serves SwaggerUI. + + The full path to the API spec must be given as a plug option. + The API spec should be served at the given path, see `OpenApiSpex.Plug.RenderSpec` + + ## Configuring SwaggerUI + + SwaggerUI can be configured through plug `opts`. + All options will be converted from `snake_case` to `camelCase` and forwarded to the `SwaggerUIBundle` constructor. + See the [swagger-ui configuration docs](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) for details. + Should dynamic configuration be required, the `config_url` option can be set to an API endpoint that will provide additional config. + + ## Example + + scope "/" do + pipe_through :browser # Use the default browser stack + + get "/", MyAppWeb.PageController, :index + get "/swaggerui", OpenApiSpex.Plug.SwaggerUI, + path: "/api/openapi", + default_model_expand_depth: 3, + display_operation_id: true + end + + # Other scopes may use custom stacks. + scope "/api" do + pipe_through :api + resources "/users", MyAppWeb.UserController, only: [:index, :create, :show] + get "/openapi", OpenApiSpex.Plug.RenderSpec, :show + end + """ + @behaviour Plug + + @html """ + + + +
+ +