lablup / lablup/backend.ai

Implement Notification Handlers for Webhook, SMTP, Slack, and Discord

Open
#6,441 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
670
Forks
183
Avg merge
17h 7m
Merged PRs (30d)
358

Description

## User Story

As a Backend.AI developer, I want notification handlers for various delivery channels (Webhook, SMTP, Slack, Discord), so that notifications can be delivered to external systems and communication platforms.

## Context

This story implements concrete notification handlers that process NotificationEvent and deliver them through different channels based on NotificationChannel configuration.

## Functional Requirements

### 1. Notification Handler Interface

Define common handler protocol:

```python
class NotificationHandler(Protocol):
async def send(
self,
channel: NotificationChannel,
message: dict[str, Any],
event: NotificationEvent,
) -> None:
\"\"\"Send notification through this handler\"\"\"

async def validate_config(self, config: dict[str, Any]) -> None:
\"\"\"Validate channel configuration\"\"\"

async def test(self, channel: NotificationChannel) -> bool:
\"\"\"Send test notification\"\"\"
```

### 2. Webhook Handler

HTTP POST/PUT webhook delivery:

```python
class WebhookHandler:
async def send(self, channel, message, event):
config = channel.config
# Required: url
# Optional: method, headers, auth, timeout, retry

# Build request
# Send with retry logic
# Handle response

async def validate_config(self, config):
# Validate: url, method, headers format
# Check URL accessibility (optional)
```

**Config schema**:

```json
{
"url": "https://example.com/webhook",
"method": "POST",
"headers": {
"Authorization": "Bearer token",
"Content-Type": "application/json"
},
"timeout": 30,
"retry": {
"max_attempts": 3,
"backoff": "exponential"
}
}
```

### 3. SMTP Handler

Email delivery via SMTP:

```python
class SMTPHandler:
async def send(self, channel, message, event):
config = channel.config
# Required: host, port, from, to
# Optional: tls, auth (username/password), subject_prefix

# Build email (HTML + plain text)
# Send via SMTP

async def validate_config(self, config):
# Validate email addresses
# Check SMTP connectivity (optional)
```

**Config schema**:

```json
{
"host": "smtp.gmail.com",
"port": 587,
"from": "noreply@lablup.com",
"to": ["admin@lablup.com"],
"tls": true,
"auth": {
"username": "user",
"password": "pass"
},
"subject_prefix": "[Backend.AI]"
}
```

### 4. Slack Handler

Slack webhook integration:

```python
class SlackHandler:
async def send(self, channel, message, event):
config = channel.config
# Required: webhook_url
# Optional: channel, username, icon_emoji

# Convert to Slack message format
# Post to webhook

async def validate_config(self, config):
# Validate webhook_url format
# Test webhook (optional)
```

**Slack message format**:

```json
{
"channel": "#dev-notifications",
"username": "Backend.AI",
"icon_emoji": ":robot_face:",
"attachments": [{
"color": "good",
"title": "Task completed",
"text": "...",
"fields": [...],
"footer": "Backend.AI Notification"
}]
}
```

### 5. Discord Handler

Discord webhook integration:

```python
class DiscordHandler:
async def send(self, channel, message, event):
config = channel.config
# Required: webhook_url
# Optional: username, avatar_url

# Convert to Discord embed format
# Post to webhook
```

**Discord embed format**:

```json
{
"username": "Backend.AI",
"embeds": [{
"title": "Task completed",
"description": "...",
"color": 3066993,
"fields": [...],
"footer": {"text": "Backend.AI"}
}]
}
```

### 6. Template Rendering

Implement Jinja2 template rendering:

```python
class TemplateRenderer:
def render(self, template_str: str, event: NotificationEvent) -> dict:
template = jinja2.Template(template_str)

context = {
**event.context,
"event_id": event.event_id,
"timestamp": event.timestamp,
"subject": event.subject,
"severity": event.severity,
**event.metadata,
}

rendered = template.render(**context)
return json.loads(rendered)
```

### 7. Handler Registry Integration

Register all handlers:

```python
# In worker initialization
registry = HandlerRegistry()
registry.register("webhook", WebhookHandler())
registry.register("smtp", SMTPHandler())
registry.register("slack", SlackHandler())
registry.register("discord", DiscordHandler())
```

## Technical Requirements

1. **Retry Logic**: Exponential backoff for transient failures
1. **Timeout**: Configurable per handler
1. **Error Handling**: Proper exception hierarchy
1. **Logging**: Comprehensive logging for debugging
1. **Testing**: Unit tests with mocks

## Acceptance Criteria

- [ ] WebhookHandler with retry and auth support
- [ ] SMTPHandler with TLS and authentication
- [ ] SlackHandler with proper message formatting
- [ ] DiscordHandler with embed support
- [ ] Template renderer with Jinja2
- [ ] Config validation for each handler
- [ ] Test notification functionality
- [ ] Retry logic with exponential backoff
- [ ] Comprehensive error handling
- [ ] Unit tests for each handler
- [ ] Integration tests with mock servers

## Related Issues

Epic: BA-302
Depends on: BA-2861 (Core infrastructure)

JIRA Issue: BA-2865

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.