Code Monkey home page Code Monkey logo

bureaucrat's Introduction

Bureaucrat

Bureaucrat is a library that lets you generate API documentation of your Phoenix app from tests.

Installation

First, add Bureaucrat to your mix.exs dependencies:

defp deps do
  [{:bureaucrat, "~> 0.2.10"}]
end

Bureaucrat needs a json library and defaults to Poison. It must be added as a dependency:

defp deps do
  [
    {:bureaucrat, "~> 0.2.10"},
    {: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.

Usage

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.

Pipe |> doc() automatically

If you don't want to call |> doc() on each request, you can import Bureaucrat.Macros.

  • It automatically adds |> doc() to the Phoenix.ConnTest macros
  • It creates other macros: get_undocumented, post_undocumented, patch_undocumented, put_undocumented and delete_undocumented, to be used in requests you want to skip docs

To achieve this, replace

import Phoenix.ConnTest

By

import Phoenix.ConnTest, only: :functions
import Bureaucrat.Helpers
import Bureaucrat.Macros

Custom intro sections

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.

Documenting Phoenix Channels

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).

Swagger & Slate Integration

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.

API Blueprint support

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

API Blueprint usage note

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()?

Postman support

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.

Configuration

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 under YourApp.Api.V1 namespace to be written to web/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.

bureaucrat's People

Contributors

a8t avatar alexandremcosta avatar ardhitama avatar arnodirlam avatar bjamet avatar canuc avatar cilim avatar dependabot[bot] avatar fastjames avatar graborg avatar gueloremanuel avatar hackling avatar hauntedhost avatar jangnezda avatar kerrd89 avatar kuzaxak avatar lasseebert avatar linjunpop avatar mbuhot avatar naps62 avatar opakalex avatar peck avatar raymondboswel avatar rikydeg avatar riverrun avatar rookieone avatar sasa1977 avatar tteerawat avatar vbaranov avatar zhisme avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bureaucrat's Issues

supporting doc from functions

Currently the doc macro must be invoked directly from the test. If it is invoked from a function, a compile-time exception is raised. As a result, we're facing a dilemma: either avoid wrappers, which can lead to excessively noisy code, or avoid generating documentation.

I think it's possible to do better, and allow devs to wrap the requests and still generate the docs. Here's the proposed approach:

  1. At compile time, use nil for the description if the immediate caller is not test
  2. At runtime, if the description is nil, try to deduce it from the runtime stack trace using Process.info(self(), :current_stacktrace).
  3. If the description can't be found, don't generate the doc (i.e. automatically fallback to _undocumented), but emit a warning.

In most cases this should work automagically. In exceptional cases, e.g. making a request from a setup block, it would still work while emitting a warning. The fix in such cases could be switching to _undocumented or explicitly passing the description.

WDYT?

Enumerable is not implemented for nil

Hello all,

First of all, thank you for this work!

I have a trouble currently setting up Bureaucrat with Slate & Swagger in an Phoenix API.

When I run DOC=1 mix test, I get this error:

00:49:37.308 [error] GenServer #PID<0.3151.0> terminating ** (Protocol.UndefinedError) protocol Enumerable not implemented for nil. This protocol is implemented for: Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, HashDict, Map, MapSet, File.Stream, Date.Range, HashSet, List, Range, Stream, Function, GenEvent.Stream, IO.Stream (elixir) /builddir/build/BUILD/elixir-1.8.1/lib/elixir/lib/enum.ex:1: Enumerable.impl_for!/1 (elixir) /builddir/build/BUILD/elixir-1.8.1/lib/elixir/lib/enum.ex:141: Enumerable.reduce/3 (elixir) lib/enum.ex:3015: Enum.each/2 (elixir) lib/enum.ex:775: anonymous fn/3 in Enum.each/2 (stdlib) maps.erl:257: :maps.fold_1/3 (elixir) lib/enum.ex:1956: Enum.each/2 (bureaucrat) lib/bureaucrat/swagger_slate_markdown_writer.ex:287: Bureaucrat.SwaggerSlateMarkdownWriter.write_operations_for_tag/4 (elixir) lib/enum.ex:769: Enum."-each/2-lists^foreach/1-0-"/2 (elixir) lib/enum.ex:769: Enum.each/2 (elixir) lib/enum.ex:1331: anonymous fn/3 in Enum.map/2 (stdlib) maps.erl:257: :maps.fold_1/3 (elixir) lib/enum.ex:1956: Enum.map/2 (bureaucrat) lib/bureaucrat/formatter.ex:10: Bureaucrat.Formatter.handle_cast/2 (stdlib) gen_server.erl:637: :gen_server.try_dispatch/4 (stdlib) gen_server.erl:711: :gen_server.handle_msg/6 (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3 Last message: {:"$gen_cast", {:suite_finished, 326751, nil}} 00:49:37.310 [error] Task #PID<0.3130.0> started from #PID<0.93.0> terminating ** (stop) exited in: GenServer.stop(#PID<0.3151.0>, :normal, 30000) ** (EXIT) exited in: :sys.terminate(#PID<0.3151.0>, :normal, :infinity) ** (EXIT) an exception was raised: ** (Protocol.UndefinedError) protocol Enumerable not implemented for nil. This protocol is implemented for: Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, HashDict, Map, MapSet, File.Stream, Date.Range, HashSet, List, Range, Stream, Function, GenEvent.Stream, IO.Stream (elixir) /builddir/build/BUILD/elixir-1.8.1/lib/elixir/lib/enum.ex:1: Enumerable.impl_for!/1 (elixir) /builddir/build/BUILD/elixir-1.8.1/lib/elixir/lib/enum.ex:141: Enumerable.reduce/3 (elixir) lib/enum.ex:3015: Enum.each/2 (elixir) lib/enum.ex:775: anonymous fn/3 in Enum.each/2 (stdlib) maps.erl:257: :maps.fold_1/3 (elixir) lib/enum.ex:1956: Enum.each/2 (bureaucrat) lib/bureaucrat/swagger_slate_markdown_writer.ex:287: Bureaucrat.SwaggerSlateMarkdownWriter.write_operations_for_tag/4 (elixir) lib/enum.ex:769: Enum."-each/2-lists^foreach/1-0-"/2 (elixir) lib/enum.ex:769: Enum.each/2 (elixir) lib/enum.ex:1331: anonymous fn/3 in Enum.map/2 (stdlib) maps.erl:257: :maps.fold_1/3 (elixir) lib/enum.ex:1956: Enum.map/2 (bureaucrat) lib/bureaucrat/formatter.ex:10: Bureaucrat.Formatter.handle_cast/2 (stdlib) gen_server.erl:637: :gen_server.try_dispatch/4 (stdlib) gen_server.erl:711: :gen_server.handle_msg/6 (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3 (elixir) lib/gen_server.ex:948: GenServer.stop/3 (ex_unit) lib/ex_unit/event_manager.ex:24: anonymous fn/2 in ExUnit.EventManager.stop/1 (elixir) lib/enum.ex:1940: Enum."-reduce/3-lists^foldl/2-0-"/3 (ex_unit) lib/ex_unit/event_manager.ex:23: ExUnit.EventManager.stop/1 (ex_unit) lib/ex_unit/runner.ex:28: ExUnit.Runner.run/2 (elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2 (elixir) lib/task/supervised.ex:35: Task.Supervised.reply/5 (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3 Function: &ExUnit.run/0 Args: [] ** (EXIT from #PID<0.93.0>) exited in: GenServer.stop(#PID<0.3151.0>, :normal, 30000) ** (EXIT) exited in: :sys.terminate(#PID<0.3151.0>, :normal, :infinity) ** (EXIT) an exception was raised: ** (Protocol.UndefinedError) protocol Enumerable not implemented for nil. This protocol is implemented for: Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, HashDict, Map, MapSet, File.Stream, Date.Range, HashSet, List, Range, Stream, Function, GenEvent.Stream, IO.Stream (elixir) /builddir/build/BUILD/elixir-1.8.1/lib/elixir/lib/enum.ex:1: Enumerable.impl_for!/1 (elixir) /builddir/build/BUILD/elixir-1.8.1/lib/elixir/lib/enum.ex:141: Enumerable.reduce/3 (elixir) lib/enum.ex:3015: Enum.each/2 (elixir) lib/enum.ex:775: anonymous fn/3 in Enum.each/2 (stdlib) maps.erl:257: :maps.fold_1/3 (elixir) lib/enum.ex:1956: Enum.each/2 (bureaucrat) lib/bureaucrat/swagger_slate_markdown_writer.ex:287: Bureaucrat.SwaggerSlateMarkdownWriter.write_operations_for_tag/4 (elixir) lib/enum.ex:769: Enum."-each/2-lists^foreach/1-0-"/2 (elixir) lib/enum.ex:769: Enum.each/2 (elixir) lib/enum.ex:1331: anonymous fn/3 in Enum.map/2 (stdlib) maps.erl:257: :maps.fold_1/3 (elixir) lib/enum.ex:1956: Enum.map/2 (bureaucrat) lib/bureaucrat/formatter.ex:10: Bureaucrat.Formatter.handle_cast/2 (stdlib) gen_server.erl:637: :gen_server.try_dispatch/4 (stdlib) gen_server.erl:711: :gen_server.handle_msg/6 (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

I have some tags in my swagger.json though.

Can I borrow some help?

GenEvent deprecation warning

Hi,

After I upgraded my elixir to the newest version, I am getting this deprecation warning:

passing GenEvent handlers (Bureaucrat.Formatter in this case) in the :formatters option of ExUnit is deprecated, please pass a GenServer instead. Check the documentation for the ExUnit.Formatter module for more information

I saw it's fixed with this commit, but the new version of bureaucrat is not released. Can you tell me when it is going to be released?

Thanks!

README part for Swagger Integration

Swagger Integration was added by @mbuhot in #9 but not documentated yet in the README.

To help people who want to use it, improve popularity of bureaucrat, and to be able to continue development, this should be added.

GenEvent Formatter error

Running off the latest commit (necessary to bypass a parameters issue merged in #28)

UPDATE: using elixir 1.3 is causing the issue, master is incompatible

12:06:34.502 [error] GenEvent handler Bureaucrat.Formatter installed in #PID<0.689.0> terminating
** (UndefinedFunctionError) function Bureaucrat.Formatter.handle_event/2 is undefined or private. Did you mean one of:

      * handle_cast/2
      * handle_info/2

    (bureaucrat) Bureaucrat.Formatter.handle_event({:suite_started, [seed: 424463, max_cases: 16, include: [], exclude: [], timeout: 60000, assert_receive_timeout: 100, refute_receive_timeout: 100, autorun: false, included_applications: [], trace: false, stacktrace_depth: 20, formatters: [ExUnit.CLIFormatter, Bureaucrat.Formatter], colors: [], capture_log: false]}, nil)
    (stdlib) gen_event.erl:538: :gen_event.server_update/4
    (stdlib) gen_event.erl:520: :gen_event.server_notify/4
    (stdlib) gen_event.erl:261: :gen_event.handle_msg/5
    (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3

I believe this is from the refactor to turn the formatter into a GenServer: 98af5c8#diff-0368a301ccafadcc320f87373218a0e4

Can someone confirm that this PR didn't break the formatter? I am using the swagger_slate_markdown_writer if that is relevant

Generating Docs with Intro and Custom Pages

Its unclear for the documentation on this repo how to create a nice API documentation in slate with custom intro pages and sections that are required of high quality docs. Is there a good reference for this? I don't see how the _intro pages work if you want to have your docs managed by multiple different source files for easier maintenance.

Parameters in doc() causing list conversion Error

I am facing the following error when passing :operation_id in doc().

[error] GenEvent handler Bureaucrat.Formatter installed in #PID<0.359.0> terminating
** (ArgumentError) cannot convert the given list to a string.

To be converted to a string, a list must contain only:

  • strings
  • integers representing Unicode codepoints
  • or a list containing one of these three elements

Please check the given list or call inspect/1 to get the list representation, got:

[operation_id: "create_user"]

(elixir) lib/list.ex:709: List.to_string/1
(bureaucrat) lib/bureaucrat/markdown_writer.ex:43: Bureaucrat.MarkdownWriter.write_example/2
(elixir) lib/enum.ex:645: Enum."-each/2-lists^foreach/1-0-"/2
(elixir) lib/enum.ex:645: Enum.each/2
(elixir) lib/enum.ex:651: anonymous fn/3 in Enum.each/2
(stdlib) lists.erl:1263: :lists.foldl/3
(elixir) lib/enum.ex:1772: Enum.each/2
(elixir) lib/enum.ex:645: Enum."-each/2-lists^foreach/1-0-"/2
(elixir) lib/enum.ex:645: Enum.each/2
(elixir) lib/enum.ex:1233: anonymous fn/3 in Enum.map/2
(stdlib) lists.erl:1263: :lists.foldl/3
(elixir) lib/enum.ex:1772: Enum.map/2
(bureaucrat) lib/bureaucrat/formatter.ex:10: Bureaucrat.Formatter.handle_event/2
(stdlib) gen_event.erl:570: :gen_event.server_update/4
(stdlib) gen_event.erl:552: :gen_event.server_notify/4
(stdlib) gen_event.erl:293: :gen_event.handle_msg/5
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

Last message: {:suite_finished, 985361, nil}
State: nil

My Environment:
$ mix -v
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:10] [hipe] [kernel-poll:false]
Mix 1.4.4

GenServer.call(Bureaucrat.Recorder) not alive

Hello 👋

I updated the library to 0.2.1 and used the ApiBlueprintWriter and everything went smoothly. I have the API documentation generated and it's working well with aglio.

I just wanted to report that I got this error after everything was finished:

10:58:40.116 [error] GenServer #PID<0.4398.0> terminating
** (stop) exited in: GenServer.call(Bureaucrat.Recorder, :get_records, 5000)
    ** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
    (elixir) lib/gen_server.ex:820: GenServer.call/3
    (bureaucrat) lib/bureaucrat/formatter.ex:20: Bureaucrat.Formatter.generate_docs/0
    (bureaucrat) lib/bureaucrat/formatter.ex:10: Bureaucrat.Formatter.handle_cast/2
    (stdlib) gen_server.erl:616: :gen_server.try_dispatch/4
    (stdlib) gen_server.erl:686: :gen_server.handle_msg/6
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:suite_finished, 178304628, nil}}
Finished in 178.3 seconds
585 tests, 0 failures

