feat: add marketplace metrics, privacy features, and service registry endpoints
- Add Prometheus metrics for marketplace API throughput and error rates with new dashboard panels - Implement confidential transaction models with encryption support and access control - Add key management system with registration, rotation, and audit logging - Create services and registry routers for service discovery and management - Integrate ZK proof generation for privacy-preserving receipts - Add metrics instru
This commit is contained in:
369
enterprise-connectors/python-sdk/aitbc_enterprise/base.py
Normal file
369
enterprise-connectors/python-sdk/aitbc_enterprise/base.py
Normal file
@@ -0,0 +1,369 @@
|
||||
"""
|
||||
Base connector class for AITBC Enterprise Connectors
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any, Optional, List, Union, Callable, Awaitable
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
import json
|
||||
|
||||
from .core import AITBCClient, ConnectorConfig
|
||||
from .exceptions import AITBCError, ConnectorError, ValidationError
|
||||
from .webhooks import WebhookHandler
|
||||
from .validators import BaseValidator
|
||||
|
||||
|
||||
@dataclass
|
||||
class OperationResult:
|
||||
"""Result of a connector operation"""
|
||||
success: bool
|
||||
data: Optional[Dict[str, Any]] = None
|
||||
error: Optional[str] = None
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
timestamp: datetime = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.timestamp is None:
|
||||
self.timestamp = datetime.utcnow()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Transaction:
|
||||
"""Standard transaction representation"""
|
||||
id: str
|
||||
amount: float
|
||||
currency: str
|
||||
status: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
metadata: Dict[str, Any] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"id": self.id,
|
||||
"amount": self.amount,
|
||||
"currency": self.currency,
|
||||
"status": self.status,
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"updated_at": self.updated_at.isoformat(),
|
||||
"metadata": self.metadata or {}
|
||||
}
|
||||
|
||||
|
||||
class BaseConnector(ABC):
|
||||
"""Base class for all enterprise connectors"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
client: AITBCClient,
|
||||
config: ConnectorConfig,
|
||||
validator: Optional[BaseValidator] = None,
|
||||
webhook_handler: Optional[WebhookHandler] = None
|
||||
):
|
||||
self.client = client
|
||||
self.config = config
|
||||
self.logger = logging.getLogger(f"aitbc.{self.__class__.__name__}")
|
||||
|
||||
# Injected dependencies
|
||||
self.validator = validator
|
||||
self.webhook_handler = webhook_handler
|
||||
|
||||
# Connector state
|
||||
self._initialized = False
|
||||
self._last_sync = None
|
||||
|
||||
# Event handlers
|
||||
self._operation_handlers: Dict[str, List[Callable]] = {}
|
||||
|
||||
# Metrics
|
||||
self._operation_count = 0
|
||||
self._error_count = 0
|
||||
|
||||
async def initialize(self) -> None:
|
||||
"""Initialize the connector"""
|
||||
if self._initialized:
|
||||
return
|
||||
|
||||
try:
|
||||
# Perform connector-specific initialization
|
||||
await self._initialize()
|
||||
|
||||
# Set up webhooks if configured
|
||||
if self.config.webhook_endpoint and self.webhook_handler:
|
||||
await self._setup_webhooks()
|
||||
|
||||
# Register event handlers
|
||||
self._register_handlers()
|
||||
|
||||
self._initialized = True
|
||||
self.logger.info(f"{self.__class__.__name__} initialized")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to initialize {self.__class__.__name__}: {e}")
|
||||
raise ConnectorError(f"Initialization failed: {e}")
|
||||
|
||||
async def cleanup(self) -> None:
|
||||
"""Cleanup connector resources"""
|
||||
try:
|
||||
# Perform connector-specific cleanup
|
||||
await self._cleanup()
|
||||
|
||||
# Cleanup webhooks
|
||||
if self.webhook_handler:
|
||||
await self.webhook_handler.cleanup()
|
||||
|
||||
self._initialized = False
|
||||
self.logger.info(f"{self.__class__.__name__} cleaned up")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error during cleanup: {e}")
|
||||
|
||||
async def execute_operation(
|
||||
self,
|
||||
operation: str,
|
||||
data: Dict[str, Any],
|
||||
**kwargs
|
||||
) -> OperationResult:
|
||||
"""Execute an operation with validation and error handling"""
|
||||
if not self._initialized:
|
||||
await self.initialize()
|
||||
|
||||
start_time = datetime.utcnow()
|
||||
|
||||
try:
|
||||
# Validate input if validator is configured
|
||||
if self.validator:
|
||||
await self.validator.validate(operation, data)
|
||||
|
||||
# Pre-operation hook
|
||||
await self._before_operation(operation, data)
|
||||
|
||||
# Execute the operation
|
||||
result = await self._execute_operation(operation, data, **kwargs)
|
||||
|
||||
# Post-operation hook
|
||||
await self._after_operation(operation, data, result)
|
||||
|
||||
# Update metrics
|
||||
self._operation_count += 1
|
||||
|
||||
# Emit operation event
|
||||
await self._emit_operation_event(operation, result)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
self._error_count += 1
|
||||
self.logger.error(f"Operation {operation} failed: {e}")
|
||||
|
||||
error_result = OperationResult(
|
||||
success=False,
|
||||
error=str(e),
|
||||
timestamp=datetime.utcnow()
|
||||
)
|
||||
|
||||
# Emit error event
|
||||
await self._emit_operation_event(f"{operation}.error", error_result)
|
||||
|
||||
return error_result
|
||||
|
||||
finally:
|
||||
# Log operation duration
|
||||
duration = (datetime.utcnow() - start_time).total_seconds()
|
||||
self.logger.debug(f"Operation {operation} completed in {duration:.3f}s")
|
||||
|
||||
async def batch_execute(
|
||||
self,
|
||||
operations: List[Dict[str, Any]],
|
||||
max_concurrent: int = 10
|
||||
) -> List[OperationResult]:
|
||||
"""Execute multiple operations concurrently"""
|
||||
semaphore = asyncio.Semaphore(max_concurrent)
|
||||
|
||||
async def _execute_with_semaphore(op_data):
|
||||
async with semaphore:
|
||||
return await self.execute_operation(**op_data)
|
||||
|
||||
tasks = [_execute_with_semaphore(op) for op in operations]
|
||||
return await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
async def sync(
|
||||
self,
|
||||
since: Optional[datetime] = None,
|
||||
filters: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Synchronize data with external system"""
|
||||
if not self._initialized:
|
||||
await self.initialize()
|
||||
|
||||
try:
|
||||
# Perform sync
|
||||
result = await self._sync(since, filters)
|
||||
|
||||
# Update last sync timestamp
|
||||
self._last_sync = datetime.utcnow()
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Sync failed: {e}")
|
||||
raise ConnectorError(f"Sync failed: {e}")
|
||||
|
||||
async def validate_webhook(self, payload: Dict[str, Any], signature: str) -> bool:
|
||||
"""Validate incoming webhook payload"""
|
||||
if not self.webhook_handler:
|
||||
return False
|
||||
|
||||
return await self.webhook_handler.validate(payload, signature)
|
||||
|
||||
async def handle_webhook(self, payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Handle incoming webhook"""
|
||||
if not self.webhook_handler:
|
||||
raise ConnectorError("Webhook handler not configured")
|
||||
|
||||
return await self.webhook_handler.handle(payload)
|
||||
|
||||
def add_operation_handler(
|
||||
self,
|
||||
operation: str,
|
||||
handler: Callable[[Dict[str, Any]], Awaitable[None]]
|
||||
):
|
||||
"""Add handler for specific operation"""
|
||||
if operation not in self._operation_handlers:
|
||||
self._operation_handlers[operation] = []
|
||||
self._operation_handlers[operation].append(handler)
|
||||
|
||||
def remove_operation_handler(
|
||||
self,
|
||||
operation: str,
|
||||
handler: Callable
|
||||
):
|
||||
"""Remove handler for specific operation"""
|
||||
if operation in self._operation_handlers:
|
||||
try:
|
||||
self._operation_handlers[operation].remove(handler)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Abstract methods to be implemented by subclasses
|
||||
|
||||
@abstractmethod
|
||||
async def _initialize(self) -> None:
|
||||
"""Connector-specific initialization"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def _cleanup(self) -> None:
|
||||
"""Connector-specific cleanup"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def _execute_operation(
|
||||
self,
|
||||
operation: str,
|
||||
data: Dict[str, Any],
|
||||
**kwargs
|
||||
) -> OperationResult:
|
||||
"""Execute connector-specific operation"""
|
||||
pass
|
||||
|
||||
async def _sync(
|
||||
self,
|
||||
since: Optional[datetime],
|
||||
filters: Optional[Dict[str, Any]]
|
||||
) -> Dict[str, Any]:
|
||||
"""Default sync implementation"""
|
||||
return {
|
||||
"synced_at": datetime.utcnow().isoformat(),
|
||||
"records": 0,
|
||||
"message": "Sync not implemented"
|
||||
}
|
||||
|
||||
# Hook methods
|
||||
|
||||
async def _before_operation(
|
||||
self,
|
||||
operation: str,
|
||||
data: Dict[str, Any]
|
||||
) -> None:
|
||||
"""Called before operation execution"""
|
||||
pass
|
||||
|
||||
async def _after_operation(
|
||||
self,
|
||||
operation: str,
|
||||
data: Dict[str, Any],
|
||||
result: OperationResult
|
||||
) -> None:
|
||||
"""Called after operation execution"""
|
||||
pass
|
||||
|
||||
# Private methods
|
||||
|
||||
async def _setup_webhooks(self) -> None:
|
||||
"""Setup webhook endpoints"""
|
||||
if not self.webhook_handler:
|
||||
return
|
||||
|
||||
await self.webhook_handler.setup(
|
||||
endpoint=self.config.webhook_endpoint,
|
||||
secret=self.config.webhook_secret
|
||||
)
|
||||
|
||||
def _register_handlers(self) -> None:
|
||||
"""Register default event handlers"""
|
||||
# Register with client if needed
|
||||
pass
|
||||
|
||||
async def _emit_operation_event(
|
||||
self,
|
||||
event: str,
|
||||
result: OperationResult
|
||||
) -> None:
|
||||
"""Emit operation event to handlers"""
|
||||
if event in self._operation_handlers:
|
||||
tasks = []
|
||||
for handler in self._operation_handlers[event]:
|
||||
try:
|
||||
tasks.append(handler(result.to_dict() if result.data else {}))
|
||||
except Exception as e:
|
||||
self.logger.error(f"Handler error: {e}")
|
||||
|
||||
if tasks:
|
||||
await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
# Properties
|
||||
|
||||
@property
|
||||
def is_initialized(self) -> bool:
|
||||
"""Check if connector is initialized"""
|
||||
return self._initialized
|
||||
|
||||
@property
|
||||
def last_sync(self) -> Optional[datetime]:
|
||||
"""Get last sync timestamp"""
|
||||
return self._last_sync
|
||||
|
||||
@property
|
||||
def metrics(self) -> Dict[str, Any]:
|
||||
"""Get connector metrics"""
|
||||
return {
|
||||
"operation_count": self._operation_count,
|
||||
"error_count": self._error_count,
|
||||
"error_rate": self._error_count / max(self._operation_count, 1),
|
||||
"last_sync": self._last_sync.isoformat() if self._last_sync else None
|
||||
}
|
||||
|
||||
# Context manager
|
||||
|
||||
async def __aenter__(self):
|
||||
"""Async context manager entry"""
|
||||
await self.initialize()
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||
"""Async context manager exit"""
|
||||
await self.cleanup()
|
||||
Reference in New Issue
Block a user