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

Support anonymous functions as custom validator function #87

Merged
merged 2 commits into from
Nov 15, 2023
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
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ ExJsonSchema.Schema.resolve(%{"format" => "custom"}, custom_format_validator: {M

The configured function is called with the arguments `(format, data)` and is expected to return `true`, `false` or a `{:error, %Error.Format{expected: "something"}}` tuple, depending whether the data is valid for the given format. For compatibility with JSON schema, it is expected to return `true` when the format is unknown by your callback function.

The custom function can also be an anonymous function:

```elixir
ExJsonSchema.Schema.resolve(%{"format" => "custom"}, custom_format_validator: fn format, data -> true end)
```

> Note that the anonymous function version of the custom validator is only available in the option to Schema.resolve/2 and not as Application config, as using anonymous functions as configuration is not allowed.

[format-spec]: https://json-schema.org/understanding-json-schema/reference/string.html#format

## License
Expand All @@ -180,5 +188,5 @@ Released under the [MIT license](https://github.com/jonasschmidt/ex_json_schema/

## TODO

* Add some source code documentation
* Enable providing JSON for known schemata at resolve time
- Add some source code documentation
- Enable providing JSON for known schemata at resolve time
2 changes: 1 addition & 1 deletion lib/ex_json_schema/schema/root.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ defmodule ExJsonSchema.Schema.Root do
location: :root | String.t(),
definitions: %{String.t() => ExJsonSchema.Schema.resolved()},
version: non_neg_integer | nil,
custom_format_validator: {module(), atom()} | nil
custom_format_validator: {module(), atom()} | (String.t(), any() -> boolean | {:error, any()}) | nil
}
end
12 changes: 10 additions & 2 deletions lib/ex_json_schema/validator/format.ex
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,16 @@ defmodule ExJsonSchema.Validator.Format do
validate_with_custom_validator(validator, format, data)
end

defp validate_with_custom_validator({mod, fun}, format, data) do
case apply(mod, fun, [format, data]) do
defp do_validate(%Root{custom_format_validator: validator}, format, data) when is_function(validator) do
validate_with_custom_validator(validator, format, data)
end

defp validate_with_custom_validator(validator, format, data) do
result = case validator do
{mod, fun} -> apply(mod, fun, [format, data])
fun when is_function(fun, 2) -> fun.(format, data)
end
case result do
true -> []
false -> [%Error{error: %Error.Format{expected: format}}]
{:error, error} -> [%Error{error: error}]
Expand Down
25 changes: 25 additions & 0 deletions test/ex_json_schema/validator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,31 @@ defmodule ExJsonSchema.ValidatorTest do
)
end

test "configuring an anonymous function as custom format validator" do
anonymous_validation_fn = fn "myformat", data ->
data == "mydata"
end

schema =
Schema.resolve(
%{
"properties" => %{
"error" => %{"format" => "myformat"}
}
},
custom_format_validator: anonymous_validation_fn
)

assert :ok = validate(schema, %{"error" => "mydata"})

assert_validation_errors(
schema,
%{"error" => ""},
[{"Expected to be a valid myformat.", "#/error"}],
[%Error{error: %Error.Format{expected: "myformat"}, path: "#/error"}]
)
end

test "passing the formatter as an option" do
assert :ok = validate(%{"type" => "string"}, "foo", error_formatter: Error.StringFormatter)

Expand Down