Randomized with seed 750804

Ability to document versioned APIs when routes are not used to implement versioning

First and foremost, thank you for your work on this library! It has been crucial in my API development.

I am using custom mime-types in the Accept header as the manner by which I version my API. For instance: application/vnd.my-app.v1+json. A plug detects the custom mime-type and sets a value in the conn to the mapped version number, something like: :v1. Then the controller, views, etc can pattern match on the conn version value to fork with different behaviors. For more info on this technique see this blog post.

Due to this API versioning implementation, it seems I am forced to document all API versions within the same file. Obviously, this makes the versions of the API harder to grok.

It would be nice if there was a way for me to generate my API version docs to different files even though the same module is implementing them.

Maybe you already know of a way to accomplish this with no changes to bureaucrat? If not, this could possibly be accomplished through:

  1. Configuring the output READMEs to be dependent on the test file and not the module under test
  2. Providing configuration options for the module under test that could look at something in the conn and send the output to a different configured file

What are your thoughts? Thanks again!

SwaggerSlateMarkdownWriter does not handle schema reference in parameters

If in a swagger file we have a parameter that references another schema (model) like this:

  "paths": {
    "/some_resource": {
      "post": {
        "description": "Create new new resource",
        "operationId": "create_resource",
        "parameters": [{
          "description": "Resource object",
          "in": "body",
          "name": "resource",
          "required": false,
          "schema": {
            "$ref": "#/definitions/Resource"
          }
        }],
        ...
      }
    }
  }

