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)