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."""