then the resulting markdown won't contain the link to the schema:

...
#### Parameters

| Parameter   | Description | In |Type      | Required | Default | Example |
|-------------|-------------|----|----------|----------|---------|---------|
|resource|Resource object|body||false|||
...

Note that the Type column is empty. Instead, it should equal to [Resource](#resource).

Ability to customize formatting of payloads

Currently in the default and swagger writers, the request and response bodies are just assumed to be JSON and passed through the Poison.encode/1 with the pretty: true option.

In order to support other use cases (e.g., XML, other JSON libs) or just more control of the formatting, it would be great if it were possible to configure this without re-implementing the entire writer.

This could be done via options to the writer:

config :bureaucrat,
  writer: {Bureaucrat.MarkdownWriter, payload_formatter: MyHelper}

Along with a default implementation based on the current strategy.

Also open to other suggestions. I can work on a PR if that's something you'd be willing to include.

Support phoenix channels (websockets) documentation

I think bureaucrat could be great for documenting phoenix channel messages and broadcasts, in addition to the API requests and responses it already supports. I'm not sure if it makes sense to support websockets without phoenix, but in any case phoenix could be an optional dependency.

The simplest and most common use cases for now, from my perspective, would be the ones described in the phoenix channel docs:

Use Cases

  1. Connecting a socket, with reply (:ok/:error)
    connect(UserSocket, %{"some" => "params"})
  2. Joining a channel, with reply (:ok/:error, optional reply body)
    subscribe_and_join(RoomChannel, "room:lobby", %{"id" => 3})
  3. Pushing a message from client to server, with reply (:ok/:error, optional reply body)
    push socket, "my_event", %{"some" => "data"}
  4. Broadcasting from client to server (no reply)
    broadcast_from socket, "my_event", %{"some" => "data"}
  5. Pushing a message from server to client (no reply)
    assert_push "my_event", %{"some" => "data"}
  6. Broadcasting from server to client (no reply)
    assert_broadcast "my_event", %{"some" => "data"}

I guess 1. - 4. are close to what bureaucrat records already.
For 5. - 6. it would be good to filter according to what's asserted, so you filter out the "noise" such as presence_diff broadcasts when you don't need them. Instead of going through assert, this could possibly be done with an alias function such as doc_assert or so.

Would that be within the scope of bureaucrat for you?
Do you see any low-hanging fruits here?

Not able to use with umbrella app with multiple .md output files

I am using Bureaucrat to generate my API docs for one of my apps inside the umbrella project. So far so good. I decided to use it to generate API docs for another app inside the same umbrella with a separate Markdown output file. But after doing this, I got somehow mixed results. File for app2 is OK, but file for app1 contains records from app1 and app2 combined. I guess it's because of Bureaucrat.Recorder is being used as a singleton basically.
Do you have any idea if there's a simple workaround available? It's not a requirement to have API docs in an output file per app, but I need to have a clear separation of which API docs are related to which app.
Thanks a lot. :-)

