docs: add code quality and type checking workflows to master index
Some checks failed
Documentation Validation / validate-docs (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
API Endpoint Tests / test-api-endpoints (push) Has been cancelled
CLI Tests / test-cli (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-agent-sdk path:packages/py/aitbc-agent-sdk]) (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-core path:packages/py/aitbc-core]) (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-crypto path:packages/py/aitbc-crypto]) (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-sdk path:packages/py/aitbc-sdk]) (push) Has been cancelled
Package Tests / test-javascript-packages (map[name:aitbc-sdk-js path:packages/js/aitbc-sdk]) (push) Has been cancelled
Package Tests / test-javascript-packages (map[name:aitbc-token path:packages/solidity/aitbc-token]) (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
Systemd Sync / sync-systemd (push) Has been cancelled
Some checks failed
Documentation Validation / validate-docs (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
API Endpoint Tests / test-api-endpoints (push) Has been cancelled
CLI Tests / test-cli (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-agent-sdk path:packages/py/aitbc-agent-sdk]) (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-core path:packages/py/aitbc-core]) (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-crypto path:packages/py/aitbc-crypto]) (push) Has been cancelled
Package Tests / test-python-packages (map[name:aitbc-sdk path:packages/py/aitbc-sdk]) (push) Has been cancelled
Package Tests / test-javascript-packages (map[name:aitbc-sdk-js path:packages/js/aitbc-sdk]) (push) Has been cancelled
Package Tests / test-javascript-packages (map[name:aitbc-token path:packages/solidity/aitbc-token]) (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
Systemd Sync / sync-systemd (push) Has been cancelled
- Add Code Quality Module section with pre-commit hooks and quality checks - Add Type Checking CI/CD Module section with MyPy workflow and coverage - Update README with code quality achievements and project structure - Migrate FastAPI apps from deprecated on_event to lifespan context manager - Update pyproject.toml files to reference consolidated dependencies - Remove unused app.py import in coordinator-api - Add type hints to agent
This commit is contained in:
@@ -5,23 +5,26 @@ Provides structured error responses for consistent API error handling.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional, List
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ErrorDetail(BaseModel):
|
||||
"""Detailed error information."""
|
||||
field: Optional[str] = Field(None, description="Field that caused the error")
|
||||
|
||||
field: str | None = Field(None, description="Field that caused the error")
|
||||
message: str = Field(..., description="Error message")
|
||||
code: Optional[str] = Field(None, description="Error code for programmatic handling")
|
||||
code: str | None = Field(None, description="Error code for programmatic handling")
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
"""Standardized error response for all API errors."""
|
||||
error: Dict[str, Any] = Field(..., description="Error information")
|
||||
|
||||
error: dict[str, Any] = Field(..., description="Error information")
|
||||
timestamp: str = Field(default_factory=lambda: datetime.utcnow().isoformat() + "Z")
|
||||
request_id: Optional[str] = Field(None, description="Request ID for tracing")
|
||||
|
||||
request_id: str | None = Field(None, description="Request ID for tracing")
|
||||
|
||||
class Config:
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
@@ -29,78 +32,76 @@ class ErrorResponse(BaseModel):
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Invalid input data",
|
||||
"status": 422,
|
||||
"details": [
|
||||
{"field": "email", "message": "Invalid email format", "code": "invalid_format"}
|
||||
]
|
||||
"details": [{"field": "email", "message": "Invalid email format", "code": "invalid_format"}],
|
||||
},
|
||||
"timestamp": "2026-02-13T21:00:00Z",
|
||||
"request_id": "req_abc123"
|
||||
"request_id": "req_abc123",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AITBCError(Exception):
|
||||
"""Base exception for all AITBC errors"""
|
||||
|
||||
error_code: str = "INTERNAL_ERROR"
|
||||
status_code: int = 500
|
||||
|
||||
def to_response(self, request_id: Optional[str] = None) -> ErrorResponse:
|
||||
|
||||
def to_response(self, request_id: str | None = None) -> ErrorResponse:
|
||||
"""Convert exception to standardized error response."""
|
||||
return ErrorResponse(
|
||||
error={
|
||||
"code": self.error_code,
|
||||
"message": str(self),
|
||||
"status": self.status_code,
|
||||
"details": []
|
||||
},
|
||||
request_id=request_id
|
||||
error={"code": self.error_code, "message": str(self), "status": self.status_code, "details": []},
|
||||
request_id=request_id,
|
||||
)
|
||||
|
||||
|
||||
class AuthenticationError(AITBCError):
|
||||
"""Raised when authentication fails"""
|
||||
|
||||
error_code: str = "AUTHENTICATION_ERROR"
|
||||
status_code: int = 401
|
||||
|
||||
|
||||
def __init__(self, message: str = "Authentication failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class AuthorizationError(AITBCError):
|
||||
"""Raised when authorization fails"""
|
||||
|
||||
error_code: str = "AUTHORIZATION_ERROR"
|
||||
status_code: int = 403
|
||||
|
||||
|
||||
def __init__(self, message: str = "Not authorized to perform this action"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class RateLimitError(AITBCError):
|
||||
"""Raised when rate limit is exceeded"""
|
||||
|
||||
error_code: str = "RATE_LIMIT_EXCEEDED"
|
||||
status_code: int = 429
|
||||
|
||||
|
||||
def __init__(self, message: str = "Rate limit exceeded", retry_after: int = 60):
|
||||
super().__init__(message)
|
||||
self.retry_after = retry_after
|
||||
|
||||
def to_response(self, request_id: Optional[str] = None) -> ErrorResponse:
|
||||
|
||||
def to_response(self, request_id: str | None = None) -> ErrorResponse:
|
||||
return ErrorResponse(
|
||||
error={
|
||||
"code": self.error_code,
|
||||
"message": str(self),
|
||||
"status": self.status_code,
|
||||
"details": [{"retry_after": self.retry_after}]
|
||||
"details": [{"retry_after": self.retry_after}],
|
||||
},
|
||||
request_id=request_id
|
||||
request_id=request_id,
|
||||
)
|
||||
|
||||
|
||||
class APIError(AITBCError):
|
||||
"""Raised when API request fails"""
|
||||
|
||||
error_code: str = "API_ERROR"
|
||||
status_code: int = 500
|
||||
|
||||
|
||||
def __init__(self, message: str, status_code: int = None, response: dict = None):
|
||||
super().__init__(message)
|
||||
self.status_code = status_code or self.status_code
|
||||
@@ -109,141 +110,149 @@ class APIError(AITBCError):
|
||||
|
||||
class ConfigurationError(AITBCError):
|
||||
"""Raised when configuration is invalid"""
|
||||
|
||||
error_code: str = "CONFIGURATION_ERROR"
|
||||
status_code: int = 500
|
||||
|
||||
|
||||
def __init__(self, message: str = "Invalid configuration"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class ConnectorError(AITBCError):
|
||||
"""Raised when connector operation fails"""
|
||||
|
||||
error_code: str = "CONNECTOR_ERROR"
|
||||
status_code: int = 502
|
||||
|
||||
|
||||
def __init__(self, message: str = "Connector operation failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class PaymentError(ConnectorError):
|
||||
"""Raised when payment operation fails"""
|
||||
|
||||
error_code: str = "PAYMENT_ERROR"
|
||||
status_code: int = 402
|
||||
|
||||
|
||||
def __init__(self, message: str = "Payment operation failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class ValidationError(AITBCError):
|
||||
"""Raised when data validation fails"""
|
||||
|
||||
error_code: str = "VALIDATION_ERROR"
|
||||
status_code: int = 422
|
||||
|
||||
def __init__(self, message: str = "Validation failed", details: List[ErrorDetail] = None):
|
||||
|
||||
def __init__(self, message: str = "Validation failed", details: list[ErrorDetail] = None):
|
||||
super().__init__(message)
|
||||
self.details = details or []
|
||||
|
||||
def to_response(self, request_id: Optional[str] = None) -> ErrorResponse:
|
||||
|
||||
def to_response(self, request_id: str | None = None) -> ErrorResponse:
|
||||
return ErrorResponse(
|
||||
error={
|
||||
"code": self.error_code,
|
||||
"message": str(self),
|
||||
"status": self.status_code,
|
||||
"details": [{"field": d.field, "message": d.message, "code": d.code} for d in self.details]
|
||||
"details": [{"field": d.field, "message": d.message, "code": d.code} for d in self.details],
|
||||
},
|
||||
request_id=request_id
|
||||
request_id=request_id,
|
||||
)
|
||||
|
||||
|
||||
class WebhookError(AITBCError):
|
||||
"""Raised when webhook processing fails"""
|
||||
|
||||
error_code: str = "WEBHOOK_ERROR"
|
||||
status_code: int = 500
|
||||
|
||||
|
||||
def __init__(self, message: str = "Webhook processing failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class ERPError(ConnectorError):
|
||||
"""Raised when ERP operation fails"""
|
||||
|
||||
error_code: str = "ERP_ERROR"
|
||||
status_code: int = 502
|
||||
|
||||
|
||||
def __init__(self, message: str = "ERP operation failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class SyncError(ConnectorError):
|
||||
"""Raised when synchronization fails"""
|
||||
|
||||
error_code: str = "SYNC_ERROR"
|
||||
status_code: int = 500
|
||||
|
||||
|
||||
def __init__(self, message: str = "Synchronization failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class TimeoutError(AITBCError):
|
||||
"""Raised when operation times out"""
|
||||
|
||||
error_code: str = "TIMEOUT_ERROR"
|
||||
status_code: int = 504
|
||||
|
||||
|
||||
def __init__(self, message: str = "Operation timed out"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class TenantError(ConnectorError):
|
||||
"""Raised when tenant operation fails"""
|
||||
|
||||
error_code: str = "TENANT_ERROR"
|
||||
status_code: int = 400
|
||||
|
||||
|
||||
def __init__(self, message: str = "Tenant operation failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class QuotaExceededError(ConnectorError):
|
||||
"""Raised when resource quota is exceeded"""
|
||||
|
||||
error_code: str = "QUOTA_EXCEEDED"
|
||||
status_code: int = 429
|
||||
|
||||
|
||||
def __init__(self, message: str = "Quota exceeded", limit: int = None):
|
||||
super().__init__(message)
|
||||
self.limit = limit
|
||||
|
||||
def to_response(self, request_id: Optional[str] = None) -> ErrorResponse:
|
||||
|
||||
def to_response(self, request_id: str | None = None) -> ErrorResponse:
|
||||
details = [{"limit": self.limit}] if self.limit else []
|
||||
return ErrorResponse(
|
||||
error={
|
||||
"code": self.error_code,
|
||||
"message": str(self),
|
||||
"status": self.status_code,
|
||||
"details": details
|
||||
},
|
||||
request_id=request_id
|
||||
error={"code": self.error_code, "message": str(self), "status": self.status_code, "details": details},
|
||||
request_id=request_id,
|
||||
)
|
||||
|
||||
|
||||
class BillingError(ConnectorError):
|
||||
"""Raised when billing operation fails"""
|
||||
|
||||
error_code: str = "BILLING_ERROR"
|
||||
status_code: int = 402
|
||||
|
||||
|
||||
def __init__(self, message: str = "Billing operation failed"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class NotFoundError(AITBCError):
|
||||
"""Raised when a resource is not found"""
|
||||
|
||||
error_code: str = "NOT_FOUND"
|
||||
status_code: int = 404
|
||||
|
||||
|
||||
def __init__(self, message: str = "Resource not found"):
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class ConflictError(AITBCError):
|
||||
"""Raised when there's a conflict (e.g., duplicate resource)"""
|
||||
|
||||
error_code: str = "CONFLICT"
|
||||
status_code: int = 409
|
||||
|
||||
|
||||
def __init__(self, message: str = "Resource conflict"):
|
||||
super().__init__(message)
|
||||
|
||||
Reference in New Issue
Block a user