Magik.Contract (Magik v0.10.0) View Source
Contract
helps to define a contract for function call, and do validate contract data with:
- Validate type
- Validate required
- Validate
in
|not_in
enum - Valiate length for
string
,enumerable
- Validate number
- Validate string against regex pattern
- Custom validation function
- With support nested type
- Clean not allowed fields
@update_user_contract %{
user: [type: User, required: true],
attributes: [type: %{
email: [type: :string],
status: [type: :string, in: ~w(active in_active)]
age: [type: :integer, number: [min: 10, max: 80]],
}, required: true]
}
def update_user(contract) do
with {:ok, validated_data} do
validated_data.user
|> Ecto.Changeset.change(validated_data.attributes)
|> Repo.update
else
{:error, errors} -> IO.inspect(errors)
end
end
NOTES: Contract only validate data, not cast data
Support validation
Type
Support built-in types;
boolean
integer
,float
number
- string or integerstring
tuple
map
array
list
atom
function
keyword
struct
array
of type
Example:
Magik.Contract.validate(%{name: "Bluz"}, %{name: [type: :string]})
Magik.Contract.validate(%{id: 123}, %{name: [type: :integer]})
Magik.Contract.validate(%{id: 123}, %{name: [type: {:array, :integer}]})
Magik.Contract.validate(%{user: %User{}}, %{user: [type: User]})
Magik.Contract.validate(%{user: %User{}}, %{user: [type: {:array: User}]})
Required
Magik.Contract.validate(%{name: "Bluz"}, %{name: [type: :string, required: true]})
Allow nil
Magik.Contract.validate(
%{name: "Bluz", email: nil},
%{
name: [type: :string],
email: [type: string, allow_nil: false]
})
Inclusion/Exclusion
Magik.Contract.validate(
%{status: "active"},
%{status: [type: :string, in: ~w(active in_active)]}
)
Magik.Contract.validate(
%{status: "active"},
%{status: [type: :string, not_in: ~w(banned locked)]}
)
Format
Validate string against regex pattern
Magik.Contract.validate(
%{email: "[email protected]"},
%{name: [type: :string, format: ~r/.+?@.+.com/]
})
Number
Validate number value
Magik.Contract.validate(
%{age: 200},
%{age: [type: :integer, number[greater_than: 0, less_than: 100]]
})
Support conditions
equal_to
greater_than_or_equal_to
|min
greater_than
less_than
less_than_or_equal_to
|max
Length
Check length of list
, map
, string
, keyword
, tuple
Supported condtions are the same with Number check
Magik.Contract.validate(
%{title: "Hello world"},
%{age: [type: :string, length: [min: 10, max: 100]]
})
Custom validation function
Invoke given function to validate value. The function signature must be
func(field_name ::(String.t() | atom()), value :: any(), all_params :: map()) :: :ok | {:error, message}
Magik.Contract.validate(
%{email: "[email protected]"},
%{email: [type: :string, func: &validate_email/3]})
def validate_email(_name, email, _params) do
if Regex.match?(~r/[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,4}$/, email) do
:ok
else
{:error, "not a valid email"}
end
end
Nested map
Nested map declaration is the same.
data = %{name: "Doe John", address: %{city: "HCM", street: "NVL"} }
schema = %{
name: [type: :string],
address: [type: %{
city: [type: :string],
street: [type: :string]
}]
}
Magik.Contract.validate(data, schema)
Nested list
data = %{name: "Doe John", address: [%{city: "HCM", street: "NVL"}] }
address = %{ city: [type: :string], street: [type: :string] }
schema = %{
name: [type: :string],
address: [type: {:array, address}]
}
Magik.Contract.validate(data, schema)
Link to this section Summary
Functions
Validate data against given schema
Link to this section Functions
Specs
Validate data against given schema