ExParamsSchema (ex_params_schema v0.1.0)

Copy Markdown View Source

LiveView、Phoenix Controller、JSON API などで受け取る文字列中心の params を検証し、 型付き構造体へ変換するための DSL です。

use ExParamsSchema を指定したモジュールで defschema/1defschema/2 のブロック内に field/2field/3 を 使うと、構造体、t/0parse/1 が生成されます。

defmodule ExampleParams do
  use ExParamsSchema

  defschema do
    field :id, :integer, minimum: 1, maximum: 24
    field :value, :integer, minimum: 0, maximum: 255
  end
end

ExampleParams.parse(%{"id" => "1", "value" => "128"})
#=> {:ok, %ExampleParams{id: 1, value: 128}}

:boolean:date:datetime:integer:float:number:string:null:any、atom enum、 map、list を組み合わせられます。文字列は型変換してから JSON Schema Draft 7 による標準制約の 検証を行います。詳細は README と docs/ を参照してください。

Summary

Types

compile!/1 が返す、再利用可能なコンパイル済みスキーマです。

呼び出し側が任意に指定できるエラー理由です。

parse_detailed/1parse_detailed/2 が返す、フィールド単位の検証エラーです。

実行時に確定するフィールド値です。

Functions

params モジュールでスキーマ DSL を利用できるようにします。

スキーマ定義の map を実行用スキーマへコンパイルします。

スキーマを宣言します。

オプション付きで 1 フィールドを宣言します。

コンパイル済みスキーマまたは map 形式のフィールド定義から JSON Schema Draft 7 を返します。

コンパイル済みスキーマでパラメーターを検証・変換します。

コンパイル済みスキーマでパラメーターを検証・変換し、失敗時に全ての検証エラーを返します。

Types

array_field_type()

@type array_field_type() :: [definition()]

atom_enum_type()

@type atom_enum_type() :: {kind :: :enum, allowed_values :: [atom()]}

compiled_schema()

@opaque compiled_schema()

compile!/1 が返す、再利用可能なコンパイル済みスキーマです。

compiled_schema_definition()

@type compiled_schema_definition() ::
  {fields :: [ExParamsSchema.Definition.Field.t()],
   options :: ExParamsSchema.Schema.Options.t()}

custom_definition()

@type custom_definition() :: {module :: module(), options :: keyword()}

custom_field_type()

@type custom_field_type() ::
  {kind :: :custom, module :: module(), options :: keyword()}

definition()

@type definition() :: field_type() | definition_with_options() | custom_definition()

definition_with_options()

@type definition_with_options() ::
  {field_type :: field_type(), options :: field_options()}

detailed_parse_result()

@type detailed_parse_result() ::
  {:ok, params :: parsed_params()} | {:error, errors :: [validation_error()]}

error_reason()

@type error_reason() :: dynamic()

呼び出し側が任意に指定できるエラー理由です。

field()

@type field() ::
  {name :: field_name(), type :: field_type(), options :: field_options()}

field_name()

@type field_name() :: atom()

field_options()

@type field_options() :: [
  source: String.t() | atom(),
  in: [value()] | Range.t() | MapSet.t(),
  enum: [value()],
  minimum: number(),
  maximum: number(),
  min_length: non_neg_integer(),
  max_length: non_neg_integer(),
  pattern: String.t(),
  format: String.t(),
  min_items: non_neg_integer(),
  max_items: non_neg_integer(),
  unique_items: boolean(),
  json_schema: map() | boolean(),
  nullable: boolean(),
  optional: boolean(),
  strict: boolean(),
  default: value(),
  error: error_reason()
]

field_type()

object_field_type()

@type object_field_type() :: %{optional(field_name()) => definition()}

parse_result()

@type parse_result() ::
  {:ok, params :: parsed_params()} | {:error, reason :: error_reason()}

parsed_params()

@type parsed_params() :: map()

scalar_field_type()

@type scalar_field_type() ::
  :any
  | :boolean
  | :date
  | :datetime
  | :float
  | :integer
  | :null
  | :number
  | :string

schema_definition()

@type schema_definition() :: %{required(field_name()) => definition()}

schema_options()

@type schema_options() :: keyword()

validation_error()

@type validation_error() :: ExParamsSchema.ValidationError.t()

