Current section

Files

Jump to
nex_json_schema lib ex_json_schema validator properties.ex
Raw

lib/ex_json_schema/validator/properties.ex

defmodule NExJsonSchema.Validator.Properties do
alias NExJsonSchema.Schema
alias NExJsonSchema.Validator
@spec validate(Root.t(), Schema.resolved(), NExJsonSchema.json()) :: Validator.errors_with_list_paths()
def validate(root, schema, properties = %{}) do
validated_known_properties = validate_known_properties(root, schema, properties)
validation_errors(validated_known_properties) ++
validate_additional_properties(
root,
schema["additionalProperties"],
unvalidated_properties(properties, validated_known_properties)
)
end
@spec validate(Root.t(), Schema.resolved(), NExJsonSchema.data()) :: []
def validate(_, _, _), do: []
defp validate_known_properties(root, schema, properties) do
validate_named_properties(root, schema["properties"], properties) ++
validate_pattern_properties(root, schema["patternProperties"], properties)
end
defp validate_named_properties(root, schema, properties) do
schema
|> Enum.filter(&Map.has_key?(properties, elem(&1, 0)))
|> Enum.map(fn {name, property_schema} ->
{name, Validator.validate(root, property_schema, properties[name], [name])}
end)
end
defp validate_pattern_properties(_, nil, _), do: []
defp validate_pattern_properties(root, schema, properties) do
Enum.flat_map(schema, &validate_pattern_property(root, &1, properties))
end
defp validate_pattern_property(root, {pattern, schema}, properties) do
properties_matching(properties, pattern)
|> Enum.map(fn {name, property} ->
{name, Validator.validate(root, schema, property, [name])}
end)
end
defp validate_additional_properties(root, schema, properties) when is_map(schema) do
Enum.flat_map(properties, fn {name, property} -> Validator.validate(root, schema, property, [name]) end)
end
defp validate_additional_properties(_, false, properties) when map_size(properties) > 0 do
Enum.map(properties, fn {name, _} ->
{Validator.format_error(
:schema,
"schema does not allow additional properties",
properties: properties
), [name]}
end)
end
defp validate_additional_properties(_, _, _), do: []
defp validation_errors(validated_properties) when is_map(validated_properties) do
validated_properties |> Map.values() |> List.flatten()
end
defp validation_errors(validated_properties) when is_list(validated_properties) do
validated_properties |> Keyword.values() |> List.flatten()
end
defp properties_matching(properties, pattern) do
regex = Regex.compile!(pattern)
Enum.filter(properties, &Regex.match?(regex, elem(&1, 0)))
end
defp unvalidated_properties(properties, validated_properties) do
unvalidated =
properties
|> keys_as_set()
|> MapSet.difference(keys_as_set(validated_properties))
|> Enum.to_list()
Map.take(properties, unvalidated)
end
defp keys_as_set(properties) when is_map(properties) do
properties |> Map.keys() |> Enum.into(MapSet.new())
end
defp keys_as_set(properties) when is_list(properties) do
properties |> Keyword.keys() |> Enum.into(MapSet.new())
end
end