from __future__ import annotations from typing import Any from yuxi.utils.logging_config import logger def normalize_approver_id(raw: str) -> str: if not raw: return "" if raw.startswith("users/") or raw.startswith("user:"): return raw if "@" in raw: return f"users/{raw}" return f"users/{raw}" def resolve_approver_ids( dm_allow_from: list[str], default_to: str = "", ) -> list[str]: approvers: list[str] = [] for entry in dm_allow_from: if entry == "*": continue normalized = normalize_approver_id(entry) if normalized: approvers.append(normalized) if not approvers and default_to: approvers.append(normalize_approver_id(default_to)) return approvers def build_exec_approval_context( action_id: str, approvers: list[str], metadata: dict[str, Any] | None = None, ) -> dict[str, Any]: return { "action_id": action_id, "approvers": approvers, "approved_by": [], "rejected_by": [], "metadata": metadata or {}, } def record_approval_decision( context: dict[str, Any], user_id: str, approved: bool, ) -> bool: normalized = normalize_approver_id(user_id) if normalized not in context.get("approvers", []): logger.warning(f"User {normalized} is not an authorized approver") return False if approved: if normalized in context.get("approved_by", []): return True context.setdefault("approved_by", []).append(normalized) else: if normalized in context.get("rejected_by", []): return True context.setdefault("rejected_by", []).append(normalized) return True def is_fully_approved(context: dict[str, Any]) -> bool: approvers = context.get("approvers", []) approved = context.get("approved_by", []) return bool(approvers) and set(approvers) == set(approved)