Release version 0.2.5

It looks like the readme mentions 0.2.5, which hasn't been released. Could you release a version?

Allow poison ~> 5.0

The version of poison in this project is far behind the latest version. Due to some other dependencies I need bureaucrat to support poison ~> 5.0 as well.

GenEvent/GenServer error for Bureaucrat.MarkdownWriter.write/2

Environment

  • Elixir version: 1.4.5
  • Phoenix version: 1.3.0
  • Operating system: macOS Sierra 10.12.6

I stumbled upon a few errors when I tried implementing Bureaucrat in an application I've been working on recently, so I decided to create a new phoenix application with one JSON resource and try creating docs for its automatically generated tests. This is the test app: https://github.com/NicolayD/bureaucrat_test

With {:bureaucrat, "~> 0.1.4"} as a dependency I got the following error (you can see the doc(conn) call on line 23):

image

When I tried getting Bureaucrat directly from the github repo with {:bureaucrat, github: "api-hogs/bureaucrat", only: :test}, I got the following message:

image

Could this be related to Elixir 1.4?

Slate friendly markdown

Hi and thanks for creating bureaucrat.
I've written a custom markdown writer that outputs in a format which can be used to create a slate (https://github.com/tripit/slate) static site.
Is this a feature worth submitting back to the bureaucrat library?
If so I can send a PR.

