637 lines
25 KiB
Python
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)
|
|
|