Fix new services implementation 8
This commit is contained in:
@@ -120,15 +120,31 @@ export const useForecast = () => {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getForecastAlerts = useCallback(async (tenantId: string): Promise<ForecastAlert[]> => {
|
||||
const getForecastAlerts = useCallback(async (tenantId: string): Promise<any> => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
const response = await forecastingService.getForecastAlerts(tenantId);
|
||||
setAlerts(response.data);
|
||||
|
||||
return response.data;
|
||||
// Handle different response formats
|
||||
if (response && response.alerts) {
|
||||
// New format: { alerts: [...], total_returned: N, ... }
|
||||
setAlerts(response.alerts);
|
||||
return response;
|
||||
} else if (response && response.data) {
|
||||
// Old format: { data: [...] }
|
||||
setAlerts(response.data);
|
||||
return { alerts: response.data };
|
||||
} else if (Array.isArray(response)) {
|
||||
// Direct array format
|
||||
setAlerts(response);
|
||||
return { alerts: response };
|
||||
} else {
|
||||
// Unknown format - return empty
|
||||
setAlerts([]);
|
||||
return { alerts: [] };
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to get forecast alerts';
|
||||
setError(message);
|
||||
|
||||
@@ -79,7 +79,10 @@ export const useRealAlerts = () => {
|
||||
|
||||
try {
|
||||
// Get forecast alerts from backend
|
||||
const forecastAlerts = await getForecastAlerts(tenantId);
|
||||
const response = await getForecastAlerts(tenantId);
|
||||
|
||||
// Extract alerts array from paginated response
|
||||
const forecastAlerts = response.alerts || [];
|
||||
|
||||
// Filter only active alerts
|
||||
const activeAlerts = forecastAlerts.filter(alert => alert.is_active);
|
||||
|
||||
@@ -121,7 +121,7 @@ async def proxy_tenant_models(request: Request, tenant_id: str = Path(...), path
|
||||
async def proxy_tenant_forecasts(request: Request, tenant_id: str = Path(...), path: str = ""):
|
||||
"""Proxy tenant forecast requests to forecasting service"""
|
||||
target_path = f"/api/v1/tenants/{tenant_id}/forecasts/{path}".rstrip("/")
|
||||
return await _proxy_to_forecasting_service(request, target_path)
|
||||
return await _proxy_to_forecasting_service(request, target_path, tenant_id=tenant_id)
|
||||
|
||||
@router.api_route("/{tenant_id}/predictions/{path:path}", methods=["GET", "POST", "OPTIONS"])
|
||||
async def proxy_tenant_predictions(request: Request, tenant_id: str = Path(...), path: str = ""):
|
||||
@@ -149,7 +149,7 @@ async def proxy_tenant_inventory(request: Request, tenant_id: str = Path(...), p
|
||||
# The inventory service expects /api/v1/tenants/{tenant_id}/inventory/{path}
|
||||
# Keep the full path structure for inventory service
|
||||
target_path = f"/api/v1/tenants/{tenant_id}/inventory/{path}".rstrip("/")
|
||||
return await _proxy_to_inventory_service(request, target_path)
|
||||
return await _proxy_to_inventory_service(request, target_path, tenant_id=tenant_id)
|
||||
|
||||
@router.api_route("/{tenant_id}/ingredients{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"])
|
||||
async def proxy_tenant_ingredients(request: Request, tenant_id: str = Path(...), path: str = ""):
|
||||
@@ -157,7 +157,7 @@ async def proxy_tenant_ingredients(request: Request, tenant_id: str = Path(...),
|
||||
# The inventory service ingredient endpoints are now tenant-scoped: /api/v1/tenants/{tenant_id}/ingredients/{path}
|
||||
# Keep the full tenant path structure
|
||||
target_path = f"/api/v1/tenants/{tenant_id}/ingredients{path}".rstrip("/")
|
||||
return await _proxy_to_inventory_service(request, target_path)
|
||||
return await _proxy_to_inventory_service(request, target_path, tenant_id=tenant_id)
|
||||
|
||||
# ================================================================
|
||||
# PROXY HELPER FUNCTIONS
|
||||
@@ -179,19 +179,19 @@ async def _proxy_to_training_service(request: Request, target_path: str):
|
||||
"""Proxy request to training service"""
|
||||
return await _proxy_request(request, target_path, settings.TRAINING_SERVICE_URL)
|
||||
|
||||
async def _proxy_to_forecasting_service(request: Request, target_path: str):
|
||||
async def _proxy_to_forecasting_service(request: Request, target_path: str, tenant_id: str = None):
|
||||
"""Proxy request to forecasting service"""
|
||||
return await _proxy_request(request, target_path, settings.FORECASTING_SERVICE_URL)
|
||||
return await _proxy_request(request, target_path, settings.FORECASTING_SERVICE_URL, tenant_id=tenant_id)
|
||||
|
||||
async def _proxy_to_notification_service(request: Request, target_path: str):
|
||||
"""Proxy request to notification service"""
|
||||
return await _proxy_request(request, target_path, settings.NOTIFICATION_SERVICE_URL)
|
||||
|
||||
async def _proxy_to_inventory_service(request: Request, target_path: str):
|
||||
async def _proxy_to_inventory_service(request: Request, target_path: str, tenant_id: str = None):
|
||||
"""Proxy request to inventory service"""
|
||||
return await _proxy_request(request, target_path, settings.INVENTORY_SERVICE_URL)
|
||||
return await _proxy_request(request, target_path, settings.INVENTORY_SERVICE_URL, tenant_id=tenant_id)
|
||||
|
||||
async def _proxy_request(request: Request, target_path: str, service_url: str):
|
||||
async def _proxy_request(request: Request, target_path: str, service_url: str, tenant_id: str = None):
|
||||
"""Generic proxy function with enhanced error handling"""
|
||||
|
||||
# Handle OPTIONS requests directly for CORS
|
||||
@@ -214,6 +214,10 @@ async def _proxy_request(request: Request, target_path: str, service_url: str):
|
||||
headers = dict(request.headers)
|
||||
headers.pop("host", None)
|
||||
|
||||
# Add tenant ID header if provided
|
||||
if tenant_id:
|
||||
headers["X-Tenant-ID"] = tenant_id
|
||||
|
||||
# Get request body if present
|
||||
body = None
|
||||
if request.method in ["POST", "PUT", "PATCH"]:
|
||||
|
||||
@@ -242,6 +242,70 @@ async def get_enhanced_tenant_forecasts(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/tenants/{tenant_id}/forecasts/alerts")
|
||||
@track_execution_time("enhanced_get_alerts_duration_seconds", "forecasting-service")
|
||||
async def get_enhanced_forecast_alerts(
|
||||
tenant_id: str = Path(..., description="Tenant ID"),
|
||||
active_only: bool = Query(True, description="Return only active alerts"),
|
||||
skip: int = Query(0, description="Number of records to skip"),
|
||||
limit: int = Query(50, description="Number of records to return"),
|
||||
request_obj: Request = None,
|
||||
current_tenant: str = Depends(get_current_tenant_id_dep),
|
||||
enhanced_forecasting_service: EnhancedForecastingService = Depends(get_enhanced_forecasting_service)
|
||||
):
|
||||
"""Get forecast alerts using enhanced repository pattern"""
|
||||
metrics = get_metrics_collector(request_obj)
|
||||
|
||||
try:
|
||||
# Enhanced tenant validation
|
||||
if tenant_id != current_tenant:
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_access_denied_total")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Access denied to tenant resources"
|
||||
)
|
||||
|
||||
# Record metrics
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_total")
|
||||
|
||||
# Get alerts using enhanced service
|
||||
alerts = await enhanced_forecasting_service.get_tenant_alerts(
|
||||
tenant_id=tenant_id,
|
||||
active_only=active_only,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_success_total")
|
||||
|
||||
return {
|
||||
"tenant_id": tenant_id,
|
||||
"alerts": alerts,
|
||||
"total_returned": len(alerts),
|
||||
"active_only": active_only,
|
||||
"pagination": {
|
||||
"skip": skip,
|
||||
"limit": limit
|
||||
},
|
||||
"enhanced_features": True,
|
||||
"repository_integration": True
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_errors_total")
|
||||
logger.error("Failed to get enhanced forecast alerts",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to get forecast alerts"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/tenants/{tenant_id}/forecasts/{forecast_id}")
|
||||
@track_execution_time("enhanced_get_forecast_duration_seconds", "forecasting-service")
|
||||
async def get_enhanced_forecast_by_id(
|
||||
@@ -363,70 +427,6 @@ async def delete_enhanced_forecast(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/tenants/{tenant_id}/forecasts/alerts")
|
||||
@track_execution_time("enhanced_get_alerts_duration_seconds", "forecasting-service")
|
||||
async def get_enhanced_forecast_alerts(
|
||||
tenant_id: str = Path(..., description="Tenant ID"),
|
||||
active_only: bool = Query(True, description="Return only active alerts"),
|
||||
skip: int = Query(0, description="Number of records to skip"),
|
||||
limit: int = Query(50, description="Number of records to return"),
|
||||
request_obj: Request = None,
|
||||
current_tenant: str = Depends(get_current_tenant_id_dep),
|
||||
enhanced_forecasting_service: EnhancedForecastingService = Depends(get_enhanced_forecasting_service)
|
||||
):
|
||||
"""Get forecast alerts using enhanced repository pattern"""
|
||||
metrics = get_metrics_collector(request_obj)
|
||||
|
||||
try:
|
||||
# Enhanced tenant validation
|
||||
if tenant_id != current_tenant:
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_access_denied_total")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Access denied to tenant resources"
|
||||
)
|
||||
|
||||
# Record metrics
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_total")
|
||||
|
||||
# Get alerts using enhanced service
|
||||
alerts = await enhanced_forecasting_service.get_tenant_alerts(
|
||||
tenant_id=tenant_id,
|
||||
active_only=active_only,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_success_total")
|
||||
|
||||
return {
|
||||
"tenant_id": tenant_id,
|
||||
"alerts": alerts,
|
||||
"total_returned": len(alerts),
|
||||
"active_only": active_only,
|
||||
"pagination": {
|
||||
"skip": skip,
|
||||
"limit": limit
|
||||
},
|
||||
"enhanced_features": True,
|
||||
"repository_integration": True
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
if metrics:
|
||||
metrics.increment_counter("enhanced_get_alerts_errors_total")
|
||||
logger.error("Failed to get enhanced forecast alerts",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to get forecast alerts"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/tenants/{tenant_id}/forecasts/statistics")
|
||||
@track_execution_time("enhanced_forecast_statistics_duration_seconds", "forecasting-service")
|
||||
async def get_enhanced_forecast_statistics(
|
||||
|
||||
@@ -83,10 +83,68 @@ class EnhancedForecastingService:
|
||||
skip: int = 0, limit: int = 100) -> List[Dict]:
|
||||
"""Get tenant forecasts with filtering"""
|
||||
try:
|
||||
# Implementation would use repository pattern to fetch forecasts
|
||||
return []
|
||||
# Get session and initialize repositories
|
||||
async with self.database_manager.get_background_session() as session:
|
||||
repos = await self._init_repositories(session)
|
||||
|
||||
# Build filters
|
||||
filters = {"tenant_id": tenant_id}
|
||||
if inventory_product_id:
|
||||
filters["inventory_product_id"] = inventory_product_id
|
||||
|
||||
# If date range specified, use specialized method
|
||||
if start_date and end_date:
|
||||
forecasts = await repos['forecast'].get_forecasts_by_date_range(
|
||||
tenant_id=tenant_id,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
inventory_product_id=inventory_product_id
|
||||
)
|
||||
else:
|
||||
# Use general get_multi with tenant filter
|
||||
forecasts = await repos['forecast'].get_multi(
|
||||
filters=filters,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
order_by="forecast_date",
|
||||
order_desc=True
|
||||
)
|
||||
|
||||
# Convert to dict format
|
||||
forecast_list = []
|
||||
for forecast in forecasts:
|
||||
forecast_dict = {
|
||||
"id": str(forecast.id),
|
||||
"tenant_id": str(forecast.tenant_id),
|
||||
"inventory_product_id": forecast.inventory_product_id,
|
||||
"location": forecast.location,
|
||||
"forecast_date": forecast.forecast_date.isoformat(),
|
||||
"predicted_demand": float(forecast.predicted_demand),
|
||||
"confidence_lower": float(forecast.confidence_lower),
|
||||
"confidence_upper": float(forecast.confidence_upper),
|
||||
"confidence_level": float(forecast.confidence_level),
|
||||
"model_id": forecast.model_id,
|
||||
"model_version": forecast.model_version,
|
||||
"algorithm": forecast.algorithm,
|
||||
"business_type": forecast.business_type,
|
||||
"is_holiday": forecast.is_holiday,
|
||||
"is_weekend": forecast.is_weekend,
|
||||
"processing_time_ms": forecast.processing_time_ms,
|
||||
"created_at": forecast.created_at.isoformat() if forecast.created_at else None
|
||||
}
|
||||
forecast_list.append(forecast_dict)
|
||||
|
||||
logger.info("Retrieved tenant forecasts",
|
||||
tenant_id=tenant_id,
|
||||
count=len(forecast_list),
|
||||
filters=filters)
|
||||
|
||||
return forecast_list
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get tenant forecasts", error=str(e))
|
||||
logger.error("Failed to get tenant forecasts",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e))
|
||||
raise
|
||||
|
||||
async def get_forecast_by_id(self, forecast_id: str) -> Optional[Dict]:
|
||||
@@ -199,7 +257,7 @@ class EnhancedForecastingService:
|
||||
date=request.forecast_date.isoformat())
|
||||
|
||||
# Get session and initialize repositories
|
||||
async with self.database_manager.get_session() as session:
|
||||
async with self.database_manager.get_background_session() as session:
|
||||
repos = await self._init_repositories(session)
|
||||
|
||||
# Step 1: Check cache first
|
||||
|
||||
Reference in New Issue
Block a user