Latest version can't handle %Phoenix.Socket.Reply

I suspect that commit changed the behaviour of the operation ID getter.

It would be nice to add support for Phoenix.Socket.Reply.

Stacktrace:

** (FunctionClauseError) no function clause matching in Bureaucrat.Helpers.get_default_operation_id/1

     The following arguments were given to Bureaucrat.Helpers.get_default_operation_id/1:

         # 1
         %Phoenix.Socket.Reply{join_ref: 77772, payload: %{final_decision: nil, id: "5b069f36-24c8-483c-9e1a-9aeed6def3ff", score: %{"manual" => 10}}, ref: #Reference<0.299170896.563085323.98609>, status: :ok, topic: "assessment_review:5b069f36-24c8-483c-9e1a-9aeed6def3ff"}

     Attempted function clauses (showing 3 out of 3):

         def get_default_operation_id(%Plug.Conn{private: private})
         def get_default_operation_id(%Phoenix.Socket.Message{topic: topic, event: event})
         def get_default_operation_id(%Phoenix.Socket.Broadcast{topic: topic, event: event})

     code: assert_reply(ref, :ok, data) |> doc
     stacktrace:
       (bureaucrat 0.2.8) lib/bureaucrat/helpers.ex:122: Bureaucrat.Helpers.get_default_operation_id/1
       test/hdo_assessor_web/acceptance/assessment_review_channel_test.exs:129: (test)

Support for recently updated dependencies, Phoenix 1.4, Elixir 1.7.x

Description

2 Parts

Part 1:

It appears that Bureaucrat works as expected in the Elixir 1.7.x environment, however it throws a warning if above Elixir 1.6. Would you like a PR for adding Elixir 1.7 testing?

Part 2:

Is there a Roadmap to support Phoenix 1.4? It seems like Bureaucrat should still work with 1.4 as long as the default path is changed. Would you like a PR for adding Phoenix 1.4 testing and/or accept a new default path proposal?

Links

Elixir Semantic Versioning
Phoenix Versioning

Related Issues:

#41 #22

Edit

It may also be a good idea to switch the default JSON parser to Jason as Phoenix 1.4 now uses this as a default.

body_params is empty

Because I'm using put_req_header in my test setup and then passing the conn into the test the body_params is empty.

Is there anyway around this at all?

Support Elixir 1.7

I don't know what's required for Elixir 1.7 support, but seeing this warning:

warning: the dependency :bureaucrat requires Elixir "~> 1.6.1" but you are running on v1.7.3

API docs not generated anymore after upgrade to elixir 1.12.2 and Erlang/OTP 24 from Elixir 1.11.2 & Erlang/OTP 23

Environment

OS: Ubuntu Linux (20.04 LTS)
Erlang/OTP 24 [erts-12.0.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit]

Elixir 1.12.2 (compiled with Erlang/OTP 22)

Configuration

mix.exs

...
  defp deps do
    [
    ...
   {:jason, "~> 1.0"},
   {:bureaucrat, "~> 0.2.7"},
    ...
    ]
...

test_helper.exs

Bureaucrat.start(
  default_path: "docs/api/README.md",
  paths: [],
  titles: [
    {FooBarWeb.FooController, "/api/foo"},
    {FooBarWeb.BarController, "/api/bar"}
  ],
  env_var: "DOC",
  json_library: Jason
)
ExUnit.start(formatters: [ExUnit.CLIFormatter, Bureaucrat.Formatter], exclude: [:skip])
Ecto.Adapters.SQL.Sandbox.mode(TransactionServer.Repo, :manual)

Current behaviour

DOC=1 mix test has no effect on the documentation generated prior to the upgrade to Elixir 1.12.2 and Erlang/OTP 24 from Elixir 1.11.2 & Erlang/OTP 23

Expected behaviour

On running DOC=1 mix test , the documentation for the API calls should be updated where changes have been made.

Swagger Integration

As part of a custom markdown writer for bureaucrat, I've extracted descriptions, parameters and response schemas from a swagger file.
It works similarly to the swagger2markup project.
Is this a feature worth integrating into the bureaucrat library?
My initial implementation is currently tied to my markdown writer module, but it feels like a cross-cutting feature that could benefit any output formatter.

function Bureaucrat.SwaggerSlateMarkdownWriter.write/2 is undefined

Hello everyone!

I'm trying Bureaucrat right now and I'm facing an issue on a new Phoenix app.
I inserted bureaucrat as a dependency in my mix.exs file.
I added in the test helper the right lignes to make Bureaucrate start and find my already existing swagger file.
Finally as a test I added a doc call to one of my test.

Unfortunately, when running them, this error occurs:
** (UndefinedFunctionError) function Bureaucrat.SwaggerSlateMarkdownWriter.write/2 is undefined (module Bureaucrat.SwaggerSlateMarkdownWriter is not available)

Do you have any ideas how I can fix this? It's like if this module was not included in the project...
Thanks!

SwaggerSlateMarkdownWriter overwrites property description from referenced model

Having a swagger json with definitions like this:

{
  "definitions": {
    "Company": {
      "description": "A long description with  newlines \nand other stuff, so that model description is more informative.\n\nWith some edges cases ...",
      "properties": {
        "name": {
          "description": "Company name",
          "type": "string"
        }
      },
      "title": "Company",
      "type": "object"
    },
    "Owner": {
      "description": "A user that owns a company.",
      "properties": {
        "company": {
          "$ref": "#/definitions/Company",
          "description": "The company object"
        },
        "firstName": {
          "description": "First name",
          "type": "string"
        }
      },
      "title": "Owner",
      "type": "object"
    }
}

Will produce a markdown where the Owner.company.description will be overwritten by Company.description:

# Models

## Company

A long description with  newlines
and other stuff, so that model description is more informative.

With some edges cases ...

|Property|Description|Type|Required|
|--------|-----------|----|--------|
|name|Company name|string|true|

## Owner

A user that owns a company.

|Property|Description|Type|Required|
|--------|-----------|----|--------|
|firstName|First name|string|true|
|company|A long description with  newlines
and other stuff, so that model description is more informative.

With some edges cases ...
|[Company](#company)|false|

...

Note the description of company property in Owner model. Instead of it being The company object from Owner.company.description, it's a long description from Company.description. This also breaks the generated Slate html formatting. I think a better approach would be to honor the original property description from swagger definitions.

I am not sure if this is actually a bug, because the SwaggerSlateMarkdownWriter.resolve_type/2 function was written specifically to do it that way. It seems intended behaviour, but I wonder if there could be an option setting or something similar to force preservation of original property descriptions.

Warning: this check/guard will always yield the same result

Every single doc/2 line in my test suite generates the following warning:

warning: this check/guard will always yield the same result

Example block:

build_conn()
|> get("/users")
|> doc(description: "List users")
|> json_response(:ok)

The following arguments were given to Code.ensure_loaded?/1:

hi i am getting this error

my test-helper code
`Bureaucrat.start(
env_var: "DOC",
writer: Bureaucrat.SwaggerSlateMarkdownWriter,
default_path: "doc/source/index.html.md",
swagger: "priv/static/swagger.json"
|> File.read!()
|> Poison.decode!()
)

ExUnit.start(formatters: [ ExUnit.CLIFormatter,Bureaucrat.Formatter ])`

in conn case
import Bureaucrat.Helpers

** (FunctionClauseError) no function clause matching in Code.ensure_loaded?/1

The following arguments were given to Code.ensure_loaded?/1:

    # 1
    10

Attempted function clauses (showing 1 out of 1):

    def ensure_loaded?(module) when is_atom(module)

(elixir) lib/code.ex:869: Code.ensure_loaded?/1
(ecto) lib/ecto/adapters/sql.ex:339: Ecto.Adapters.SQL.normalize_pool/1
(elixir) lib/keyword.ex:831: Keyword.update/4
(elixir) lib/keyword.ex:835: Keyword.update/4
(ecto) lib/ecto/adapters/sql.ex:324: Ecto.Adapters.SQL.ensure_all_started/3
(ecto) lib/mix/ecto.ex:99: Mix.Ecto.ensure_started/2
(ecto) lib/mix/tasks/ecto.migrate.ex:76: anonymous fn/4 in Mix.Tasks.Ecto.Migrate.run/2
(elixir) lib/enum.ex:737: Enum."-each/2-lists^foreach/1-0-"/2

swagger_slate_markdown_writer does not write parameters

I have swagger.json which looks like:

"api/v1/routes": {
      "post": {
         ...
        "parameters": [
          {
            "schema": "string",
            "required": false,
            "name": "name",
            "in": "body",
            "description": "Route name"
          }
        ],
     ...
 }

bureaucrat fails to write parameters when parameters is a list, which is how PhoenixSwagger generates parameters

Optional JSON library

Now that Phoenix has moved to use Jason over Poison, it seems redundant to have multiple json libraries in one application.

Suggestion:
Make the json library configurable.

Something like

config :bureaucrat, :json_library, Jason # Or Poison / others

This has been done in other hex packages too, so I think it should be doable :)

I will happily try to make a PR if the suggestion is approved.

Pls provide an updated example without slate integration | Example bug?

Also this
test "GET /api/v1/products" do conn = conn() |> get("/api/v1/products") |> doc assert conn.status == 200 end

generates following error message after running "DOC=1 mix test":

== Compilation error in file test/backend_web/controllers/page_controller_test.exs ==
** (CompileError) test/backend_web/controllers/page_controller_test.exs:16: undefined function conn/0
(elixir 1.12.2) src/elixir_locals.erl:114: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
(stdlib 3.15.2) erl_eval.erl:685: :erl_eval.do_apply/6
(elixir 1.12.2) lib/kernel/parallel_compiler.ex:428: Kernel.ParallelCompiler.require_file/2
(elixir 1.12.2) lib/kernel/parallel_compiler.ex:321: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

I´m using Elixir version "1.12.2" with Phoenix 1.5.12 and this version of bureaucrat:
{:bureaucrat, git: "https://github.com/api-hogs/bureaucrat.git", only: [:dev, :test]} (but it also doesnt work with version 0.2.7)

Swagger & Slate Integration consistently erroring

What I'm trying achieve

I'm trying to use the integration with Swagger & Slate to generate API docs automatically.
I've followed to the letter the guidelines.

What is happening

When I run DOC=1 mix test, all the tests pass but I get the following error:

11:09:17.606 [error] GenServer #PID<0.519.0> terminating
** (Protocol.UndefinedError) protocol Enumerable not implemented for nil of type Atom. This protocol is implemented for the following type(s): DBConnection.PrepareStream, DBConnection.Stream, Date.Range, Ecto.Adapters.SQL.Stream, File.Stream, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, Jason.OrderedObject, List, Map, MapSet, Phoenix.LiveView.LiveStream, Postgrex.Stream, Range, Stream
    (elixir 1.14.5) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir 1.14.5) lib/enum.ex:166: Enumerable.reduce/3
    (elixir 1.14.5) lib/enum.ex:1150: Enum.find/3
    (bureaucrat 0.2.9) lib/bureaucrat/swagger_slate_markdown_writer.ex:236: Bureaucrat.SwaggerSlateMarkdownWriter.write_operations_for_tag/4
    (elixir 1.14.5) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir 1.14.5) lib/enum.ex:1662: anonymous fn/3 in Enum.map/2
    (stdlib 5.0.2) maps.erl:416: :maps.fold_1/4
    (elixir 1.14.5) lib/enum.ex:2480: Enum.map/2
    (bureaucrat 0.2.9) lib/bureaucrat/formatter.ex:22: Bureaucrat.Formatter.suite_finished/0
    (stdlib 5.0.2) gen_server.erl:1103: :gen_server.try_handle_cast/3
    (stdlib 5.0.2) gen_server.erl:1165: :gen_server.handle_msg/6
    (stdlib 5.0.2) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:suite_finished, %{async: 163093, run: 509077, load: nil}}}
