Documentation
¶
Overview ¶
Package handler holds the stabilized public custom-handler contract surface for Datly 1.0.
Its core is intentionally small:
- Func[I,O] for simple pure handlers
- Contract[I,O] for explicit runtime-aware handlers
- Binder plus InputKey for invocation-scoped value access
- Session as the reduced binder/response facade; logger, validator, message bus, and data services are binder-scoped capabilities rather than session APIs
- generic session and pre-invoke hook context carriers
- plain public lifecycle hooks
It must not regrow Datly runtime internals such as SQL execution, transaction/state managers, route execution, or transport-specific hooks.
Index ¶
- Constants
- func HookApplied(ctx context.Context) bool
- func InvokeWithSession(ctx context.Context, sess Session, input any, ...) (any, error)
- func IsCacheWarmup(ctx context.Context) bool
- func RunPreInvokeHook(ctx context.Context, input any, hook func(context.Context, any) error) error
- func WithCacheWarmup(ctx context.Context, phase WarmupPhase) context.Context
- func WithHookApplied(ctx context.Context) context.Context
- func WithReadMetadata(ctx context.Context, value ReadMetadata) context.Context
- func WithSession(ctx context.Context, sess Session) context.Context
- type AfterQueueHook
- type AfterSequenceHook
- type Binder
- type Capabilities
- type Conflict
- type Contract
- type ContractFunc
- type DML
- type Data
- type DataSync
- type EntityHooks
- type EntitySnapshot
- type EntityState
- type ErrorFinalizer
- type FieldSet
- type Finalizer
- type Flusher
- type Func
- type Initializer
- type InjectorFinalizer
- type InjectorLookup
- type InputCapturer
- type InvocationInfo
- type InvocationPurpose
- type LifecycleContext
- type Logger
- type MessageBus
- type NoParent
- type OnFetcher
- type OnRelationer
- type OriginalPresence
- type Outcome
- type ReadMetadata
- type ReadMetadataConsumer
- type ReadOutputProjection
- type ReadProjection
- type ReadStep
- type Route
- type Sequencer
- type Session
- type TransactionOutcome
- type TransactionStarter
- type TransactionState
- type Validation
- type ValidationOptions
- type ValidationReference
- type Validator
- type ValueKey
- type Violation
- type WarmupPhase
- type WriteAction
- type WriteHook
- type WriteInitializer
- type WriteValidator
Constants ¶
const DataSyncKey = syncKey("dataSync")
DataSyncKey is the context key used to carry *DataSync through hooks that coordinate sibling view readiness.
Variables ¶
This section is empty.
Functions ¶
func HookApplied ¶
HookApplied reports whether ctx already carries the pre-invoke hook-applied marker.
func InvokeWithSession ¶
func InvokeWithSession( ctx context.Context, sess Session, input any, invoke func(context.Context, any) (any, error), ) (any, error)
InvokeWithSession marks the hook as already applied, attaches sess to the invocation context, and then delegates to invoke.
func IsCacheWarmup ¶
func RunPreInvokeHook ¶
RunPreInvokeHook runs hook unless the context already carries the hook-applied marker.
func WithCacheWarmup ¶
func WithCacheWarmup(ctx context.Context, phase WarmupPhase) context.Context
WithCacheWarmup marks a trusted runtime-owned warmup phase. Transport values must never be converted into this context capability.
func WithHookApplied ¶
WithHookApplied marks ctx so a pre-invoke hook can recognize that it has already run for the current invocation.
func WithReadMetadata ¶
func WithReadMetadata(ctx context.Context, value ReadMetadata) context.Context
WithReadMetadata installs invocation-local read-only evidence. A nil value shadows inherited evidence so nested components cannot borrow parent fields.
Types ¶
type AfterQueueHook ¶
type AfterQueueHook[T, P, O any] interface { AfterQueue(context.Context, *T, LifecycleContext[T, P, O]) error }
AfterQueueHook observes successfully queued mutation work. Queueing is not a commit: publishing commit-dependent messages belongs to outcome-aware finalization, not this hook. The same object also serves Init and Validate.
type AfterSequenceHook ¶
type AfterSequenceHook[T, P, O any] interface { AfterSequence(context.Context, *T, LifecycleContext[T, P, O]) error }
AfterSequenceHook is an optional capability of the same invocation-scoped EntityHooks object. The generated program calls it after sequencing and before diffing. Validated business values and their markers must remain unchanged; only identity/link processing allowed by the compiled policy may adjust rows.
type Binder ¶
type Binder interface {
Bind(ctx context.Context, target any) error
Lookup(ctx context.Context, key ValueKey) (any, bool, error)
}
Binder resolves and binds values for one invocation scope. It is intentionally narrow and does not expose service construction.
Lookup returns (nil, false, nil) for an unknown key. An error is reserved for resolution failures of a known key.
type Capabilities ¶
type Capabilities struct {
Differ differ.Differ
Logger Logger
Validator Validator
MessageBus MessageBus
// Connector is absent unless the application explicitly grants DB access.
// It is not part of managed Data and never provides a transaction implicitly.
Connector connector.Provider
}
Capabilities groups fixed handler primitives supplied by an invocation. Database Data remains invocation-owned and is exposed independently by DataKey.
type Conflict ¶
Conflict reports a failed comparison with the authorized Previous projection. It is a validation result; it does not imply database locking or an atomic compare-and-write operation.
func (*Conflict) StatusCode ¶
StatusCode exposes the validation conflict through the standard response contract.
type Contract ¶
type Contract[I any, O any] interface { Exec(ctx context.Context, sess Session, input *I, output *O) error }
Contract is the minimal public custom-handler contract. Metadata remains the canonical source of handler identity; this is conformance-facing only.
type ContractFunc ¶
ContractFunc adapts a function to Contract.
type DML ¶
type DML interface {
Insert(table string, value any) error
Update(table string, value any) error
Delete(table string, value any) error
Execute(statement string, args ...any) error
}
DML is the narrow buffered write contract available to handlers. Transaction completion is available separately through Flusher and Data.
type Data ¶
Data is the complete handler data capability. Focused consumers may request DML, Sequencer, or Flusher independently.
type DataSync ¶
type DataSync struct {
// contains filtered or unexported fields
}
DataSync coordinates cross-view hook ordering. A relation registers its Holder key before fetching begins (Put), releases it when data is ready (Delete), and dependents block in Wait until the lock is released. Ported faithfully from xdatly/handler/sync.go.
func NewDataSync ¶
func NewDataSync() *DataSync
type EntityHooks ¶
type EntityHooks[T, P, O any] interface { Init(context.Context, *T, LifecycleContext[T, P, O]) error Validate(context.Context, *T, LifecycleContext[T, P, O]) error }
EntityHooks is implemented by a reusable, invocation-scoped hook object. Init runs after initial presence synchronization and invariant backfill; it uses marker-aware setters when changing business values. Validate then runs on the same object and must not mutate values, markers or the previous snapshot. Component adapters supply input-specific configuration, scoped services and the invocation-owned typed output without coupling entity types themselves to an application contract.
type EntitySnapshot ¶
type EntitySnapshot[T any] interface { OriginalPresence SyncPresence(current *T) error }
EntitySnapshot retains an entity's pre-Init/InitMCP processing baseline and original presence. It is not the previous database row used for backfill. Generated implementations keep captured values, identities and associations private and may describe a particular relation path or selected projection.
SyncPresence traverses the captured entity graph, marking changed non-identity values while preserving explicit working markers. It reads business values without changing them and never mutates the snapshot or reinterprets later IDs as original identity. Nil current is a no-op. On error, all working markers remain unchanged; implementations validate associations before applying marks. Ambiguous entity associations return an error rather than guessing a match.
These are two separate methods: generated owned entities expose entity.SyncPresence(snapshot EntitySnapshot[T]) error, which delegates to this interface's snapshot.SyncPresence(entity *T) error. Linked entities use the latter directly without adding methods to a foreign Go type.
type EntityState ¶
type EntityState[T, P any] struct { Previous *T Parent *P Original OriginalPresence PreviousFields FieldSet SelfParent *T }
EntityState contains the typed context of one entity-processing operation. Previous must be detached from working values and treated as read-only by hooks. PreviousFields identifies loaded fields when previous rows are a projection; a missing field must not be interpreted as a known zero value. Parent is the declared enclosing relation parent and is nil for a root role. SelfParent is the immediate parent through a recursive self edge; it is nil at the self-tree root. A self descendant retains the enclosing Parent, so a single typed hook contract can observe both contexts without using any. Original identity presence is independent from any identifier assigned later by initialization or sequencing.
type ErrorFinalizer ¶
ErrorFinalizer is the reduced public error-aware lifecycle hook for outputs that need the terminal handler error when finalizing.
type FieldSet ¶
FieldSet is a read-only view of canonical Go field names. Has returns false for unknown fields; it does not resolve JSON names or SQL aliases.
type Finalizer ¶
Finalizer is the reduced public success-path lifecycle hook for outputs or other handler-bound values that need post-execution cleanup/follow-up without exposing transport- or runtime-specific finalization concerns.
type Flusher ¶
Flusher executes buffered writes. Implementations complete transactions they open themselves and leave supplied transactions to their owner.
type Func ¶
Func is the typed pure-function handler contract. Use Contract when a custom handler needs scoped DI or response control.
type Initializer ¶
Initializer is the reduced public pre-execution lifecycle hook for inputs or other handler-bound values that need request-context initialization without binding themselves to Datly runtime internals.
type InjectorFinalizer ¶
type InjectorFinalizer interface {
Finalize(context.Context, InjectorLookup) error
}
InjectorFinalizer composes conditional dependencies before transaction completion. Its child work participates in the root unit of work. Errors prevent owned commit; an existing operation error is never discarded. It takes precedence over ordinary output finalizers. OutcomeFinalizer handlers retain ownership of their separate result-aware completion lifecycle.
type InjectorLookup ¶
InjectorLookup resolves a route without executing it. The returned Binder executes that component once on its first Bind or ResultKey lookup. Bind uses native binding tags to match the component result to the destination. Both lookup and binders are valid only during the Finalize call; calls must be awaited. Call contexts supply cancellation while retaining invocation values.
type InputCapturer ¶
InputCapturer optionally captures bound input before Init and InitMCP run. CaptureInput must not mutate input. It returns an invocation-local snapshot with detached original values/markers, or nil when no capture is needed. The runtime treats the result as opaque rather than implementing mutation policy or exposing a second binding model.
type InvocationInfo ¶
type InvocationInfo struct {
// contains filtered or unexported fields
}
InvocationInfo is immutable execution metadata propagated through context. Private fields prevent consumers from changing the capability selected by the runtime after retrieving it.
func InvocationFromContext ¶
func InvocationFromContext(ctx context.Context) *InvocationInfo
InvocationFromContext returns request metadata when no explicit invocation descriptor has been installed.
func (*InvocationInfo) IsCacheWarmup ¶
func (i *InvocationInfo) IsCacheWarmup() bool
func (*InvocationInfo) MayBypassRowAuthorization ¶
func (i *InvocationInfo) MayBypassRowAuthorization() bool
MayBypassRowAuthorization reports an explicit runtime capability. Consumers should not infer authorization policy from IsCacheWarmup alone.
func (*InvocationInfo) Purpose ¶
func (i *InvocationInfo) Purpose() InvocationPurpose
func (*InvocationInfo) WarmupPhase ¶
func (i *InvocationInfo) WarmupPhase() WarmupPhase
type InvocationPurpose ¶
type InvocationPurpose uint8
InvocationPurpose identifies why a handler or predicate is executing.
const ( InvocationPurposeRequest InvocationPurpose = iota InvocationPurposeCacheWarmup )
type LifecycleContext ¶
type LifecycleContext[T, P, O any] struct { EntityState[T, P] Output *O }
LifecycleContext contains the typed state and component output available to one entity lifecycle callback. Output is the invocation-owned response value allocated before lifecycle processing begins. Hooks may use it to accumulate validation violations, warnings and other response metadata. Hooks must not retain Output after the callback or use it concurrently.
EntityState is embedded so existing state access remains direct: callbacks continue to use state.Previous, state.Parent, state.Original and state.SelfParent while gaining state.Output.
type MessageBus ¶
type NoParent ¶
type NoParent struct{}
NoParent is the typed parent parameter for a root entity's hook state. EntityState[T, NoParent].Parent is nil.
type OnFetcher ¶
OnFetcher is implemented by typed rows that need initialization immediately after SQLx has populated the row and before Datly indexes or relates it.
type OnRelationer ¶
OnRelationer is implemented by typed rows that need notification after all selected child relations have been assembled.
type OriginalPresence ¶
OriginalPresence exposes copied pre-initialization marker bits. Available distinguishes a missing marker from a known marker whose bits are all false. Implementations must not read from the working entity's mutable marker.
type Outcome ¶
type Outcome struct {
Error error
Transactions []TransactionOutcome
}
Outcome is a detached completion snapshot for one root invocation. Error is the operation/completion error. A later finalizer/publication error does not change a reported database commit into a rollback.
func (Outcome) CommitConfirmed ¶
CommitConfirmed is safe for post-commit publication only when every actual managed transaction committed, at least one exists, and completion succeeded. No transaction, caller ownership and unsupported custom Data are not proof.
func (Outcome) State ¶
func (o Outcome) State() TransactionState
State summarizes unit reports without flattening mixed multi-database work into a misleading success or rollback. Transactions retain the full detail.
type ReadMetadata ¶
type ReadMetadata interface {
Projection(inputField string) (ReadProjection, error)
}
ReadMetadata resolves evidence by the canonical Go field path on the bound component input. It describes the successful binding, not a later reread of a similarly named view. It contains no database handles or mutable row data.
func ReadMetadataFromContext ¶
func ReadMetadataFromContext(ctx context.Context) (ReadMetadata, bool)
type ReadMetadataConsumer ¶
type ReadMetadataConsumer interface {
RequiresReadMetadata() bool
}
ReadMetadataConsumer opts a handler/definition into input read evidence. Ordinary handlers that do not request it retain their normal read path.
type ReadOutputProjection ¶
type ReadOutputProjection interface {
Output(holder string) (ReadProjection, error)
}
ReadOutputProjection optionally addresses additional output-envelope slots such as independent derived aggregates. It is separate from ReadProjection so existing implementations keep their root-only contract unchanged.
type ReadProjection ¶
type ReadProjection interface {
RootHolder() string
DirectOutput() bool
Fields(rootOrdinal int, relations ...ReadStep) (FieldSet, error)
}
ReadProjection exposes evidence from one completed typed read. Fields uses the final root ordinal followed by optional relation steps. Unknown evidence and invalid paths return errors; nil must never mean all fields were loaded. RootHolder describes an output envelope; it is empty for direct outputs.
type ReadStep ¶
ReadStep addresses a relation holder and its final row ordinal. Holder is a canonical Go holder path, not a SQL alias or a client-supplied field name.
type Route ¶
Route selects a component by its method and concrete URL (including query parameters). Scope and Name optionally require an exact registered component identity; route lookup still enforces the runtime's exposed route catalog.
type Sequencer ¶
type Sequencer interface {
Allocate(ctx context.Context, table string, dest any, selector string) error
}
Sequencer allocates identifiers before buffered writes are flushed.
type Session ¶
Session is the reduced per-invocation custom-handler facade. It stays intentionally small to avoid recreating the old fat session surface.
type TransactionOutcome ¶
type TransactionOutcome struct {
// Unit is an invocation-local ordinal, not a database or transaction handle.
Unit int
State TransactionState
// Error is a unit-local preparation/completion error, when available.
Error error
}
type TransactionStarter ¶
TransactionStarter starts or joins the existing managed Data transaction. Repeated calls and downstream components join the same database unit. The invocation owner completes it; a caller-supplied transaction remains owned by the caller. Distinct databases remain distinct units, not one distributed transaction.
type TransactionState ¶
type TransactionState string
TransactionState reports managed transaction completion, never a planned action. Unknown states deliberately do not imply rollback or commit.
const ( TransactionNone TransactionState = "none" TransactionCommitted TransactionState = "committed" TransactionRolledBack TransactionState = "rolled_back" TransactionCallerPending TransactionState = "caller_pending" TransactionCommitUnknown TransactionState = "commit_unknown" TransactionRollbackUnknown TransactionState = "rollback_unknown" TransactionUnknown TransactionState = "unknown" // Partial means some units committed while other participating units did not. TransactionPartial TransactionState = "partial" TransactionMixed TransactionState = "mixed" )
type Validation ¶
type Validation struct {
Failed bool `json:"failed,omitempty"`
Violations []*Violation `json:"violations,omitempty"`
Code int `json:"-"`
}
Validation is the typed result shared by Go and template validation consumers. Either Failed or a nonempty Violations list represents failure.
func (*Validation) Err ¶
func (v *Validation) Err() error
Err preserves the typed result for errors.As and returns nil for success.
func (*Validation) Error ¶
func (v *Validation) Error() string
func (*Validation) Messages ¶
func (v *Validation) Messages() []string
Messages returns detached human-readable diagnostics. Raw input values are not included automatically in errors or protocol responses.
func (*Validation) ResponseBody ¶
func (v *Validation) ResponseBody() any
func (*Validation) StatusCode ¶
func (v *Validation) StatusCode() int
StatusCode defaults failed validation to 422; applications may select an explicit code, such as 401 for an authorization-related validation result.
type ValidationOptions ¶
type ValidationOptions struct {
Action WriteAction
// Previous is the detached typed database row matched using original
// identity. Inserts require nil; updates require a row with its complete
// mapped identity. Unique checks exclude that tuple, never working IDs.
Previous any
// PreviousFields is actual read provenance for Previous. Updates require
// it; unknown identity or constraint dependencies cannot be replaced by
// zero values from a partial projection. Inserts require nil.
PreviousFields FieldSet
// Fields is effective validation coverage, not the persistence Has mask.
// Inserts require nil (complete checks). Updates require explicit coverage;
// invariant-backfilled fields may be covered without becoming client writes.
// Names are canonical Go field names, as with EntityState.PreviousFields.
Fields FieldSet
// DeferredFields identifies unavailable INSERT inputs for a generated
// business-validation pass. It is not a sparse write mask. Only compiled
// producer authority and original absence may supply this set; a mandatory
// final pass must validate the completed payload with no fields deferred.
// UPDATE and calls with SatisfiedReferences cannot defer fields.
DeferredFields FieldSet
// SatisfiedReferences identifies exact reference-existence checks already
// proven by the generated pending-write graph for an INSERT payload. Other
// constraints remain active. The generated owner must prove matching typed
// parent keys, INSERT action and write order in the same transaction scope.
// These are trusted framework facts, never request-bindable assertions.
SatisfiedReferences []ValidationReference
// Connector selects a configured connector within the invocation scope.
// Empty selects that scope's configured default, not an arbitrary database.
Connector string
Location string
Shallow bool
}
ValidationOptions supplies mutation policy to the scoped Validator capability. It carries no database or transaction handles. The capability rejects unknown connectors, unknown actions and inconsistent options before performing checks. Generated entity validation is shallow: all frames are validated by the program before any custom EntityHooks.Validate method runs. The framework capability accepts either one entity and one ValidationOptions, or a typed entity slice and one []ValidationOptions argument of equal length. Batch policies retain each candidate's matched prior row and diagnostic location; candidates in a batch must share the entity type and connector.
type ValidationReference ¶
ValidationReference identifies one native reference constraint exactly. Empty schema remains empty; field names are canonical candidate Go fields.
type Validator ¶
type Validator interface {
Validate(ctx context.Context, value any, opts ...any) (*Validation, error)
}
Validator is the injectable validation service available to custom handlers.
type ValueKey ¶
type ValueKey string
const ( // InputKey is the reserved canonical component input key exposed to hooks, // predicates, and compatibility paths that need direct input visibility. InputKey ValueKey = "input" // SelectorsKey is the invocation-scoped dynamic view-selector capability. // Values are resolved through the Binder; readers must not carry a parallel // selector input channel. SelectorsKey ValueKey = "selectors" )
const ( // DifferKey identifies the typed comparison capability. DifferKey ValueKey = "differ" // LoggerKey identifies the invocation-scoped logger capability. LoggerKey ValueKey = "logger" // ValidatorKey identifies the invocation-scoped validation capability. ValidatorKey ValueKey = "validator" // MessageBusKey identifies the invocation-scoped message-bus capability. MessageBusKey ValueKey = "mbus" // ConnectorKey is an opt-in exact-name DB provider for specialized handlers. ConnectorKey ValueKey = "connector" )
const ( // DMLKey identifies the invocation-scoped buffered write capability. DMLKey ValueKey = "dml" // SequencerKey identifies the invocation-scoped identifier allocator. SequencerKey ValueKey = "sequencer" // FlusherKey identifies explicit execution of buffered writes. FlusherKey ValueKey = "flusher" // DataKey identifies the complete invocation data capability. DataKey ValueKey = "data" )
const ( // CallerOutputKey is the declared source for a conditional child's input fields. // Only fields authored with this source receive the calling output's values. CallerOutputKey ValueKey = "caller_output" // ResultKey resolves the complete typed result of a route-specific binder. ResultKey ValueKey = "result" )
const FrameworkValidatorKey ValueKey = "frameworkValidator"
FrameworkValidatorKey is reserved for the mandatory generated validation phase. It uses the same Validator interface, but cannot be replaced by request values or the optional custom-handler Validator capability.
const InputSnapshotKey ValueKey = "inputSnapshot"
InputSnapshotKey exposes an opt-in capture result through the invocation binder. It is runtime-owned and must never be supplied by transport data or component providers. Generated adapters own the concrete snapshot type.
const ReadMetadataKey ValueKey = "readMetadata"
const TransactionStarterKey ValueKey = "transactionStarter"
TransactionStarterKey selects the invocation-owned transaction startup capability. It never grants commit/rollback or raw transaction access.
type Violation ¶
type Violation struct {
Location string `json:"location,omitempty"`
Field string `json:"field,omitempty"`
Message string `json:"message,omitempty"`
Check string `json:"check,omitempty"`
}
Violation describes a failed validation rule without owning validation execution.
type WarmupPhase ¶
type WarmupPhase uint8
WarmupPhase distinguishes warmup validation from the cache-filling phase.
const ( WarmupPhaseNone WarmupPhase = iota WarmupPhasePrepare WarmupPhaseFill )
type WriteAction ¶
type WriteAction string
WriteAction is a planned operation, not evidence that a write was committed.
const ( WriteInsert WriteAction = "insert" WriteUpdate WriteAction = "update" WriteDelete WriteAction = "delete" )
type WriteHook ¶
type WriteHook[T, P any] interface { BeforeWrite(context.Context, *T, EntityState[T, P], WriteAction) error }
WriteHook optionally customizes business values before validation, using marker-aware setters after initial synchronization. Identity reconciliation after diffing is a separate phase; this hook is not a commit or message-publication callback.
type WriteInitializer ¶
WriteInitializer is implemented by typed entities that need preparation after generated relation keys and sequences are available but before a generated handler validates or queues the entity for persistence.
type WriteValidator ¶
WriteValidator is implemented by typed entities that need validation after write initialization and before a generated handler queues DML.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package exec provides dependency-light execution context contracts shared by xdatly handlers and runtimes.
|
Package exec provides dependency-light execution context contracts shared by xdatly handlers and runtimes. |
|
Package mcp defines the MCP capabilities and lifecycle contracts exposed to xdatly handlers without coupling them to a Datly runtime implementation.
|
Package mcp defines the MCP capabilities and lifecycle contracts exposed to xdatly handlers without coupling them to a Datly runtime implementation. |
|
Package mutation defines the generated, typed policy consumed by the optional mutation handler.
|
Package mutation defines the generated, typed policy consumed by the optional mutation handler. |