Mission Control: notify OpenClaw via tools/invoke on task events

This commit is contained in:
Abhimanyu Saharan
2026-02-02 16:05:18 +05:30
parent 63419a26f1
commit 2bcd078c53
7 changed files with 254 additions and 12 deletions

View File

@@ -79,11 +79,17 @@ def list_employees(session: Session = Depends(get_session)):
def create_employee(payload: EmployeeCreate, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
emp = Employee(**payload.model_dump())
session.add(emp)
session.commit()
try:
session.flush()
log_activity(session, actor_employee_id=actor_employee_id, entity_type="employee", entity_id=emp.id, verb="created", payload={"name": emp.name, "type": emp.employee_type})
session.commit()
except IntegrityError:
session.rollback()
raise HTTPException(status_code=409, detail="Employee create violates constraints")
session.refresh(emp)
log_activity(session, actor_employee_id=actor_employee_id, entity_type="employee", entity_id=emp.id, verb="created", payload={"name": emp.name, "type": emp.employee_type})
session.commit()
return emp
return Employee.model_validate(emp)
@router.patch("/employees/{employee_id}", response_model=Employee)
@@ -97,8 +103,13 @@ def update_employee(employee_id: int, payload: EmployeeUpdate, session: Session
setattr(emp, k, v)
session.add(emp)
session.commit()
try:
session.flush()
log_activity(session, actor_employee_id=actor_employee_id, entity_type="employee", entity_id=emp.id, verb="updated", payload=data)
session.commit()
except IntegrityError:
session.rollback()
raise HTTPException(status_code=409, detail="Employee update violates constraints")
session.refresh(emp)
log_activity(session, actor_employee_id=actor_employee_id, entity_type="employee", entity_id=emp.id, verb="updated", payload=data)
session.commit()
return emp
return Employee.model_validate(emp)

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
from sqlmodel import Session, select
from sqlalchemy.exc import IntegrityError
@@ -10,6 +10,7 @@ from app.api.utils import log_activity, get_actor_employee_id
from app.db.session import get_session
from app.models.work import Task, TaskComment
from app.schemas.work import TaskCommentCreate, TaskCreate, TaskUpdate
from app.integrations.notify import NotifyContext, notify_openclaw
router = APIRouter(tags=["work"])
@@ -25,7 +26,7 @@ def list_tasks(project_id: int | None = None, session: Session = Depends(get_ses
@router.post("/tasks", response_model=Task)
def create_task(payload: TaskCreate, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
def create_task(payload: TaskCreate, background: BackgroundTasks, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
if payload.created_by_employee_id is None:
payload = TaskCreate(**{**payload.model_dump(), "created_by_employee_id": actor_employee_id})
@@ -51,16 +52,19 @@ def create_task(payload: TaskCreate, session: Session = Depends(get_session), ac
raise HTTPException(status_code=409, detail="Task create violates constraints")
session.refresh(task)
background.add_task(notify_openclaw, session, NotifyContext(event="task.created", actor_employee_id=actor_employee_id, task=task))
# Explicitly return a serializable payload (guards against empty {} responses)
return Task.model_validate(task)
@router.patch("/tasks/{task_id}", response_model=Task)
def update_task(task_id: int, payload: TaskUpdate, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
def update_task(task_id: int, payload: TaskUpdate, background: BackgroundTasks, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
task = session.get(Task, task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
before = {"assignee_employee_id": task.assignee_employee_id, "reviewer_employee_id": task.reviewer_employee_id, "status": task.status}
data = payload.model_dump(exclude_unset=True)
if "status" in data and data["status"] not in ALLOWED_STATUSES:
raise HTTPException(status_code=400, detail="Invalid status")
@@ -79,6 +83,18 @@ def update_task(task_id: int, payload: TaskUpdate, session: Session = Depends(ge
raise HTTPException(status_code=409, detail="Task update violates constraints")
session.refresh(task)
# notify based on meaningful changes
changed = {}
if before.get("assignee_employee_id") != task.assignee_employee_id:
changed["assignee_employee_id"] = {"from": before.get("assignee_employee_id"), "to": task.assignee_employee_id}
background.add_task(notify_openclaw, session, NotifyContext(event="task.assigned", actor_employee_id=actor_employee_id, task=task, changed_fields=changed))
if before.get("status") != task.status:
changed["status"] = {"from": before.get("status"), "to": task.status}
background.add_task(notify_openclaw, session, NotifyContext(event="status.changed", actor_employee_id=actor_employee_id, task=task, changed_fields=changed))
if not changed and data:
background.add_task(notify_openclaw, session, NotifyContext(event="task.updated", actor_employee_id=actor_employee_id, task=task, changed_fields=data))
return Task.model_validate(task)
@@ -106,7 +122,7 @@ def list_task_comments(task_id: int, session: Session = Depends(get_session)):
@router.post("/task-comments", response_model=TaskComment)
def create_task_comment(payload: TaskCommentCreate, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
def create_task_comment(payload: TaskCommentCreate, background: BackgroundTasks, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
if payload.author_employee_id is None:
payload = TaskCommentCreate(**{**payload.model_dump(), "author_employee_id": actor_employee_id})
@@ -122,4 +138,7 @@ def create_task_comment(payload: TaskCommentCreate, session: Session = Depends(g
raise HTTPException(status_code=409, detail="Comment create violates constraints")
session.refresh(c)
task = session.get(Task, c.task_id)
if task is not None:
background.add_task(notify_openclaw, session, NotifyContext(event="comment.created", actor_employee_id=actor_employee_id, task=task, comment=c))
return TaskComment.model_validate(c)