HH/docs/EXTERNAL_API_NOTIFICATION.md

517 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# External API Notification Service
## Overview
The PX360 notification system has been extended to support sending emails and SMS via external API endpoints. This allows integration with third-party notification services while maintaining backward compatibility with existing Django SMTP functionality.
## Features
-**Separate API Methods**: `send_email_via_api()` and `send_sms_via_api()`
-**Generic Request Format**: Simple JSON structure compatible with most APIs
-**Flexible Authentication**: Supports both `Bearer` token and `X-API-KEY` header
-**Fire-and-Forget**: Accepts any 2xx response (API processes in background)
-**Retry Logic**: Exponential backoff with configurable max retries
-**Comprehensive Logging**: All attempts logged to Django logger and NotificationLog
-**Error Handling**: Handles timeouts, connection errors, and HTTP errors
-**Configurable**: All settings via environment variables
-**Non-Breaking**: Existing code continues to work unchanged
## Configuration
### Environment Variables
Add these to your `.env` file:
```bash
# External Email API
EMAIL_API_ENABLED=false # Enable/disable email API
EMAIL_API_URL=https://api.example.com/send-email
EMAIL_API_KEY=your-api-key-here
EMAIL_API_AUTH_METHOD=bearer # Options: bearer, api_key
EMAIL_API_METHOD=POST
EMAIL_API_TIMEOUT=10 # Request timeout in seconds
EMAIL_API_MAX_RETRIES=3 # Maximum retry attempts
EMAIL_API_RETRY_DELAY=2 # Initial delay in seconds
# External SMS API
SMS_API_ENABLED=false # Enable/disable SMS API
SMS_API_URL=https://api.example.com/send-sms
SMS_API_KEY=your-api-key-here
SMS_API_AUTH_METHOD=bearer # Options: bearer, api_key
SMS_API_METHOD=POST
SMS_API_TIMEOUT=10 # Request timeout in seconds
SMS_API_MAX_RETRIES=3 # Maximum retry attempts
SMS_API_RETRY_DELAY=2 # Initial delay in seconds
```
### Settings Structure
The configuration is automatically loaded into `settings.EXTERNAL_NOTIFICATION_API`:
```python
EXTERNAL_NOTIFICATION_API = {
'email': {
'enabled': env.bool('EMAIL_API_ENABLED', default=False),
'url': env('EMAIL_API_URL', default=''),
'api_key': env('EMAIL_API_KEY', default=''),
'auth_method': env('EMAIL_API_AUTH_METHOD', default='bearer'),
'method': env('EMAIL_API_METHOD', default='POST'),
'timeout': env.int('EMAIL_API_TIMEOUT', default=10),
'max_retries': env.int('EMAIL_API_MAX_RETRIES', default=3),
'retry_delay': env.int('EMAIL_API_RETRY_DELAY', default=2),
},
'sms': {
'enabled': env.bool('SMS_API_ENABLED', default=False),
'url': env('SMS_API_URL', default=''),
'api_key': env('SMS_API_KEY', default=''),
'auth_method': env('SMS_API_AUTH_METHOD', default='bearer'),
'method': env('SMS_API_METHOD', default='POST'),
'timeout': env.int('SMS_API_TIMEOUT', default=10),
'max_retries': env.int('SMS_API_MAX_RETRIES', default=3),
'retry_delay': env.int('SMS_API_RETRY_DELAY', default=2),
},
}
```
## API Request Format
### Email API Request
**Endpoint**: POST to `EMAIL_API_URL`
**Headers**:
```
Content-Type: application/json
Authorization: Bearer {EMAIL_API_KEY} # or X-API-KEY: {EMAIL_API_KEY}
```
**Request Body**:
```json
{
"to": "recipient@example.com",
"subject": "Email Subject",
"message": "Plain text message",
"html_message": "<html>Optional HTML content</html>"
}
```
**Expected Response**: Any 2xx status code (200-299)
### SMS API Request
**Endpoint**: POST to `SMS_API_URL`
**Headers**:
```
Content-Type: application/json
Authorization: Bearer {SMS_API_KEY} # or X-API-KEY: {SMS_API_KEY}
```
**Request Body**:
```json
{
"to": "+966501234567",
"message": "SMS message text"
}
```
**Expected Response**: Any 2xx status code (200-299)
## Usage
### Basic Email via API
```python
from apps.notifications.services import NotificationService
# Simple email
log = NotificationService.send_email_via_api(
message='Your account has been created',
email='user@example.com',
subject='Welcome to PX360'
)
# Email with HTML
log = NotificationService.send_email_via_api(
message='Plain text version',
email='user@example.com',
subject='Welcome',
html_message='<h1>Welcome</h1><p>Your account is ready!</p>'
)
# Email with tracking
complaint = Complaint.objects.get(id=123)
log = NotificationService.send_email_via_api(
message='You have a new complaint...',
email='staff@example.com',
subject='New Complaint',
related_object=complaint,
metadata={'complaint_id': str(complaint.id)}
)
```
### Basic SMS via API
```python
from apps.notifications.services import NotificationService
# Simple SMS
log = NotificationService.send_sms_via_api(
message='Your verification code is 123456',
phone='+966501234567'
)
# SMS with tracking
survey_instance = SurveyInstance.objects.get(id=123)
log = NotificationService.send_sms_via_api(
message='Please complete your survey: https://...',
phone='+966501234567',
related_object=survey_instance,
metadata={'survey_id': str(survey_instance.id)}
)
```
### Comparison with Existing Methods
```python
from apps.notifications.services import NotificationService
# Existing method - uses Django SMTP
NotificationService.send_email(
email='user@example.com',
subject='Welcome',
message='Hello!'
)
# New method - uses external API
NotificationService.send_email_via_api(
message='Hello!',
email='user@example.com',
subject='Welcome'
)
```
## Retry Logic
Both API methods implement exponential backoff retry:
1. **Initial attempt** (retry 0)
2. Wait 2 seconds (retry_delay)
3. **First retry** (retry 1)
4. Wait 4 seconds (retry_delay × 2^1)
5. **Second retry** (retry 2)
6. Wait 8 seconds (retry_delay × 2^2)
7. **Final retry** (retry 3)
Configurable via:
- `EMAIL_API_MAX_RETRIES` / `SMS_API_MAX_RETRIES` (default: 3)
- `EMAIL_API_RETRY_DELAY` / `SMS_API_RETRY_DELAY` (default: 2 seconds)
## Error Handling
All errors are handled gracefully:
### Connection Errors
- Logged as "Connection error"
- Retry attempted if retries available
### Timeout Errors
- Logged as "Request timeout"
- Retry attempted if retries available
### HTTP Errors
- Non-2xx status codes logged with status number
- Retry attempted if retries available
### Unexpected Errors
- Full error message logged
- Retry attempted if retries available
### Final State
- After all retries exhausted, NotificationLog marked as `failed`
- Error details stored in log's error_message field
## Database Tracking
All API notifications are tracked in the `NotificationLog` model:
```python
log = NotificationService.send_email_via_api(
message='Hello',
email='user@example.com',
subject='Test'
)
# Check status
print(log.status) # 'sent' or 'failed'
# Check metadata
print(log.metadata)
# {
# 'api_url': 'https://api.example.com/send-email',
# 'auth_method': 'bearer',
# ...
# }
```
## Logging
All API calls are logged to Django logger at different levels:
### INFO Level
- Successful API calls
- API request initiation
### WARNING Level
- Non-2xx HTTP responses
- Connection errors (during retries)
- Timeout errors (during retries)
### ERROR Level
- Unexpected exceptions
- Final failure after all retries
Logs are written to:
- Console (in development)
- `logs/px360.log` (file)
- `logs/integrations.log` (integration-specific)
## Examples
### Example 1: Send Welcome Email via API
```python
def send_welcome_email(user):
from apps.notifications.services import NotificationService
log = NotificationService.send_email_via_api(
message=f'Welcome {user.get_full_name()}!',
email=user.email,
subject='Welcome to PX360'
)
if log and log.status == 'sent':
print("Email sent successfully via API")
else:
print("Failed to send email")
```
### Example 2: Send SMS Verification Code
```python
def send_verification_code(phone, code):
from apps.notifications.services import NotificationService
log = NotificationService.send_sms_via_api(
message=f'Your verification code is: {code}',
phone=phone
)
return log.status == 'sent'
```
### Example 3: Send Complaint Notification via API
```python
def notify_complaint_department(complaint):
from apps.notifications.services import NotificationService
department = complaint.department
log = NotificationService.send_email_via_api(
message=f'New complaint received from {complaint.patient.get_full_name()}',
email=department.contact_email,
subject=f'New Complaint: {complaint.ticket_number}',
related_object=complaint,
metadata={
'complaint_id': str(complaint.id),
'ticket_number': complaint.ticket_number
}
)
return log
```
### Example 4: Send Survey Invitation via API
```python
def send_survey_invitation_via_api(survey_instance):
from apps.notifications.services import NotificationService
patient = survey_instance.patient
survey_url = survey_instance.get_survey_url()
if survey_instance.delivery_channel == 'email':
log = NotificationService.send_email_via_api(
message=f'Please complete your survey: {survey_url}',
email=patient.email,
subject='Experience Survey',
related_object=survey_instance
)
elif survey_instance.delivery_channel == 'sms':
log = NotificationService.send_sms_via_api(
message=f'Complete your survey: {survey_url}',
phone=patient.phone,
related_object=survey_instance
)
return log
```
## Troubleshooting
### API Disabled
**Problem**: Emails/SMS not being sent via API
**Solution**: Check that `EMAIL_API_ENABLED` or `SMS_API_ENABLED` is set to `true` in `.env`
```bash
# Check environment variable
echo $EMAIL_API_ENABLED
# Enable if disabled
EMAIL_API_ENABLED=true
```
### Authentication Failures
**Problem**: API returning 401 or 403 errors
**Solution**: Verify API key and auth method
```bash
# Check auth method
EMAIL_API_AUTH_METHOD=bearer # or api_key
# Verify API key is correct
EMAIL_API_KEY=your-actual-api-key
```
### Connection Timeout
**Problem**: Requests timing out
**Solution**: Increase timeout value
```bash
EMAIL_API_TIMEOUT=30 # Increase from 10 to 30 seconds
```
### Frequent Failures
**Problem**: API calls failing consistently
**Solution**: Check logs for specific error messages
```bash
# View logs
tail -f logs/px360.log | grep "API"
# Check NotificationLog in Django admin
# Navigate to /admin/notifications/notificationlog/
```
### No API Calls Being Made
**Problem**: Methods returning `None`
**Solution**: Verify configuration is loaded
```python
from django.conf import settings
# Check configuration
print(settings.EXTERNAL_NOTIFICATION_API)
# Should show 'email' and 'sms' keys with configuration
```
## Migration Guide
### Migrating from Django SMTP to API
**Before** (using Django SMTP):
```python
NotificationService.send_email(
email='user@example.com',
subject='Welcome',
message='Hello!'
)
```
**After** (using external API):
```python
NotificationService.send_email_via_api(
message='Hello!',
email='user@example.com',
subject='Welcome'
)
```
Note: The parameter order is slightly different - `message` comes before `email` for API methods.
### Gradual Migration Strategy
You can use both methods in parallel:
```python
def send_email(email, subject, message):
from apps.notifications.services import NotificationService
from django.conf import settings
# Use API if enabled
if settings.EXTERNAL_NOTIFICATION_API['email']['enabled']:
return NotificationService.send_email_via_api(
message=message,
email=email,
subject=subject
)
# Fall back to Django SMTP
else:
return NotificationService.send_email(
email=email,
subject=subject,
message=message
)
```
## Security Considerations
1. **API Keys**: Never commit `.env` file to version control
2. **HTTPS**: Always use HTTPS URLs for API endpoints
3. **Authentication**: Use Bearer tokens or API keys, never basic auth
4. **Logging**: API keys are not logged in full (metadata stores auth_method, not key)
5. **Timeouts**: Set reasonable timeouts to prevent hanging requests
## Performance Impact
- **Network Latency**: Each API call adds network round-trip time
- **Retries**: Failed requests are retried with exponential backoff
- **Logging**: Minimal overhead from logging to database
- **Async Consideration**: For high-volume scenarios, consider using Celery tasks
## Future Enhancements
Potential improvements for the API notification service:
1. **Async Support**: Integrate with Celery for background processing
2. **Webhooks**: Support for delivery status callbacks
3. **Templates**: Built-in email/SMS template support
4. **Rate Limiting**: Implement API rate limiting
5. **Bulk Operations**: Support for batch email/SMS sending
6. **Provider SDKs**: Integrate provider-specific SDKs (SendGrid, Twilio, etc.)
## Support
For issues or questions:
1. Check logs: `logs/px360.log` and `logs/integrations.log`
2. Verify `.env` configuration
3. Check `NotificationLog` entries in Django admin
4. Test API endpoint independently (curl, Postman, etc.)
## Related Documentation
- [Notification Service](../apps/notifications/README.md)
- [Environment Configuration](../config/settings/DJANGO_ENVIRON_CONFIG.md)
- [Logging Configuration](../config/settings/base.py#logging-configuration)