New alert service
This commit is contained in:
227
services/tenant/app/api/tenant_hierarchy.py
Normal file
227
services/tenant/app/api/tenant_hierarchy.py
Normal file
@@ -0,0 +1,227 @@
|
||||
"""
|
||||
Tenant Hierarchy API - Handles parent-child tenant relationships
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Path
|
||||
from typing import List, Dict, Any
|
||||
from uuid import UUID
|
||||
|
||||
from app.schemas.tenants import TenantResponse
|
||||
from app.services.tenant_service import EnhancedTenantService
|
||||
from app.repositories.tenant_repository import TenantRepository
|
||||
from shared.auth.decorators import get_current_user_dep
|
||||
from shared.routing.route_builder import RouteBuilder
|
||||
from shared.database.base import create_database_manager
|
||||
from shared.monitoring.metrics import track_endpoint_metrics
|
||||
import structlog
|
||||
|
||||
logger = structlog.get_logger()
|
||||
router = APIRouter()
|
||||
route_builder = RouteBuilder("tenants")
|
||||
|
||||
|
||||
# Dependency injection for enhanced tenant service
|
||||
def get_enhanced_tenant_service():
|
||||
try:
|
||||
from app.core.config import settings
|
||||
database_manager = create_database_manager(settings.DATABASE_URL, "tenant-service")
|
||||
return EnhancedTenantService(database_manager)
|
||||
except Exception as e:
|
||||
logger.error("Failed to create enhanced tenant service", error=str(e))
|
||||
raise HTTPException(status_code=500, detail="Service initialization failed")
|
||||
|
||||
|
||||
@router.get(route_builder.build_base_route("{tenant_id}/children", include_tenant_prefix=False), response_model=List[TenantResponse])
|
||||
@track_endpoint_metrics("tenant_children_list")
|
||||
async def get_tenant_children(
|
||||
tenant_id: UUID = Path(..., description="Parent Tenant ID"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
||||
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
||||
):
|
||||
"""
|
||||
Get all child tenants for a parent tenant.
|
||||
This endpoint returns all active child tenants associated with the specified parent tenant.
|
||||
"""
|
||||
try:
|
||||
logger.info(
|
||||
"Get tenant children request received",
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user.get("user_id"),
|
||||
user_type=current_user.get("type", "user"),
|
||||
is_service=current_user.get("type") == "service",
|
||||
role=current_user.get("role"),
|
||||
service_name=current_user.get("service", "none")
|
||||
)
|
||||
|
||||
# Skip access check for service-to-service calls
|
||||
is_service_call = current_user.get("type") == "service"
|
||||
if not is_service_call:
|
||||
# Verify user has access to the parent tenant
|
||||
access_info = await tenant_service.verify_user_access(current_user["user_id"], str(tenant_id))
|
||||
if not access_info.has_access:
|
||||
logger.warning(
|
||||
"Access denied to parent tenant",
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user.get("user_id")
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Access denied to parent tenant"
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
"Service-to-service call - bypassing access check",
|
||||
service=current_user.get("service"),
|
||||
tenant_id=str(tenant_id)
|
||||
)
|
||||
|
||||
# Get child tenants from repository
|
||||
from app.models.tenants import Tenant
|
||||
async with tenant_service.database_manager.get_session() as session:
|
||||
tenant_repo = TenantRepository(Tenant, session)
|
||||
child_tenants = await tenant_repo.get_child_tenants(str(tenant_id))
|
||||
|
||||
logger.debug(
|
||||
"Get tenant children successful",
|
||||
tenant_id=str(tenant_id),
|
||||
child_count=len(child_tenants)
|
||||
)
|
||||
|
||||
# Convert to plain dicts while still in session to avoid lazy-load issues
|
||||
child_dicts = []
|
||||
for child in child_tenants:
|
||||
# Handle subscription_tier safely - avoid lazy load
|
||||
try:
|
||||
# Try to get subscription_tier if subscriptions are already loaded
|
||||
sub_tier = child.__dict__.get('_subscription_tier_cache', 'enterprise')
|
||||
except:
|
||||
sub_tier = 'enterprise' # Default for enterprise children
|
||||
|
||||
child_dict = {
|
||||
'id': str(child.id),
|
||||
'name': child.name,
|
||||
'subdomain': child.subdomain,
|
||||
'business_type': child.business_type,
|
||||
'business_model': child.business_model,
|
||||
'address': child.address,
|
||||
'city': child.city,
|
||||
'postal_code': child.postal_code,
|
||||
'latitude': child.latitude,
|
||||
'longitude': child.longitude,
|
||||
'phone': child.phone,
|
||||
'email': child.email,
|
||||
'timezone': child.timezone,
|
||||
'owner_id': str(child.owner_id),
|
||||
'parent_tenant_id': str(child.parent_tenant_id) if child.parent_tenant_id else None,
|
||||
'tenant_type': child.tenant_type,
|
||||
'hierarchy_path': child.hierarchy_path,
|
||||
'subscription_tier': sub_tier, # Use the safely retrieved value
|
||||
'ml_model_trained': child.ml_model_trained,
|
||||
'last_training_date': child.last_training_date,
|
||||
'is_active': child.is_active,
|
||||
'is_demo': child.is_demo,
|
||||
'demo_session_id': child.demo_session_id,
|
||||
'created_at': child.created_at,
|
||||
'updated_at': child.updated_at
|
||||
}
|
||||
child_dicts.append(child_dict)
|
||||
|
||||
# Convert to Pydantic models outside the session without from_attributes
|
||||
child_responses = [TenantResponse(**child_dict) for child_dict in child_dicts]
|
||||
return child_responses
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error("Get tenant children failed",
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user.get("user_id"),
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Get tenant children failed"
|
||||
)
|
||||
|
||||
|
||||
@router.get(route_builder.build_base_route("{tenant_id}/children/count", include_tenant_prefix=False))
|
||||
@track_endpoint_metrics("tenant_children_count")
|
||||
async def get_tenant_children_count(
|
||||
tenant_id: UUID = Path(..., description="Parent Tenant ID"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
||||
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
||||
):
|
||||
"""
|
||||
Get count of child tenants for a parent tenant.
|
||||
This endpoint returns the number of active child tenants associated with the specified parent tenant.
|
||||
"""
|
||||
try:
|
||||
logger.info(
|
||||
"Get tenant children count request received",
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user.get("user_id")
|
||||
)
|
||||
|
||||
# Skip access check for service-to-service calls
|
||||
is_service_call = current_user.get("type") == "service"
|
||||
if not is_service_call:
|
||||
# Verify user has access to the parent tenant
|
||||
access_info = await tenant_service.verify_user_access(current_user["user_id"], str(tenant_id))
|
||||
if not access_info.has_access:
|
||||
logger.warning(
|
||||
"Access denied to parent tenant",
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user.get("user_id")
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Access denied to parent tenant"
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
"Service-to-service call - bypassing access check",
|
||||
service=current_user.get("service"),
|
||||
tenant_id=str(tenant_id)
|
||||
)
|
||||
|
||||
# Get child count from repository
|
||||
from app.models.tenants import Tenant
|
||||
async with tenant_service.database_manager.get_session() as session:
|
||||
tenant_repo = TenantRepository(Tenant, session)
|
||||
child_count = await tenant_repo.get_child_tenant_count(str(tenant_id))
|
||||
|
||||
logger.debug(
|
||||
"Get tenant children count successful",
|
||||
tenant_id=str(tenant_id),
|
||||
child_count=child_count
|
||||
)
|
||||
|
||||
return {
|
||||
"parent_tenant_id": str(tenant_id),
|
||||
"child_count": child_count
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error("Get tenant children count failed",
|
||||
tenant_id=str(tenant_id),
|
||||
user_id=current_user.get("user_id"),
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Get tenant children count failed"
|
||||
)
|
||||
|
||||
|
||||
# Register the router in the main app
|
||||
def register_hierarchy_routes(app):
|
||||
"""Register hierarchy routes with the main application"""
|
||||
from shared.routing.route_builder import RouteBuilder
|
||||
route_builder = RouteBuilder("tenants")
|
||||
|
||||
# Include the hierarchy routes with proper tenant prefix
|
||||
app.include_router(
|
||||
router,
|
||||
prefix="/api/v1",
|
||||
tags=["tenant-hierarchy"]
|
||||
)
|
||||
Reference in New Issue
Block a user