""" Route helper utilities for standardizing error handling and responses. """ from functools import wraps from typing import Any, Callable, Optional from flask import flash, jsonify, redirect, request, url_for from flask_login import current_user from app.utils.api_responses import ( error_response, forbidden_response, not_found_response, success_response, unauthorized_response, ) def handle_service_result( result: dict, success_redirect: Optional[str] = None, success_message: Optional[str] = None, error_redirect: Optional[str] = None, json_response: bool = False, ): """ Handle service layer result and return appropriate response. Args: result: Service result dict with 'success', 'message', etc. success_redirect: URL to redirect to on success (for HTML forms) success_message: Custom success message (overrides service message) error_redirect: URL to redirect to on error (for HTML forms) json_response: If True, return JSON response; if False, use flash messages Returns: Flask response (redirect or JSON) """ if result.get("success"): message = success_message or result.get("message", "Operation successful") if json_response: return success_response( data=result.get("data") or result.get("invoice") or result.get("project") or result.get("task"), message=message, ) else: flash(message, "success") if success_redirect: return redirect(success_redirect) return redirect(url_for("main.dashboard")) else: message = result.get("message", "An error occurred") error_code = result.get("error", "error") if json_response: status_code = 400 if error_code == "not_found": status_code = 404 elif error_code == "permission_denied": status_code = 403 return error_response(message=message, error_code=error_code, status_code=status_code) else: flash(message, "error") if error_redirect: return redirect(error_redirect) return redirect(request.referrer or url_for("main.dashboard")) def json_api(f: Callable) -> Callable: """ Decorator to ensure route returns JSON API responses. Automatically handles service results and converts to JSON. Usage: @json_api @route('/api/projects', methods=['POST']) def create_project(): service = ProjectService() result = service.create_project(...) return handle_service_result(result, json_response=True) """ @wraps(f) def decorated_function(*args, **kwargs): # Set JSON response flag request.is_json_api = True return f(*args, **kwargs) return decorated_function def require_admin_or_owner(owner_id_getter: Callable[[Any], int]): """ Decorator to require admin or ownership of resource. Args: owner_id_getter: Function that extracts owner ID from route args/kwargs Usage: @require_admin_or_owner(lambda **kwargs: kwargs['project_id']) def view_project(project_id): ... """ def decorator(f: Callable) -> Callable: @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: if request.is_json or request.path.startswith("/api/"): return unauthorized_response() flash("Please log in to access this page", "error") return redirect(url_for("auth.login")) owner_id = owner_id_getter(*args, **kwargs) if not current_user.is_admin and current_user.id != owner_id: if request.is_json or request.path.startswith("/api/"): return forbidden_response("You do not have permission to access this resource") flash("You do not have permission to access this resource", "error") return redirect(url_for("main.dashboard")) return f(*args, **kwargs) return decorated_function return decorator