""" Integration app views with comprehensive CRUD operations following healthcare best practices. """ from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.contrib import messages from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from django.views.generic import ( ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView, FormView ) from django.urls import reverse_lazy, reverse from django.http import JsonResponse, HttpResponse from django.db.models import Q, Count, Avg, Sum from django.utils import timezone from django.core.paginator import Paginator from django.db import transaction from datetime import datetime, timedelta, date import json import requests from django.utils.translation import gettext_lazy as _ from .models import ( ExternalSystem, IntegrationEndpoint, DataMapping, IntegrationExecution, WebhookEndpoint, WebhookExecution, IntegrationLog ) from .forms import ( ExternalSystemForm, IntegrationEndpointForm, DataMappingForm, WebhookEndpointForm ) import logging logger = logging.getLogger(__name__) # ============================================================================ # DASHBOARD AND OVERVIEW VIEWS # ============================================================================ class IntegrationDashboardView(LoginRequiredMixin, TemplateView): """ Main integration dashboard with comprehensive statistics and recent activity. """ template_name = 'integration/dashboard.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Basic statistics context.update({ 'total_systems': ExternalSystem.objects.filter(tenant=self.request.user.tenant).count(), 'total_endpoints': IntegrationEndpoint.objects.filter(external_system__tenant=self.request.user.tenant).count(), 'total_mappings': DataMapping.objects.filter(endpoint__external_system__tenant=self.request.user.tenant).count(), 'total_webhooks': WebhookEndpoint.objects.filter(external_system__tenant=self.request.user.tenant).count(), }) # Recent activity context.update({ 'recent_executions': IntegrationExecution.objects.filter( endpoint__external_system__tenant=self.request.user.tenant ).order_by('-started_at')[:10], 'recent_webhook_executions': WebhookExecution.objects.filter( webhook__external_system__tenant=self.request.user.tenant ).order_by('-processed_at')[:5], 'recent_logs': IntegrationLog.objects.filter( endpoint__external_system__tenant=self.request.user.tenant ).order_by('-timestamp')[:10], }) # Execution statistics today = timezone.now().date() context.update({ 'executions_today': IntegrationExecution.objects.filter( endpoint__external_system__tenant=self.request.user.tenant, started_at__date=today ).count(), 'successful_executions': IntegrationExecution.objects.filter( endpoint__external_system__tenant=self.request.user.tenant, started_at__date=today, status='SUCCESS' ).count(), 'failed_executions': IntegrationExecution.objects.filter( endpoint__external_system__tenant=self.request.user.tenant, started_at__date=today, status='FAILED' ).count(), }) # System health context.update({ 'healthy_systems': ExternalSystem.objects.filter( tenant=self.request.user.tenant, is_active=True, is_healthy=True ).count(), 'unhealthy_systems': ExternalSystem.objects.filter( tenant=self.request.user.tenant, is_active=True, is_healthy=False, ).count(), }) return context # ============================================================================ # EXTERNAL SYSTEM VIEWS (FULL CRUD - Master Data) # ============================================================================ class ExternalSystemListView(LoginRequiredMixin, ListView): """ List all external systems with filtering and search capabilities. """ model = ExternalSystem template_name = 'integration/external_system_list.html' context_object_name = 'external_systems' paginate_by = 20 def get_queryset(self): queryset = ExternalSystem.objects.filter(tenant=self.request.user.tenant) # Search functionality search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(system_name__icontains=search) | Q(description__icontains=search) | Q(base_url__icontains=search) ) # Filter by system type system_type = self.request.GET.get('system_type') if system_type: queryset = queryset.filter(system_type=system_type) # Filter by status is_active = self.request.GET.get('is_active') if is_active: queryset = queryset.filter(is_active=is_active == 'true') # Filter by health status health_status = self.request.GET.get('health_status') if health_status: queryset = queryset.filter(last_health_check_status=health_status) return queryset.order_by('system_name') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['search'] = self.request.GET.get('search', '') context['selected_system_type'] = self.request.GET.get('system_type', '') context['selected_is_active'] = self.request.GET.get('is_active', '') context['selected_health_status'] = self.request.GET.get('health_status', '') return context class ExternalSystemDetailView(LoginRequiredMixin, DetailView): """ Display detailed information about a specific external system. """ model = ExternalSystem template_name = 'integration/external_system_detail.html' context_object_name = 'external_system' def get_queryset(self): return ExternalSystem.objects.filter(tenant=self.request.user.tenant) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) external_system = self.get_object() # Related endpoints context['endpoints'] = IntegrationEndpoint.objects.filter( external_system=external_system ).order_by('endpoint_name') # Recent executions context['recent_executions'] = IntegrationExecution.objects.filter( endpoint__external_system=external_system ).order_by('-execution_time')[:10] # Execution statistics context['total_executions'] = IntegrationExecution.objects.filter( endpoint__external_system=external_system ).count() context['successful_executions'] = IntegrationExecution.objects.filter( endpoint__external_system=external_system, status='SUCCESS' ).count() return context class ExternalSystemCreateView(LoginRequiredMixin, CreateView): """ Create a new external system. """ model = ExternalSystem form_class = ExternalSystemForm template_name = 'integration/systems/external_system_form.html' success_url = reverse_lazy('integration:external_system_list') def form_valid(self, form): form.instance.tenant = self.request.user.tenant messages.success(self.request, 'External system created successfully.') return super().form_valid(form) class ExternalSystemUpdateView(LoginRequiredMixin, UpdateView): """ Update an existing external system. """ model = ExternalSystem form_class = ExternalSystemForm template_name = 'integration/systems/external_system_form.html' def get_queryset(self): return ExternalSystem.objects.filter(tenant=self.request.user.tenant) def get_success_url(self): return reverse('integration:external_system_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): messages.success(self.request, 'External system updated successfully.') return super().form_valid(form) class ExternalSystemDeleteView(LoginRequiredMixin, DeleteView): """ Delete an external system. """ model = ExternalSystem template_name = 'integration/systems/external_system_confirm_delete.html' success_url = reverse_lazy('integration:external_system_list') def get_queryset(self): return ExternalSystem.objects.filter(tenant=self.request.user.tenant) def delete(self, request, *args, **kwargs): external_system = self.get_object() # Check if system has endpoints if IntegrationEndpoint.objects.filter(external_system=external_system).exists(): messages.error(request, 'Cannot delete system that has integration endpoints.') return redirect('integration:external_system_detail', pk=external_system.pk) messages.success(request, f'External system {external_system.system_name} deleted successfully.') return super().delete(request, *args, **kwargs) # ============================================================================ # INTEGRATION ENDPOINT VIEWS (FULL CRUD - Operational Data) # ============================================================================ class IntegrationEndpointListView(LoginRequiredMixin, ListView): """ List all integration endpoints with filtering capabilities. """ model = IntegrationEndpoint template_name = 'integration/integration_endpoint_list.html' context_object_name = 'endpoints' paginate_by = 20 def get_queryset(self): queryset = IntegrationEndpoint.objects.filter(tenant=self.request.user.tenant) # Search functionality search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(endpoint_name__icontains=search) | Q(description__icontains=search) | Q(endpoint_url__icontains=search) ) # Filter by external system external_system = self.request.GET.get('external_system') if external_system: queryset = queryset.filter(external_system_id=external_system) # Filter by method method = self.request.GET.get('method') if method: queryset = queryset.filter(method=method) # Filter by status is_active = self.request.GET.get('is_active') if is_active: queryset = queryset.filter(is_active=is_active == 'true') return queryset.select_related('external_system').order_by('endpoint_name') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['external_systems'] = ExternalSystem.objects.filter( tenant=self.request.user.tenant ).order_by('system_name') context['search'] = self.request.GET.get('search', '') context['selected_external_system'] = self.request.GET.get('external_system', '') context['selected_method'] = self.request.GET.get('method', '') context['selected_is_active'] = self.request.GET.get('is_active', '') return context class IntegrationEndpointDetailView(LoginRequiredMixin, DetailView): """ Display detailed information about a specific integration endpoint. """ model = IntegrationEndpoint template_name = 'integration/integration_endpoint_detail.html' context_object_name = 'endpoint' def get_queryset(self): return IntegrationEndpoint.objects.filter(tenant=self.request.user.tenant) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) endpoint = self.get_object() # Data mappings context['data_mappings'] = DataMapping.objects.filter( endpoint=endpoint ).order_by('field_name') # Recent executions context['recent_executions'] = IntegrationExecution.objects.filter( endpoint=endpoint ).order_by('-execution_time')[:10] # Execution statistics context['total_executions'] = IntegrationExecution.objects.filter( endpoint=endpoint ).count() context['successful_executions'] = IntegrationExecution.objects.filter( endpoint=endpoint, status='SUCCESS' ).count() return context class IntegrationEndpointCreateView(LoginRequiredMixin, CreateView): """ Create a new integration endpoint. """ model = IntegrationEndpoint form_class = IntegrationEndpointForm template_name = 'integration/integration_endpoint_form.html' success_url = reverse_lazy('integration:integration_endpoint_list') def form_valid(self, form): form.instance.tenant = self.request.user.tenant messages.success(self.request, 'Integration endpoint created successfully.') return super().form_valid(form) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs class IntegrationEndpointUpdateView(LoginRequiredMixin, UpdateView): """ Update an existing integration endpoint. """ model = IntegrationEndpoint form_class = IntegrationEndpointForm template_name = 'integration/integration_endpoint_form.html' def get_queryset(self): return IntegrationEndpoint.objects.filter(tenant=self.request.user.tenant) def get_success_url(self): return reverse('integration:integration_endpoint_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): messages.success(self.request, 'Integration endpoint updated successfully.') return super().form_valid(form) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs class IntegrationEndpointDeleteView(LoginRequiredMixin, DeleteView): """ Delete an integration endpoint. """ model = IntegrationEndpoint template_name = 'integration/integration_endpoint_confirm_delete.html' success_url = reverse_lazy('integration:integration_endpoint_list') def get_queryset(self): return IntegrationEndpoint.objects.filter(tenant=self.request.user.tenant) def delete(self, request, *args, **kwargs): messages.success(request, 'Integration endpoint deleted successfully.') return super().delete(request, *args, **kwargs) # ============================================================================ # DATA MAPPING VIEWS (FULL CRUD - Operational Data) # ============================================================================ class DataMappingListView(LoginRequiredMixin, ListView): """ List all data mappings with filtering capabilities. """ model = DataMapping template_name = 'integration/data_mapping_list.html' context_object_name = 'data_mappings' paginate_by = 20 def get_queryset(self): queryset = DataMapping.objects.filter(tenant=self.request.user.tenant) # Search functionality search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(field_name__icontains=search) | Q(source_field__icontains=search) | Q(target_field__icontains=search) ) # Filter by endpoint endpoint = self.request.GET.get('endpoint') if endpoint: queryset = queryset.filter(endpoint_id=endpoint) # Filter by direction direction = self.request.GET.get('direction') if direction: queryset = queryset.filter(direction=direction) # Filter by status is_active = self.request.GET.get('is_active') if is_active: queryset = queryset.filter(is_active=is_active == 'true') return queryset.select_related('endpoint').order_by('field_name') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['endpoints'] = IntegrationEndpoint.objects.filter( tenant=self.request.user.tenant ).order_by('endpoint_name') context['search'] = self.request.GET.get('search', '') context['selected_endpoint'] = self.request.GET.get('endpoint', '') context['selected_direction'] = self.request.GET.get('direction', '') context['selected_is_active'] = self.request.GET.get('is_active', '') return context class DataMappingDetailView(LoginRequiredMixin, DetailView): """ Display detailed information about a specific data mapping. """ model = DataMapping template_name = 'integration/data_mapping_detail.html' context_object_name = 'data_mapping' def get_queryset(self): return DataMapping.objects.filter(tenant=self.request.user.tenant) class DataMappingCreateView(LoginRequiredMixin, CreateView): """ Create a new data mapping. """ model = DataMapping form_class = DataMappingForm template_name = 'integration/data_mapping_form.html' success_url = reverse_lazy('integration:data_mapping_list') def form_valid(self, form): form.instance.tenant = self.request.user.tenant messages.success(self.request, 'Data mapping created successfully.') return super().form_valid(form) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs class DataMappingUpdateView(LoginRequiredMixin, UpdateView): """ Update an existing data mapping. """ model = DataMapping form_class = DataMappingForm template_name = 'integration/data_mapping_form.html' def get_queryset(self): return DataMapping.objects.filter(tenant=self.request.user.tenant) def get_success_url(self): return reverse('integration:data_mapping_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): messages.success(self.request, 'Data mapping updated successfully.') return super().form_valid(form) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs class DataMappingDeleteView(LoginRequiredMixin, DeleteView): """ Delete a data mapping. """ model = DataMapping template_name = 'integration/data_mapping_confirm_delete.html' success_url = reverse_lazy('integration:data_mapping_list') def get_queryset(self): return DataMapping.objects.filter(tenant=self.request.user.tenant) def delete(self, request, *args, **kwargs): messages.success(request, 'Data mapping deleted successfully.') return super().delete(request, *args, **kwargs) class IntegrationExecutionListView(LoginRequiredMixin, ListView): """ List all integration executions (read-only). """ model = IntegrationExecution template_name = 'integration/executions/integration_execution_list.html' context_object_name = 'executions' paginate_by = 20 def get_queryset(self): tenant = self.request.user.tenant queryset = IntegrationExecution.objects.filter(endpoint__external_system__tenant=tenant) # Filter by endpoint endpoint = self.request.GET.get('endpoint') if endpoint: queryset = queryset.filter(endpoint_id=endpoint) # Filter by status status = self.request.GET.get('status') if status: queryset = queryset.filter(status=status) # Filter by date range start_date = self.request.GET.get('start_date') end_date = self.request.GET.get('end_date') if start_date: queryset = queryset.filter(started_at__date__gte=start_date) if end_date: queryset = queryset.filter(completed_at__date__lte=end_date) return queryset.select_related('endpoint').order_by('-completed_at') def get_context_data(self, **kwargs): tenant = self.request.user.tenant context = super().get_context_data(**kwargs) context['endpoints'] = IntegrationEndpoint.objects.filter( external_system__tenant=tenant ).order_by('name') context['selected_endpoint'] = self.request.GET.get('endpoint', '') context['selected_status'] = self.request.GET.get('status', '') context['start_date'] = self.request.GET.get('start_date', '') context['end_date'] = self.request.GET.get('end_date', '') return context class IntegrationExecutionDetailView(LoginRequiredMixin, DetailView): """ Display detailed information about a specific integration execution. """ model = IntegrationExecution template_name = 'integration/executions/integration_execution_detail.html' context_object_name = 'execution' def get_queryset(self): return IntegrationExecution.objects.filter(tenant=self.request.user.tenant) class WebhookEndpointListView(LoginRequiredMixin, ListView): """ List all webhook endpoints with filtering capabilities. """ model = WebhookEndpoint template_name = 'integration/webhooks/webhook_endpoint_list.html' context_object_name = 'webhook_endpoints' paginate_by = 20 def get_queryset(self): queryset = WebhookEndpoint.objects.filter(tenant=self.request.user.tenant) # Search functionality search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(endpoint_name__icontains=search) | Q(description__icontains=search) | Q(webhook_url__icontains=search) ) # Filter by event type event_type = self.request.GET.get('event_type') if event_type: queryset = queryset.filter(event_type=event_type) # Filter by status is_active = self.request.GET.get('is_active') if is_active: queryset = queryset.filter(is_active=is_active == 'true') return queryset.order_by('endpoint_name') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['search'] = self.request.GET.get('search', '') context['selected_event_type'] = self.request.GET.get('event_type', '') context['selected_is_active'] = self.request.GET.get('is_active', '') return context class WebhookEndpointDetailView(LoginRequiredMixin, DetailView): """ Display detailed information about a specific webhook endpoint. """ model = WebhookEndpoint template_name = 'integration/webhooks/webhook_endpoint_detail.html' context_object_name = 'webhook_endpoint' def get_queryset(self): return WebhookEndpoint.objects.filter(tenant=self.request.user.tenant) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) webhook_endpoint = self.get_object() # Recent executions context['recent_executions'] = WebhookExecution.objects.filter( webhook_endpoint=webhook_endpoint ).order_by('-execution_time')[:10] # Execution statistics context['total_executions'] = WebhookExecution.objects.filter( webhook_endpoint=webhook_endpoint ).count() context['successful_executions'] = WebhookExecution.objects.filter( webhook_endpoint=webhook_endpoint, status='SUCCESS' ).count() return context class WebhookEndpointCreateView(LoginRequiredMixin, CreateView): """ Create a new webhook endpoint. """ model = WebhookEndpoint form_class = WebhookEndpointForm template_name = 'integration/webhooks/webhook_endpoint_form.html' success_url = reverse_lazy('integration:webhook_endpoint_list') def form_valid(self, form): form.instance.tenant = self.request.user.tenant messages.success(self.request, 'Webhook endpoint created successfully.') return super().form_valid(form) class WebhookEndpointUpdateView(LoginRequiredMixin, UpdateView): """ Update an existing webhook endpoint. """ model = WebhookEndpoint form_class = WebhookEndpointForm template_name = 'integration/webhooks/webhook_endpoint_form.html' def get_queryset(self): return WebhookEndpoint.objects.filter(tenant=self.request.user.tenant) def get_success_url(self): return reverse('integration:webhook_endpoint_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): messages.success(self.request, 'Webhook endpoint updated successfully.') return super().form_valid(form) class WebhookEndpointDeleteView(LoginRequiredMixin, DeleteView): """ Delete a webhook endpoint. """ model = WebhookEndpoint template_name = 'integration/webhooks/webhook_endpoint_confirm_delete.html' success_url = reverse_lazy('integration:webhook_endpoint_list') def get_queryset(self): return WebhookEndpoint.objects.filter(tenant=self.request.user.tenant) def delete(self, request, *args, **kwargs): messages.success(request, 'Webhook endpoint deleted successfully.') return super().delete(request, *args, **kwargs) class WebhookExecutionListView(LoginRequiredMixin, ListView): """ List all webhook executions (read-only). """ model = WebhookExecution template_name = 'integration/executions/webhook_execution_list.html' context_object_name = 'webhook_executions' paginate_by = 20 def get_queryset(self): queryset = WebhookExecution.objects.filter(tenant=self.request.user.tenant) # Filter by webhook endpoint webhook_endpoint = self.request.GET.get('webhook_endpoint') if webhook_endpoint: queryset = queryset.filter(webhook_endpoint_id=webhook_endpoint) # Filter by status status = self.request.GET.get('status') if status: queryset = queryset.filter(status=status) # Filter by date range start_date = self.request.GET.get('start_date') end_date = self.request.GET.get('end_date') if start_date: queryset = queryset.filter(execution_time__date__gte=start_date) if end_date: queryset = queryset.filter(execution_time__date__lte=end_date) return queryset.select_related('webhook_endpoint').order_by('-execution_time') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['webhook_endpoints'] = WebhookEndpoint.objects.filter( tenant=self.request.user.tenant ).order_by('endpoint_name') context['selected_webhook_endpoint'] = self.request.GET.get('webhook_endpoint', '') context['selected_status'] = self.request.GET.get('status', '') context['start_date'] = self.request.GET.get('start_date', '') context['end_date'] = self.request.GET.get('end_date', '') return context class WebhookExecutionDetailView(LoginRequiredMixin, DetailView): """ Display detailed information about a specific webhook execution. """ model = WebhookExecution template_name = 'integration/executions/webhook_execution_detail.html' context_object_name = 'webhook_execution' def get_queryset(self): return WebhookExecution.objects.filter(tenant=self.request.user.tenant) class IntegrationLogListView(LoginRequiredMixin, ListView): """ List all integration logs (read-only). """ model = IntegrationLog template_name = 'integration/logs/integration_log_list.html' context_object_name = 'integration_logs' paginate_by = 50 def get_queryset(self): queryset = IntegrationLog.objects.filter(tenant=self.request.user.tenant) # Filter by log level log_level = self.request.GET.get('log_level') if log_level: queryset = queryset.filter(log_level=log_level) # Filter by component component = self.request.GET.get('component') if component: queryset = queryset.filter(component__icontains=component) # Filter by date range start_date = self.request.GET.get('start_date') end_date = self.request.GET.get('end_date') if start_date: queryset = queryset.filter(timestamp__date__gte=start_date) if end_date: queryset = queryset.filter(timestamp__date__lte=end_date) return queryset.order_by('-timestamp') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['selected_log_level'] = self.request.GET.get('log_level', '') context['selected_component'] = self.request.GET.get('component', '') context['start_date'] = self.request.GET.get('start_date', '') context['end_date'] = self.request.GET.get('end_date', '') return context class IntegrationLogDetailView(LoginRequiredMixin, DetailView): """ Display detailed information about a specific integration log. """ model = IntegrationLog template_name = 'integration/logs/integration_log_detail.html' context_object_name = 'integration_log' def get_queryset(self): return IntegrationLog.objects.filter(tenant=self.request.user.tenant) @login_required def integration_stats(request): """ Return integration statistics for dashboard updates. """ today = timezone.now().date() context = { 'total_systems': ExternalSystem.objects.filter(tenant=request.user.tenant).count(), 'total_endpoints': IntegrationEndpoint.objects.filter(endpoint__external_system__tenant=request.user.tenant).count(), 'total_mappings': DataMapping.objects.filter(tenant=request.user.tenant).count(), 'total_webhooks': WebhookEndpoint.objects.filter(tenant=request.user.tenant).count(), 'executions_today': IntegrationExecution.objects.filter( tenant=request.user.tenant, execution_time__date=today ).count(), 'successful_executions': IntegrationExecution.objects.filter( tenant=request.user.tenant, execution_time__date=today, status='SUCCESS' ).count(), 'healthy_systems': ExternalSystem.objects.filter( tenant=request.user.tenant, is_active=True, last_health_check_status='HEALTHY' ).count(), } return render(request, 'integration/partials/integration_stats.html', context) @login_required def system_health(request): """ Return system health information for dashboard updates. """ context = { 'healthy_systems': ExternalSystem.objects.filter( tenant=request.user.tenant, is_active=True, is_healthy=True ).count(), 'unhealthy_systems': ExternalSystem.objects.filter( tenant=request.user.tenant, is_active=True, is_healthy=False ).count(), } return render(request, 'integration/partials/system_health.html', context) @login_required def test_connection(request, system_id): """ Test connection to an external system. """ try: external_system = ExternalSystem.objects.get( id=system_id, tenant=request.user.tenant ) # Simulate connection test (implement actual logic based on system type) try: # Basic URL connectivity test response = requests.get( external_system.base_url, timeout=external_system.timeout_seconds or 30, verify=False # In production, handle SSL verification properly ) if response.status_code == 200: external_system.last_health_check_time = timezone.now() external_system.last_health_check_status = 'HEALTHY' external_system.last_health_check_message = 'Connection successful' external_system.save() messages.success(request, f'Connection to {external_system.name} successful.') else: external_system.last_health_check_time = timezone.now() external_system.last_health_check_status = 'UNHEALTHY' external_system.last_health_check_message = f'HTTP {response.status_code}' external_system.save() messages.warning(request, f'Connection to {external_system.name} returned HTTP {response.status_code}.') except requests.RequestException as e: external_system.last_health_check_time = timezone.now() external_system.last_health_check_status = 'UNHEALTHY' external_system.last_health_check_message = str(e) external_system.save() messages.error(request, f'Connection to {external_system.name} failed: {str(e)}') except ExternalSystem.DoesNotExist: messages.error(request, 'External system not found.') return redirect('integration:external_system_detail', pk=system_id) @login_required def execute_endpoint(request, endpoint_id): """ Execute an integration endpoint. """ try: endpoint = IntegrationEndpoint.objects.get( id=endpoint_id, tenant=request.user.tenant ) # Create execution record execution = IntegrationExecution.objects.create( tenant=request.user.tenant, endpoint=endpoint, execution_time=timezone.now(), status='RUNNING', triggered_by=request.user ) try: # Simulate endpoint execution (implement actual logic) # This would involve calling the actual endpoint with proper data execution.status = 'SUCCESS' execution.completion_time = timezone.now() execution.response_data = '{"status": "success", "message": "Endpoint executed successfully"}' execution.save() messages.success(request, f'Endpoint {endpoint.name} executed successfully.') except Exception as e: execution.status = 'FAILED' execution.completion_time = timezone.now() execution.error_message = str(e) execution.save() messages.error(request, f'Endpoint execution failed: {str(e)}') except IntegrationEndpoint.DoesNotExist: messages.error(request, 'Integration endpoint not found.') return redirect('integration:integration_endpoint_detail', pk=endpoint_id) @login_required def test_data_mapping(request, mapping_id): """ Test a data mapping. """ try: data_mapping = DataMapping.objects.get( id=mapping_id, tenant=request.user.tenant ) # Simulate mapping test (implement actual logic) # This would involve testing the transformation logic messages.success(request, f'Data mapping {data_mapping.name} tested successfully.') except DataMapping.DoesNotExist: messages.error(request, 'Data mapping not found.') return redirect('integration:data_mapping_detail', pk=mapping_id) @login_required def bulk_execute_endpoints(request): """ Execute multiple endpoints in bulk. """ endpoint_ids = request.POST.getlist('endpoint_ids') if not endpoint_ids: messages.error(request, 'No endpoints selected for execution.') return redirect('integration:integration_endpoint_list') executed_count = 0 failed_count = 0 for endpoint_id in endpoint_ids: try: endpoint = IntegrationEndpoint.objects.get( id=endpoint_id, tenant=request.user.tenant, is_active=True ) # Create execution record execution = IntegrationExecution.objects.create( tenant=request.user.tenant, endpoint=endpoint, execution_time=timezone.now(), status='SUCCESS', # Simulate success completion_time=timezone.now(), triggered_by=request.user, response_data='{"status": "success", "bulk_execution": true}' ) executed_count += 1 except IntegrationEndpoint.DoesNotExist: failed_count += 1 continue except Exception: failed_count += 1 continue if executed_count > 0: messages.success(request, f'Successfully executed {executed_count} endpoints.') if failed_count > 0: messages.warning(request, f'{failed_count} endpoints failed to execute.') return redirect('integration:integration_endpoint_list') # # # """ # Views for the integration app. # """ # import json # import logging # import uuid # from datetime import datetime, timedelta # # from django.shortcuts import render, get_object_or_404, redirect # from django.http import JsonResponse, HttpResponse # from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView # from django.views.generic.edit import FormView # from django.views.decorators.csrf import csrf_exempt # from django.views.decorators.http import require_POST # from django.utils.decorators import method_decorator # from django.contrib.auth.decorators import login_required, permission_required # from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin # from django.urls import reverse, reverse_lazy # from django.utils.translation import gettext_lazy as _ # from django.contrib import messages # from django.db.models import Q, Count, Avg, Max, Min, Sum # from django.db import transaction # from django.utils import timezone # from django.contrib.contenttypes.models import ContentType # # from .models import ( # ExternalSystem, IntegrationEndpoint, DataMapping, # IntegrationExecution, WebhookEndpoint, WebhookExecution, # IntegrationLog # ) # from .forms import ( # ExternalSystemForm, IntegrationEndpointForm, DataMappingForm, # WebhookEndpointForm, ManualIntegrationExecutionForm, IntegrationLogFilterForm # ) # # logger = logging.getLogger(__name__) # # # class DashboardView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # Integration dashboard view showing an overview of integration status. # """ # model = ExternalSystem # template_name = 'integration/dashboard.html' # context_object_name = 'external_systems' # permission_required = 'integration.view_externalsystem' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return ExternalSystem.objects.filter( # tenant=self.request.user.tenant # ).prefetch_related('endpoints') # return ExternalSystem.objects.none() # # def get_context_data(self, **kwargs): # """Add additional context data for the dashboard.""" # context = super().get_context_data(**kwargs) # # # Add statistics for recent executions # recent_executions = IntegrationExecution.objects.filter( # endpoint__external_system__tenant=self.request.user.tenant, # started_at__gte=timezone.now() - timedelta(days=7) # ) # # # Execution stats by status # execution_stats = { # 'total': recent_executions.count(), # 'success': recent_executions.filter(status='SUCCESS').count(), # 'error': recent_executions.filter(status='ERROR').count(), # 'warning': recent_executions.filter(status='WARNING').count(), # 'pending': recent_executions.filter(status__in=['PENDING', 'IN_PROGRESS']).count(), # } # context['execution_stats'] = execution_stats # # # Calculate success rate # if execution_stats['total'] > 0: # success_rate = (execution_stats['success'] / execution_stats['total']) * 100 # context['success_rate'] = round(success_rate, 1) # else: # context['success_rate'] = 0 # # # Get recent logs # context['recent_logs'] = IntegrationLog.objects.filter( # tenant=self.request.user.tenant # ).order_by('-timestamp')[:10] # # # Count active webhooks # context['active_webhooks'] = WebhookEndpoint.objects.filter( # tenant=self.request.user.tenant, # is_active=True # ).count() # # return context # # # class ExternalSystemListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # List view for external systems. # """ # model = ExternalSystem # template_name = 'integration/external_system/list.html' # context_object_name = 'external_systems' # permission_required = 'integration.view_externalsystem' # paginate_by = 15 # # def get_queryset(self): # """Filter by user's tenant and apply search parameters.""" # queryset = ExternalSystem.objects.all() # # # Filter by tenant # if hasattr(self.request.user, 'tenant'): # queryset = queryset.filter(tenant=self.request.user.tenant) # # # Apply search filter if provided # search_query = self.request.GET.get('q') # if search_query: # queryset = queryset.filter( # Q(name__icontains=search_query) | # Q(system_type__icontains=search_query) | # Q(description__icontains=search_query) # ) # # # Apply status filter if provided # status_filter = self.request.GET.get('status') # if status_filter == 'active': # queryset = queryset.filter(is_active=True) # elif status_filter == 'inactive': # queryset = queryset.filter(is_active=False) # # # Apply system type filter if provided # system_type = self.request.GET.get('system_type') # if system_type: # queryset = queryset.filter(system_type=system_type) # # return queryset.select_related('tenant') # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add filters to context # context['search_query'] = self.request.GET.get('q', '') # context['status_filter'] = self.request.GET.get('status', '') # context['system_type'] = self.request.GET.get('system_type', '') # # # Add system type choices # context['system_type_choices'] = ExternalSystem.SYSTEM_TYPE_CHOICES # # return context # # # class ExternalSystemDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """ # Detail view for an external system. # """ # model = ExternalSystem # template_name = 'integration/external_system/detail.html' # context_object_name = 'external_system' # permission_required = 'integration.view_externalsystem' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return ExternalSystem.objects.filter(tenant=self.request.user.tenant) # return ExternalSystem.objects.none() # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add endpoints for this system # context['endpoints'] = self.object.endpoints.all() # # # Add recent executions # context['recent_executions'] = IntegrationExecution.objects.filter( # endpoint__external_system=self.object # ).order_by('-started_at')[:10] # # # Add health check timestamp # if self.object.last_connected_at: # last_check = timezone.now() - self.object.last_connected_at # context['last_check_minutes'] = int(last_check.total_seconds() // 60) # # return context # # # class ExternalSystemCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create view for an external system. # """ # model = ExternalSystem # form_class = ExternalSystemForm # template_name = 'integration/external_system/form.html' # permission_required = 'integration.add_externalsystem' # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Add External System') # context['submit_text'] = _('Create System') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('External system created successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:external_system_detail', kwargs={'pk': self.object.pk}) # # # class ExternalSystemUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update view for an external system. # """ # model = ExternalSystem # form_class = ExternalSystemForm # template_name = 'integration/external_system/form.html' # permission_required = 'integration.change_externalsystem' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return ExternalSystem.objects.filter(tenant=self.request.user.tenant) # return ExternalSystem.objects.none() # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Edit External System') # context['submit_text'] = _('Update System') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('External system updated successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:external_system_detail', kwargs={'pk': self.object.pk}) # # # class ExternalSystemDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete view for an external system. # """ # model = ExternalSystem # template_name = 'integration/external_system/confirm_delete.html' # permission_required = 'integration.delete_externalsystem' # success_url = reverse_lazy('integration:external_system_list') # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return ExternalSystem.objects.filter(tenant=self.request.user.tenant) # return ExternalSystem.objects.none() # # def delete(self, request, *args, **kwargs): # """Handle the delete action.""" # messages.success(request, _('External system deleted successfully.')) # return super().delete(request, *args, **kwargs) # # # class IntegrationEndpointListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # List view for integration endpoints. # """ # model = IntegrationEndpoint # template_name = 'integration/endpoint/list.html' # context_object_name = 'endpoints' # permission_required = 'integration.view_integrationendpoint' # paginate_by = 15 # # def get_queryset(self): # """Filter by user's tenant and apply search parameters.""" # queryset = IntegrationEndpoint.objects.all() # # # Filter by tenant # if hasattr(self.request.user, 'tenant'): # queryset = queryset.filter(external_system__tenant=self.request.user.tenant) # # # Filter by external system if provided # system_id = self.request.GET.get('system') # if system_id: # queryset = queryset.filter(external_system_id=system_id) # # # Apply search filter if provided # search_query = self.request.GET.get('q') # if search_query: # queryset = queryset.filter( # Q(name__icontains=search_query) | # Q(description__icontains=search_query) | # Q(endpoint_path__icontains=search_query) # ) # # # Apply direction filter if provided # direction = self.request.GET.get('direction') # if direction: # queryset = queryset.filter(direction=direction) # # return queryset.select_related('external_system', 'external_system__tenant') # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add filters to context # context['search_query'] = self.request.GET.get('q', '') # context['direction'] = self.request.GET.get('direction', '') # context['system_id'] = self.request.GET.get('system', '') # # # Add external systems for filter dropdown # if hasattr(self.request.user, 'tenant'): # context['external_systems'] = ExternalSystem.objects.filter( # tenant=self.request.user.tenant # ) # # # Add direction choices # context['direction_choices'] = IntegrationEndpoint.DIRECTION_CHOICES # # return context # # # class IntegrationEndpointDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """ # Detail view for an integration endpoint. # """ # model = IntegrationEndpoint # template_name = 'integration/endpoint/detail.html' # context_object_name = 'endpoint' # permission_required = 'integration.view_integrationendpoint' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return IntegrationEndpoint.objects.filter( # external_system__tenant=self.request.user.tenant # ) # return IntegrationEndpoint.objects.none() # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add data mappings # context['data_mappings'] = self.object.data_mappings.all().order_by('priority') # # # Add recent executions # context['recent_executions'] = self.object.executions.order_by('-started_at')[:10] # # # Add manual execution form # context['execution_form'] = ManualIntegrationExecutionForm( # user=self.request.user, # initial={'endpoint': self.object.pk} # ) # # return context # # # class IntegrationEndpointCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create view for an integration endpoint. # """ # model = IntegrationEndpoint # form_class = IntegrationEndpointForm # template_name = 'integration/endpoint/form.html' # permission_required = 'integration.add_integrationendpoint' # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_initial(self): # """Set initial values for the form.""" # initial = super().get_initial() # # # Set external system if provided in URL # system_id = self.request.GET.get('system') # if system_id: # initial['external_system'] = system_id # # return initial # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Add Integration Endpoint') # context['submit_text'] = _('Create Endpoint') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('Integration endpoint created successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:endpoint_detail', kwargs={'pk': self.object.pk}) # # # class IntegrationEndpointUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update view for an integration endpoint. # """ # model = IntegrationEndpoint # form_class = IntegrationEndpointForm # template_name = 'integration/endpoint/form.html' # permission_required = 'integration.change_integrationendpoint' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return IntegrationEndpoint.objects.filter( # external_system__tenant=self.request.user.tenant # ) # return IntegrationEndpoint.objects.none() # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Edit Integration Endpoint') # context['submit_text'] = _('Update Endpoint') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('Integration endpoint updated successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:endpoint_detail', kwargs={'pk': self.object.pk}) # # # class IntegrationEndpointDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete view for an integration endpoint. # """ # model = IntegrationEndpoint # template_name = 'integration/endpoint/confirm_delete.html' # permission_required = 'integration.delete_integrationendpoint' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return IntegrationEndpoint.objects.filter( # external_system__tenant=self.request.user.tenant # ) # return IntegrationEndpoint.objects.none() # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:endpoint_list') # # def delete(self, request, *args, **kwargs): # """Handle the delete action.""" # messages.success(request, _('Integration endpoint deleted successfully.')) # return super().delete(request, *args, **kwargs) # # # class DataMappingListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # List view for data mappings. # """ # model = DataMapping # template_name = 'integration/mapping/list.html' # context_object_name = 'mappings' # permission_required = 'integration.view_datamapping' # paginate_by = 20 # # def get_queryset(self): # """Filter by user's tenant and apply search parameters.""" # queryset = DataMapping.objects.all() # # # Filter by tenant # if hasattr(self.request.user, 'tenant'): # queryset = queryset.filter( # endpoint__external_system__tenant=self.request.user.tenant # ) # # # Filter by endpoint if provided # endpoint_id = self.request.GET.get('endpoint') # if endpoint_id: # queryset = queryset.filter(endpoint_id=endpoint_id) # # # Apply search filter if provided # search_query = self.request.GET.get('q') # if search_query: # queryset = queryset.filter( # Q(name__icontains=search_query) | # Q(description__icontains=search_query) | # Q(source_field__icontains=search_query) | # Q(target_field__icontains=search_query) # ) # # # Apply mapping type filter if provided # mapping_type = self.request.GET.get('mapping_type') # if mapping_type: # queryset = queryset.filter(mapping_type=mapping_type) # # return queryset.select_related( # 'endpoint', 'endpoint__external_system', 'created_by' # ).order_by('endpoint__name', 'priority') # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add filters to context # context['search_query'] = self.request.GET.get('q', '') # context['mapping_type'] = self.request.GET.get('mapping_type', '') # context['endpoint_id'] = self.request.GET.get('endpoint', '') # # # Add endpoints for filter dropdown # if hasattr(self.request.user, 'tenant'): # context['endpoints'] = IntegrationEndpoint.objects.filter( # external_system__tenant=self.request.user.tenant # ).select_related('external_system') # # # Add mapping type choices # context['mapping_type_choices'] = DataMapping.MAPPING_TYPE_CHOICES # # return context # # # class DataMappingDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """ # Detail view for a data mapping. # """ # model = DataMapping # template_name = 'integration/mapping/detail.html' # context_object_name = 'mapping' # permission_required = 'integration.view_datamapping' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return DataMapping.objects.filter( # endpoint__external_system__tenant=self.request.user.tenant # ) # return DataMapping.objects.none() # # # class DataMappingCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create view for a data mapping. # """ # model = DataMapping # form_class = DataMappingForm # template_name = 'integration/mapping/form.html' # permission_required = 'integration.add_datamapping' # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_initial(self): # """Set initial values for the form.""" # initial = super().get_initial() # # # Set endpoint if provided in URL # endpoint_id = self.request.GET.get('endpoint') # if endpoint_id: # initial['endpoint'] = endpoint_id # # return initial # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Add Data Mapping') # context['submit_text'] = _('Create Mapping') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('Data mapping created successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # if 'endpoint' in self.request.GET: # return reverse('integration:endpoint_detail', # kwargs={'pk': self.request.GET.get('endpoint')}) # return reverse('integration:mapping_detail', kwargs={'pk': self.object.pk}) # # # class DataMappingUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update view for a data mapping. # """ # model = DataMapping # form_class = DataMappingForm # template_name = 'integration/mapping/form.html' # permission_required = 'integration.change_datamapping' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return DataMapping.objects.filter( # endpoint__external_system__tenant=self.request.user.tenant # ) # return DataMapping.objects.none() # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Edit Data Mapping') # context['submit_text'] = _('Update Mapping') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('Data mapping updated successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:mapping_detail', kwargs={'pk': self.object.pk}) # # # class DataMappingDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete view for a data mapping. # """ # model = DataMapping # template_name = 'integration/mapping/confirm_delete.html' # permission_required = 'integration.delete_datamapping' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return DataMapping.objects.filter( # endpoint__external_system__tenant=self.request.user.tenant # ) # return DataMapping.objects.none() # # def get_success_url(self): # """Return URL to redirect to on success.""" # if self.object.endpoint: # return reverse('integration:endpoint_detail', # kwargs={'pk': self.object.endpoint.pk}) # return reverse('integration:mapping_list') # # def delete(self, request, *args, **kwargs): # """Handle the delete action.""" # messages.success(request, _('Data mapping deleted successfully.')) # return super().delete(request, *args, **kwargs) # # # class WebhookEndpointListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # List view for webhook endpoints. # """ # model = WebhookEndpoint # template_name = 'integration/webhook/list.html' # context_object_name = 'webhooks' # permission_required = 'integration.view_webhookendpoint' # paginate_by = 15 # # def get_queryset(self): # """Filter by user's tenant and apply search parameters.""" # queryset = WebhookEndpoint.objects.all() # # # Filter by tenant # if hasattr(self.request.user, 'tenant'): # queryset = queryset.filter(tenant=self.request.user.tenant) # # # Apply search filter if provided # search_query = self.request.GET.get('q') # if search_query: # queryset = queryset.filter( # Q(name__icontains=search_query) | # Q(description__icontains=search_query) | # Q(path__icontains=search_query) # ) # # # Apply security level filter if provided # security_level = self.request.GET.get('security_level') # if security_level: # queryset = queryset.filter(security_level=security_level) # # # Apply status filter # status_filter = self.request.GET.get('status') # if status_filter == 'active': # queryset = queryset.filter(is_active=True) # elif status_filter == 'inactive': # queryset = queryset.filter(is_active=False) # # return queryset.select_related('tenant') # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add filters to context # context['search_query'] = self.request.GET.get('q', '') # context['security_level'] = self.request.GET.get('security_level', '') # context['status_filter'] = self.request.GET.get('status', '') # # # Add security level choices # context['security_level_choices'] = WebhookEndpoint.SECURITY_LEVEL_CHOICES # # return context # # # class WebhookEndpointDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """ # Detail view for a webhook endpoint. # """ # model = WebhookEndpoint # template_name = 'integration/webhook/detail.html' # context_object_name = 'webhook' # permission_required = 'integration.view_webhookendpoint' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return WebhookEndpoint.objects.filter(tenant=self.request.user.tenant) # return WebhookEndpoint.objects.none() # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add recent executions # context['recent_executions'] = WebhookExecution.objects.filter( # webhook=self.object # ).order_by('-timestamp')[:10] # # # Add webhook URL # context['webhook_url'] = self.object.get_full_url() # # return context # # # class WebhookEndpointCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create view for a webhook endpoint. # """ # model = WebhookEndpoint # form_class = WebhookEndpointForm # template_name = 'integration/webhook/form.html' # permission_required = 'integration.add_webhookendpoint' # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Add Webhook Endpoint') # context['submit_text'] = _('Create Webhook') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('Webhook endpoint created successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:webhook_detail', kwargs={'pk': self.object.pk}) # # # class WebhookEndpointUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update view for a webhook endpoint. # """ # model = WebhookEndpoint # form_class = WebhookEndpointForm # template_name = 'integration/webhook/form.html' # permission_required = 'integration.change_webhookendpoint' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return WebhookEndpoint.objects.filter(tenant=self.request.user.tenant) # return WebhookEndpoint.objects.none() # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # """Add context for the template.""" # context = super().get_context_data(**kwargs) # context['title'] = _('Edit Webhook Endpoint') # context['submit_text'] = _('Update Webhook') # return context # # def form_valid(self, form): # """Handle valid form.""" # messages.success(self.request, _('Webhook endpoint updated successfully.')) # return super().form_valid(form) # # def get_success_url(self): # """Return URL to redirect to on success.""" # return reverse('integration:webhook_detail', kwargs={'pk': self.object.pk}) # # # class WebhookEndpointDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete view for a webhook endpoint. # """ # model = WebhookEndpoint # template_name = 'integration/webhook/confirm_delete.html' # permission_required = 'integration.delete_webhookendpoint' # success_url = reverse_lazy('integration:webhook_list') # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return WebhookEndpoint.objects.filter(tenant=self.request.user.tenant) # return WebhookEndpoint.objects.none() # # def delete(self, request, *args, **kwargs): # """Handle the delete action.""" # messages.success(request, _('Webhook endpoint deleted successfully.')) # return super().delete(request, *args, **kwargs) # # # class IntegrationLogListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # List view for integration logs. # """ # model = IntegrationLog # template_name = 'integration/log/list.html' # context_object_name = 'logs' # permission_required = 'integration.view_integrationlog' # paginate_by = 50 # # def get_queryset(self): # """Filter by user's tenant and apply search parameters.""" # queryset = IntegrationLog.objects.all() # # # Filter by tenant # if hasattr(self.request.user, 'tenant'): # queryset = queryset.filter(tenant=self.request.user.tenant) # # # Get filter form # form = IntegrationLogFilterForm(self.request.GET) # if form.is_valid(): # # Apply integration name filter # if form.cleaned_data.get('integration_name'): # queryset = queryset.filter( # integration_name__icontains=form.cleaned_data['integration_name'] # ) # # # Apply event type filter # if form.cleaned_data.get('event_type'): # queryset = queryset.filter( # event_type__icontains=form.cleaned_data['event_type'] # ) # # # Apply status filter # if form.cleaned_data.get('status'): # queryset = queryset.filter(status=form.cleaned_data['status']) # # # Apply direction filter # if form.cleaned_data.get('direction'): # queryset = queryset.filter(direction=form.cleaned_data['direction']) # # # Apply date range filter # if form.cleaned_data.get('date_from'): # queryset = queryset.filter( # timestamp__gte=form.cleaned_data['date_from'] # ) # if form.cleaned_data.get('date_to'): # queryset = queryset.filter( # timestamp__lte=form.cleaned_data['date_to'] # ) # # # Apply related object filters # if form.cleaned_data.get('related_object_type'): # queryset = queryset.filter( # related_object_type=form.cleaned_data['related_object_type'] # ) # if form.cleaned_data.get('related_object_id'): # queryset = queryset.filter( # related_object_id=form.cleaned_data['related_object_id'] # ) # # return queryset.select_related('tenant', 'user', 'related_object_type').order_by('-timestamp') # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add filter form # context['filter_form'] = IntegrationLogFilterForm(self.request.GET) # # return context # # # class IntegrationLogDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """ # Detail view for an integration log. # """ # model = IntegrationLog # template_name = 'integration/log/detail.html' # context_object_name = 'log' # permission_required = 'integration.view_integrationlog' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return IntegrationLog.objects.filter(tenant=self.request.user.tenant) # return IntegrationLog.objects.none() # # # class IntegrationExecutionListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """ # List view for integration executions. # """ # model = IntegrationExecution # template_name = 'integration/execution/list.html' # context_object_name = 'executions' # permission_required = 'integration.view_integrationexecution' # paginate_by = 30 # # def get_queryset(self): # """Filter by user's tenant and apply search parameters.""" # queryset = IntegrationExecution.objects.all() # # # Filter by tenant # if hasattr(self.request.user, 'tenant'): # queryset = queryset.filter(endpoint__external_system__tenant=self.request.user.tenant) # # # Apply endpoint filter if provided # endpoint_id = self.request.GET.get('endpoint') # if endpoint_id: # queryset = queryset.filter(endpoint_id=endpoint_id) # # # Apply status filter if provided # status = self.request.GET.get('status') # if status: # queryset = queryset.filter(status=status) # # # Apply date range filter # date_from = self.request.GET.get('date_from') # if date_from: # try: # date_from = datetime.strptime(date_from, '%Y-%m-%d') # queryset = queryset.filter(started_at__gte=date_from) # except (ValueError, TypeError): # pass # # date_to = self.request.GET.get('date_to') # if date_to: # try: # date_to = datetime.strptime(date_to, '%Y-%m-%d') # date_to = date_to.replace(hour=23, minute=59, second=59) # queryset = queryset.filter(started_at__lte=date_to) # except (ValueError, TypeError): # pass # # return queryset.select_related( # 'endpoint', 'endpoint__external_system', 'triggered_by' # ).order_by('-started_at') # # def get_context_data(self, **kwargs): # """Add additional context data.""" # context = super().get_context_data(**kwargs) # # # Add filters to context # context['endpoint_id'] = self.request.GET.get('endpoint', '') # context['status'] = self.request.GET.get('status', '') # context['date_from'] = self.request.GET.get('date_from', '') # context['date_to'] = self.request.GET.get('date_to', '') # # # Add status choices # context['status_choices'] = IntegrationExecution.STATUS_CHOICES # # # Add endpoints for filter dropdown # if hasattr(self.request.user, 'tenant'): # context['endpoints'] = IntegrationEndpoint.objects.filter( # external_system__tenant=self.request.user.tenant # ).select_related('external_system') # # return context # # # class IntegrationExecutionDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """ # Detail view for an integration execution. # """ # model = IntegrationExecution # template_name = 'integration/execution/detail.html' # context_object_name = 'execution' # permission_required = 'integration.view_integrationexecution' # # def get_queryset(self): # """Filter by user's tenant.""" # if hasattr(self.request.user, 'tenant'): # return IntegrationExecution.objects.filter( # endpoint__external_system__tenant=self.request.user.tenant # ) # return IntegrationExecution.objects.none() # class ManualExecutionView(LoginRequiredMixin, PermissionRequiredMixin, FormView): # """ # View for manually executing an integration endpoint. # """ # form_class = ManualIntegrationExecutionForm # template_name = 'integration/executions/manual_execution.html' # permission_required = 'integration.add_integrationexecution' # # def get_form_kwargs(self): # """Add user to form kwargs.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_initial(self): # """Set initial values for the form.""" # initial = super().get_initial() # # # Set endpoint if provided in URL # endpoint_id = self.request.GET.get('endpoint') # if endpoint_id: # initial['endpoint'] = endpoint_id # # return initial # # def form_valid(self, form): # """Execute the integration when the form is valid.""" # try: # execution = form.execute() # messages.success( # self.request, # _('Integration executed successfully. Status: {}').format( # execution.get_status_display() # ) # ) # return redirect('integration:execution_detail', pk=execution.pk) # except Exception as e: # messages.error( # self.request, # _('Error executing integration: {}').format(str(e)) # ) # return self.form_invalid(form) @login_required @permission_required('integration.change_integrationexecution') def retry_execution(request, pk): """ View for retrying a failed integration execution. """ execution = get_object_or_404(IntegrationExecution, pk=pk) # Check tenant access if hasattr(request.user, 'tenant') and execution.endpoint.external_system.tenant != request.user.tenant: messages.error(request, _('You do not have permission to retry this execution.')) return redirect('integration:execution_list') # Check if execution can be retried if execution.status not in ['ERROR', 'WARNING']: messages.error(request, _('Only failed executions can be retried.')) return redirect('integration:execution_detail', pk=execution.pk) # Retry the execution new_execution = execution.retry(user=request.user) if new_execution: messages.success(request, _('Integration execution retried.')) return redirect('integration:execution_detail', pk=new_execution.pk) else: messages.error(request, _('Failed to retry execution.')) return redirect('integration:execution_detail', pk=execution.pk) @login_required @permission_required('integration.view_externalsystem') def check_system_health(request, pk): """ View for checking the health of an external system. """ tenant = request.user.tenant system = get_object_or_404(ExternalSystem, pk=pk) # Check tenant access if not tenant: messages.error(request, _('You do not have permission to check this system.')) return redirect('integration:external_system_list') # Check the health if system.check_health(): messages.success(request, _('Connection to {} is healthy.').format(system.name)) else: messages.error( request, _('Connection to {} failed: {}').format(system.name, system.health_status_message) ) return redirect('integration:external_system_detail', pk=system.pk) @method_decorator(csrf_exempt, name='dispatch') @require_POST def webhook_receiver(request, path): """ View for receiving webhook calls from external systems. This is a public endpoint that doesn't require authentication directly, but validates the request using the webhook's security configuration. """ # Find the webhook by path webhook = get_object_or_404(WebhookEndpoint, path=path, is_active=True) # Create execution record execution = WebhookExecution( webhook=webhook, client_ip=request.META.get('REMOTE_ADDR', '') ) # Store headers execution.headers = {key: value for key, value in request.headers.items()} # Parse payload based on content type try: if request.content_type == 'application/json': execution.payload = json.loads(request.body) elif request.content_type == 'application/x-www-form-urlencoded': execution.payload = dict(request.POST) else: execution.payload = {'raw': request.body.decode('utf-8', errors='replace')} except Exception as e: execution.status = 'REJECTED' execution.error_message = f"Failed to parse payload: {str(e)}" execution.response_code = 400 execution.save() return JsonResponse( {'error': 'Invalid payload format'}, status=400 ) # Validate security if webhook.security_level != 'NONE': security_valid = False error_message = '' if webhook.security_level == 'TOKEN': # Check for token in Authorization header or query parameter auth_header = request.headers.get('Authorization', '') query_token = request.GET.get('token', '') if auth_header.startswith('Bearer ') and auth_header[7:] == webhook.secret_token: security_valid = True elif query_token and query_token == webhook.secret_token: security_valid = True else: error_message = 'Invalid or missing authentication token' elif webhook.security_level == 'HMAC': # Implement HMAC signature validation signature = request.headers.get('X-Signature', '') if not signature: error_message = 'Missing signature header' else: # HMAC validation would go here security_valid = True # Placeholder elif webhook.security_level == 'IP': # Check if client IP is in allowed list client_ip = request.META.get('REMOTE_ADDR', '') allowed_ips = [ip.strip() for ip in webhook.allowed_ips.split(',') if ip.strip()] if not allowed_ips or client_ip in allowed_ips: security_valid = True else: error_message = f'IP address {client_ip} not in allowed list' if not security_valid: execution.status = 'REJECTED' execution.error_message = f"Security validation failed: {error_message}" execution.response_code = 403 execution.save() return JsonResponse( {'error': 'Authentication failed'}, status=403 ) # Process the webhook try: # Import and call the handler function module_path, function_name = webhook.handler_function.rsplit('.', 1) module = __import__(module_path, fromlist=[function_name]) handler = getattr(module, function_name) # Call the handler with the execution record start_time = timezone.now() result = handler(execution.payload, webhook=webhook, execution=execution) processing_time = (timezone.now() - start_time).total_seconds() * 1000 # Update execution record execution.status = 'SUCCESS' execution.response_data = result execution.response_code = 200 execution.processing_time_ms = int(processing_time) execution.save() # Return the result return JsonResponse(result, status=200) except Exception as e: logger.exception(f"Error processing webhook {webhook.name}: {str(e)}") # Update execution record execution.status = 'ERROR' execution.error_message = str(e) execution.response_code = 500 execution.response_data = {'error': 'Internal server error'} execution.save() # Return error response return JsonResponse( {'error': 'Internal server error'}, status=500 ) @login_required @permission_required('integration.change_webhookendpoint') def regenerate_webhook_token(request, pk): """ View for regenerating a webhook's secret token. """ tenant = request.user.tenant webhook = get_object_or_404(WebhookEndpoint, pk=pk) # Check tenant access if not tenant: messages.error(request, _('You do not have permission to modify this webhook.')) return redirect('integration:webhook_list') # Regenerate the token webhook.regenerate_token() messages.success(request, _('Webhook token regenerated successfully.')) return redirect('integration:webhook_detail', pk=webhook.pk) @login_required @permission_required('integration.view_integrationlog') def integration_stats(request): """ View for displaying integration statistics. """ if hasattr(request.user, 'tenant'): tenant = request.user.tenant # Date range for stats days = int(request.GET.get('days', 30)) end_date = timezone.now() start_date = end_date - timedelta(days=days) # Get logs for period logs = IntegrationLog.objects.filter( tenant=tenant, timestamp__gte=start_date, timestamp__lte=end_date ) # Calculate statistics stats = { 'total_count': logs.count(), 'success_count': logs.filter(status='SUCCESS').count(), 'error_count': logs.filter(status='ERROR').count(), 'warning_count': logs.filter(status='WARNING').count(), 'inbound_count': logs.filter(direction='INBOUND').count(), 'outbound_count': logs.filter(direction='OUTBOUND').count(), } # Calculate success rate if stats['total_count'] > 0: stats['success_rate'] = (stats['success_count'] / stats['total_count']) * 100 else: stats['success_rate'] = 0 # Get top integration names top_integrations = logs.values('integration_name') \ .annotate(count=Count('integration_name')) \ .order_by('-count')[:10] # Get top error types top_errors = logs.filter(status='ERROR') \ .values('error_message') \ .annotate(count=Count('error_message')) \ .order_by('-count')[:10] # Get average response times avg_duration = logs.aggregate(avg_duration=Avg('duration_ms')) stats['avg_duration_ms'] = avg_duration['avg_duration'] or 0 context = { 'stats': stats, 'top_integrations': top_integrations, 'top_errors': top_errors, 'start_date': start_date, 'end_date': end_date, 'days': days, } return render(request, 'integration/partials/integration_stats.html', context) messages.error(request, _('You do not have permission to view integration statistics.')) return redirect('integration:dashboard')