from rest_framework import viewsets, status from rest_framework.pagination import PageNumberPagination from django_filters.rest_framework import DjangoFilterBackend from rest_framework.response import Response from rest_framework.decorators import action from .models import Integration, EventSubscription, DeliveryLog from .serializers import ( IntegrationSerializer, EventSubscriptionSerializer, DeliveryLogSerializer, ) from apps.accounts.permissions import ( Authenticated, permission_classes_by_action, ) class IntegrationViewSet(viewsets.ModelViewSet): queryset = Integration.objects.all() serializer_class = IntegrationSerializer def get_permissions(self): try: return [perm() for perm in permission_classes_by_action[self.action]] except KeyError: return [Authenticated()] @action(detail=True, methods=["get"], url_path="subscriptions") def list_subscriptions(self, request, pk=None): qs = EventSubscription.objects.filter(integration_id=pk) serializer = EventSubscriptionSerializer(qs, many=True) return Response(serializer.data) @action(detail=True, methods=["put"], url_path=r"subscriptions/set") def set_subscriptions(self, request, pk=None): """ Replace the integration's subscriptions with the provided list. Body format: [{"event": "channel_start", "enabled": true, "payload_template": "..."}, ...] Any existing subscriptions not in the list will be deleted; missing ones will be created/updated. """ try: integration = Integration.objects.get(pk=pk) except Integration.DoesNotExist: return Response( {"detail": "Integration not found"}, status=status.HTTP_404_NOT_FOUND ) data = request.data if not isinstance(data, list): return Response( {"detail": "Expected a list of subscriptions"}, status=status.HTTP_400_BAD_REQUEST, ) # Validate incoming items using serializer (without integration field) # We'll attach the integration explicitly valid_events = set(evt for evt, _ in EventSubscription.EVENT_CHOICES) incoming = [] for item in data: if not isinstance(item, dict): return Response( {"detail": "Each subscription must be an object"}, status=status.HTTP_400_BAD_REQUEST, ) event = item.get("event") if event not in valid_events: return Response( {"detail": f"Invalid event: {event}"}, status=status.HTTP_400_BAD_REQUEST, ) incoming.append( { "event": event, "enabled": bool(item.get("enabled", True)), "payload_template": item.get("payload_template"), } ) incoming_events = {s["event"] for s in incoming} # Delete subscriptions that are no longer present EventSubscription.objects.filter(integration=integration).exclude( event__in=incoming_events ).delete() # Upsert incoming subscriptions updated = [] for sub in incoming: obj, _created = EventSubscription.objects.update_or_create( integration=integration, event=sub["event"], defaults={ "enabled": sub["enabled"], "payload_template": sub.get("payload_template"), }, ) updated.append(obj) serializer = EventSubscriptionSerializer(updated, many=True) return Response(serializer.data, status=status.HTTP_200_OK) class EventSubscriptionViewSet(viewsets.ModelViewSet): queryset = EventSubscription.objects.all() serializer_class = EventSubscriptionSerializer class DeliveryLogViewSet(viewsets.ReadOnlyModelViewSet): queryset = DeliveryLog.objects.all().order_by("-created_at") serializer_class = DeliveryLogSerializer filter_backends = [DjangoFilterBackend] # Support server-side pagination with page_size query param class ConnectLogsPagination(PageNumberPagination): page_size = 50 page_size_query_param = "page_size" max_page_size = 250 pagination_class = ConnectLogsPagination def get_queryset(self): qs = super().get_queryset() # Optional filters: integration id and type integration_id = self.request.query_params.get("integration") if integration_id: qs = qs.filter(subscription__integration_id=integration_id) integration_type = self.request.query_params.get("type") if integration_type: qs = qs.filter(subscription__integration__type=integration_type) return qs