Documentation
¶
Overview ¶
Package hook provides the privacy and lifecycle hook system for Grove.
Hooks run before and after database operations, enabling:
- Multi-tenant isolation (inject WHERE tenant_id = ?)
- PII redaction (redact fields tagged with grove:",privacy:pii")
- Audit logging (log mutations to Chronicle)
- Access control (deny unauthorized operations)
The hook system does not implement authorization logic — it provides the integration point for any permissions library.
Index ¶
- func RunModelAfterDelete(ctx context.Context, qc *QueryContext, model any) error
- func RunModelAfterInsert(ctx context.Context, qc *QueryContext, model any) error
- func RunModelAfterScan(ctx context.Context, qc *QueryContext, model any) error
- func RunModelAfterUpdate(ctx context.Context, qc *QueryContext, model any) error
- func RunModelBeforeDelete(ctx context.Context, qc *QueryContext, model any) error
- func RunModelBeforeInsert(ctx context.Context, qc *QueryContext, model any) error
- func RunModelBeforeScan(ctx context.Context, qc *QueryContext, model any) error
- func RunModelBeforeUpdate(ctx context.Context, qc *QueryContext, model any) error
- type AfterDeleteHook
- type AfterInsertHook
- type AfterScanHook
- type AfterUpdateHook
- type BeforeDeleteHook
- type BeforeInsertHook
- type BeforeScanHook
- type BeforeUpdateHook
- type Condition
- type Decision
- type Engine
- func (e *Engine) AddHook(h any, scope ...Scope)
- func (e *Engine) RunPostMutation(ctx context.Context, qc *QueryContext, data, result any) error
- func (e *Engine) RunPostQuery(ctx context.Context, qc *QueryContext, result any) error
- func (e *Engine) RunPreMutation(ctx context.Context, qc *QueryContext, data any) (*HookResult, error)
- func (e *Engine) RunPreQuery(ctx context.Context, qc *QueryContext) (*HookResult, error)
- func (e *Engine) RunStreamRowHook(ctx context.Context, qc *QueryContext, row any) (int, error)
- type ExtraFilter
- type HookResult
- type Operation
- type PostMutationHook
- type PostQueryHook
- type PreMutationHook
- type PreQueryHook
- type QueryContext
- type Scope
- type StreamRowHook
- type TagSource
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RunModelAfterDelete ¶
func RunModelAfterDelete(ctx context.Context, qc *QueryContext, model any) error
RunModelAfterDelete calls AfterDeleteHook on the model if implemented.
func RunModelAfterInsert ¶
func RunModelAfterInsert(ctx context.Context, qc *QueryContext, model any) error
RunModelAfterInsert calls AfterInsertHook on the model if implemented.
func RunModelAfterScan ¶
func RunModelAfterScan(ctx context.Context, qc *QueryContext, model any) error
RunModelAfterScan calls AfterScanHook on the model if implemented.
func RunModelAfterUpdate ¶
func RunModelAfterUpdate(ctx context.Context, qc *QueryContext, model any) error
RunModelAfterUpdate calls AfterUpdateHook on the model if implemented.
func RunModelBeforeDelete ¶
func RunModelBeforeDelete(ctx context.Context, qc *QueryContext, model any) error
RunModelBeforeDelete calls BeforeDeleteHook on the model if implemented.
func RunModelBeforeInsert ¶
func RunModelBeforeInsert(ctx context.Context, qc *QueryContext, model any) error
RunModelBeforeInsert calls BeforeInsertHook on the model if implemented.
func RunModelBeforeScan ¶
func RunModelBeforeScan(ctx context.Context, qc *QueryContext, model any) error
RunModelBeforeScan calls BeforeScanHook on the model if implemented.
func RunModelBeforeUpdate ¶
func RunModelBeforeUpdate(ctx context.Context, qc *QueryContext, model any) error
RunModelBeforeUpdate calls BeforeUpdateHook on the model if implemented.
Types ¶
type AfterDeleteHook ¶
type AfterDeleteHook interface {
AfterDelete(ctx context.Context, qc *QueryContext) error
}
AfterDeleteHook is called after a successful DELETE operation.
type AfterInsertHook ¶
type AfterInsertHook interface {
AfterInsert(ctx context.Context, qc *QueryContext) error
}
AfterInsertHook is called after a successful INSERT operation.
type AfterScanHook ¶
type AfterScanHook interface {
AfterScan(ctx context.Context, qc *QueryContext) error
}
AfterScanHook is called after scanning query results into the model. Useful for computed fields, decryption, or post-load transformations.
type AfterUpdateHook ¶
type AfterUpdateHook interface {
AfterUpdate(ctx context.Context, qc *QueryContext) error
}
AfterUpdateHook is called after a successful UPDATE operation.
type BeforeDeleteHook ¶
type BeforeDeleteHook interface {
BeforeDelete(ctx context.Context, qc *QueryContext) error
}
BeforeDeleteHook is called before a DELETE operation.
type BeforeInsertHook ¶
type BeforeInsertHook interface {
BeforeInsert(ctx context.Context, qc *QueryContext) error
}
BeforeInsertHook is called before an INSERT operation. The model can modify itself (e.g., set CreatedAt) or return an error to abort.
type BeforeScanHook ¶
type BeforeScanHook interface {
BeforeScan(ctx context.Context, qc *QueryContext) error
}
BeforeScanHook is called before scanning query results into the model.
type BeforeUpdateHook ¶
type BeforeUpdateHook interface {
BeforeUpdate(ctx context.Context, qc *QueryContext) error
}
BeforeUpdateHook is called before an UPDATE operation.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine manages hook registration and execution.
func (*Engine) AddHook ¶
AddHook registers a hook with the given scope. The hook must implement at least one of PreQueryHook, PostQueryHook, PreMutationHook, PostMutationHook.
func (*Engine) RunPostMutation ¶
RunPostMutation executes all matching PostMutationHook hooks.
func (*Engine) RunPostQuery ¶
RunPostQuery executes all matching PostQueryHook hooks.
func (*Engine) RunPreMutation ¶
func (e *Engine) RunPreMutation(ctx context.Context, qc *QueryContext, data any) (*HookResult, error)
RunPreMutation executes all matching PreMutationHook hooks.
func (*Engine) RunPreQuery ¶
func (e *Engine) RunPreQuery(ctx context.Context, qc *QueryContext) (*HookResult, error)
RunPreQuery executes all matching PreQueryHook hooks. Returns aggregated extra filters and any deny error.
func (*Engine) RunStreamRowHook ¶
RunStreamRowHook executes all matching StreamRowHook hooks for a single streamed row. Returns the decision as an int (matching Decision constants) and any error. A Skip decision means the row should be skipped; a Deny decision means iteration should stop.
type ExtraFilter ¶
type ExtraFilter struct {
// Clause is a raw WHERE fragment with placeholders.
// e.g., "tenant_id = $1"
Clause string
Args []any
// NativeFilter is a driver-specific filter document (e.g., bson.M for MongoDB).
NativeFilter any
}
ExtraFilter is a condition that a hook wants to inject into the query.
type HookResult ¶
type HookResult struct {
Decision Decision
Error error // Set when Decision == Deny.
Filters []ExtraFilter // Additional conditions to inject (pre-query).
}
HookResult is returned by pre-query/pre-mutation hooks.
type PostMutationHook ¶
type PostMutationHook interface {
AfterMutation(ctx context.Context, qc *QueryContext, data any, result any) error
}
PostMutationHook runs after mutations.
type PostQueryHook ¶
type PostQueryHook interface {
AfterQuery(ctx context.Context, qc *QueryContext, result any) error
}
PostQueryHook runs after query execution with the results.
type PreMutationHook ¶
type PreMutationHook interface {
BeforeMutation(ctx context.Context, qc *QueryContext, data any) (*HookResult, error)
}
PreMutationHook runs before mutations (INSERT/UPDATE/DELETE).
type PreQueryHook ¶
type PreQueryHook interface {
BeforeQuery(ctx context.Context, qc *QueryContext) (*HookResult, error)
}
PreQueryHook runs before the query is executed.
type QueryContext ¶
type QueryContext struct {
// Operation type.
Operation Operation
// Table or collection name.
Table string
// Model type (reflect.Type of the struct).
ModelType reflect.Type
// Columns being accessed (SELECT) or mutated (INSERT/UPDATE).
Columns []string
// PrivacyColumns maps columns to their grove:",privacy:X" classification.
// Only populated for columns that have a privacy tag.
PrivacyColumns map[string]string
// Conditions extracted from WHERE/filter (informational).
Conditions []Condition
// RawQuery is the built query string (available after building, before execution).
RawQuery string
// RawArgs are the query arguments.
RawArgs []any
// TenantID from context, if set.
TenantID string
// InTransaction reports whether the query runs inside an explicit
// transaction (built through a driver's Tx wrapper). Hooks that apply
// different policies per path — e.g. a tenant backstop that trusts
// RLS inside stamped transactions but denies pool-path access —
// branch on this.
InTransaction bool
// TagSource indicates whether the model uses grove:"..." or bun:"..." tags.
TagSource TagSource
// Values holds user-supplied context values.
Values map[string]any
}
QueryContext carries metadata about the pending operation.
type Scope ¶
type Scope struct {
// Tables restricts the hook to these tables. Empty means all tables.
Tables []string
// Operations restricts the hook to these operations. Empty means all operations.
Operations []Operation
// Priority determines execution order (lower = earlier). Default: 100.
Priority int
}
Scope determines when a hook applies.
type StreamRowHook ¶
type StreamRowHook interface {
OnStreamRow(ctx context.Context, qc *QueryContext, row any) (Decision, error)
}
StreamRowHook runs on every row yielded by a stream. This is critical for long-lived streams where permissions can change. Pre-query hooks run once when the stream is opened (filter injection, deny). StreamRowHook runs per-row as each row is decoded from the cursor.