Normandy.Schema.Introspection (normandy v0.6.0)

View Source

Utilities for introspecting Normandy schemas at runtime.

This module provides functions to query schema metadata, including:

  • Field definitions and types
  • Required fields
  • Constraints and validations
  • Virtual and computed fields
  • JSON Schema specifications

Examples

defmodule User do
  use Normandy.Schema

  schema do
    field(:id, :integer, required: true)
    field(:name, :string, required: true, min_length: 3)
    field(:email, :string, format: "email")
    field(:age, :integer, minimum: 0)
  end
end

# List all field names
Introspection.list_fields(User)
#=> [:id, :name, :email, :age]

# Get required fields
Introspection.get_required_fields(User)
#=> [:id, :name]

# Get field type
Introspection.get_field_type(User, :name)
#=> :string

# Get field constraints
Introspection.get_field_constraints(User, :name)
#=> %{min_length: 3, required: true}

# Check if field is virtual
Introspection.virtual_field?(User, :name)
#=> false

Summary

Functions

Returns the description of the schema if defined.

Returns all constraints defined for a specific field.

Returns metadata about a specific field including type, constraints, and description.

Returns the type of a specific field.

Returns a list of all required fields.

Returns the complete JSON Schema specification for the schema.

Checks if a schema has composition constraints (anyOf, oneOf, allOf).

Checks if a schema has conditional constraints (if/then/else).

Returns a list of all fields with their complete metadata.

Lists all field names defined in the schema.

Checks if a field is virtual (excluded from JSON schema).

Functions

get_description(schema_module)

@spec get_description(module()) :: String.t() | nil

Returns the description of the schema if defined.

Returns nil if no description is set.

Examples

iex> Introspection.get_description(User)
"User account information"

get_field_constraints(schema_module, field_name)

@spec get_field_constraints(module(), atom()) :: map()

Returns all constraints defined for a specific field.

Constraints include: required, min_length, max_length, pattern, format, minimum, maximum, min_items, max_items, unique_items, enum, etc.

Returns an empty map if the field has no constraints.

Examples

iex> Introspection.get_field_constraints(User, :name)
%{required: true, min_length: 3}

iex> Introspection.get_field_constraints(User, :email)
%{format: "email"}

get_field_metadata(schema_module, field_name)

@spec get_field_metadata(module(), atom()) :: map() | nil

Returns metadata about a specific field including type, constraints, and description.

This is a convenience function that combines information from multiple introspection functions.

Examples

iex> Introspection.get_field_metadata(User, :name)
%{
  type: :string,
  required: true,
  constraints: %{min_length: 3},
  virtual: false,
  description: "User's full name"
}

get_field_type(schema_module, field_name)

@spec get_field_type(module(), atom()) :: atom() | {atom(), any()} | nil

Returns the type of a specific field.

Returns nil if the field does not exist.

Examples

iex> Introspection.get_field_type(User, :name)
:string

iex> Introspection.get_field_type(User, :nonexistent)
nil

get_required_fields(schema_module)

@spec get_required_fields(module()) :: [atom()]

Returns a list of all required fields.

Examples

iex> Introspection.get_required_fields(User)
[:id, :name]

get_specification(schema_module)

@spec get_specification(module()) :: map()

Returns the complete JSON Schema specification for the schema.

Examples

iex> Introspection.get_specification(User)
%{
  type: :object,
  properties: %{...},
  required: [:id, :name]
}

has_composition?(schema_module)

@spec has_composition?(module()) :: boolean()

Checks if a schema has composition constraints (anyOf, oneOf, allOf).

Checks both the schema root level and individual field properties.

Examples

iex> Introspection.has_composition?(User)
false

iex> Introspection.has_composition?(PolymorphicSchema)
true

has_conditionals?(schema_module)

@spec has_conditionals?(module()) :: boolean()

Checks if a schema has conditional constraints (if/then/else).

Checks both the schema root level and individual field properties.

Examples

iex> Introspection.has_conditionals?(User)
false

iex> Introspection.has_conditionals?(ConditionalSchema)
true

list_all_metadata(schema_module)

@spec list_all_metadata(module()) :: %{required(atom()) => map()}

Returns a list of all fields with their complete metadata.

Examples

iex> Introspection.list_all_metadata(User)
%{
  id: %{type: :integer, required: true, ...},
  name: %{type: :string, required: true, ...},
  email: %{type: :string, required: false, ...},
  age: %{type: :integer, required: false, ...}
}

list_fields(schema_module)

@spec list_fields(module()) :: [atom()]

Lists all field names defined in the schema.

Examples

iex> Introspection.list_fields(User)
[:id, :name, :email, :age]

virtual_field?(schema_module, field_name)

@spec virtual_field?(module(), atom()) :: boolean()

Checks if a field is virtual (excluded from JSON schema).

Virtual fields exist in the struct but may not be included in the JSON Schema representation unless explicitly configured.

Examples

iex> Introspection.virtual_field?(User, :computed_field)
true

iex> Introspection.virtual_field?(User, :name)
false