Current section
Files
Jump to
Current section
Files
lib/helper/derive/derive.ex
defmodule MishkaDeveloperTools.Helper.Derive do
alias MishkaDeveloperTools.Helper.Derive.{Parser, SanitizerDerive, ValidationDerive}
@spec derive(
{:error, any(), any()}
| {:ok, any(), [binary()]}
| {:error, any(), any(), :halt}
| {:error, any(), :nested, list(), any(), [binary()]}
) :: {:ok, map()} | {:error, any(), any()}
def derive({:error, type, message, :halt}) do
{:error, type, message}
end
def derive({:error, _, :nested, builders_errors, data, derive_inputs}),
do: derive({:ok, data, derive_inputs}, builders_errors)
def derive({:error, _, _} = error), do: error
@spec derive({:ok, any(), list(String.t())}, list()) ::
{:ok, map()} | {:error, :bad_parameters, list()}
def derive({:ok, data, derive_inputs}, extra_error \\ []) do
reduced_fields =
Enum.reduce(derive_inputs, %{}, fn map, acc ->
derives = Parser.parser(map.derive)
field = Map.get(data, map.field)
hint = Map.get(map, :hint) || []
update_reduced_fields(field, derives, hint, map, acc)
end)
{:error, :bad_parameters, get_error} = error = error_handler(reduced_fields, extra_error)
if length(get_error) == 0, do: {:ok, Map.merge(data, reduced_fields)}, else: error
end
defp update_reduced_fields(nil, _parsed_derive, _hint, _map, acc), do: acc
defp update_reduced_fields(get_field, parsed_derive, hints, map, acc)
when is_list(parsed_derive) and parsed_derive != [] do
converted_validated_values =
Enum.zip([parsed_derive, get_field, hints])
|> Enum.map(fn {derive, value, hint} ->
derive = if(derive == [], do: nil, else: derive)
{all_data, validated_errors} =
{map.field, value}
|> SanitizerDerive.call(Map.get(derive || %{}, :sanitize))
|> ValidationDerive.call(Map.get(derive || %{}, :validate), hint)
if length(validated_errors) > 0, do: {:error, validated_errors}, else: all_data
end)
errors =
converted_validated_values
|> Enum.filter(&(is_tuple(&1) and elem(&1, 0) == :error))
|> Enum.map(fn {:error, errors} -> errors end)
|> Enum.concat()
Map.put(
acc,
map.field,
if(length(errors) > 0, do: {:error, errors}, else: converted_validated_values)
)
end
defp update_reduced_fields(get_field, parsed_derive, hint, map, acc) do
# destruct because we consider empty list default value when there is no derive
parsed_derive = if(parsed_derive == [], do: nil, else: parsed_derive)
{all_data, validated_errors} =
{map.field, get_field}
|> SanitizerDerive.call(Map.get(parsed_derive || %{}, :sanitize))
|> ValidationDerive.call(Map.get(parsed_derive || %{}, :validate), hint)
converted_validated_values =
if length(validated_errors) > 0, do: {:error, validated_errors}, else: all_data
Map.put(acc, map.field, converted_validated_values)
end
@spec error_handler(map(), list(any())) :: {:error, :bad_parameters, any()}
def error_handler(reduced_fields, extra_error \\ []) do
errors =
Enum.find(extra_error, fn %{field: _, errors: {type, _}} -> type == :required_fields end)
|> case do
nil ->
get_error =
reduced_fields
|> Map.values()
|> Enum.filter(&(is_tuple(&1) && elem(&1, 0) == :error))
|> Enum.map(fn {:error, errors} -> errors end)
|> Enum.concat()
|> halt_errors()
get_error ++ extra_error
_ ->
extra_error
end
{:error, :bad_parameters, errors}
end
defp halt_errors(errors_list) do
errors_list
|> Enum.reduce_while([], fn item, acc ->
if Map.get(item, :status) == :halt,
do: {:halt, acc ++ [Map.delete(item, :status)]},
else: {:cont, acc ++ [item]}
end)
end
@spec get_derives_from_success_conditional_data(list(any())) :: any()
@doc false
def get_derives_from_success_conditional_data(conds) do
Enum.reduce(conds, [], fn
{field, {{:ok, _data}, opts}}, acc ->
get_derive = Keyword.get(opts, :derive, [])
get_hint = Keyword.get(opts, :hint, [])
acc ++ [Map.new([{:derive, get_derive}, {:field, field}, {:hint, get_hint}])]
{field, values}, acc ->
%{derive: derives, hint: hints} =
Enum.reduce(values, %{derive: [], hint: []}, fn {{:ok, _value}, opts}, acc ->
get_derive = Keyword.get(opts, :derive, [])
get_hint = Keyword.get(opts, :hint, [])
Map.merge(acc, %{derive: acc.derive ++ [get_derive], hint: acc.hint ++ [get_hint]})
end)
acc ++ [Map.new([{:derive, derives}, {:field, field}, {:hint, hints}])]
end)
end
end