11:09:17.612 [error] Task #PID<0.501.0> started from #PID<0.101.0> terminating
** (stop) exited in: GenServer.stop(#PID<0.519.0>, :normal, :infinity)
    ** (EXIT) exited in: :sys.terminate(#PID<0.519.0>, :normal, :infinity)
        ** (EXIT) an exception was raised:
            ** (Protocol.UndefinedError) protocol Enumerable not implemented for nil of type Atom. This protocol is implemented for the following type(s): DBConnection.PrepareStream, DBConnection.Stream, Date.Range, Ecto.Adapters.SQL.Stream, File.Stream, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, Jason.OrderedObject, List, Map, MapSet, Phoenix.LiveView.LiveStream, Postgrex.Stream, Range, Stream
                (elixir 1.14.5) lib/enum.ex:1: Enumerable.impl_for!/1
                (elixir 1.14.5) lib/enum.ex:166: Enumerable.reduce/3
                (elixir 1.14.5) lib/enum.ex:1150: Enum.find/3
                (bureaucrat 0.2.9) lib/bureaucrat/swagger_slate_markdown_writer.ex:236: Bureaucrat.SwaggerSlateMarkdownWriter.write_operations_for_tag/4
                (elixir 1.14.5) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
                (elixir 1.14.5) lib/enum.ex:1662: anonymous fn/3 in Enum.map/2
                (stdlib 5.0.2) maps.erl:416: :maps.fold_1/4
                (elixir 1.14.5) lib/enum.ex:2480: Enum.map/2
                (bureaucrat 0.2.9) lib/bureaucrat/formatter.ex:22: Bureaucrat.Formatter.suite_finished/0
                (stdlib 5.0.2) gen_server.erl:1103: :gen_server.try_handle_cast/3
                (stdlib 5.0.2) gen_server.erl:1165: :gen_server.handle_msg/6
                (stdlib 5.0.2) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
    (elixir 1.14.5) lib/gen_server.ex:995: GenServer.stop/3
    (ex_unit 1.14.5) lib/ex_unit/event_manager.ex:22: anonymous fn/2 in ExUnit.EventManager.stop/1
    (elixir 1.14.5) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ex_unit 1.14.5) lib/ex_unit/event_manager.ex:21: ExUnit.EventManager.stop/1
    (ex_unit 1.14.5) lib/ex_unit/runner.ex:62: ExUnit.Runner.run_with_trap/2
    (ex_unit 1.14.5) lib/ex_unit/runner.ex:31: ExUnit.Runner.run/2
    (elixir 1.14.5) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
    (elixir 1.14.5) lib/task/supervised.ex:34: Task.Supervised.reply/4
    (stdlib 5.0.2) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
Function: #Function<0.96829595/0 in ExUnit.async_run/0>
    Args: []
** (EXIT from #PID<0.101.0>) exited in: GenServer.stop(#PID<0.519.0>, :normal, :infinity)
    ** (EXIT) exited in: :sys.terminate(#PID<0.519.0>, :normal, :infinity)
        ** (EXIT) an exception was raised:
            ** (Protocol.UndefinedError) protocol Enumerable not implemented for nil of type Atom. This protocol is implemented for the following type(s): DBConnection.PrepareStream, DBConnection.Stream, Date.Range, Ecto.Adapters.SQL.Stream, File.Stream, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, Jason.OrderedObject, List, Map, MapSet, Phoenix.LiveView.LiveStream, Postgrex.Stream, Range, Stream
                (elixir 1.14.5) lib/enum.ex:1: Enumerable.impl_for!/1
                (elixir 1.14.5) lib/enum.ex:166: Enumerable.reduce/3
                (elixir 1.14.5) lib/enum.ex:1150: Enum.find/3
                (bureaucrat 0.2.9) lib/bureaucrat/swagger_slate_markdown_writer.ex:236: Bureaucrat.SwaggerSlateMarkdownWriter.write_operations_for_tag/4
                (elixir 1.14.5) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
                (elixir 1.14.5) lib/enum.ex:1662: anonymous fn/3 in Enum.map/2
                (stdlib 5.0.2) maps.erl:416: :maps.fold_1/4
                (elixir 1.14.5) lib/enum.ex:2480: Enum.map/2
                (bureaucrat 0.2.9) lib/bureaucrat/formatter.ex:22: Bureaucrat.Formatter.suite_finished/0
                (stdlib 5.0.2) gen_server.erl:1103: :gen_server.try_handle_cast/3
                (stdlib 5.0.2) gen_server.erl:1165: :gen_server.handle_msg/6
                (stdlib 5.0.2) proc_lib.erl:241: :proc_lib.init_p_do_apply/3

What I've tried

  • Adding the operation_id(...) to the operations in question.
    FYI this is missing from the guidelines. Let me know if you'd like me to add it.

Am I missing something? The swagger.json file seems to be being generated fine, but both the markdown and the HTML generated from it don't include any operations.

key :phoenix_controller not found

Hi

I set up my project as per the README and I'm getting this when I run my tests, piping through the doc() function (only with DOC=1 mix test):

** (KeyError) key :phoenix_controller not found in: %{phoenix_recycled: true, plug_skip_csrf_protection: true}
    (bureaucrat) lib/bureaucrat/markdown_writer.ex:138: anonymous fn/1 in Bureaucrat.MarkdownWriter.group_records/1

I tried debugging inside the lib but I'm none the wiser on how the 'private' values get set in this records collection.

Here's my env:

$ elixir -v
Erlang/OTP 19 [erts-8.0.2] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.3.3
$ mix deps | grep "*"
* connection 1.0.4 (Hex package) (mix)
* fs 0.9.1 (Hex package) (rebar)
* gettext 0.12.1 (Hex package) (mix)
* ranch 1.2.1 (Hex package) (rebar)
* poolboy 1.5.1 (Hex package) (rebar)
* decimal 1.2.0 (Hex package) (mix)
* poison 2.2.0 (Hex package) (mix)
* db_connection 1.0.0 (Hex package) (mix)
* cowlib 1.0.2 (Hex package) (rebar)
* cowboy 1.0.4 (Hex package) (rebar)
* mime 1.0.1 (Hex package) (mix)
* plug 1.2.2 (Hex package) (mix)
* bureaucrat 0.1.4 (Hex package) (mix)
* phoenix_html 2.6.2 (Hex package) (mix)
* phoenix 1.1.6 (Hex package) (mix)
* phoenix_live_reload 1.0.5 (Hex package) (mix)
* postgrex 0.11.2 (Hex package) (mix)
* ecto 1.1.9 (Hex package) (mix)
* phoenix_ecto 2.0.2 (Hex package) (mix)

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.