View Source Icon.Schema behaviour (ICON 2.0 SDK v0.1.0)
This module defines a schema.
Schemas serve the purpose of validating both requests and responses. The idea is to have a map defining the types and validations for our JSON payloads.
Defining Schemas
A schema can be either anonymous or not. For non-anonymous schemas, we need to
use
this module and define a schema using defschema/1
e.g. the following is
an (incomplete) transaction.
defmodule Transaction do
use Icon.Schema
defschema(%{
from: {:eoa_address, required: true},
to: {:address, required: true},
value: {:loop, default: 0}
})
end
As seen in the previous example, the values change depending on the types and options each key has. The available primitive types are:
:address
(same asIcon.Schema.Types.Address
).:any
(does nothing with the data).:binary_data
(same asIcon.Schema.Types.BinaryData
).:boolean
(same asIcon.Schema.Types.Boolean
).:eoa_address
(same asIcon.Schema.Types.EOA
).:error
(same asIcon.Schema.Error
).:event_log
(same asIcon.Schema.Types.EventLog
).:hash
(same asIcon.Schema.Types.Hash
).:integer
(same asIcon.Schema.Types.Integer
).:loop
(same asIcon.Schema.Types.Loop
).:score_address
(same asIcon.Schema.Types.SCORE
).:signature
(same asIcon.Schema.Types.Signature
).:string
(same asIcon.Schema.Types.String
).:timestamp
(same asIcon.Schema.Types.Timestamp
).enum([atom()])
(same as{:enum, [atom()]}
).
Then we have complex types:
- Anonymous schema:
t()
. - A homogeneous list of type
t
:list(t)
. - Any of the types listed in the list:
any([{atom(), t}], atom())
. The first atom is the value of the second atom in the params.
Additionally, we can implement our own primite types and named schemas with
the Icon.Schema.Type
behaviour and this behaviour respectively. The
module name should be used as the actual type.
The available options are the following:
default
- Default value for the key. It can be a closure for late binding.required
- Whether the key is required or not.field
- Name of the key to check to choose the rightany()
type. This value should be anatom()
, so it'll probably come from anenum()
type.
Note:
nil
and""
are considered empty values. They will be ignored for not mandatory keys and will add errors for mandatory keys.
Schema Caching
When a schema is generated with the function generate/1
, it is also cached
as a :persistent_term
in order to avoid generating the same thing twice.
This makes the first schema generation slower, but accessing the generated
schema should be then quite fast.
Schema Struct
When defining a schema with use Icon.Schema
, we can use the apply/2
function to put the loaded data into the struct e.g. for the following schema:
defmodule Block do
use Icon.Schema
defschema(%{
height: :integer,
hash: :hash
transactions: list(Transaction)
})
end
we can then validate a payload with the following:
payload = %{
"height" => "0x2a",
"hash" => "c71303ef8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238",
"transactions" => [
%{
"from" => "hxbe258ceb872e08851f1f59694dac2558708ece11",
"to" => "cxb0776ee37f5b45bfaea8cff1d8232fbb6122ec32",
"value" => "0x2a"
}
]
}
%Block{
height: 42,
hash: "0xc71303ef8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238",
transactions: [
%Transaction{
from: "hxbe258ceb872e08851f1f59694dac2558708ece11",
to: "cxb0776ee37f5b45bfaea8cff1d8232fbb6122ec32",
value: 42
}
]
} =
Block
|> Icon.Schema.generate()
|> Icon.Schema.new(payload)
|> Icon.Schema.load()
|> Icon.Schema.apply(into: Block)
Link to this section Summary
Functions
Schema state.
Uses the schema behaviour.
Generates a union of types.
Applies schema state
.
Generates a schema and its struct.
Dumps data from a schema state.
Generates an enum type.
Generates a full type_or_schema
, given a schema definition. It caches the
generated schema, to avoid regenarating the same every time.
Generates a list of types.
Loads data from a schema state.
Generates a new schema state.
Link to this section Types
Specs
external_type() :: internal_type() | {:list, external_type()} | {:any, [{atom(), external_type()}], atom()} | {:enum, [atom()]} | :address | :any | :binary_data | :boolean | :eoa_address | :error | :event_log | :hash | :integer | :loop | :score_address | :signature | :string | :timestamp
External types.
Specs
internal_type() :: {:list, internal_type()} | {:any, [{atom(), internal_type()}], atom()} | {:enum, [atom()]} | t() | Icon.Schema.Error | Icon.Schema.Types.Address | Icon.Schema.Types.Any | Icon.Schema.Types.BinaryData | Icon.Schema.Types.Boolean | Icon.Schema.Types.EOA | Icon.Schema.Types.EventLog | Icon.Schema.Types.Hash | Icon.Schema.Types.Integer | Icon.Schema.Types.Loop | Icon.Schema.Types.SCORE | Icon.Schema.Types.Signature | Icon.Schema.Types.String | Icon.Schema.Types.Timestamp | module()
Internal types.
Specs
state() :: %Icon.Schema{ data: map(), errors: map(), is_valid?: boolean(), params: map(), schema: t() }
Schema state.
Specs
Schema.
Specs
type() :: external_type() | {external_type(), keyword()}
Type.
Link to this section Callbacks
Specs
init() :: t()
Callback for defining a schema.
Link to this section Functions
Schema state.
Specs
Uses the schema behaviour.
Specs
any([{atom(), external_type()}], atom()) :: {:any, [{atom(), external_type()}], atom()}
Generates a union of types.
Specs
apply( state(), keyword() ) :: {:ok, map()} | {:error, Icon.Schema.Error.t()}
Applies schema state
.
Specs
Generates a schema and its struct.
Specs
Dumps data from a schema state.
Specs
Generates an enum type.
Specs
Generates a full type_or_schema
, given a schema definition. It caches the
generated schema, to avoid regenarating the same every time.
Specs
list(external_type()) :: {:list, external_type()}
Generates a list of types.
Specs
Loads data from a schema state.
Specs
Generates a new schema state.