2025-08-12 13:33:25 +03:00

637 lines
25 KiB
Python

from rest_framework import viewsets, permissions, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from django.db.models import Q, Count, Avg, Sum
from django.utils import timezone
from datetime import timedelta
import json
import time
from ..models import (
ExternalSystem, IntegrationEndpoint, DataMapping,
IntegrationExecution, WebhookEndpoint, WebhookExecution, IntegrationLog
)
from .serializers import (
ExternalSystemSerializer, IntegrationEndpointSerializer, DataMappingSerializer,
IntegrationExecutionSerializer, WebhookEndpointSerializer, WebhookExecutionSerializer,
IntegrationLogSerializer, IntegrationStatsSerializer, SystemTestSerializer,
EndpointExecuteSerializer, WebhookCreateSerializer, DataSyncSerializer,
MappingTestSerializer, SystemHealthCheckSerializer, IntegrationConfigSerializer,
BulkExecutionSerializer
)
from core.utils import AuditLogger
class BaseViewSet(viewsets.ModelViewSet):
"""Base ViewSet with common functionality"""
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
def get_queryset(self):
# Filter by tenant if user has one
if hasattr(self.request.user, 'tenant') and self.request.user.tenant:
return self.queryset.filter(tenant=self.request.user.tenant)
return self.queryset
def perform_create(self, serializer):
if hasattr(self.request.user, 'tenant'):
serializer.save(tenant=self.request.user.tenant)
else:
serializer.save()
class ExternalSystemViewSet(BaseViewSet):
"""ViewSet for ExternalSystem model"""
queryset = ExternalSystem.objects.all()
serializer_class = ExternalSystemSerializer
filterset_fields = ['system_type', 'status', 'is_active']
search_fields = ['name', 'description', 'base_url']
ordering_fields = ['name', 'system_type', 'last_health_check']
ordering = ['name']
@action(detail=False, methods=['get'])
def active(self, request):
"""Get active external systems"""
queryset = self.get_queryset().filter(is_active=True)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def test_connection(self, request, pk=None):
"""Test connection to external system"""
system = self.get_object()
serializer = SystemTestSerializer(data={'system_id': pk})
if serializer.is_valid():
try:
# Mock connection test - in real implementation, would test actual connection
start_time = time.time()
# Simulate connection test
time.sleep(0.1)
response_time = time.time() - start_time
test_result = {
'success': True,
'message': 'Connection successful',
'response_time': round(response_time, 3),
'system_status': 'ONLINE',
'api_version': system.api_version
}
# Update system health check timestamp
system.last_health_check = timezone.now()
system.status = 'ONLINE'
system.save()
# Log the action
AuditLogger.log_action(
user=request.user,
action='SYSTEM_CONNECTION_TESTED',
model='ExternalSystem',
object_id=str(system.system_id),
details={
'system_name': system.name,
'test_result': 'SUCCESS',
'response_time': response_time
}
)
return Response(test_result)
except Exception as e:
# Update system status on failure
system.status = 'OFFLINE'
system.save()
return Response({
'success': False,
'message': f'Connection failed: {str(e)}',
'error': str(e)
}, status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['post'])
def health_check(self, request):
"""Perform health check on multiple systems"""
serializer = SystemHealthCheckSerializer(data=request.data)
if serializer.is_valid():
system_ids = serializer.validated_data.get('system_ids')
include_endpoints = serializer.validated_data.get('include_endpoints', True)
if system_ids:
systems = self.get_queryset().filter(id__in=system_ids)
else:
systems = self.get_queryset().filter(is_active=True)
health_results = []
for system in systems:
try:
# Mock health check
health_status = {
'system_id': system.system_id,
'name': system.name,
'status': 'HEALTHY',
'response_time': 0.15,
'last_check': timezone.now(),
'endpoints': []
}
if include_endpoints:
endpoints = system.endpoints.filter(is_active=True)
for endpoint in endpoints:
endpoint_status = {
'endpoint_id': endpoint.endpoint_id,
'name': endpoint.name,
'status': 'HEALTHY',
'response_time': 0.12
}
health_status['endpoints'].append(endpoint_status)
health_results.append(health_status)
# Update system health
system.last_health_check = timezone.now()
system.status = 'ONLINE'
system.save()
except Exception as e:
health_results.append({
'system_id': system.system_id,
'name': system.name,
'status': 'UNHEALTHY',
'error': str(e),
'last_check': timezone.now()
})
system.status = 'OFFLINE'
system.save()
return Response({
'message': 'Health check completed',
'results': health_results,
'total_systems': len(health_results),
'healthy_systems': len([r for r in health_results if r.get('status') == 'HEALTHY'])
})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class IntegrationEndpointViewSet(BaseViewSet):
"""ViewSet for IntegrationEndpoint model"""
queryset = IntegrationEndpoint.objects.all()
serializer_class = IntegrationEndpointSerializer
filterset_fields = ['system', 'method', 'is_active']
search_fields = ['name', 'description', 'endpoint_url']
ordering_fields = ['name', 'system__name']
ordering = ['system__name', 'name']
@action(detail=False, methods=['get'])
def active(self, request):
"""Get active endpoints"""
queryset = self.get_queryset().filter(is_active=True)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def execute(self, request, pk=None):
"""Execute an integration endpoint"""
endpoint = self.get_object()
serializer = EndpointExecuteSerializer(data=request.data)
if serializer.is_valid():
try:
# Create execution record
execution = IntegrationExecution.objects.create(
endpoint=endpoint,
start_time=timezone.now(),
status='RUNNING',
request_data=serializer.validated_data.get('request_data', {}),
tenant=getattr(request.user, 'tenant', None)
)
# Mock execution - in real implementation, would make actual API call
time.sleep(0.1) # Simulate processing time
# Mock response data
response_data = {
'status': 'success',
'data': {'result': 'Mock response data'},
'timestamp': timezone.now().isoformat()
}
# Update execution with results
execution.end_time = timezone.now()
execution.status = 'SUCCESS'
execution.response_data = response_data
execution.save()
# Log the action
AuditLogger.log_action(
user=request.user,
action='ENDPOINT_EXECUTED',
model='IntegrationExecution',
object_id=str(execution.execution_id),
details={
'endpoint_name': endpoint.name,
'system_name': endpoint.system.name,
'status': 'SUCCESS'
}
)
return Response({
'message': 'Endpoint executed successfully',
'execution': IntegrationExecutionSerializer(execution).data
})
except Exception as e:
# Update execution with error
if 'execution' in locals():
execution.end_time = timezone.now()
execution.status = 'FAILED'
execution.error_message = str(e)
execution.save()
return Response({
'error': f'Endpoint execution failed: {str(e)}'
}, status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['post'])
def bulk_execute(self, request):
"""Execute multiple endpoints"""
serializer = BulkExecutionSerializer(data=request.data)
if serializer.is_valid():
endpoint_ids = serializer.validated_data['endpoint_ids']
request_data = serializer.validated_data.get('request_data', {})
parallel = serializer.validated_data.get('parallel_execution', True)
endpoints = IntegrationEndpoint.objects.filter(id__in=endpoint_ids)
results = []
for endpoint in endpoints:
try:
# Create execution record
execution = IntegrationExecution.objects.create(
endpoint=endpoint,
start_time=timezone.now(),
status='RUNNING',
request_data=request_data,
tenant=getattr(request.user, 'tenant', None)
)
# Mock execution
time.sleep(0.05) # Simulate processing
execution.end_time = timezone.now()
execution.status = 'SUCCESS'
execution.response_data = {'result': f'Success for {endpoint.name}'}
execution.save()
results.append({
'endpoint_id': endpoint.endpoint_id,
'endpoint_name': endpoint.name,
'status': 'SUCCESS',
'execution_id': execution.execution_id
})
except Exception as e:
results.append({
'endpoint_id': endpoint.endpoint_id,
'endpoint_name': endpoint.name,
'status': 'FAILED',
'error': str(e)
})
return Response({
'message': 'Bulk execution completed',
'results': results,
'total_endpoints': len(results),
'successful': len([r for r in results if r['status'] == 'SUCCESS'])
})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class DataMappingViewSet(BaseViewSet):
"""ViewSet for DataMapping model"""
queryset = DataMapping.objects.all()
serializer_class = DataMappingSerializer
filterset_fields = ['endpoint', 'mapping_type', 'is_required', 'is_active']
search_fields = ['name', 'description', 'source_field', 'target_field']
ordering_fields = ['name', 'endpoint__name']
ordering = ['endpoint__name', 'name']
@action(detail=True, methods=['post'])
def test_mapping(self, request, pk=None):
"""Test data mapping transformation"""
mapping = self.get_object()
serializer = MappingTestSerializer(data=request.data)
if serializer.is_valid():
try:
test_data = serializer.validated_data['test_data']
# Mock transformation - in real implementation, would apply actual mapping rules
transformed_data = {
'original': test_data,
'transformed': {
mapping.target_field: test_data.get(mapping.source_field, mapping.default_value)
},
'mapping_applied': {
'source_field': mapping.source_field,
'target_field': mapping.target_field,
'transformation_rules': mapping.transformation_rules
}
}
return Response({
'message': 'Mapping test completed',
'result': transformed_data
})
except Exception as e:
return Response({
'error': f'Mapping test failed: {str(e)}'
}, status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class IntegrationExecutionViewSet(viewsets.ReadOnlyModelViewSet):
"""ViewSet for IntegrationExecution model (read-only)"""
queryset = IntegrationExecution.objects.all()
serializer_class = IntegrationExecutionSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['endpoint', 'status']
search_fields = ['endpoint__name', 'error_message']
ordering_fields = ['start_time', 'end_time']
ordering = ['-start_time']
def get_queryset(self):
if hasattr(self.request.user, 'tenant') and self.request.user.tenant:
return self.queryset.filter(tenant=self.request.user.tenant)
return self.queryset
@action(detail=False, methods=['get'])
def recent(self, request):
"""Get recent executions"""
queryset = self.get_queryset().order_by('-start_time')[:50]
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def failed(self, request):
"""Get failed executions"""
queryset = self.get_queryset().filter(status='FAILED')
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
class WebhookEndpointViewSet(BaseViewSet):
"""ViewSet for WebhookEndpoint model"""
queryset = WebhookEndpoint.objects.all()
serializer_class = WebhookEndpointSerializer
filterset_fields = ['is_active']
search_fields = ['name', 'description', 'url_path']
ordering_fields = ['name', 'created_at']
ordering = ['name']
@action(detail=False, methods=['post'])
def create_webhook(self, request):
"""Create a new webhook endpoint"""
serializer = WebhookCreateSerializer(data=request.data)
if serializer.is_valid():
import secrets
# Generate URL path and secret key
url_path = f"/webhooks/{secrets.token_urlsafe(16)}"
secret_key = secrets.token_urlsafe(32)
# Create webhook
webhook = WebhookEndpoint.objects.create(
name=serializer.validated_data['name'],
description=serializer.validated_data.get('description', ''),
url_path=url_path,
secret_key=secret_key,
allowed_ips=serializer.validated_data.get('allowed_ips', []),
event_types=serializer.validated_data['event_types'],
tenant=getattr(request.user, 'tenant', None)
)
# Log the action
AuditLogger.log_action(
user=request.user,
action='WEBHOOK_CREATED',
model='WebhookEndpoint',
object_id=str(webhook.webhook_id),
details={
'webhook_name': webhook.name,
'event_types': webhook.event_types
}
)
return Response({
'message': 'Webhook created successfully',
'webhook': WebhookEndpointSerializer(webhook).data
})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['get'])
def active(self, request):
"""Get active webhooks"""
queryset = self.get_queryset().filter(is_active=True)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
class WebhookExecutionViewSet(viewsets.ReadOnlyModelViewSet):
"""ViewSet for WebhookExecution model (read-only)"""
queryset = WebhookExecution.objects.all()
serializer_class = WebhookExecutionSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['webhook', 'event_type', 'status']
search_fields = ['webhook__name', 'event_type']
ordering_fields = ['created_at', 'processed_at']
ordering = ['-created_at']
def get_queryset(self):
if hasattr(self.request.user, 'tenant') and self.request.user.tenant:
return self.queryset.filter(tenant=self.request.user.tenant)
return self.queryset
class IntegrationLogViewSet(viewsets.ReadOnlyModelViewSet):
"""ViewSet for IntegrationLog model (read-only)"""
queryset = IntegrationLog.objects.all()
serializer_class = IntegrationLogSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['level', 'execution', 'webhook_execution']
search_fields = ['message', 'details']
ordering_fields = ['created_at', 'level']
ordering = ['-created_at']
def get_queryset(self):
if hasattr(self.request.user, 'tenant') and self.request.user.tenant:
return self.queryset.filter(tenant=self.request.user.tenant)
return self.queryset
class IntegrationStatsViewSet(viewsets.ViewSet):
"""ViewSet for integration statistics"""
permission_classes = [permissions.IsAuthenticated]
@action(detail=False, methods=['get'])
def dashboard(self, request):
"""Get integration dashboard statistics"""
tenant_filter = {}
if hasattr(request.user, 'tenant') and request.user.tenant:
tenant_filter['tenant'] = request.user.tenant
today = timezone.now().date()
# System statistics
systems = ExternalSystem.objects.filter(**tenant_filter)
total_systems = systems.count()
active_systems = systems.filter(is_active=True).count()
# Endpoint statistics
endpoints = IntegrationEndpoint.objects.filter(**tenant_filter)
total_endpoints = endpoints.count()
# Execution statistics
executions = IntegrationExecution.objects.filter(**tenant_filter)
executions_today = executions.filter(start_time__date=today).count()
successful_executions = executions.filter(
start_time__date=today,
status='SUCCESS'
).count()
failed_executions = executions.filter(
start_time__date=today,
status='FAILED'
).count()
# Average response time
completed_executions = executions.filter(
status='SUCCESS',
start_time__date=today,
end_time__isnull=False
)
avg_response_time = 0
if completed_executions.exists():
total_time = sum([
(exec.end_time - exec.start_time).total_seconds()
for exec in completed_executions
])
avg_response_time = total_time / completed_executions.count()
# Webhook statistics
webhook_executions = WebhookExecution.objects.filter(
**tenant_filter,
created_at__date=today
).count()
# System health breakdown
system_health = {}
for system in systems.filter(is_active=True):
health_status = 'HEALTHY' if system.status == 'ONLINE' else 'UNHEALTHY'
system_health[system.name] = health_status
# Execution trends (last 7 days)
execution_trends = []
for i in range(7):
date = today - timedelta(days=i)
daily_executions = executions.filter(start_time__date=date).count()
execution_trends.append({
'date': date.isoformat(),
'executions': daily_executions
})
stats = {
'total_systems': total_systems,
'active_systems': active_systems,
'total_endpoints': total_endpoints,
'executions_today': executions_today,
'successful_executions': successful_executions,
'failed_executions': failed_executions,
'avg_response_time': round(avg_response_time, 3),
'webhook_executions': webhook_executions,
'system_health': system_health,
'execution_trends': execution_trends
}
serializer = IntegrationStatsSerializer(stats)
return Response(serializer.data)
@action(detail=False, methods=['post'])
def sync_data(self, request):
"""Synchronize data with external systems"""
serializer = DataSyncSerializer(data=request.data)
if serializer.is_valid():
system = ExternalSystem.objects.get(id=serializer.validated_data['system_id'])
sync_type = serializer.validated_data['sync_type']
entity_types = serializer.validated_data['entity_types']
# Mock data synchronization
sync_results = []
for entity_type in entity_types:
try:
# Mock sync process
sync_result = {
'entity_type': entity_type,
'status': 'SUCCESS',
'records_processed': 150, # Mock count
'records_updated': 25,
'records_created': 10,
'errors': 0
}
sync_results.append(sync_result)
except Exception as e:
sync_results.append({
'entity_type': entity_type,
'status': 'FAILED',
'error': str(e)
})
# Log the action
AuditLogger.log_action(
user=request.user,
action='DATA_SYNC_EXECUTED',
model='ExternalSystem',
object_id=str(system.system_id),
details={
'system_name': system.name,
'sync_type': sync_type,
'entity_types': entity_types,
'total_entities': len(entity_types)
}
)
return Response({
'message': 'Data synchronization completed',
'system': system.name,
'sync_type': sync_type,
'results': sync_results,
'total_entities': len(sync_results),
'successful': len([r for r in sync_results if r['status'] == 'SUCCESS'])
})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)