Skip to content

Commit

Permalink
Auto-define open_api_operation/1 for each operation spec
Browse files Browse the repository at this point in the history
  • Loading branch information
moxley committed Aug 25, 2020
1 parent b2f19fd commit 4e1e1e9
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 32 deletions.
22 changes: 20 additions & 2 deletions lib/open_api_spex/operation_dsl.ex
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
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) || []
end
end
end

@doc false
def operation_def(action, spec) do
quote do
if !Module.get_attribute(__MODULE__, :operation_defined) do
Expand All @@ -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)
Expand All @@ -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
72 changes: 42 additions & 30 deletions test/operation_dsl_test.exs
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 4e1e1e9

Please sign in to comment.