Packages
eview
0.3.0
0.16.0
retired
0.15.0
0.14.0
0.13.0
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.1
0.11.0
0.10.8
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.9
0.2.7
0.2.6
0.2.5
0.2.3
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
Plug that converts response to Nebo #15 API spec format.
Current section
Files
Jump to
Current section
Files
lib/eview/helpers/changeset_validations_parser.ex
if Code.ensure_loaded?(Ecto) do
defmodule EView.Helpers.ChangesetValidationsParser do
@moduledoc """
This module converts changeset to a error structure described in API Manifest.
"""
@enty_type "json_data_property"
@jsonpath_root "$"
@jsonpath_joiner "."
def changeset_to_rules(%Ecto.Changeset{} = changeset) do
changeset
|> Ecto.Changeset.traverse_errors(&construct_rule/3)
|> Enum.flat_map(&errors_flatener(&1, @jsonpath_root))
end
defp construct_rule(%Ecto.Changeset{validations: validations}, field, {message, opts}) do
validation_name = opts[:validation]
# Special case for cast validation that stores type in field that dont match validation name
validations = if validation_name == :cast,
do: put_cast_validation(field, validations, opts),
else: validations
# Special case for metadata validator that can't modify to changeset validations
validations = if validation_name == :length,
do: put_length_validation(field, validations, opts),
else: validations
%{
description: get_rule_description(message, opts),
rule: opts[:validation],
params: reduce_rule_params(field, validation_name, validations)
}
end
defp put_cast_validation(field, validations, opts) do
[{field, {:cast, opts[:type]}} | validations]
end
defp put_length_validation(field, validations, opts) do
validation = Keyword.take(opts, [:min, :max, :is])
[{field, {:length, validation}} | validations]
end
defp get_rule_description(message, opts) do
Enum.reduce(opts, message, fn
# Lists
{key, value}, acc when is_list(value) ->
String.replace(acc, "%{#{key}}", Enum.join(value, ", "))
# Everything else is a string
{key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end
defp reduce_rule_params(field, validation_name, validations) do
validations
|> Keyword.get_values(field)
|> Enum.reduce([], fn
# Validation with keywords
{^validation_name, [h | _] = keyword}, acc when is_tuple(h) ->
keyword ++ acc
# With list
{^validation_name, list}, acc when is_list(list) ->
list ++ acc
# With regex pattern
{^validation_name, %Regex{} = regex}, acc ->
[inspect(regex) | acc]
# Or at least parseable
{^validation_name, rule_description}, acc ->
[rule_description | acc]
# Skip rest
_, acc ->
acc
end)
|> Enum.uniq
end
# Recursively flatten errors map
defp errors_flatener({field, rules}, prefix) when is_list(rules) do
[%{
entry_type: @enty_type,
entry: prefix <> @jsonpath_joiner <> to_string(field),
rules: rules
}]
end
defp errors_flatener({field, %{} = nested_errors}, prefix) do
Enum.flat_map(nested_errors, &errors_flatener(&1, prefix <> @jsonpath_joiner <> to_string(field)))
end
end
end