Files
ai-contract-platform/backend/app/api/governance.py
T

294 lines
11 KiB
Python

from typing import Optional
from fastapi import APIRouter, Depends, Form, Header
from sqlalchemy.orm import Session
from app.core.response import success_response
from app.db import get_db_session
from app.services.audit_log_service import AuditLogService
from app.services.auth_service import AuthService
from app.services.governance_service import GovernanceService
router = APIRouter(prefix="/governance", tags=["governance"])
ROUTE_PERMISSION_SCOPE = "governance_management"
ROUTE_PERMISSION_ACTIONS = {
"assets_list": "governance:assets:list",
"asset_detail": "governance:assets:detail",
"assignments_list": "governance:assignments:list",
"assignment_save": "governance:assignments:save",
"decision_submit": "governance:decisions:submit",
"evidence_save": "governance:evidences:save",
"release_summary": "governance:releases:summary",
"release_detail": "governance:releases:detail",
"formal_launch_import_preview": "governance:imports:formal_launch:preview",
"formal_launch_import": "governance:imports:formal_launch",
}
def get_governance_service(db: Session = Depends(get_db_session)) -> GovernanceService:
return GovernanceService(db)
def get_audit_log_service(db: Session = Depends(get_db_session)) -> AuditLogService:
return AuditLogService(db)
def require_current_user(
authorization: Optional[str] = Header(default=None),
db: Session = Depends(get_db_session),
):
return AuthService(db).require_current_user(authorization)
@router.get("/assets")
def list_governance_assets(
page: int = 1,
page_size: int = 20,
asset_type: Optional[str] = None,
governance_status: Optional[str] = None,
review_status: Optional[str] = None,
production_status: Optional[str] = None,
assignment_role: Optional[str] = None,
governance_service: GovernanceService = Depends(get_governance_service),
current_user: dict = Depends(require_current_user),
):
return success_response(
governance_service.list_assets(
current_user=current_user,
page=page,
page_size=page_size,
asset_type=asset_type,
governance_status=governance_status,
review_status=review_status,
production_status=production_status,
assignment_role=assignment_role,
)
)
@router.get("/assets/{governance_asset_id}")
def get_governance_asset_detail(
governance_asset_id: str,
governance_service: GovernanceService = Depends(get_governance_service),
current_user: dict = Depends(require_current_user),
):
return success_response(governance_service.get_asset_detail(current_user, governance_asset_id))
@router.get("/assignments")
def list_governance_assignments(
page: int = 1,
page_size: int = 20,
assignment_role: Optional[str] = None,
status_filter: Optional[str] = None,
assignee_user_id: Optional[str] = None,
governance_asset_id: Optional[str] = None,
governance_service: GovernanceService = Depends(get_governance_service),
current_user: dict = Depends(require_current_user),
):
return success_response(
governance_service.list_assignments(
current_user=current_user,
page=page,
page_size=page_size,
assignment_role=assignment_role,
status_filter=status_filter,
assignee_user_id=assignee_user_id,
governance_asset_id=governance_asset_id,
)
)
@router.post("/assignments")
def save_governance_assignment(
governance_asset_id: str = Form(...),
assignment_role: str = Form(...),
assignee_user_id: Optional[str] = Form(default=None),
assignee_name: Optional[str] = Form(default=None),
due_at: Optional[str] = Form(default=None),
status_value: Optional[str] = Form(default=None),
priority: Optional[str] = Form(default=None),
notes: Optional[str] = Form(default=None),
assignment_id: Optional[str] = Form(default=None),
is_active: bool = Form(default=True),
governance_service: GovernanceService = Depends(get_governance_service),
audit_log_service: AuditLogService = Depends(get_audit_log_service),
current_user: dict = Depends(require_current_user),
):
result = governance_service.save_assignment(
current_user=current_user,
governance_asset_id=governance_asset_id,
assignment_role=assignment_role,
assignee_user_id=assignee_user_id,
assignee_name=assignee_name,
due_at=due_at,
status_value=status_value,
priority=priority,
notes=notes,
assignment_id=assignment_id,
is_active=is_active,
)
audit_log_service.record_event(
tenant_id=current_user.get("tenant_id"),
user_id=current_user.get("id"),
action="save_governance_assignment",
resource_type="governance_assignment",
resource_id=result["id"],
detail_json={
"governance_asset_id": result["governance_asset_id"],
"assignment_role": result["assignment_role"],
"assignee_user_id": result["assignee_user_id"],
"assignee_name": result["assignee_name"],
"actor_role": current_user.get("role"),
"status": result["status"],
"priority": result["priority"],
"is_active": result["is_active"],
},
)
return success_response(result)
@router.post("/decisions")
def submit_governance_decision(
governance_asset_id: str = Form(...),
assignment_id: Optional[str] = Form(default=None),
decision_role: str = Form(...),
decision_result: str = Form(...),
decision_summary: Optional[str] = Form(default=None),
required_changes: Optional[str] = Form(default=None),
decided_by_name: Optional[str] = Form(default=None),
decision_at: Optional[str] = Form(default=None),
metadata_json: Optional[str] = Form(default=None),
governance_service: GovernanceService = Depends(get_governance_service),
audit_log_service: AuditLogService = Depends(get_audit_log_service),
current_user: dict = Depends(require_current_user),
):
result = governance_service.submit_decision(
current_user=current_user,
governance_asset_id=governance_asset_id,
assignment_id=assignment_id,
decision_role=decision_role,
decision_result=decision_result,
decision_summary=decision_summary,
required_changes=required_changes,
decided_by_name=decided_by_name,
decision_at=decision_at,
metadata_json=metadata_json,
)
audit_log_service.record_event(
tenant_id=current_user.get("tenant_id"),
user_id=current_user.get("id"),
action="submit_governance_decision",
resource_type="governance_decision",
resource_id=result["decision"]["id"],
detail_json={
"governance_asset_id": governance_asset_id,
"assignment_id": assignment_id,
"decision_role": result["decision"]["decision_role"],
"decision_result": result["decision"]["decision_result"],
"actor_role": current_user.get("role"),
},
)
return success_response(result)
@router.post("/evidences")
def save_governance_evidence(
governance_asset_id: str = Form(...),
decision_id: Optional[str] = Form(default=None),
file_id: Optional[str] = Form(default=None),
evidence_type: Optional[str] = Form(default=None),
storage_type: Optional[str] = Form(default=None),
external_url: Optional[str] = Form(default=None),
file_name: Optional[str] = Form(default=None),
checksum: Optional[str] = Form(default=None),
status_value: Optional[str] = Form(default=None),
reviewer_name: Optional[str] = Form(default=None),
collected_at: Optional[str] = Form(default=None),
metadata_json: Optional[str] = Form(default=None),
governance_service: GovernanceService = Depends(get_governance_service),
audit_log_service: AuditLogService = Depends(get_audit_log_service),
current_user: dict = Depends(require_current_user),
):
result = governance_service.save_evidence(
current_user=current_user,
governance_asset_id=governance_asset_id,
decision_id=decision_id,
file_id=file_id,
evidence_type=evidence_type,
storage_type=storage_type,
external_url=external_url,
file_name=file_name,
checksum=checksum,
status_value=status_value,
reviewer_name=reviewer_name,
collected_at=collected_at,
metadata_json=metadata_json,
)
audit_log_service.record_event(
tenant_id=current_user.get("tenant_id"),
user_id=current_user.get("id"),
action="save_governance_evidence",
resource_type="governance_evidence",
resource_id=result["evidence"]["id"],
detail_json={
"governance_asset_id": governance_asset_id,
"decision_id": decision_id,
"actor_role": current_user.get("role"),
"status": result["evidence"]["status"],
"storage_type": result["evidence"]["storage_type"],
"external_url": result["evidence"]["external_url"],
"file_id": result["evidence"]["file_id"],
},
)
return success_response(result)
@router.get("/releases/summary")
def get_governance_release_summary(
governance_service: GovernanceService = Depends(get_governance_service),
current_user: dict = Depends(require_current_user),
):
return success_response(governance_service.get_release_summary(current_user))
@router.get("/releases/{release_batch_id}")
def get_governance_release_detail(
release_batch_id: str,
governance_service: GovernanceService = Depends(get_governance_service),
current_user: dict = Depends(require_current_user),
):
return success_response(governance_service.get_release_detail(current_user, release_batch_id))
@router.get("/imports/formal-launch/preview")
def preview_formal_launch_governance_import(
governance_service: GovernanceService = Depends(get_governance_service),
current_user: dict = Depends(require_current_user),
):
return success_response(governance_service.preview_formal_launch_import(current_user))
@router.post("/imports/formal-launch")
def import_formal_launch_governance(
batch_name: Optional[str] = Form(default=None),
governance_service: GovernanceService = Depends(get_governance_service),
audit_log_service: AuditLogService = Depends(get_audit_log_service),
current_user: dict = Depends(require_current_user),
):
result = governance_service.import_formal_launch(current_user, batch_name=batch_name)
audit_log_service.record_event(
tenant_id=current_user.get("tenant_id"),
user_id=current_user.get("id"),
action="import_formal_launch_governance",
resource_type="governance_import",
resource_id=result["release_batch"]["id"],
detail_json={
"batch_name": result["release_batch"]["batch_name"],
"actor_role": current_user.get("role"),
"status": result["release_batch"]["status"],
"imported": result["imported"],
},
)
return success_response(result)