Skip to content

Commit cd50619

Browse files
committed
Validate keys given to operation/2 macro
1 parent bc1606b commit cd50619

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

lib/open_api_spex/controller_specs.ex

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,35 @@ defmodule OpenApiSpex.ControllerSpecs do
375375
def operation_spec(_module, _action, false = _spec), do: nil
376376

377377
def operation_spec(module, action, spec) do
378+
allowed_keys = [
379+
:callbacks,
380+
:description,
381+
:deprecated,
382+
:external_docs,
383+
:operation_id,
384+
:parameters,
385+
:request_body,
386+
:responses,
387+
:security,
388+
:summary,
389+
:tags
390+
]
391+
392+
validation_result =
393+
spec
394+
|> Enum.reject(fn {key, _val} -> extension_key?(key) end)
395+
|> Keyword.validate(allowed_keys)
396+
397+
case validation_result do
398+
{:ok, _spec} ->
399+
:ok
400+
401+
{:error, unknown_keys} ->
402+
raise ArgumentError,
403+
"Unknown keys given to operation/2: #{inspect(unknown_keys)}. " <>
404+
"Allowed keys are: #{inspect(allowed_keys)}, and keys starting with 'x-'."
405+
end
406+
378407
spec = Map.new(spec)
379408
shared_tags = Module.get_attribute(module, :shared_tags, []) |> List.flatten()
380409

@@ -386,9 +415,7 @@ defmodule OpenApiSpex.ControllerSpecs do
386415

387416
extensions =
388417
spec
389-
|> Enum.filter(fn {key, _val} ->
390-
is_atom(key) && String.starts_with?(to_string(key), "x-")
391-
end)
418+
|> Enum.filter(fn {key, _val} -> extension_key?(key) end)
392419
|> Map.new(fn {key, value} -> {to_string(key), value} end)
393420

394421
%Operation{
@@ -406,4 +433,8 @@ defmodule OpenApiSpex.ControllerSpecs do
406433
extensions: extensions
407434
}
408435
end
436+
437+
defp extension_key?(key) do
438+
is_atom(key) && String.starts_with?(to_string(key), "x-")
439+
end
409440
end

test/controller_specs_test.exs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ defmodule OpenApiSpex.ControllerSpecsTest do
77
alias OpenApiSpexTest.DslController
88
alias OpenApiSpexTest.DslControllerOperationStructs
99

10-
describe "operation/1" do
10+
describe "operation/2" do
1111
test "supports :parameters" do
1212
assert %OpenApiSpex.Operation{
1313
responses: %{},
@@ -157,5 +157,39 @@ defmodule OpenApiSpex.ControllerSpecsTest do
157157
assert %OpenApiSpex.Operation{extensions: %{"x-foo" => "bar"}} =
158158
DslController.open_api_operation(:index)
159159
end
160+
161+
test "raises when unknown key is provided" do
162+
msg =
163+
"Unknown keys given to operation/2: [:unknown]. Allowed keys are: " <>
164+
"[:callbacks, :description, :deprecated, :external_docs, :operation_id, :parameters, " <>
165+
":request_body, :responses, :security, :summary, :tags], and keys starting with 'x-'."
166+
167+
assert_raise ArgumentError, msg, fn ->
168+
Code.eval_string("""
169+
defmodule TestController do
170+
use OpenApiSpex.ControllerSpecs
171+
172+
operation :index,
173+
summary: "Users index",
174+
parameters: [
175+
username: [
176+
in: :query,
177+
description: "Filter by username",
178+
type: :string
179+
]
180+
],
181+
responses: [
182+
ok: {"Users index response", "application/json", UsersIndexResponse}
183+
],
184+
unknown: "value",
185+
"x-foo": "bar"
186+
187+
def index(conn, _) do
188+
json(conn, [])
189+
end
190+
end
191+
""")
192+
end
193+
end
160194
end
161195
end

0 commit comments

Comments
 (0)