Implement Service Layer API for Notification Event Emission
- Dominant language
- Python
- Stars
- 670
- Forks
- 183
- Avg merge
- 17h 7m
- Merged PRs (30d)
- 358
Description
## User Story
As a Backend.AI service developer, I want a clean API to emit notification events from service layer, so that I can easily integrate notifications into business logic without dealing with low-level details.
## Context
This story provides high-level service layer APIs for emitting notifications, making it easy for services to publish events to the Notification Center.
## Functional Requirements
### 1. Service Layer API
Provide simple, type-safe API for services:
```python
# src/ai/backend/manager/api/notification_service.py
class NotificationService:
def __init__(self, redis_client, db_session):
self.publisher = NotificationPublisher(redis_client)
self.db = db_session
async def emit(
self,
event_type: str,
subject: str,
context: dict[str, Any],
*,
body: str | None = None,
severity: Literal["info", "warning", "error", "critical"] = "info",
source: str | None = None,
metadata: dict[str, Any] | None = None,
action_url: str | None = None,
actions: list[dict] | None = None,
) -> str:
\"\"\"
Emit a notification event.
Returns:
event_id: Unique identifier for the emitted event
\"\"\"
event = NotificationEvent(
event_id=f"{event_type}-{uuid4()}",
event_type=event_type,
timestamp=datetime.now(timezone.utc),
source=source or "manager",
subject=subject,
body=body,
severity=severity,
context=context,
metadata=metadata or {},
action_url=action_url,
actions=actions,
)
await self.publisher.publish(event)
return event.event_id
```
### 2. Helper Functions for Common Patterns
Provide convenience methods for frequent use cases:
```python
class NotificationService:
# ... existing methods ...
async def emit_task_completed(
self,
task_id: str,
success_count: int,
failure_count: int,
duration: float,
**extra_context
):
\"\"\"Emit background task completion notification\"\"\"
return await self.emit(
event_type="bgtask.completed",
subject=f"Task {task_id} completed",
severity="info" if failure_count == 0 else "warning",
context={
"task_id": task_id,
"success_count": success_count,
"failure_count": failure_count,
"total_count": success_count + failure_count,
"success_rate": (success_count / (success_count + failure_count) * 100)
if (success_count + failure_count) > 0 else 0,
"duration": duration,
**extra_context
},
action_url=f"/tasks/{task_id}"
)
async def emit_quota_warning(
self,
domain: str,
resource_type: str,
current: float,
limit: float,
threshold_percent: float,
):
\"\"\"Emit resource quota warning\"\"\"
return await self.emit(
event_type="quota.warning",
subject=f"Resource quota warning: {resource_type}",
severity="warning",
context={
"domain": domain,
"resource_type": resource_type,
"current": current,
"limit": limit,
"usage_percent": (current / limit * 100) if limit > 0 else 0,
"threshold_percent": threshold_percent,
}
)
async def emit_system_error(
self,
error_type: str,
error_message: str,
stack_trace: str | None = None,
**extra_context
):
\"\"\"Emit system error notification\"\"\"
return await self.emit(
event_type="system.error",
subject=f"System error: {error_type}",
body=error_message,
severity="error",
context={
"error_type": error_type,
"error_message": error_message,
"stack_trace": stack_trace,
**extra_context
}
)
```
### 3. Context Manager for Batch Emission
Support efficient batch emission:
```python
class NotificationBatch:
def __init__(self, service: NotificationService):
self.service = service
self.events: list[NotificationEvent] = []
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.events:
await self.service.publisher.publish_batch(self.events)
def add(self, event: NotificationEvent):
self.events.append(event)
# Usage
async with NotificationBatch(notification_service) as batch:
for item in items:
event = NotificationEvent(...)
batch.add(event)
# All events sent on exit
```
### 4. Integration with Existing Services
Example integration points:
```python
# In bgtask reporter
class BackgroundTaskReporter:
def __init__(self, ..., notification_service: NotificationService):
self.notification_service = notification_service
async def report_completion(self, ...):
# Existing logic
await self._update_task_state(...)
# New: Emit notification
await self.notification_service.emit_task_completed(
task_id=task_id,
success_count=success_count,
failure_count=failure_count,
duration=duration,
)
```
## Acceptance Criteria
- [ ] NotificationService class with emit() method
- [ ] Helper methods for common event types
- [ ] Batch emission context manager
- [ ] Type hints and proper documentation
- [ ] Dependency injection via SharedConfig or Factory
- [ ] Unit tests with mock publisher
- [ ] Integration test with Redis
- [ ] Example usage in documentation
## Related Issues
Epic: BA-302
Depends on: BA-2861 (Core infrastructure)
JIRA Issue: BA-2863
Contributor guide
Assessment
This issue has not been assessed yet.