Bureaucrat is a library that lets you generate API documentation of your Phoenix app from tests.
First, add Bureaucrat to your mix.exs
dependencies:
defp deps do
[{:bureaucrat, "~> 0.2.9"}]
end
Bureaucrat needs a json library and defaults to Poison. It must be added as a dependency:
defp deps do
[
{:bureaucrat, "~> 0.2.9"},
{:poison, "~> 3.0"}
]
end
Then, update your dependencies:
$ mix deps.get
Next, in your test/test_helper.exs
you should start Bureaucrat and configure
ExUnit to use its formatter. You would probably like to keep the default
ExUnit.CLIFormatter
as well.
Bureaucrat.start
ExUnit.start(formatters: [ExUnit.CLIFormatter, Bureaucrat.Formatter])
And finally, import Bureaucrat helpers in test/support/conn_case.ex
:
defmodule Spell.ConnCase do
using do
quote do
import Bureaucrat.Helpers
end
end
end
To generate Phoenix channel documentation, import the helpers in test/support/channel_case.ex
alike.
Bureaucrat collects data from connection structs used in tests.
If you want a connection to be documented, pass it to the doc/1
function:
test "GET /api/v1/products" do
conn = conn
|> get("/api/v1/products")
|> doc()
assert conn.status == 200
end
Additional options can be passed to the backend formatter:
test "GET /api/v1/products" do
conn = conn
|> get("/api/v1/products")
|> doc(description: "List all products", operation_id: "list_products")
assert conn.status == 200
end
Then, to generate the documentation file(s) run DOC=1 mix test
.
The default output file is API.md
in the project root.
If you don't want to call |> doc()
on each request, you can import Bureaucrat.Macros
.
- It automatically adds
|> doc()
to thePhoenix.ConnTest
macros - It creates other macros:
get_undocumented
,post_undocumented
,patch_undocumented
,put_undocumented
anddelete_undocumented
, to be used in requests you want to skip docs
You can also use the _undocumented
version to overcome the fact that |> doc()
can only be called
from a test block, not a private function.
To achieve this, replace
import Phoenix.ConnTest
By
import Phoenix.ConnTest, only: :functions
import Bureaucrat.Helpers
import Bureaucrat.Macros
To add a custom intro section, for each output file, bureaucrat will look for an intro markdown file in the output directory,
named like the output file with a _intro
or _INTRO
suffix (before .md
, if present), e.g.
web/controllers/README
->web/controllers/README_INTRO
web/controllers/readme.md
->web/controllers/readme_intro.md
Currently the supported writers are the default Bureaucrat.MarkdownWriter
and Bureaucrat.ApiBlueprintWriter
.
Bureaucrat can also generate documentation for messages, replies and broadcasts in Phoenix Channels.
Results of assert_push
, assert_broadcast
and the underlying assert_receive
(if used for messages or broadcasts) can be passed to the doc
function.
To document usage of Phoenix.ChannelTest helpers connect
, push
, broadcast_from
and broadcast_from!
, Bureaucrat includes documenting alternatives, prefixed with doc_
:
doc_connect
doc_push
doc_broadcast_from
doc_broadcast_from!
test "message:new broadcasts are pushed to the client", %{socket: socket} do
doc_broadcast_from! socket, "message:new", %{body: "Hello there!", timestamp: 1483971926566, user: "marla"}
assert_push("message:new", %{body: "Hello there!", timestamp: 1483971926566, user: "marla"})
|> doc()
end
Channels docs output is currently only supported by the Bureaucrat.MarkdownWriter
and only to the default_path
(see Configuration below).
Bureaucrat comes with the Bureaucrat.SwaggerSlateMarkdownWriter
backend that will merge test examples with a swagger spec to produce markdown files that can be processed with the slate static generator.
To configure swagger integration, first write a swagger file by hand or generate one using phoenix_swagger. In the example below, the swagger file exists in the project at priv/static/swagger.json
.
Clone the slate project into a directory in your project:
git clone --shallow https://github.com/lord/slate doc
Configure Bureaucrat writer
, default_path
and swagger
:
Bureaucrat.start(
env_var: "DOC",
writer: Bureaucrat.SwaggerSlateMarkdownWriter,
default_path: "doc/source/index.html.md",
swagger: "priv/static/swagger.json" |> File.read!() |> Poison.decode!())
Within each test, link the test example to a swagger operation by passing an operation_id
to the doc
helper:
test "creates and renders resource when data is valid", %{conn: conn} do
conn =
conn
|> post(user_path(conn, :create), user: @valid_attrs)
|> doc(operation_id: "create_user")
assert json_response(conn, 201)["data"]["id"]
assert Repo.get_by(User, @valid_attrs)
end
Now generate documentation with DOC=1 mix test
.
Use slate to convert the markdown to HTML:
cd doc
bundle install
bundle exec middleman build
To serve the documentation directly from your application, copy the slate build output to your priv/static
directory:
mkdir priv/static/doc
cp -R doc/build/* priv/static/doc
Whitelist the doc
directory for static assets in the Plug.Static
configuration:
plug Plug.Static,
at: "/", from: :swagger_demo, gzip: false,
only: ~w(css doc fonts images js favicon.ico robots.txt)
Run your application with mix phoenix.server
and visit http://localhost:4000/doc/index.html
to see your documentation.
For a full example see the examples/swagger_demo
project.
Bureaucrat also supports generating markdown files that are formatted in the API Blueprint syntax.
Simply set the Bureaucrat.ApiBlueprintWriter
in your configuration file and run the usual:
DOC=1 mix test
After the markdown file has been successfully generated you can use aglio to produce the html file:
aglio -i web/controllers/api/v1/documentation.md -o web/controllers/api/v1/documentation.html
If you're piping through custom plugs than can prevent the HTTP requests to land in the controllers (authentication, authorization) and you want to document these cases you'll need the plug_doc()
helper:
describe "unauthenticated user" do
test "GET all items", %{conn: conn} do
conn
|> get(item_path(conn, :index))
|> plug_doc(module: __MODULE__, action: :index)
|> doc()
|> assert_unauthenticated()
end
end
Without the plug_doc()
helper Bureaucrat doesn't know the phoenix_controller
(since the request never landed in the controller) and an error is raised: ** (RuntimeError) GET all items (/api/v1/items) doesn't have required :phoenix_controller key. Have you forgotten to plug_doc()?
Bureaucrat also supports generating json files that are formatted in the Postman Collection v2.1 schema.
It writes one folder per controller, one request per action and one response per test example.
ExUnit test descriptions are used as response example names, along with the response status.
Params/query/body keys that are ended in _id
have values substituted by environment {{variables}}
.
Sets the collection name as the json filename specified in path
configuration.
Supports bearer authentication header.
Bureaucrat.start(
writer: Bureaucrat.PostmanWriter,
prefix: "Elixir.MyAppWeb",
default_path: "docs/my_app.json"
)
Supports all configurations but not custom titles
or intro files.
Use prefix
to trim the prefix out of the Postman folder names.
The configuration options can be passed to Bureaucrat.start
:
Bureaucrat.start(
writer: Bureaucrat.MarkdownWriter,
default_path: "web/controllers/README.md",
paths: [],
titles: [],
env_var: "DOC",
json_library: Poison
)
The available options are:
:writer
: The module used to generate docs from the list of captured connections.:default_path
: The path where the docs are written by default.:paths
: Allows you to specify different doc paths for some of your modules. For example[{YourApp.Api.V1, "web/controllers/api/v1/README.md"}]
will cause the docs for controllers underYourApp.Api.V1
namespace to be written toweb/controllers/api/v1/README.md
.:titles
: Allows you to specify explicit titles for some of your modules. For example[{YourApp.Api.V1.UserController, "API /v1/users"}]
will change the title (Table of Contents entry and heading) for this controller.:prefix
: Allows you to remove the prefix of the test module names:env_var
: The environment variable used as a flag to trigger doc generation.:json_library
: The JSON library to use. Poison (the default) and Jason both work.