"""Component domain policies — Phase 11.2.""" from __future__ import annotations from typing import Any from app.models.types import ( ComponentStatus, ComponentVersionStatus, ) from app.validators.components import ( ensure_component_status_transition, ensure_version_mutable, ensure_version_status_transition, ) from shared.exceptions import AppError class ComponentLifecyclePolicy: """Enforces component catalog status transitions.""" def assert_status_change( self, *, current: ComponentStatus, target: ComponentStatus ) -> ComponentStatus: ensure_component_status_transition(current, target) return target def assert_can_soft_delete(self, *, status: ComponentStatus) -> None: if status == ComponentStatus.ARCHIVED: return # Soft-delete allowed from any status; archive is preferred path. return class ComponentVersionPolicy: """Enforces immutability and publish/retire rules.""" def assert_can_update(self, version: Any) -> None: ensure_version_mutable(version) def assert_publish( self, *, current: ComponentVersionStatus ) -> ComponentVersionStatus: ensure_version_status_transition(current, ComponentVersionStatus.PUBLISHED) return ComponentVersionStatus.PUBLISHED def assert_retire( self, *, current: ComponentVersionStatus ) -> ComponentVersionStatus: ensure_version_status_transition(current, ComponentVersionStatus.RETIRED) return ComponentVersionStatus.RETIRED class PagePlacementPolicy: """Composition shells may only pin published (non-retired) versions.""" def assert_version_placeable(self, version: Any) -> None: status = getattr(version, "status", None) if status != ComponentVersionStatus.PUBLISHED: raise AppError( "فقط نسخه منتشرشده قابل جایگذاری است", error_code="component_version_not_published", status_code=409, details={"status": getattr(status, "value", status)}, ) if getattr(version, "is_deleted", False): raise AppError( "نسخه حذف‌شده قابل جایگذاری نیست", error_code="component_version_deleted", status_code=409, ) def assert_component_placeable(self, component: Any) -> None: status = getattr(component, "status", None) if status in (ComponentStatus.ARCHIVED,): raise AppError( "کامپوننت بایگانی‌شده قابل جایگذاری نیست", error_code="component_archived", status_code=409, ) if getattr(component, "is_deleted", False): raise AppError( "کامپوننت حذف‌شده قابل جایگذاری نیست", error_code="component_deleted", status_code=409, )