Documentation
¶
Overview ¶
Package schema provides the `@schema NAME { Field Type `json:"..." default:"..." validate:"..."` ... }` directive. The declaration lands in meta["schemas"]; the `:schema=NAME` modifier attaches the name to an atom so the strict-mode validation hook can find it.
Typical use:
@schema TestUser {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"email"`
}
{ name: "Maksymilian" } -> runtime.noop:schema=TestUser
Missing fields can receive type-aware defaults. ApplyDefaults returns a new effective payload map, and Schema.validate passes that map downstream.
Index ¶
- Constants
- Variables
- func ApplyDefaults(schema Schema, payload any) (map[string]any, error)
- func Bundle(_ map[string]string) compiler.Bundle
- func Library() action.Library
- func SplitFields(s Schema) (payload, config []string)
- func ToJSONSchema(s Schema) map[string]any
- func Validate(ctx context.Context, schema Schema, payload any) error
- func WithContextSchemas(ctx context.Context, schemas []Schema) context.Context
- type Field
- type Kind
- type Schema
- type ValidateRequest
Constants ¶
const ID = "schema"
Variables ¶
var Directive = compiler.Directive{
Name: "schema",
Example: `@schema Plan { Summary string }`,
Handler: handleDirective,
}
Directive parses `@schema NAME { ... }` into a Schema appended to meta["schemas"]. Duplicate names are a compile error.
var SchemaModifier = compiler.String("schema", func(b *action.Builder[any, any], name string) *action.Builder[any, any] { return b.Tag("schema:" + name) })
SchemaModifier attaches a schema name to an atom as a tag.
var ValidateAction = action.New("schema.validate", func(ctx context.Context, req ValidateRequest) (any, error) { if req.Name == "" { return nil, xerr.BadRequest("schema.validate: schema name is required (use @{ name: \"SchemaName\" })") } schemas, _ := schemasContextKey.From(ctx) targetSchema, ok := schemas[req.Name] if !ok { return nil, xerr.NotFound("schema.validate: schema '" + req.Name + "' not registered in workflow") } targetPayload := req.Payload if targetPayload == nil { targetPayload, _ = rootInputKey.From(ctx) } if targetPayload == nil { return nil, xerr.BadRequest("schema.validate: no payload available to validate") } effective, err := ApplyDefaults(targetSchema, targetPayload) if err != nil { return nil, err } if err := validateValues(ctx, targetSchema, effective); err != nil { return nil, err } return effective, nil }).Description("Validate payload against a declared @schema"). Tag("schema", "validation"). Build()
Functions ¶
func ApplyDefaults ¶ added in v0.30.2
ApplyDefaults returns a copy of payload with each missing field filled from its schema field's `default` tag. Explicitly supplied values, including nil, are preserved. The input map is never modified.
Scalar defaults are parsed according to the declared field type. Slice, map, and struct defaults use JSON values, for example `default:"[1,2]"` or `default:"{\"enabled\":true}"`.
func SplitFields ¶ added in v0.17.0
func ToJSONSchema ¶ added in v0.22.0
func Validate ¶
Validate applies missing defaults and checks payload against the declared schema.
For every field that is present, the check proceeds in two stages:
- Go-kind check (checkFieldKind): the payload value's kind must match the schema field's declared type.
- Tag check (validation.Var): when the field carries a `validate:"..."` tag beyond bare "required", the value is validated against the tag expression using the same validator/v10 instance that powers validation.Struct.
The presence check for validate:"required" is handled inline, so a missing field reports as "is required" without invoking the validator. An empty string for a required field is also reported as "is required" because validator/v10 treats empty as the zero value.
ctx is threaded from the caller so context-aware custom rules registered via validation.RegisterCustom see the live context, and so a canceled invocation aborts validation promptly.