Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cast x-validate when decoded schema #647

Merged
merged 2 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib/open_api_spex/cast.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,12 @@ defmodule OpenApiSpex.Cast do
@spec cast(t()) :: {:ok, term()} | {:error, [Error.t()]}

# Custom validator
def cast(%__MODULE__{schema: %{"x-validate": module}} = ctx) when module != nil,
do: module.cast(ctx)
def cast(%__MODULE__{schema: %{"x-validate": module}} = ctx)
when is_atom(module) and module != nil,
do: module.cast(ctx)

def cast(%__MODULE__{schema: %{"x-validate": module}} = ctx) when is_binary(module),
do: module |> Elixir.String.split(".") |> Module.concat() |> then(& &1.cast(ctx))

# nil schema
def cast(%__MODULE__{value: value, schema: nil}),
Expand Down
51 changes: 33 additions & 18 deletions test/cast_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ defmodule OpenApiSpec.CastTest do
def cast(ctx), do: Cast.cast(ctx)

describe "cast/1" do
defmodule CustomValidator.EvenInt do
require OpenApiSpex

alias OpenApiSpex.Cast

OpenApiSpex.schema(%{
description: "An even integer",
type: :integer,
"x-validate": __MODULE__
})

def cast(context = %Cast{value: value}) when is_integer(value) and rem(value, 2) == 0,
do: Cast.ok(context)

def cast(context), do: Cast.error(context, {:custom, "Must be an even integer"})
end

test "unknown schema type" do
assert {:error, [error]} = cast(value: "string", schema: %Schema{type: :nope})
assert error.reason == :invalid_schema_type
Expand Down Expand Up @@ -223,24 +240,7 @@ defmodule OpenApiSpec.CastTest do
end

test "cast custom error with custom validator" do
defmodule EvenInt do
require OpenApiSpex

alias OpenApiSpex.Cast

OpenApiSpex.schema(%{
description: "An even integer",
type: :integer,
"x-validate": __MODULE__
})

def cast(context = %Cast{value: value}) when is_integer(value) and rem(value, 2) == 0,
do: Cast.ok(context)

def cast(context), do: Cast.error(context, {:custom, "Must be an even integer"})
end

schema = %Schema{type: :object, properties: %{even_number: EvenInt.schema()}}
schema = %Schema{type: :object, properties: %{even_number: CustomValidator.EvenInt.schema()}}

assert {:error, errors} = cast(value: %{"even_number" => 1}, schema: schema)
assert [error] = errors
Expand All @@ -250,6 +250,21 @@ defmodule OpenApiSpec.CastTest do
assert Error.message_with_path(error) == "#/even_number: Must be an even integer"
end

test "cast with custom validator from decoded schema" do
spec =
"./test/support/encoded_schema.json"
|> File.read!()
|> Jason.decode!()
|> OpenApiSpex.OpenApi.Decode.decode()

%{
components: %{schemas: %{"CustomValidationDecoded" => custom_validation_schema}}
} = spec

assert {:ok, %{even_num: 2}} =
cast(value: %{"even_num" => 2}, schema: custom_validation_schema)
end

test "nil value with xxxOf" do
schema = %Schema{anyOf: [%Schema{nullable: true, type: :string}]}
assert {:ok, nil} = cast(value: nil, schema: schema)
Expand Down
9 changes: 9 additions & 0 deletions test/support/encoded_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,15 @@
"not": {
"type": "string"
}
},
"CustomValidationDecoded": {
"type": "object",
"properties": {
"even_num": {
"type": "integer",
"x-validate": "OpenApiSpec.CastTest.CustomValidator.EvenInt"
}
}
}
},
"links": {
Expand Down
Loading