feat(B-HOOK): Lifecycle Hooks + Outbox Events — 55 neue Hooks, 10 Domain Events
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
B-HOOK-CORE: Company Hooks (6: before/after create/update/delete) B-HOOK-MAIL: Mail Hooks (5: after_receive, before/after_delete, before/after_move) B-HOOK-DMS: DMS Hooks (10: after_upload, before/after_update/delete/restore, folder CRUD) B-HOOK-CAL: Calendar Hooks (4: before/after update/delete) B-HOOK-TASK: Task Hooks (6: before/after create/update/delete) B-HOOK-COMM: Communication Hooks (8: conversation create, message/edit/delete) B-HOOK-AI: Agent Hooks (2: before/after_run) B-HOOK-WF: Workflow Hooks (4: before/after_start, after_complete/cancel) B-HOOK-TAG: Tag Hooks (8: create/assign/unassign/delete) B-HOOK-SEARCH: Search Filters (2: before/after_search) B-EVT-OUTBOX: 10 Domain Events (task.completed, file.created/deleted/restored, mail.received, workflow.started/completed/cancelled, agent.run_started/completed) B-HOOK-TEST: 79 Tests in test_lifecycle_hooks.py — alle grün B-HOOK-DOC: Plugin-Dev-Guide Kapitel 8 aktualisiert (Hook-Liste + Outbox Event-Liste)
This commit is contained in:
@@ -1003,6 +1003,88 @@ await do_action("contact.before_create", contact_data, db=db)
|
||||
display_name = await apply_filters("contact.format_display_name", contact.name)
|
||||
```
|
||||
|
||||
#### Verfügbare Hooks
|
||||
|
||||
**Actions** (fire-and-forget, kein Return-Wert):
|
||||
|
||||
| Hook Name | Modul | Parameter |
|
||||
|-----------|-------|----------|
|
||||
| `contact.before_create` | `app/services/contact_service.py` | `data, db, tenant_id, user_id` |
|
||||
| `contact.after_create` | `app/services/contact_service.py` | `serialized, db, tenant_id, user_id` |
|
||||
| `contact.before_update` | `app/services/contact_service.py` | `data, db, tenant_id, user_id, contact_id` |
|
||||
| `contact.after_update` | `app/services/contact_service.py` | `snapshot, db, tenant_id, user_id, contact_id` |
|
||||
| `contact.before_delete` | `app/services/contact_service.py` | `db, tenant_id, contact_id, user_id` |
|
||||
| `contact.after_delete` | `app/services/contact_service.py` | `db, tenant_id, contact_id, user_id` |
|
||||
| `company.before_create` | `app/routes/companies.py` | `body, db, tenant_id, user_id` |
|
||||
| `company.after_create` | `app/routes/companies.py` | `serialized, db, tenant_id, user_id` |
|
||||
| `company.before_update` | `app/routes/companies.py` | `body, db, tenant_id, user_id, company_id` |
|
||||
| `company.after_update` | `app/routes/companies.py` | `serialized, db, tenant_id, user_id, company_id` |
|
||||
| `company.before_delete` | `app/routes/companies.py` | `db, tenant_id, user_id, company_id` |
|
||||
| `company.after_delete` | `app/routes/companies.py` | `snapshot, db, tenant_id, user_id, company_id` |
|
||||
| `mail.before_send` | `app/plugins/builtins/mail/services.py` | `mail_data` (Filter) |
|
||||
| `mail.after_send` | `app/plugins/builtins/mail/services.py` | `mail_data, db, account, msg_id` |
|
||||
| `mail.after_receive` | `app/plugins/builtins/mail/services.py` | `payload, db, tenant_id` |
|
||||
| `mail.before_delete` | `app/plugins/builtins/mail/services.py` | `mail_id, tenant_id, permanent, db` |
|
||||
| `mail.after_delete` | `app/plugins/builtins/mail/services.py` | `mail_id, tenant_id, permanent, db` |
|
||||
| `mail.before_move` | `app/plugins/builtins/mail/services.py` | `mail_id, tenant_id, source_folder_id, target_folder_id, db` |
|
||||
| `mail.after_move` | `app/plugins/builtins/mail/services.py` | `mail_id, tenant_id, source_folder_id, target_folder_id, db` |
|
||||
| `dms.before_upload` | `app/plugins/builtins/dms/routes.py` | `upload_data` (Filter) |
|
||||
| `dms.after_upload` | `app/plugins/builtins/dms/routes.py` | `payload, db, tenant_id, user_id` |
|
||||
| `dms.before_update` | `app/plugins/builtins/dms/routes.py` | `data, db, tenant_id, user_id, file_id` |
|
||||
| `dms.after_update` | `app/plugins/builtins/dms/routes.py` | `payload, db, tenant_id, user_id, file_id` |
|
||||
| `dms.before_delete` | `app/plugins/builtins/dms/routes.py` | `db, tenant_id, user_id, file_id` |
|
||||
| `dms.after_delete` | `app/plugins/builtins/dms/routes.py` | `db, tenant_id, user_id, file_id` |
|
||||
| `dms.before_restore` | `app/plugins/builtins/dms/routes.py` | `db, tenant_id, user_id, file_id` |
|
||||
| `dms.after_restore` | `app/plugins/builtins/dms/routes.py` | `payload, db, tenant_id, user_id, file_id` |
|
||||
| `dms.folder.before_create` | `app/plugins/builtins/dms/routes.py` | `body, db, tenant_id, user_id` |
|
||||
| `dms.folder.after_create` | `app/plugins/builtins/dms/routes.py` | `payload, db, tenant_id, user_id` |
|
||||
| `dms.folder.before_delete` | `app/plugins/builtins/dms/routes.py` | `db, tenant_id, user_id, folder_id` |
|
||||
| `dms.folder.after_delete` | `app/plugins/builtins/dms/routes.py` | `db, tenant_id, user_id, folder_id` |
|
||||
| `calendar.before_appointment` | `app/plugins/builtins/calendar/routes.py` | `body, tenant_id, user_id, cal_id` |
|
||||
| `calendar.after_appointment` | `app/plugins/builtins/calendar/routes.py` | `entry_id, tenant_id, user_id` |
|
||||
| `calendar.before_update` | `app/plugins/builtins/calendar/routes.py` | `body, tenant_id, user_id, entry_id` |
|
||||
| `calendar.after_update` | `app/plugins/builtins/calendar/routes.py` | `entry_id, tenant_id, user_id` |
|
||||
| `calendar.before_delete` | `app/plugins/builtins/calendar/routes.py` | `tenant_id, user_id, entry_id` |
|
||||
| `calendar.after_delete` | `app/plugins/builtins/calendar/routes.py` | `tenant_id, user_id, entry_id` |
|
||||
| `task.before_create` | `app/plugins/builtins/tasks/services.py` | `data, db, tenant_id, user_id` |
|
||||
| `task.after_create` | `app/plugins/builtins/tasks/services.py` | `serialized, db, tenant_id, user_id` |
|
||||
| `task.before_update` | `app/plugins/builtins/tasks/services.py` | `data, db, tenant_id, task_id` |
|
||||
| `task.after_update` | `app/plugins/builtins/tasks/services.py` | `serialized, db, tenant_id, task_id` |
|
||||
| `task.before_delete` | `app/plugins/builtins/tasks/services.py` | `db, tenant_id, task_id` |
|
||||
| `task.after_delete` | `app/plugins/builtins/tasks/services.py` | `db, tenant_id, task_id` |
|
||||
| `comm.conversation.before_create` | `app/plugins/builtins/kommunikation/services.py` | `tenant_id, user_id` |
|
||||
| `comm.conversation.after_create` | `app/plugins/builtins/kommunikation/services.py` | `conversation_id, tenant_id, user_id` |
|
||||
| `comm.before_message` | `app/plugins/builtins/kommunikation/services.py` | `conversation_id, tenant_id, sender_id` |
|
||||
| `comm.after_message` | `app/plugins/builtins/kommunikation/services.py` | `message_id, conversation_id, tenant_id, sender_id` |
|
||||
| `comm.before_edit` | `app/plugins/builtins/kommunikation/services.py` | `message_id, tenant_id, user_id` |
|
||||
| `comm.after_edit` | `app/plugins/builtins/kommunikation/services.py` | `message_id, tenant_id, user_id` |
|
||||
| `comm.before_delete` | `app/plugins/builtins/kommunikation/services.py` | `message_id` |
|
||||
| `comm.after_delete` | `app/plugins/builtins/kommunikation/services.py` | `message_id` |
|
||||
| `agent.before_run` | `app/plugins/builtins/automation/agent_runner.py` | `agent_id, tenant_id, trigger_type` |
|
||||
| `agent.after_run` | `app/plugins/builtins/automation/agent_runner.py` | `agent_id, tenant_id, status, result` |
|
||||
| `workflow.before_start` | `app/services/workflow_service.py` | `instance_id, workflow_id, tenant_id, user_id` |
|
||||
| `workflow.after_start` | `app/services/workflow_service.py` | `instance_id, workflow_id, tenant_id, user_id` |
|
||||
| `workflow.after_complete` | `app/services/workflow_service.py` | `instance_id, workflow_id, tenant_id, user_id` |
|
||||
| `workflow.after_cancel` | `app/services/workflow_service.py` | `instance_id, workflow_id, tenant_id, user_id` |
|
||||
| `tag.before_create` | `app/plugins/builtins/tags/routes.py` | `body, db, tenant_id, user_id` |
|
||||
| `tag.after_create` | `app/plugins/builtins/tags/routes.py` | `payload, db, tenant_id, user_id` |
|
||||
| `tag.before_assign` | `app/plugins/builtins/tags/routes.py` | `body, db, tenant_id, user_id` |
|
||||
| `tag.after_assign` | `app/plugins/builtins/tags/routes.py` | `payload, db, tenant_id, user_id` |
|
||||
| `tag.before_unassign` | `app/plugins/builtins/tags/routes.py` | `body, db, tenant_id, user_id` |
|
||||
| `tag.after_unassign` | `app/plugins/builtins/tags/routes.py` | `payload, db, tenant_id, user_id` |
|
||||
| `tag.before_delete` | `app/plugins/builtins/tags/routes.py` | `db, tenant_id, user_id, tag_id` |
|
||||
| `tag.after_delete` | `app/plugins/builtins/tags/routes.py` | `db, tenant_id, user_id, tag_id` |
|
||||
|
||||
**Filters** (modifizieren Wert, Return erforderlich):
|
||||
|
||||
| Hook Name | Modul | Parameter |
|
||||
|-----------|-------|----------|
|
||||
| `contact.format_display_name` | `app/services/contact_service.py` | `name, data, db, tenant_id, user_id` |
|
||||
| `dms.before_upload` | `app/plugins/builtins/dms/routes.py` | `upload_data` (filename, mime_type) |
|
||||
| `mail.before_send` | `app/plugins/builtins/mail/services.py` | `mail_data` |
|
||||
| `search.before_search` | `app/plugins/builtins/unified_search/search_engine.py` | `query_analysis` |
|
||||
| `search.after_search` | `app/plugins/builtins/unified_search/search_engine.py` | `all_results` |
|
||||
|
||||
### 8.2 EventBus (`app/core/event_bus.py`)
|
||||
|
||||
**Wann verwenden:** Für flüchtige interne Notifikationen, UI-Events, Proactive Suggestions. **Nicht** für Events die reliable Delivery brauchen.
|
||||
@@ -1036,6 +1118,26 @@ await enqueue_outbox_event(db, tenant_id, "contact.created", {
|
||||
|
||||
**Features:** DLQ (`error_message`, `failed_at`), Replay (`replay_failed_event`), Consumer Registry, Stats.
|
||||
|
||||
#### Verfügbare Outbox Events
|
||||
|
||||
| Event Name | Modul | Payload | Aggregate |
|
||||
|------------|-------|---------|-----------|
|
||||
| `contact.created` | `app/services/contact_service.py` | `contact_id, tenant_id, displayname, type` | `contact` |
|
||||
| `contact.updated` | `app/services/contact_service.py` | `contact_id, tenant_id, changes` | `contact` |
|
||||
| `lead.created` | `app/services/contact_service.py` | `contact_id, tenant_id` | `contact` |
|
||||
| `mail.received` | `app/plugins/builtins/mail/services.py` | `mail_id, tenant_id, account_id, folder_id, subject, from_address` | `mail` |
|
||||
| `mail.send` | `app/plugins/builtins/mail/services.py` | `mail_id, tenant_id, account_id` | `mail` |
|
||||
| `file.created` | `app/plugins/builtins/dms/routes.py` | `file_id, tenant_id, name, mime_type, size_bytes` | `dms_file` |
|
||||
| `file.deleted` | `app/plugins/builtins/dms/routes.py` | `file_id, tenant_id` | `dms_file` |
|
||||
| `file.restored` | `app/plugins/builtins/dms/routes.py` | `file_id, tenant_id` | `dms_file` |
|
||||
| `dms.file.uploaded` | `app/plugins/builtins/dms/routes.py` | (legacy alias for `file.created`) | `dms_file` |
|
||||
| `task.completed` | `app/plugins/builtins/tasks/services.py` | `task_id, tenant_id, title, assigned_to` | `task` |
|
||||
| `workflow.started` | `app/services/workflow_service.py` | `instance_id, workflow_id, tenant_id` | `workflow_instance` |
|
||||
| `workflow.completed` | `app/services/workflow_service.py` | `instance_id, workflow_id, tenant_id, status` | `workflow_instance` |
|
||||
| `workflow.cancelled` | `app/services/workflow_service.py` | `instance_id, workflow_id, tenant_id` | `workflow_instance` |
|
||||
| `agent.run_started` | `app/plugins/builtins/automation/agent_runner.py` | `agent_id, tenant_id, trigger_type` | `agent` |
|
||||
| `agent.run_completed` | `app/plugins/builtins/automation/agent_runner.py` | `agent_id, tenant_id, status, cost_usd` | `agent` |
|
||||
|
||||
### 8.4 WebhookDispatcher (`app/core/webhook_dispatcher.py`)
|
||||
|
||||
**Wann verwenden:** Für externe HTTP-Zustellung an registrierte Webhook-URLs.
|
||||
|
||||
Reference in New Issue
Block a user