/ src / solace_agent_mesh / shared / exceptions / exceptions.py
exceptions.py
  1  """
  2  Generic web exceptions for HTTP/REST APIs.
  3  
  4  This module provides a comprehensive set of generic exception classes
  5  that can be used by any web application for consistent error handling.
  6  """
  7  
  8  from typing import Any, Dict, List, Optional
  9  from enum import Enum
 10  
 11  
 12  class EntityOperation(Enum):
 13      """Operations that can be performed on entities."""
 14      CREATE = "create"
 15      UPDATE = "update"
 16      DELETE = "delete"
 17      READ = "read"
 18  
 19  
 20  class WebUIBackendException(Exception):
 21      """Base exception for all web UI backend errors."""
 22  
 23      def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
 24          self.message = message
 25          self.details = details or {}
 26          super().__init__(self.message)
 27  
 28  
 29  class ValidationError(WebUIBackendException):
 30      """
 31      Exception for validation errors with field-level details.
 32  
 33      This exception supports both simple validation messages and detailed
 34      field-level validation errors with user-friendly message formatting.
 35      """
 36  
 37      def __init__(
 38          self,
 39          message: str,
 40          validation_details: Optional[Dict[str, List[str]]] = None,
 41          entity_type: Optional[str] = None,
 42          entity_identifier: Optional[str] = None
 43      ):
 44          self.validation_details = validation_details or {}
 45          self.entity_type = entity_type
 46          self.entity_identifier = entity_identifier
 47          super().__init__(message)
 48  
 49      def get_user_friendly_message(self, operation: EntityOperation) -> str:
 50          """Generate a user-friendly error message based on context."""
 51          if not self.entity_identifier or not self.entity_type:
 52              return self.message
 53  
 54          if self.validation_details:
 55              field = next(iter(self.validation_details.keys()))
 56              error_msg = self.validation_details[field][0]
 57              return f"Unable to {operation.value} {self.entity_type} {self.entity_identifier} because of a problem with the {field}: {error_msg.lower()}"
 58  
 59          if self.message:
 60              return f"Unable to {operation.value} {self.entity_type} {self.entity_identifier}: {self.message.lower()}"
 61  
 62          return self.message
 63  
 64      @classmethod
 65      def builder(cls):
 66          """Create a ValidationErrorBuilder for fluent error construction."""
 67          return ValidationErrorBuilder()
 68  
 69  
 70  class ValidationErrorBuilder:
 71      """Builder for constructing ValidationError instances with fluent API."""
 72  
 73      def __init__(self):
 74          self._message = ""
 75          self._validation_details = {}
 76          self._entity_type = None
 77          self._entity_identifier = None
 78  
 79      def message(self, message: str):
 80          """Set the main error message."""
 81          self._message = message
 82          return self
 83  
 84      def formatted_message(self, pattern: str, *args):
 85          """Set a formatted error message."""
 86          self._message = pattern.format(*args)
 87          return self
 88  
 89      def validation_detail(self, field: str, errors: List[str]):
 90          """Add validation details for a specific field."""
 91          self._validation_details[field] = errors
 92          return self
 93  
 94      def formatted_validation_detail(self, field: str, pattern: str, *args):
 95          """Add a formatted validation detail for a field."""
 96          error_msg = pattern.format(*args)
 97          self._validation_details[field] = [error_msg]
 98          return self
 99  
100      def entity_type(self, entity_type: str):
101          """Set the entity type for context."""
102          self._entity_type = entity_type
103          return self
104  
105      def entity_identifier(self, identifier: str):
106          """Set the entity identifier for context."""
107          self._entity_identifier = identifier
108          return self
109  
110      def build(self) -> ValidationError:
111          """Build the ValidationError instance."""
112          return ValidationError(
113              self._message,
114              self._validation_details,
115              self._entity_type,
116              self._entity_identifier
117          )
118  
119  
120  class EntityNotFoundError(WebUIBackendException):
121      """
122      Generic exception for when an entity is not found.
123  
124      This replaces all specific "NotFound" exceptions with a single generic one.
125      Format: "Could not find {entity_type} with id: {entity_id}"
126      """
127  
128      def __init__(self, entity_type: str, entity_id: str):
129          self.entity_type = entity_type
130          self.entity_id = str(entity_id)
131          message = f"Could not find {entity_type} with id: {self.entity_id}"
132          details = {"entity_type": entity_type, "entity_id": self.entity_id}
133          super().__init__(message, details)
134  
135  
136  class EntityAlreadyExistsError(WebUIBackendException):
137      """Exception for when an entity already exists."""
138  
139      def __init__(self, entity_type: str, identifier: str, value: Any = None):
140          self.entity_type = entity_type
141          self.identifier = identifier
142          self.value = str(value) if value is not None else None
143  
144          if value is not None:
145              message = f"{entity_type} with {identifier} '{self.value}' already exists"
146          else:
147              message = f"{entity_type} already exists"
148  
149          details = {"entity_type": entity_type, "identifier": identifier}
150          if self.value:
151              details["value"] = self.value
152  
153          super().__init__(message, details)
154  
155  
156  class BusinessRuleViolationError(WebUIBackendException):
157      """Exception for business rule violations."""
158  
159      def __init__(self, rule: str, message: str):
160          self.rule = rule
161          details = {"rule": rule}
162          super().__init__(message, details)
163  
164  
165  class ConfigurationError(WebUIBackendException):
166      """Exception for configuration-related errors."""
167  
168      def __init__(self, component: str, message: str):
169          self.component = component
170          details = {"component": component}
171          super().__init__(message, details)
172  
173  
174  class DataIntegrityError(WebUIBackendException):
175      """Exception for data integrity violations."""
176  
177      def __init__(self, constraint: str, message: str):
178          self.constraint = constraint
179          details = {"constraint": constraint}
180          super().__init__(message, details)
181  
182  
183  class ExternalServiceError(WebUIBackendException):
184      """Exception for external service communication errors."""
185  
186      def __init__(self, service: str, message: str, status_code: Optional[int] = None):
187          self.service = service
188          self.status_code = status_code
189          details = {"service": service}
190          if status_code:
191              details["status_code"] = status_code
192          super().__init__(message, details)
193  
194  
195  class InternalServiceError(WebUIBackendException):
196      """Exception for unexpected internal errors that should never happen."""
197  
198      def __init__(self, message: str = "An unexpected error occurred"):
199          super().__init__(message)