Source code for src.share_kernel.domain.exceptions
"""Exception hierarchy for the domain and multitenant layers.
Combines the original DDD exceptions with the multitenant exception hierarchy.
"""
from __future__ import annotations
# ---------------------------------------------------------------------------
# Core DDD exceptions
# ---------------------------------------------------------------------------
[docs]
class DomainException(Exception):
"""Base pour toutes les exceptions domaine."""
def __init__(self, message: str):
super().__init__(message)
self.message = message
[docs]
class EntityNotFoundException(DomainException):
def __init__(self, entity_name: str, entity_id):
super().__init__(f"{entity_name} with id '{entity_id}' not found.")
self.entity_name = entity_name
self.entity_id = entity_id
[docs]
class BusinessRuleViolationException(DomainException):
"""Levée quand une règle métier est violée."""
[docs]
class InvalidValueObjectException(DomainException):
"""Levée quand un value object reçoit une valeur invalide."""
[docs]
class ConfigurationError(DomainException):
"""Raised for invalid library configuration."""
# ---------------------------------------------------------------------------
# Multitenant exception hierarchy
# ---------------------------------------------------------------------------
[docs]
class DjangoMultitenantError(Exception):
"""Root exception for all django-multitenant errors."""
[docs]
class TenantError(DjangoMultitenantError):
"""Base exception for tenant-related errors."""
[docs]
class TenantNotFoundError(TenantError):
"""Raised when a tenant cannot be resolved."""
[docs]
class TenantInactiveError(TenantError):
"""Raised when access is attempted on a suspended or archived tenant."""
[docs]
class TenantSchemaError(TenantError):
"""Raised for schema provisioning or migration errors."""
[docs]
class NoTenantContextError(TenantError):
"""Raised when an operation requires a tenant context but none is set."""
[docs]
class AuthorizationError(DjangoMultitenantError):
"""Base exception for authorization-related errors."""
[docs]
class AccessDeniedError(AuthorizationError):
"""Raised when ABAC evaluation results in a deny."""
def __init__(self, reason: str = "Access denied", policy_id: str | None = None):
self.reason = reason
self.policy_id = policy_id
super().__init__(reason)
[docs]
class ApprovalRequiredError(AuthorizationError):
"""Raised when an action requires approval before execution.
This is not an error per se — the PEP catches it and returns HTTP 202.
"""
def __init__(self, approval_request_id: str, message: str = "Approval required"):
self.approval_request_id = approval_request_id
self.message = message
super().__init__(message)
class PolicyValidationError(AuthorizationError):
"""Raised when a policy definition is invalid."""
class ApprovalError(AuthorizationError):
"""Base exception for approval workflow errors."""
class ApprovalNotPendingError(ApprovalError):
"""Raised when trying to act on an approval request that is not pending."""
class SelfApprovalError(ApprovalError):
"""Raised when a requester tries to approve their own request."""
class UnauthorizedApproverError(ApprovalError):
"""Raised when a user is not authorized to approve a request."""
[docs]
class MembershipError(DjangoMultitenantError):
"""Base exception for membership-related errors."""
[docs]
class MembershipNotFoundError(MembershipError):
"""Raised when a user has no active membership in the current tenant."""