From 43cf71bbaef38a271a384207a10086f328482203 Mon Sep 17 00:00:00 2001 From: Moxley Stratton Date: Sun, 3 May 2020 17:33:32 -0700 Subject: [PATCH 1/3] More informative exception when docs are missing for controller action Before: ``` ** (MatchError) no match of right hand side value: nil ``` After: ``` ** (RuntimeError) No docs found for function Elixir.SppWeb.Api.MessageController.indexb/2 ``` --- lib/open_api_spex/controller.ex | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/open_api_spex/controller.ex b/lib/open_api_spex/controller.ex index bf9389f9..4420c71b 100644 --- a/lib/open_api_spex/controller.ex +++ b/lib/open_api_spex/controller.ex @@ -126,21 +126,18 @@ defmodule OpenApiSpex.Controller do defp get_docs(module, name) do {:docs_v1, _anno, _lang, _format, _module_doc, mod_meta, mod_docs} = Code.fetch_docs(module) - {_, _, _, docs, meta} = + doc_for_function = Enum.find(mod_docs, fn {{:function, ^name, _}, _, _, _, _} -> true _ -> false end) - if docs == :none do - :error - else - docs = Map.get(docs, "en", "") - - [summary | _] = String.split(docs, ~r/\n\s*\n/, parts: 2) + doc_for_function || raise "No docs found for function #{module}.#{name}/2" + {_, _, _, docs, meta} = doc_for_function + docs = Map.get(docs, "en", "") + [summary | _] = String.split(docs, ~r/\n\s*\n/, parts: 2) - {:ok, {mod_meta, summary, docs, meta}} - end + {:ok, {mod_meta, summary, docs, meta}} end defp build_operation_id(meta, mod, name) do From 525162f3882c7483b1298c04453425c527cc55bd Mon Sep 17 00:00:00 2001 From: Moxley Stratton Date: Sun, 3 May 2020 17:46:00 -0700 Subject: [PATCH 2/3] Allow response spec to have 4th "opts" argument The shape of a response in the `@doc responses:` keyword-list/map was: ```elixir {http_status, {description, mime_type, schema}} ``` A fourth element of the inner tuple was previously accepted (maybe it ignored?), but a recent revision of OpenApiSpex made it a raised exception: ``` ** (FunctionClauseError) no function clause matching in anonymous fn/1 in OpenApiSpex.Controller.build_responses/1 ``` This commit change passes an optional forth element to the `opts` argument of `Operation.response/4`. --- lib/open_api_spex/controller.ex | 16 ++++++++++------ test/support/user_controller_annotated.ex | 12 +++++++++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/open_api_spex/controller.ex b/lib/open_api_spex/controller.ex index 4420c71b..72092b10 100644 --- a/lib/open_api_spex/controller.ex +++ b/lib/open_api_spex/controller.ex @@ -66,12 +66,12 @@ defmodule OpenApiSpex.Controller do ## Example ``` - defmodule FooController do + defmodule UserController do + @moduledoc tags: ["Users"] + use MyAppWeb, :controller use #{inspect(__MODULE__)} - @moduledoc tags: ["Foos"] - @doc """ Endpoint summary @@ -80,12 +80,13 @@ defmodule OpenApiSpex.Controller do @doc parameters: [ id: [in: :path, type: :string, required: true] ], - request_body: {"Request body to update Foo", "application/json", FooUpdateBody, required: true}, + request_body: {"Request body to update User", "application/json", UserUpdateBody, required: true}, responses: [ - ok: {"Foo document", "application/json", FooSchema} + ok: {"User document", "application/json", UserSchema}, + {302, "Redirect", "text/html", EmptyResponse, headers: %{"Location" => %Header{description: "Redirect Location"}}} ] def update(conn, %{id: id}) do - foo_params = conn.body_params + user_params = conn.body_params # … end end @@ -161,6 +162,9 @@ defmodule OpenApiSpex.Controller do {status, {description, mime, schema}} -> {Plug.Conn.Status.code(status), Operation.response(description, mime, schema)} + {status, {description, mime, schema, opts}} -> + {Plug.Conn.Status.code(status), Operation.response(description, mime, schema, opts)} + {status, %Response{} = response} -> {Plug.Conn.Status.code(status), response} end) diff --git a/test/support/user_controller_annotated.ex b/test/support/user_controller_annotated.ex index d90aa9ac..f8fc13ff 100644 --- a/test/support/user_controller_annotated.ex +++ b/test/support/user_controller_annotated.ex @@ -1,9 +1,10 @@ defmodule OpenApiSpexTest.UserControllerAnnotated do + @moduledoc tags: ["User"] + use OpenApiSpex.Controller + alias OpenApiSpex.Header alias OpenApiSpexTest.Schemas.{NotFound, Unauthorized, User} - @moduledoc tags: ["User"] - @doc """ Update a user @@ -14,7 +15,12 @@ defmodule OpenApiSpexTest.UserControllerAnnotated do ] @doc request_body: {"Request body to update a User", "application/json", User, required: true} @doc responses: [ - ok: {"User response", "application/json", User}, + ok: { + "User response", + "application/json", + User, + headers: %{"token" => %Header{description: "Access token"}} + }, unauthorized: Unauthorized.response(), not_found: NotFound.response() ] From 2aa164140d7322a28f724d3ffe666c6dacdd8eb7 Mon Sep 17 00:00:00 2001 From: Moxley Stratton Date: Fri, 8 May 2020 08:33:25 -0700 Subject: [PATCH 3/3] Return nil with warning instead. --- lib/open_api_spex/controller.ex | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/open_api_spex/controller.ex b/lib/open_api_spex/controller.ex index 72092b10..7583ebb2 100644 --- a/lib/open_api_spex/controller.ex +++ b/lib/open_api_spex/controller.ex @@ -133,12 +133,16 @@ defmodule OpenApiSpex.Controller do _ -> false end) - doc_for_function || raise "No docs found for function #{module}.#{name}/2" - {_, _, _, docs, meta} = doc_for_function - docs = Map.get(docs, "en", "") - [summary | _] = String.split(docs, ~r/\n\s*\n/, parts: 2) + if doc_for_function do + {_, _, _, docs, meta} = doc_for_function + docs = Map.get(docs, "en", "") + [summary | _] = String.split(docs, ~r/\n\s*\n/, parts: 2) - {:ok, {mod_meta, summary, docs, meta}} + {:ok, {mod_meta, summary, docs, meta}} + else + IO.warn("No docs found for function #{module}.#{name}/2") + nil + end end defp build_operation_id(meta, mod, name) do