Documentation
¶
Overview ¶
Package schema adds optional, dependency-free per-URN payload validation to a babelqueue producer or consumer (ADR-0024). A Provider supplies a JSON Schema for a message URN — typically read from a babelqueue-registry registry.json — and the message's data block is validated against it. It is opt-in: a URN with no registered schema is never validated.
The validator is an intentionally small subset of JSON Schema (draft-07) — enough to describe a message's data, mirroring the subset babelqueue-registry and php-sdk's envelope validator enforce. It is NOT a full draft-07 implementation; unknown keywords are ignored. It lives in the core module (stdlib only, zero dependencies) so it ships with every SDK.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidPayload = errors.New("babelqueue/schema: message data does not match its URN schema")
ErrInvalidPayload is returned (wrapped) when a message's data does not match the schema registered for its URN. Detect it with errors.Is.
Functions ¶
func Check ¶
Check validates a (urn, data) pair against the schema registered for urn. It is the producer-side guard: call it before publishing so invalid data never enters the queue. It returns nil when the data is valid OR when no schema is registered for the URN (opt-in); a provider lookup error is returned wrapped (transient — e.g. the registry file is briefly unavailable during a deploy).
func Validate ¶
func Validate(provider Provider, env babelqueue.Envelope) error
Validate is the envelope form of Check: it validates env.Data against env.URN()'s schema.
func Wrap ¶
func Wrap(provider Provider, handler babelqueue.Handler) babelqueue.Handler
Wrap returns handler decorated to validate each message's data against its URN schema before the handler runs (consumer-side safety net). It composes with the App's ack-on-return / retry-on-error contract:
- valid data (or no schema registered for the URN) → the handler runs unchanged;
- invalid data → ErrInvalidPayload is returned, so the message takes the retry / dead-letter path. Because invalid data will not become valid on retry, such a poison message exhausts its attempts and is dead-lettered — prefer Check producer-side to keep invalid data out of the queue entirely;
- a provider lookup error → returned, so the message is retried once the source recovers.
Register it like any handler: app.Handle(urn, schema.Wrap(provider, handler)).
Types ¶
type DirProvider ¶
type DirProvider struct {
// contains filtered or unexported fields
}
DirProvider reads schemas from a babelqueue-registry manifest (registry.json): a list of {urn, schema} entries mapping each URN to a draft-07 schema file for its data block. This is the bridge that makes the registry's governed schemas enforceable at runtime. The manifest is read once; schema files are parsed lazily and cached.
func NewDirProvider ¶
func NewDirProvider(manifestPath string) (*DirProvider, error)
NewDirProvider loads the registry manifest at manifestPath (e.g. ".../registry.json").
type MapProvider ¶
type MapProvider struct {
// contains filtered or unexported fields
}
MapProvider is an in-memory Provider, suitable for tests and for embedding schemas in code. It is read-only after construction and therefore safe for concurrent use.
func NewMapProvider ¶
func NewMapProvider(raw map[string][]byte) (*MapProvider, error)
NewMapProvider builds a MapProvider from URN → raw JSON Schema bytes, parsing each.
type Provider ¶
type Provider interface {
// Schema returns the schema registered for urn. found is false when the URN has no
// registered schema, in which case the caller skips validation (opt-in).
Schema(urn string) (sch *Schema, found bool, err error)
}
Provider is a source of per-URN data schemas. The reference MapProvider is in-memory; DirProvider reads a babelqueue-registry registry.json. A production provider (a service client, an embedded bundle) implements the same single method.
type Schema ¶
type Schema struct {
Type string // object|string|integer|number|boolean|array|null; "" = unconstrained
Required []string // object: required property names
Properties map[string]*Schema // object: per-property schemas
AdditionalProperties *bool // object: nil = open (draft-07 default true)
Items *Schema // array: item schema
Enum []any // allowed values (nil = any)
Const any // fixed value when HasConst
HasConst bool
MinLength *int // string (counted in Unicode code points)
Minimum *float64 // integer|number
// GDPRSensitive marks a property as carrying personal/sensitive data
// ("x-gdpr-sensitive": true). It is a JSON-Schema extension keyword (ADR-0030):
// validation ignores it entirely (it never makes a value valid or invalid), so a registry
// can declare sensitivity without changing how data is validated — adding it is never a
// breaking change (GR-1). GDPRCategory carries the optional free-form category form
// ("x-gdpr-sensitive": "email") for documentation; it is "" when the keyword was the
// boolean true. The gdpr subpackage consumes these marks to encrypt/decrypt the matching
// values at runtime; this mirrors babelqueue-registry's internal/schema model.
GDPRSensitive bool
GDPRCategory string
}
Schema is a parsed (subset) JSON Schema node.
func (*Schema) SensitivePaths ¶ added in v1.8.0
func (s *Schema) SensitivePaths() []SensitivePath
SensitivePaths walks the schema and returns every property marked x-gdpr-sensitive, in sorted path order. It descends into nested objects (dotted paths) and array item schemas ("field[]"). A mark on the root schema itself (path "") is reported as a path of "". It is the value-level counterpart to the registry's inventory: the gdpr subpackage uses these paths to locate the leaves it must encrypt on produce and decrypt on consume.
type SensitivePath ¶ added in v1.8.0
SensitivePath is one property marked x-gdpr-sensitive, located by its dotted path from the schema root (array elements use the "field[]" segment the validator/compat use). Category is the optional "x-gdpr-sensitive": "<category>" string, or "" when it was the boolean true.