Improve the frontend 4
This commit is contained in:
378
docs/TENANT_DELETION_IMPLEMENTATION_GUIDE.md
Normal file
378
docs/TENANT_DELETION_IMPLEMENTATION_GUIDE.md
Normal file
@@ -0,0 +1,378 @@
|
||||
# Tenant Deletion Implementation Guide
|
||||
|
||||
## Overview
|
||||
This guide documents the standardized approach for implementing tenant data deletion across all microservices in the Bakery-IA platform.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Phase 1: Tenant Service Core (✅ COMPLETED)
|
||||
|
||||
The tenant service now provides three critical endpoints:
|
||||
|
||||
1. **DELETE `/api/v1/tenants/{tenant_id}`** - Delete a tenant and all associated data
|
||||
- Verifies caller permissions (owner/admin or internal service)
|
||||
- Checks for other admins before allowing deletion
|
||||
- Cascades deletion to local tenant data (members, subscriptions)
|
||||
- Publishes `tenant.deleted` event for other services
|
||||
|
||||
2. **DELETE `/api/v1/tenants/user/{user_id}/memberships`** - Delete all memberships for a user
|
||||
- Only accessible by internal services
|
||||
- Removes user from all tenant memberships
|
||||
- Used during user account deletion
|
||||
|
||||
3. **POST `/api/v1/tenants/{tenant_id}/transfer-ownership`** - Transfer tenant ownership
|
||||
- Atomic operation to change owner and update member roles
|
||||
- Requires current owner permission or internal service call
|
||||
|
||||
4. **GET `/api/v1/tenants/{tenant_id}/admins`** - Get all tenant admins
|
||||
- Returns list of users with owner/admin roles
|
||||
- Used by auth service to check before tenant deletion
|
||||
|
||||
### Phase 2: Service-Level Deletion (IN PROGRESS)
|
||||
|
||||
Each microservice must implement tenant data deletion using the standardized pattern.
|
||||
|
||||
## Implementation Pattern
|
||||
|
||||
### Step 1: Create Deletion Service
|
||||
|
||||
Each service should create a `tenant_deletion_service.py` that implements `BaseTenantDataDeletionService`:
|
||||
|
||||
```python
|
||||
# services/{service}/app/services/tenant_deletion_service.py
|
||||
|
||||
from typing import Dict
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete, func
|
||||
import structlog
|
||||
|
||||
from shared.services.tenant_deletion import (
|
||||
BaseTenantDataDeletionService,
|
||||
TenantDataDeletionResult
|
||||
)
|
||||
|
||||
class {Service}TenantDeletionService(BaseTenantDataDeletionService):
|
||||
"""Service for deleting all {service}-related data for a tenant"""
|
||||
|
||||
def __init__(self, db_session: AsyncSession):
|
||||
super().__init__("{service}-service")
|
||||
self.db = db_session
|
||||
|
||||
async def get_tenant_data_preview(self, tenant_id: str) -> Dict[str, int]:
|
||||
"""Get counts of what would be deleted"""
|
||||
preview = {}
|
||||
|
||||
# Count each entity type
|
||||
# Example:
|
||||
# count = await self.db.scalar(
|
||||
# select(func.count(Model.id)).where(Model.tenant_id == tenant_id)
|
||||
# )
|
||||
# preview["model_name"] = count or 0
|
||||
|
||||
return preview
|
||||
|
||||
async def delete_tenant_data(self, tenant_id: str) -> TenantDataDeletionResult:
|
||||
"""Delete all data for a tenant"""
|
||||
result = TenantDataDeletionResult(tenant_id, self.service_name)
|
||||
|
||||
try:
|
||||
# Delete each entity type
|
||||
# 1. Delete child records first (respect foreign keys)
|
||||
# 2. Then delete parent records
|
||||
# 3. Use try-except for each delete operation
|
||||
|
||||
# Example:
|
||||
# try:
|
||||
# delete_stmt = delete(Model).where(Model.tenant_id == tenant_id)
|
||||
# result_proxy = await self.db.execute(delete_stmt)
|
||||
# result.add_deleted_items("model_name", result_proxy.rowcount)
|
||||
# except Exception as e:
|
||||
# result.add_error(f"Model deletion: {str(e)}")
|
||||
|
||||
await self.db.commit()
|
||||
|
||||
except Exception as e:
|
||||
await self.db.rollback()
|
||||
result.add_error(f"Fatal error: {str(e)}")
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
### Step 2: Add API Endpoints
|
||||
|
||||
Add two endpoints to the service's API router:
|
||||
|
||||
```python
|
||||
# services/{service}/app/api/{main_router}.py
|
||||
|
||||
@router.delete("/tenant/{tenant_id}")
|
||||
async def delete_tenant_data(
|
||||
tenant_id: str,
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db = Depends(get_db)
|
||||
):
|
||||
"""Delete all {service} data for a tenant (internal only)"""
|
||||
|
||||
# Only allow internal service calls
|
||||
if current_user.get("type") != "service":
|
||||
raise HTTPException(status_code=403, detail="Internal services only")
|
||||
|
||||
from app.services.tenant_deletion_service import {Service}TenantDeletionService
|
||||
|
||||
deletion_service = {Service}TenantDeletionService(db)
|
||||
result = await deletion_service.safe_delete_tenant_data(tenant_id)
|
||||
|
||||
return {
|
||||
"message": "Tenant data deletion completed",
|
||||
"summary": result.to_dict()
|
||||
}
|
||||
|
||||
|
||||
@router.get("/tenant/{tenant_id}/deletion-preview")
|
||||
async def preview_tenant_deletion(
|
||||
tenant_id: str,
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db = Depends(get_db)
|
||||
):
|
||||
"""Preview what would be deleted (dry-run)"""
|
||||
|
||||
# Allow internal services and admins
|
||||
if not (current_user.get("type") == "service" or
|
||||
current_user.get("role") in ["owner", "admin"]):
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
from app.services.tenant_deletion_service import {Service}TenantDeletionService
|
||||
|
||||
deletion_service = {Service}TenantDeletionService(db)
|
||||
preview = await deletion_service.get_tenant_data_preview(tenant_id)
|
||||
|
||||
return {
|
||||
"tenant_id": tenant_id,
|
||||
"service": "{service}-service",
|
||||
"data_counts": preview,
|
||||
"total_items": sum(preview.values())
|
||||
}
|
||||
```
|
||||
|
||||
## Services Requiring Implementation
|
||||
|
||||
### ✅ Completed:
|
||||
1. **Tenant Service** - Core deletion logic, memberships, ownership transfer
|
||||
2. **Orders Service** - Example implementation complete
|
||||
|
||||
### 🔄 In Progress:
|
||||
3. **Inventory Service** - Template created, needs testing
|
||||
|
||||
### ⏳ Pending:
|
||||
4. **Recipes Service**
|
||||
- Models to delete: Recipe, RecipeIngredient, RecipeStep, RecipeNutrition
|
||||
|
||||
5. **Production Service**
|
||||
- Models to delete: ProductionBatch, ProductionSchedule, ProductionPlan
|
||||
|
||||
6. **Sales Service**
|
||||
- Models to delete: Sale, SaleItem, DailySales, SalesReport
|
||||
|
||||
7. **Suppliers Service**
|
||||
- Models to delete: Supplier, SupplierProduct, PurchaseOrder, PurchaseOrderItem
|
||||
|
||||
8. **POS Service**
|
||||
- Models to delete: POSConfiguration, POSTransaction, POSSession
|
||||
|
||||
9. **External Service**
|
||||
- Models to delete: ExternalDataCache, APIKeyUsage
|
||||
|
||||
10. **Forecasting Service** (Already has some deletion logic)
|
||||
- Models to delete: Forecast, PredictionBatch, ModelArtifact
|
||||
|
||||
11. **Training Service** (Already has some deletion logic)
|
||||
- Models to delete: TrainingJob, TrainedModel, ModelMetrics
|
||||
|
||||
12. **Notification Service** (Already has some deletion logic)
|
||||
- Models to delete: Notification, NotificationPreference, NotificationLog
|
||||
|
||||
13. **Alert Processor Service**
|
||||
- Models to delete: Alert, AlertRule, AlertHistory
|
||||
|
||||
14. **Demo Session Service**
|
||||
- May not need tenant deletion (demo data is transient)
|
||||
|
||||
## Phase 3: Orchestration & Saga Pattern (PENDING)
|
||||
|
||||
### Goal
|
||||
Create a centralized deletion orchestrator in the auth service that:
|
||||
1. Coordinates deletion across all services
|
||||
2. Implements saga pattern for distributed transactions
|
||||
3. Provides rollback/compensation logic for failures
|
||||
4. Tracks deletion job status
|
||||
|
||||
### Components Needed
|
||||
|
||||
#### 1. Deletion Orchestrator Service
|
||||
```python
|
||||
# services/auth/app/services/deletion_orchestrator.py
|
||||
|
||||
class DeletionOrchestrator:
|
||||
"""Coordinates tenant deletion across all services"""
|
||||
|
||||
def __init__(self):
|
||||
self.service_registry = {
|
||||
"orders": OrdersServiceClient(),
|
||||
"inventory": InventoryServiceClient(),
|
||||
"recipes": RecipesServiceClient(),
|
||||
# ... etc
|
||||
}
|
||||
|
||||
async def orchestrate_tenant_deletion(
|
||||
self,
|
||||
tenant_id: str,
|
||||
deletion_job_id: str
|
||||
) -> DeletionResult:
|
||||
"""
|
||||
Execute deletion saga across all services
|
||||
Returns comprehensive result with per-service status
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
#### 2. Deletion Job Status Tracking
|
||||
```sql
|
||||
CREATE TABLE deletion_jobs (
|
||||
id UUID PRIMARY KEY,
|
||||
tenant_id UUID NOT NULL,
|
||||
initiated_by UUID NOT NULL,
|
||||
status VARCHAR(50), -- pending, in_progress, completed, failed, rolled_back
|
||||
services_completed JSONB,
|
||||
services_failed JSONB,
|
||||
total_items_deleted INTEGER,
|
||||
error_log TEXT,
|
||||
created_at TIMESTAMP,
|
||||
completed_at TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
#### 3. Service Registry
|
||||
Track all services that need to be called for deletion:
|
||||
|
||||
```python
|
||||
SERVICE_DELETION_ENDPOINTS = {
|
||||
"orders": "http://orders-service:8000/api/v1/orders/tenant/{tenant_id}",
|
||||
"inventory": "http://inventory-service:8000/api/v1/inventory/tenant/{tenant_id}",
|
||||
"recipes": "http://recipes-service:8000/api/v1/recipes/tenant/{tenant_id}",
|
||||
"production": "http://production-service:8000/api/v1/production/tenant/{tenant_id}",
|
||||
"sales": "http://sales-service:8000/api/v1/sales/tenant/{tenant_id}",
|
||||
"suppliers": "http://suppliers-service:8000/api/v1/suppliers/tenant/{tenant_id}",
|
||||
"pos": "http://pos-service:8000/api/v1/pos/tenant/{tenant_id}",
|
||||
"external": "http://external-service:8000/api/v1/external/tenant/{tenant_id}",
|
||||
"forecasting": "http://forecasting-service:8000/api/v1/forecasts/tenant/{tenant_id}",
|
||||
"training": "http://training-service:8000/api/v1/models/tenant/{tenant_id}",
|
||||
"notification": "http://notification-service:8000/api/v1/notifications/tenant/{tenant_id}",
|
||||
}
|
||||
```
|
||||
|
||||
## Phase 4: Enhanced Features (PENDING)
|
||||
|
||||
### 1. Soft Delete with Retention Period
|
||||
- Add `deleted_at` timestamp to tenants table
|
||||
- Implement 30-day retention before permanent deletion
|
||||
- Allow restoration during retention period
|
||||
|
||||
### 2. Audit Logging
|
||||
- Log all deletion operations with details
|
||||
- Track who initiated deletion and when
|
||||
- Store deletion summaries for compliance
|
||||
|
||||
### 3. Deletion Preview for All Services
|
||||
- Aggregate preview from all services
|
||||
- Show comprehensive impact analysis
|
||||
- Allow download of deletion report
|
||||
|
||||
### 4. Async Job Status Check
|
||||
- Add endpoint to check deletion job progress
|
||||
- WebSocket support for real-time updates
|
||||
- Email notification on completion
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Test each service's deletion service independently
|
||||
- Mock database operations
|
||||
- Verify correct SQL generation
|
||||
|
||||
### Integration Tests
|
||||
- Test deletion across multiple services
|
||||
- Verify CASCADE deletes work correctly
|
||||
- Test rollback scenarios
|
||||
|
||||
### End-to-End Tests
|
||||
- Full tenant deletion from API call to completion
|
||||
- Verify all data is actually deleted
|
||||
- Test with production-like data volumes
|
||||
|
||||
## Rollout Plan
|
||||
|
||||
1. **Week 1**: Complete Phase 2 for critical services (Orders, Inventory, Recipes, Production)
|
||||
2. **Week 2**: Complete Phase 2 for remaining services
|
||||
3. **Week 3**: Implement Phase 3 (Orchestration & Saga)
|
||||
4. **Week 4**: Implement Phase 4 (Enhanced Features)
|
||||
5. **Week 5**: Testing & Documentation
|
||||
6. **Week 6**: Production deployment with monitoring
|
||||
|
||||
## Monitoring & Alerts
|
||||
|
||||
### Metrics to Track
|
||||
- `tenant_deletion_duration_seconds` - How long deletions take
|
||||
- `tenant_deletion_items_deleted` - Number of items deleted per service
|
||||
- `tenant_deletion_errors_total` - Count of deletion failures
|
||||
- `tenant_deletion_jobs_status` - Current status of deletion jobs
|
||||
|
||||
### Alerts
|
||||
- Alert if deletion takes longer than 5 minutes
|
||||
- Alert if any service fails to delete data
|
||||
- Alert if CASCADE deletes don't work as expected
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Authorization**: Only owners, admins, or internal services can delete
|
||||
2. **Audit Trail**: All deletions must be logged
|
||||
3. **No Direct DB Access**: All deletions through API endpoints
|
||||
4. **Rate Limiting**: Prevent abuse of deletion endpoints
|
||||
5. **Confirmation Required**: User must confirm before deletion
|
||||
6. **GDPR Compliance**: Support right to be forgotten
|
||||
|
||||
## Current Status Summary
|
||||
|
||||
| Phase | Status | Completion |
|
||||
|-------|--------|------------|
|
||||
| Phase 1: Tenant Service Core | ✅ Complete | 100% |
|
||||
| Phase 2: Service Deletions | 🔄 In Progress | 20% (2/10 services) |
|
||||
| Phase 3: Orchestration | ⏳ Pending | 0% |
|
||||
| Phase 4: Enhanced Features | ⏳ Pending | 0% |
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Immediate**: Complete Phase 2 for remaining 8 services using the template above
|
||||
2. **Short-term**: Implement orchestration layer in auth service
|
||||
3. **Mid-term**: Add saga pattern and rollback logic
|
||||
4. **Long-term**: Implement soft delete and enhanced features
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
### New Files:
|
||||
- `/services/shared/services/tenant_deletion.py` - Base classes and utilities
|
||||
- `/services/orders/app/services/tenant_deletion_service.py` - Orders implementation
|
||||
- `/services/inventory/app/services/tenant_deletion_service.py` - Inventory template
|
||||
- `/TENANT_DELETION_IMPLEMENTATION_GUIDE.md` - This document
|
||||
|
||||
### Modified Files:
|
||||
- `/services/tenant/app/services/tenant_service.py` - Added deletion methods
|
||||
- `/services/tenant/app/services/messaging.py` - Added deletion event
|
||||
- `/services/tenant/app/api/tenants.py` - Added DELETE endpoint
|
||||
- `/services/tenant/app/api/tenant_members.py` - Added membership deletion & transfer endpoints
|
||||
- `/services/orders/app/api/orders.py` - Added tenant deletion endpoints
|
||||
|
||||
## References
|
||||
|
||||
- [Saga Pattern](https://microservices.io/patterns/data/saga.html)
|
||||
- [GDPR Right to Erasure](https://gdpr-info.eu/art-17-gdpr/)
|
||||
- [Distributed Transactions in Microservices](https://www.nginx.com/blog/microservices-pattern-distributed-transactions-saga/)
|
||||
Reference in New Issue
Block a user