516 lines
17 KiB
Markdown
516 lines
17 KiB
Markdown
# Resumen de Implementación: API de Notificaciones - VoxPopuli
|
|
|
|
**Fecha:** 29 de Abril de 2024
|
|
**Versión:** 1.0.0
|
|
**Autor:** VoxPopuli Development Team
|
|
|
|
---
|
|
|
|
## 📋 Resumen Ejecutivo
|
|
|
|
Se ha implementado **exitosamente** una nueva **API de Notificaciones** que se integra con la arquitectura existente del proyecto. Las notificaciones se disparan automáticamente cuando los reportes cambian de estado, proporcionando a los usuarios actualizaciones en tiempo real sobre el progreso de sus reportes.
|
|
|
|
### Puntos Clave:
|
|
✅ Nueva API de Notificaciones en **puerto 8002**
|
|
✅ Base de datos MongoDB dedicada (`voxpopuli_notifications`)
|
|
✅ Integración automática con eventos de cambio de estado de reportes
|
|
✅ Sistema de consumidores RabbitMQ para procesamiento asíncrono
|
|
✅ Arquitectura hexagonal consistente con el resto del proyecto
|
|
✅ Docker-compose actualizado con nuevos servicios
|
|
|
|
---
|
|
|
|
## 📁 Estructura de Archivos Creados
|
|
|
|
```
|
|
src/
|
|
├── domain/
|
|
│ └── notifications.py ✨ Nuevo
|
|
├── application/
|
|
│ ├── ports/
|
|
│ │ └── notification_repository.py ✨ Nuevo
|
|
│ └── services/
|
|
│ └── notification_services.py ✨ Nuevo
|
|
├── infrastructure/
|
|
│ ├── adapters/
|
|
│ │ └── persistence/
|
|
│ │ └── notification_repository_mongo.py ✨ Nuevo
|
|
│ └── api/
|
|
│ └── notifications/ ✨ Nuevo directorio
|
|
│ ├── __init__.py
|
|
│ ├── app.py
|
|
│ ├── router.py
|
|
│ ├── root.py
|
|
│ ├── schemas.py
|
|
│ └── notifications.py
|
|
└── consumers/
|
|
└── notification_consumer.py ✨ Nuevo
|
|
|
|
📄 FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md ✨ Nuevo
|
|
📄 IMPLEMENTATION_SUMMARY.md ✨ Nuevo (este archivo)
|
|
```
|
|
|
|
---
|
|
|
|
## 🏗️ Cambios en Archivos Existentes
|
|
|
|
### 1. `src/core/config.py`
|
|
- ✏️ Agregado: `mongodb_notifications_db` para configuración de BD de notificaciones
|
|
- ✏️ Actualizado comentario de `mongodb_url` para incluir ambas APIs
|
|
|
|
```python
|
|
mongodb_notifications_db: str = Field(
|
|
default="voxpopuli_notifications",
|
|
description="Base de datos MongoDB para Notificaciones"
|
|
)
|
|
```
|
|
|
|
### 2. `src/main.py`
|
|
- ✏️ Importado: `create_notifications_app` desde API de notificaciones
|
|
- ✏️ Importado: `NotificationConsumer`
|
|
- ✏️ Agregado: Thread para ejecutar API de notificaciones (puerto 8002)
|
|
- ✏️ Agregado: Thread para ejecutar consumidor de notificaciones
|
|
- ✏️ Actualizado: Mensajes de inicio para mostrar la nueva API
|
|
|
|
```python
|
|
# Cambios principales
|
|
from infrastructure.api.notifications.app import create_app as create_notifications_app
|
|
from consumers.notification_consumer import NotificationConsumer
|
|
|
|
# Agregado en run():
|
|
notifications_thread = threading.Thread(target=run_notifications_api, daemon=True)
|
|
notifications_consumer_thread = threading.Thread(target=run_notifications_consumer, daemon=True)
|
|
```
|
|
|
|
### 3. `src/infrastructure/adapters/rabbitmq/messages.py`
|
|
- ✏️ Agregado: Tipo de evento `UPDATE_STATUS` a `ReportEventType`
|
|
- ✏️ Agregado: Campos `old_estado`, `new_estado`, `old_visibility`, `new_visibility` a `ReportMessage`
|
|
|
|
```python
|
|
class ReportEventType(str, Enum):
|
|
CREATE = "report.create"
|
|
UPDATE_VISIBILITY = "report.update_visibility"
|
|
UPDATE_STATUS = "report.update_status" # ✨ Nuevo
|
|
DELETE = "report.delete"
|
|
|
|
@dataclass
|
|
class ReportMessage:
|
|
# ... campos existentes ...
|
|
old_estado: Optional[str] = None # ✨ Nuevo
|
|
new_estado: Optional[str] = None # ✨ Nuevo
|
|
```
|
|
|
|
### 4. `src/application/services/report_services.py`
|
|
- ✏️ **Clase `UpdateReportStatus`:** Completamente refactorizada para enviar eventos a RabbitMQ
|
|
- ✏️ Agregado: Importación de `ReportMessage` y `send_to_queue`
|
|
- ✏️ Agregado: Lógica para crear mensaje UPDATE_STATUS y enviarlo a `notifications_queue`
|
|
- ✏️ Agregado: Captura del estado anterior para comparación
|
|
|
|
```python
|
|
# Cambios en UpdateReportStatus.execute():
|
|
old_estado = report.estado # Guardar estado anterior
|
|
|
|
# Después de actualizar:
|
|
if old_estado != new_estado:
|
|
message = ReportMessage(
|
|
event_type=ReportEventType.UPDATE_STATUS,
|
|
id_reporte=report_id,
|
|
id_usuario=report.id_usuario,
|
|
old_estado=old_estado,
|
|
new_estado=new_estado,
|
|
# ... otros campos ...
|
|
)
|
|
send_to_queue("notifications_queue", message.to_dict())
|
|
```
|
|
|
|
### 5. `docker-compose.yaml`
|
|
- ✏️ **MongoDB:** Agregados credenciales de autenticación
|
|
- Usuario: `admin` / Contraseña: `admin_password`
|
|
- Agregado flag `--auth` en comando
|
|
- ✏️ **Agregado:** Servicio RabbitMQ completo con:
|
|
- Usuario: `voxpopuli` / Contraseña: `voxpopuli_pass`
|
|
- Management UI en puerto 15672
|
|
- Healthcheck configurado
|
|
- ✏️ **Volúmenes:** Agregado `rabbitmq_data` para persistencia
|
|
|
|
---
|
|
|
|
## 🚀 Nuevos Servicios en Docker Compose
|
|
|
|
### RabbitMQ (Nuevo)
|
|
```yaml
|
|
rabbitmq:
|
|
image: rabbitmq:3.13-management
|
|
container_name: voxpopuli_rabbitmq
|
|
ports:
|
|
- "5672:5672" # AMQP
|
|
- "15672:15672" # Management UI (http://localhost:15672)
|
|
credentials:
|
|
user: voxpopuli
|
|
password: voxpopuli_pass
|
|
```
|
|
|
|
**Acceso Management UI:**
|
|
- URL: http://localhost:15672
|
|
- Usuario: `voxpopuli`
|
|
- Contraseña: `voxpopuli_pass`
|
|
|
|
---
|
|
|
|
## 📊 Base de Datos MongoDB
|
|
|
|
### Nuevas Colecciones
|
|
|
|
**Base de datos:** `voxpopuli_notifications`
|
|
|
|
#### Colección: `notificaciones`
|
|
```json
|
|
{
|
|
"_id": ObjectId,
|
|
"id_usuario": 1,
|
|
"id_reporte": "uuid-del-reporte",
|
|
"message": "¡Tu reporte #uuid ha sido resuelto!",
|
|
"fecha": ISODate("2024-04-29T15:30:00Z"),
|
|
"read": false
|
|
}
|
|
```
|
|
|
|
**Índices Recomendados:**
|
|
```javascript
|
|
db.notificaciones.createIndex({ "id_usuario": 1, "fecha": -1 })
|
|
db.notificaciones.createIndex({ "id_usuario": 1, "read": 1 })
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Flujo de Eventos
|
|
|
|
### Cambio de Estado de Reporte → Notificación
|
|
|
|
```
|
|
1. Frontend llama:
|
|
PUT /reports/{report_id}/status
|
|
{ "estado": "resuelto" }
|
|
|
|
2. API de Reportes:
|
|
└─ UpdateReportStatus.execute()
|
|
├─ Valida estado
|
|
├─ Obtiene reporte actual (estado anterior)
|
|
├─ Actualiza en MongoDB
|
|
└─ NUEVO: Envía mensaje a RabbitMQ
|
|
{
|
|
"event_type": "report.update_status",
|
|
"id_reporte": "uuid",
|
|
"id_usuario": 1,
|
|
"old_estado": "en proceso",
|
|
"new_estado": "resuelto",
|
|
...
|
|
}
|
|
|
|
3. RabbitMQ:
|
|
└─ Almacena en queue `notifications_queue`
|
|
|
|
4. Consumidor de Notificaciones:
|
|
├─ Escucha la cola
|
|
├─ Recibe el evento UPDATE_STATUS
|
|
└─ Procesa mensaje:
|
|
└─ NotificationService.send_report_status_notification()
|
|
└─ Crea notificación en MongoDB
|
|
{
|
|
"id_usuario": 1,
|
|
"id_reporte": "uuid",
|
|
"message": "¡Tu reporte ha sido resuelto!",
|
|
"fecha": now(),
|
|
"read": false
|
|
}
|
|
|
|
5. Frontend (Polling o WebSocket):
|
|
└─ GET /notifications/1
|
|
└─ Obtiene notificación creada
|
|
```
|
|
|
|
---
|
|
|
|
## 📡 Colas RabbitMQ
|
|
|
|
### `reports_queue`
|
|
- **Propósito:** Eventos de creación, eliminación y cambios de reportes
|
|
- **Consumidor:** `ReportConsumer` (consumer de reportes)
|
|
- **Eventos:** CREATE, UPDATE_VISIBILITY, DELETE
|
|
|
|
### `notifications_queue` (NUEVA)
|
|
- **Propósito:** Eventos de cambios de estado para crear notificaciones
|
|
- **Consumidor:** `NotificationConsumer` (consumer de notificaciones)
|
|
- **Eventos:** UPDATE_STATUS, UPDATE_VISIBILITY
|
|
|
|
### `users_queue`
|
|
- **Propósito:** Eventos de usuarios
|
|
- **Consumidor:** `UserConsumer`
|
|
|
|
---
|
|
|
|
## 🔌 API Endpoints de Notificaciones
|
|
|
|
### Base: `http://localhost:8002`
|
|
|
|
| Método | Endpoint | Descripción | Auth |
|
|
|--------|----------|-------------|------|
|
|
| GET | `/` | Health check | No |
|
|
| POST | `/notifications/` | Crear notificación (interno) | Sí |
|
|
| GET | `/notifications/{user_id}` | Obtener notificaciones del usuario | Sí |
|
|
| GET | `/notifications/{user_id}/unread-count` | Contar no leídas | Sí |
|
|
| PUT | `/notifications/{notification_id}/read` | Marcar como leída | Sí |
|
|
| PUT | `/notifications/{user_id}/read-all` | Marcar todas como leídas | Sí |
|
|
| DELETE | `/notifications/{notification_id}` | Eliminar notificación | Sí |
|
|
|
|
**Documentación interactiva:** http://localhost:8002/docs
|
|
|
|
---
|
|
|
|
## 🔐 Seguridad y Validaciones
|
|
|
|
### Por Implementar en Frontend
|
|
|
|
1. **Autenticación:**
|
|
- Incluir JWT token en header `Authorization: Bearer {token}`
|
|
- Validar que el token no esté expirado
|
|
|
|
2. **Autorización:**
|
|
- Backend valida que `user_id` en token === `user_id` en path
|
|
- Usuario solo puede ver sus propias notificaciones
|
|
|
|
3. **Rate Limiting:**
|
|
- Considerar máximo 1 request de polling cada 10 segundos
|
|
- WebSocket es alternativa para mayor escalabilidad
|
|
|
|
---
|
|
|
|
## 🧪 Pruebas Recomendadas
|
|
|
|
### 1. Test Manual: Crear Notificación
|
|
```bash
|
|
# Terminal 1: Iniciar el proyecto
|
|
python src/main.py
|
|
|
|
# Terminal 2: Test crear notificación
|
|
curl -X POST http://localhost:8002/notifications/ \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"id_usuario": 1,
|
|
"id_reporte": "test-report-123",
|
|
"message": "Tu reporte ha sido actualizado"
|
|
}'
|
|
```
|
|
|
|
### 2. Test Manual: Obtener Notificaciones
|
|
```bash
|
|
curl http://localhost:8002/notifications/1 \
|
|
-H "Authorization: Bearer {tu_jwt_token}"
|
|
```
|
|
|
|
### 3. Test Manual: Cambio de Estado
|
|
```bash
|
|
# Cambiar estado de reporte → dispara notificación automáticamente
|
|
curl -X PUT http://localhost:8002/reports/{report_id}/status \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer {tu_jwt_token}" \
|
|
-d '{"estado": "resuelto"}'
|
|
|
|
# Luego verificar que notificación se creó:
|
|
curl http://localhost:8002/notifications/1 \
|
|
-H "Authorization: Bearer {tu_jwt_token}"
|
|
```
|
|
|
|
---
|
|
|
|
## 🐳 Comandos Docker
|
|
|
|
```bash
|
|
# Levantar todos los servicios
|
|
docker-compose up -d
|
|
|
|
# Ver logs en tiempo real
|
|
docker-compose logs -f
|
|
|
|
# Ver logs específicos de un servicio
|
|
docker-compose logs -f mongodb
|
|
docker-compose logs -f rabbitmq
|
|
|
|
# Detener servicios
|
|
docker-compose down
|
|
|
|
# Verificar estado
|
|
docker-compose ps
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Arquitectura de Capas
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ Presentación (Frontend) │
|
|
│ (Implementar según FRONTEND_NOTIFICATIONS_...) │
|
|
└────────────────────┬────────────────────────────────┘
|
|
│ HTTP/REST + WebSocket (opcional)
|
|
┌────────────────────v────────────────────────────────┐
|
|
│ API FastAPI │
|
|
│ ├─ /notifications/{user_id} │
|
|
│ ├─ /notifications/{notification_id}/read │
|
|
│ └─ ... (Ver endpoints arriba) │
|
|
└────────────────────┬────────────────────────────────┘
|
|
│
|
|
┌────────────────────v────────────────────────────────┐
|
|
│ Capa de Aplicación (Services) │
|
|
│ └─ NotificationService │
|
|
│ ├─ create_notification() │
|
|
│ ├─ get_user_notifications() │
|
|
│ ├─ mark_as_read() │
|
|
│ └─ send_report_status_notification() │
|
|
└────────────────────┬────────────────────────────────┘
|
|
│
|
|
┌────────────────────v────────────────────────────────┐
|
|
│ Capa de Dominio │
|
|
│ └─ Notification (dataclass) │
|
|
│ ├─ id_usuario │
|
|
│ ├─ id_reporte │
|
|
│ ├─ message │
|
|
│ ├─ fecha │
|
|
│ └─ read │
|
|
└────────────────────┬────────────────────────────────┘
|
|
│
|
|
┌────────────────────v────────────────────────────────┐
|
|
│ Puertos (Interfaces Abstractas) │
|
|
│ └─ NotificationRepository │
|
|
│ ├─ create() │
|
|
│ ├─ get_by_user() │
|
|
│ ├─ mark_as_read() │
|
|
│ └─ ... │
|
|
└────────────────────┬────────────────────────────────┘
|
|
│
|
|
┌────────────────────v────────────────────────────────┐
|
|
│ Infraestructura (Adaptadores) │
|
|
│ └─ NotificationRepositoryMongo │
|
|
│ ├─ Implementa NotificationRepository │
|
|
│ └─ Usa MongoDB │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 📈 Escalabilidad Futura
|
|
|
|
### Fase 2 - Recomendaciones
|
|
|
|
1. **WebSocket Server:**
|
|
- Implementar socket.io en FastAPI
|
|
- Notificaciones push en tiempo real
|
|
- Reducir latencia para usuarios conectados
|
|
|
|
2. **Cache Redis:**
|
|
- Cachear conteo de no leídas
|
|
- Sincronizar estado entre instancias
|
|
|
|
3. **Histórico de Cambios:**
|
|
- Almacenar todos los cambios de estado
|
|
- Mostrar timeline en detalle de reporte
|
|
|
|
4. **Notificaciones Push:**
|
|
- Integrar con Firebase Cloud Messaging
|
|
- Enviar notificaciones a dispositivos móviles
|
|
|
|
5. **Búsqueda Full-Text:**
|
|
- Elasticsearch para buscar en notificaciones
|
|
- Filtrado avanzado
|
|
|
|
---
|
|
|
|
## 🆘 Troubleshooting
|
|
|
|
### Problema: "Cannot connect to MongoDB"
|
|
```bash
|
|
# Solución: Asegurar que MongoDB esté levantado
|
|
docker-compose up -d mongodb
|
|
|
|
# Verificar logs
|
|
docker-compose logs mongodb
|
|
```
|
|
|
|
### Problema: "RabbitMQ connection refused"
|
|
```bash
|
|
# Solución: Asegurar que RabbitMQ esté levantado
|
|
docker-compose up -d rabbitmq
|
|
|
|
# Verificar logs
|
|
docker-compose logs rabbitmq
|
|
|
|
# Acceder a management UI
|
|
http://localhost:15672 (usuario: voxpopuli, pass: voxpopuli_pass)
|
|
```
|
|
|
|
### Problema: "Notification not found"
|
|
- Verificar que `id_usuario` es correcto
|
|
- Verificar que JWT token pertenece al usuario
|
|
- Revisar que el consumidor está ejecutando
|
|
|
|
### Problema: No se crean notificaciones al cambiar estado
|
|
```bash
|
|
# 1. Verificar que consumidor está corriendo
|
|
# (Debe haber thread "Notifications-Consumer" en logs)
|
|
|
|
# 2. Verificar mensaje en RabbitMQ
|
|
# Ir a http://localhost:15672 > Queues > notifications_queue
|
|
|
|
# 3. Revisar logs del consumidor
|
|
docker-compose logs notifications-consumer # Si estuviera en Docker
|
|
```
|
|
|
|
---
|
|
|
|
## 📚 Documentación Relacionada
|
|
|
|
- **FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md** - Guía completa para implementar frontend
|
|
- **ARCHITECTURE.md** - Arquitectura general del proyecto
|
|
- **DATABASE.md** - Información sobre bases de datos
|
|
- **README.md** - Setup y ejecución del proyecto
|
|
|
|
---
|
|
|
|
## ✅ Checklist de Implementación Backend (Completado)
|
|
|
|
- [x] Crear modelo de dominio `Notification`
|
|
- [x] Crear interfaz `NotificationRepository`
|
|
- [x] Implementar `NotificationRepositoryMongo`
|
|
- [x] Crear `NotificationService` con lógica de negocio
|
|
- [x] Crear esquemas Pydantic
|
|
- [x] Implementar endpoints de API
|
|
- [x] Crear router de notificaciones
|
|
- [x] Crear app FastAPI para notificaciones
|
|
- [x] Crear `NotificationConsumer`
|
|
- [x] Actualizar `config.py` con nueva BD
|
|
- [x] Actualizar `docker-compose.yaml`
|
|
- [x] Modificar `UpdateReportStatus` para enviar eventos
|
|
- [x] Actualizar `messages.py` con `UPDATE_STATUS`
|
|
- [x] Actualizar `main.py` para ejecutar nueva API y consumidor
|
|
- [x] Crear documentación detallada para frontend
|
|
- [x] Crear este documento de resumen
|
|
|
|
---
|
|
|
|
## 🎯 Próximos Pasos para Frontend
|
|
|
|
1. **Leer:** `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md`
|
|
2. **Crear:** Hook `useNotifications` con React Query
|
|
3. **Implementar:** Componente de icono de campana
|
|
4. **Agregar:** Dropdown de notificaciones
|
|
5. **Crear:** Página dedicada de notificaciones
|
|
6. **Integrar:** Toast notifications
|
|
7. **Implementar:** Polling cada 30 segundos
|
|
8. **Testear:** Flujo completo de usuario
|
|
|
|
---
|
|
|
|
**Estado:** ✅ Completado
|
|
**Próxima Revisión:** Después de implementación de frontend
|
|
**Contacto:** Development Team
|