-
Notifications
You must be signed in to change notification settings - Fork 0
/
map_schema_validator.ex
executable file
·121 lines (93 loc) · 3.64 KB
/
map_schema_validator.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
defmodule MapSchemaValidator do
@moduledoc """
It's a map format verifier, verify if keys/values exist in a given map, short and quick, you can specify more than one
format and verify list of values.
Just use the function [`MapSchemaValidator.validate/2`](https://hexdocs.pm) or [`MapSchemaValidator.validate!/2`](https://hexdocs.pm)
"""
defmodule InvalidMapError do
defexception message: "default message"
end
alias MapSchemaValidator.MapUtils
alias MapSchemaValidator.ValueValidator
defp params(key) do
key_string = to_string(key)
mandatory =
key_string
|> String.contains?("?")
|> Kernel.!()
key_string = String.replace(key_string, "?", "")
{mandatory, String.to_atom(key_string)}
end
@spec iterate([atom()], map(), map(), [String.t()]) :: bool()
defp iterate([], _schema, _json, _steps), do: true
defp iterate([key | rest], schema, json, steps) do
{mandatory, key_core} = params(key)
schema_value = Map.get(schema, key)
json_value = Map.get(json, key_core)
exist_in_json? = Map.has_key?(json, key_core)
key_is_value? = ValueValidator.is_valid_value?(key)
next =
case {key_is_value?, exist_in_json?} do
{true, false} ->
json
|> Map.keys()
|> Enum.reduce(
true,
&(&2 and ValueValidator.validate_values(key, to_string(&1), steps) and
ValueValidator.validate_values(key, Map.get(json, &1), steps))
)
{_, true} ->
ValueValidator.validate_values(schema_value, json_value, steps ++ [key_core])
_ ->
!mandatory
end
if next do
iterate(rest, schema, json, steps)
else
raise InvalidMapError, message: "error at: #{Enum.join(steps ++ [key_core], " -> ")}"
end
end
def validate_json!(schema, json, steps \\ []) do
schema_keys = Map.keys(schema)
corrected_schema = MapUtils.map_to_atom_keys(schema)
corrected_json = MapUtils.map_to_atom_keys(json)
iterate(schema_keys, corrected_schema, corrected_json, steps)
end
@doc """
By param schema validates the param data and compares the format and values to ensure follows a specific format.
## Examples
iex> MapSchemaValidator.validate(%{key: :string}, %{key: "value"})
{:ok, nil}
iex> MapSchemaValidator.validate(%{key: [:string, :number]}, %{key: 1})
{:ok, nil}
iex> MapSchemaValidator.validate(%{key: [%{inner_key: :string}]}, %{key: [%{inner_key: "value_1"}, %{inner_key: "value_2"}]})
{:ok, nil}
iex> MapSchemaValidator.validate(%{key: [%{inner_key: :string}]}, %{key: [%{inner_key: 1}, %{inner_key: "value_2"}]})
{:error, %MapSchemaValidator.InvalidMapError{message: "error at: key -> inner_key"}}
"""
@spec validate(map(), map()) :: {:ok | :error, any()}
def validate(schema, json) do
validate_json!(schema, json)
{:ok, nil}
rescue
e in InvalidMapError ->
{:error, e}
end
@doc """
Same as MapSchemaValidator.validate/2 but raises in fail.
## Examples
iex> MapSchemaValidator.validate!(%{key: :string}, %{key: "value"})
:ok
iex> MapSchemaValidator.validate!(%{key: [:string, :number]}, %{key: 1})
:ok
iex> MapSchemaValidator.validate!(%{key: [%{inner_key: :string}]}, %{key: [%{inner_key: "value_1"}, %{inner_key: "value_2"}]})
:ok
iex> MapSchemaValidator.validate!(%{key: [%{inner_key: :string}]}, %{key: [%{inner_key: 1}, %{inner_key: "value_2"}]})
** (MapSchemaValidator.InvalidMapError) error at: key -> inner_key
"""
@spec validate!(map(), map()) :: :ok
def validate!(schema, json) do
validate_json!(schema, json)
:ok
end
end