01

Sample Code

hook-lifecycle.py
from psp.platform.hooks import (
    HookDispatcher, HookPoint, HookContext,
    Patch, Veto, SideEffectRequest
)

dispatcher = HookDispatcher()

# Hook 1: Auto-tag emails (priority 50 - runs first)
def auto_tag(ctx: HookContext) -> Patch | None:
    if "email" in ctx.entity_data.get("title", "").lower():
        return Patch(add_items={"tags": ["email"]})
    return None

dispatcher.register(HookPoint.BEFORE_CREATE_TASK, auto_tag, priority=50)

# Hook 2: Block empty titles (priority 100)
def block_empty(ctx: HookContext) -> Veto | None:
    if not ctx.entity_data.get("title", "").strip():
        return Veto(reason="Title cannot be empty")
    return None

dispatcher.register(HookPoint.BEFORE_CREATE_TASK, block_empty, priority=100)

# Hook 3: Notify on complete
def notify(ctx: HookContext) -> SideEffectRequest:
    return SideEffectRequest(kind="notify", payload={"task": ctx.entity_id})

dispatcher.register(HookPoint.AFTER_COMPLETE_TASK, notify)

# Dispatch before_create with valid data
ctx = HookContext(
    hook_point=HookPoint.BEFORE_CREATE_TASK,
    entity_type="Task",
    entity_id=None,
    entity_data={"title": "Check email inbox"},
)
result = dispatcher.dispatch(ctx)
print(f"Vetoed: {result.vetoed}")  # False
print(f"Tags to add: {result.merged_patch.add_items}")  # {'tags': ['email']}