parse_detailed/1parse_detailed/2 が返す、フィールド単位の検証エラーです。

value()

@type value() :: dynamic()

実行時に確定するフィールド値です。

Functions

__using__(options)

(macro)

params モジュールでスキーマ DSL を利用できるようにします。

defschema/1defschema/2field/2field/3 を import します。コンパイル時に 宣言を検証し、構造体、t/0parse/1 を生成します。

compile!(definition, options \\ [])

スキーマ定義の map を実行用スキーマへコンパイルします。

iex> schema = ExParamsSchema.compile!(%{
...>   id: {:integer, source: "input-id", minimum: 1},
...>   enabled: {:boolean, default: false}
...> })
iex> ExParamsSchema.parse(%{"input-id" => "1", "enabled" => "true"}, schema)
{:ok, %{enabled: true, id: 1}}

生成したスキーマは parse/2 へ渡します。これは ExParamsSchema.Handler で、構造体を 生成せず map を受け取りたい場合にも使用します。

例外

定義が不正な場合は ArgumentError になります。キーは atom で指定してください。

defschema(options \\ [], list)

(macro)

スキーマを宣言します。

defschema do
  field :page, :integer, default: 1, minimum: 1
  field :query, :string, optional: true
end

ブロック内で field/2 または field/3 によりフィールドを宣言します。同じフィールド名や同じ 入力キーを重複して宣言できません。use ExParamsSchema, strict: true を指定している場合は、 その strict mode を引き継ぎます。

strict: を指定すると、use ExParamsSchema の設定をスキーマ単位で上書きできます。

defschema strict: true do
  field :identifier, :integer
end

strict: true の場合、未知の入力キーを拒否します。

コンパイル時エラー

strict: 以外の option、フィールド定義の不正な型・option・制約・default は ArgumentError になります。

field(name, type, options \\ [])

(macro)

オプション付きで 1 フィールドを宣言します。

field :id, :integer,
  source: "input-id",
  minimum: 1,
  error: :invalid_id

source: は入力キーを指定します。optional:nullable:default:error: は フィールドに指定できます。型ごとの制約は README を参照してください。

コンパイル時エラー

未対応の型、未知または重複した option、不正な option 値、型に適用できない制約、重複した フィールド名または入力キー、制約を満たさない default:ArgumentError になります。

json_schema(schema_or_definition, options \\ [])

@spec json_schema(schema_definition(), schema_options()) :: map()

コンパイル済みスキーマまたは map 形式のフィールド定義から JSON Schema Draft 7 を返します。

iex> ExParamsSchema.json_schema(%{count: {:integer, minimum: 1}})
...> |> get_in(["properties", "count"])
%{"minimum" => 1, "type" => "integer"}

map 形式の定義を渡す場合は compile!/1 を必要としません。strict: true を指定する場合は json_schema(definition, strict: true) を使います。 defschema を使うモジュールでは、生成される json_schema/0 で引数なしに取得できます。

parse(params, schema)

@spec parse(map(), compiled_schema()) :: parse_result()

コンパイル済みスキーマでパラメーターを検証・変換します。

iex> schema = ExParamsSchema.compile!(%{count: {:integer, minimum: 1}})
iex> ExParamsSchema.parse(%{"count" => "2"}, schema)
{:ok, %{count: 2}}

成功時は変換済みの map、失敗時はフィールドの error:、または省略時の {:invalid_param, field_name} を返します。map 以外の入力には {:error, :invalid_params} を 返します。

parse_detailed(params, schema)

@spec parse_detailed(map(), compiled_schema()) :: detailed_parse_result()

コンパイル済みスキーマでパラメーターを検証・変換し、失敗時に全ての検証エラーを返します。

iex> schema = ExParamsSchema.compile!(%{count: {:integer, minimum: 1}})
iex> match?(
...>   {:error, [%ExParamsSchema.ValidationError{path: ["count"], keyword: :minimum}]},
...>   ExParamsSchema.parse_detailed(%{"count" => "0"}, schema)
...> )
true

各エラーの pathkeywordreason を UI のフィールドメッセージに利用できます。 reason は通常の parse/2 と同じ error: の値です。型変換または必須値の読み取りが完了する 前に失敗した場合は、keyword: :cast のエラーを 1 件返します。