""" Audit log service helper for DocuVault — Phase 4. Provides write_audit_log(), a fire-and-forget helper called inline by API handlers after successful operations (D-14). Key architectural constraints: - Uses session.flush() NOT session.commit() (D-14: the caller owns the transaction; the audit entry is flushed within the same transaction but the commit remains the caller's responsibility). - NEVER raises — audit failure must not abort the primary operation. Exceptions are logged at WARNING level and silently swallowed. """ from __future__ import annotations import logging import uuid from typing import Optional from sqlalchemy.ext.asyncio import AsyncSession from db.models import AuditLog logger = logging.getLogger(__name__) async def write_audit_log( session: AsyncSession, event_type: str, user_id: Optional[uuid.UUID], actor_id: Optional[uuid.UUID], resource_id: Optional[uuid.UUID], ip_address: Optional[str], metadata_: Optional[dict] = None, ) -> None: """Write an audit log entry within the caller's transaction. Never raises — audit failure is non-fatal. Exceptions are caught and logged as warnings so the primary operation is never aborted. Uses session.flush() not session.commit() (D-14). The caller is responsible for committing the transaction; this function merely ensures the audit row is queued in the same unit of work. """ try: entry = AuditLog( event_type=event_type, user_id=user_id, actor_id=actor_id, resource_id=resource_id, ip_address=ip_address, metadata_=metadata_, ) session.add(entry) await session.flush() # flush within handler's existing transaction, not commit except Exception as exc: logger.warning("audit log write failed: %s", exc) # Do not re-raise — audit failure must never abort the primary operation