diff --git a/lib/open_api_spex/operation_dsl.ex b/lib/open_api_spex/operation_dsl.ex index e2c81dc7..3f36bae2 100644 --- a/lib/open_api_spex/operation_dsl.ex +++ b/lib/open_api_spex/operation_dsl.ex @@ -1,17 +1,27 @@ defmodule OpenApiSpex.OperationDsl do alias OpenApiSpex.OperationBuilder + @doc """ + Defines an Operation spec in a controller. + """ defmacro operation(action, spec) do operation_def(action, spec) end + @doc """ + Defines a list of tags that all operations in a controller will share. + """ defmacro tags(tags) do tags_def(tags) end + @doc false defmacro before_compile(_env) do quote do - def spec_attributes, do: @spec_attributes + def open_api_operation(action) do + module_name = __MODULE__ |> to_string() |> String.replace_leading("Elixir.", "") + IO.warn("No operation spec defined for controller action #{module_name}.#{action}") + end def controller_tags do Module.get_attribute(__MODULE__, :controller_tags) || [] @@ -19,6 +29,7 @@ defmodule OpenApiSpex.OperationDsl do end end + @doc false def operation_def(action, spec) do quote do if !Module.get_attribute(__MODULE__, :operation_defined) do @@ -30,15 +41,21 @@ defmodule OpenApiSpex.OperationDsl do end @spec_attributes {unquote(action), operation_spec(__MODULE__, unquote(spec))} + + def open_api_operation(unquote(action)) do + @spec_attributes[unquote(action)] + end end end + @doc false def tags_def(tags) do quote do @controller_tags unquote(tags) end end + @doc false def operation_spec(module, spec) do spec = Map.new(spec) tags = spec[:tags] || Module.get_attribute(module, :controller_tags) @@ -51,7 +68,8 @@ defmodule OpenApiSpex.OperationDsl do spec = Map.delete(spec, :parameters) - struct!(OpenApiSpex.Operation, initial_attrs) + OpenApiSpex.Operation + |> struct!(initial_attrs) |> struct!(spec) end end diff --git a/test/operation_dsl_test.exs b/test/operation_dsl_test.exs index 18422aec..d5c4f7c2 100644 --- a/test/operation_dsl_test.exs +++ b/test/operation_dsl_test.exs @@ -1,44 +1,56 @@ defmodule OpenApiSpex.OperationDslTest do use ExUnit.Case, async: true + import ExUnit.CaptureIO + alias OpenApiSpexTest.DslController - test "operation/1" do - assert [ - show: %OpenApiSpex.Operation{ + describe "operation/1" do + test "defines open_api_operation/1 for :show action" do + assert %OpenApiSpex.Operation{ responses: [], summary: "Show user", parameters: show_parameters, tags: show_tags - }, - index: %OpenApiSpex.Operation{ + } = DslController.open_api_operation(:show) + + assert show_tags == ["users"] + + assert [ + %OpenApiSpex.Parameter{ + description: "User ID", + example: 1001, + in: :path, + name: :id, + required: true, + schema: %OpenApiSpex.Schema{type: :integer} + } + ] = show_parameters + end + + test "defines open_api_operation/1 for :index action" do + assert %OpenApiSpex.Operation{ responses: [], summary: "User index", parameters: index_parameters - } - ] = DslController.spec_attributes() - - assert show_tags == ["users"] - - assert [ - %OpenApiSpex.Parameter{ - description: "User ID", - example: 1001, - in: :path, - name: :id, - required: true, - schema: %OpenApiSpex.Schema{type: :integer} - } - ] = show_parameters - - assert [ - %OpenApiSpex.Parameter{ - description: "Free-form query string", - example: "jane", - in: :query, - name: :query, - schema: %OpenApiSpex.Schema{type: :string} - } - ] = index_parameters + } = DslController.open_api_operation(:index) + + assert [ + %OpenApiSpex.Parameter{ + description: "Free-form query string", + example: "jane", + in: :query, + name: :query, + schema: %OpenApiSpex.Schema{type: :string} + } + ] = index_parameters + end + + test "outputs warning when action not defined for called open_api_operation" do + output = capture_io(:stderr, fn -> DslController.open_api_operation(:undefined) end) + + assert output =~ + ~r/warning:.*No operation spec defined for controller action OpenApiSpexTest.DslController.undefined/ + end end end