You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
7.1 KiB
Python
207 lines
7.1 KiB
Python
# Copyright (C) 2022 CVAT.ai Corporation
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
from drf_spectacular.utils import (
|
|
OpenApiParameter,
|
|
OpenApiResponse,
|
|
OpenApiTypes,
|
|
extend_schema,
|
|
extend_schema_view,
|
|
)
|
|
from rest_framework import status, viewsets
|
|
from rest_framework.decorators import action
|
|
from rest_framework.permissions import SAFE_METHODS
|
|
from rest_framework.response import Response
|
|
|
|
from cvat.apps.iam.permissions import WebhookPermission
|
|
|
|
from .event_type import AllEvents, OrganizationEvents, ProjectEvents
|
|
from .models import Webhook, WebhookDelivery, WebhookTypeChoice
|
|
from .serializers import (
|
|
EventsSerializer,
|
|
WebhookDeliveryReadSerializer,
|
|
WebhookReadSerializer,
|
|
WebhookWriteSerializer,
|
|
)
|
|
from .signals import signal_ping, signal_redelivery
|
|
|
|
|
|
@extend_schema(tags=["webhooks"])
|
|
@extend_schema_view(
|
|
retrieve=extend_schema(
|
|
summary="Method returns details of a webhook",
|
|
responses={"200": WebhookReadSerializer},
|
|
),
|
|
list=extend_schema(
|
|
summary="Method returns a paginated list of webhook according to query parameters",
|
|
responses={"200": WebhookReadSerializer(many=True)},
|
|
),
|
|
update=extend_schema(
|
|
summary="Method updates a webhook by id",
|
|
request=WebhookWriteSerializer,
|
|
responses={"200": WebhookReadSerializer}, # check WebhookWriteSerializer.to_representation
|
|
),
|
|
partial_update=extend_schema(
|
|
summary="Methods does a partial update of chosen fields in a webhook",
|
|
request=WebhookWriteSerializer,
|
|
responses={"200": WebhookReadSerializer}, # check WebhookWriteSerializer.to_representation
|
|
),
|
|
create=extend_schema(
|
|
request=WebhookWriteSerializer,
|
|
summary="Method creates a webhook",
|
|
responses={"201": WebhookReadSerializer} # check WebhookWriteSerializer.to_representation
|
|
),
|
|
destroy=extend_schema(
|
|
summary="Method deletes a webhook",
|
|
responses={"204": OpenApiResponse(description="The webhook has been deleted")},
|
|
),
|
|
)
|
|
class WebhookViewSet(viewsets.ModelViewSet):
|
|
queryset = Webhook.objects.all()
|
|
ordering = "-id"
|
|
http_method_names = ["get", "post", "delete", "patch", "put"]
|
|
|
|
search_fields = ("target_url", "owner", "type", "description")
|
|
filter_fields = list(search_fields) + ["id", "project_id", "updated_date"]
|
|
ordering_fields = filter_fields
|
|
lookup_fields = {"owner": "owner__username"}
|
|
iam_organization_field = "organization"
|
|
|
|
def get_serializer_class(self):
|
|
# Early exit for drf-spectacular compatibility
|
|
if getattr(self, 'swagger_fake_view', False):
|
|
return WebhookReadSerializer
|
|
|
|
if self.request.path.endswith("redelivery") or self.request.path.endswith("ping"):
|
|
return None
|
|
else:
|
|
if self.request.method in SAFE_METHODS:
|
|
return WebhookReadSerializer
|
|
else:
|
|
return WebhookWriteSerializer
|
|
|
|
def get_queryset(self):
|
|
queryset = super().get_queryset()
|
|
if self.action == "list":
|
|
perm = WebhookPermission.create_scope_list(self.request)
|
|
queryset = perm.filter(queryset)
|
|
|
|
return queryset
|
|
|
|
def perform_create(self, serializer):
|
|
serializer.save(
|
|
owner=self.request.user,
|
|
organization=self.request.iam_context["organization"],
|
|
)
|
|
|
|
@extend_schema(
|
|
summary="Method return a list of available webhook events",
|
|
parameters=[
|
|
OpenApiParameter(
|
|
"type",
|
|
description="Type of webhook",
|
|
location=OpenApiParameter.QUERY,
|
|
type=OpenApiTypes.STR,
|
|
required=False,
|
|
)
|
|
],
|
|
responses={"200": OpenApiResponse(EventsSerializer)},
|
|
)
|
|
@action(detail=False, methods=["GET"], serializer_class=EventsSerializer)
|
|
def events(self, request):
|
|
webhook_type = request.query_params.get("type", "all")
|
|
events = None
|
|
if webhook_type == "all":
|
|
events = AllEvents
|
|
elif webhook_type == WebhookTypeChoice.PROJECT:
|
|
events = ProjectEvents
|
|
elif webhook_type == WebhookTypeChoice.ORGANIZATION:
|
|
events = OrganizationEvents
|
|
|
|
if events is None:
|
|
return Response(
|
|
"Incorrect value of type parameter", status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
return Response(EventsSerializer().to_representation(events))
|
|
|
|
@extend_schema(
|
|
summary="Method return a list of deliveries for a specific webhook",
|
|
responses={"200": WebhookDeliveryReadSerializer(many=True)},
|
|
)
|
|
@action(
|
|
detail=True, methods=["GET"], serializer_class=WebhookDeliveryReadSerializer
|
|
)
|
|
def deliveries(self, request, pk):
|
|
self.get_object()
|
|
queryset = WebhookDelivery.objects.filter(webhook_id=pk).order_by(
|
|
"-updated_date"
|
|
)
|
|
|
|
page = self.paginate_queryset(queryset)
|
|
if page is not None:
|
|
serializer = WebhookDeliveryReadSerializer(
|
|
page, many=True, context={"request": request}
|
|
)
|
|
return self.get_paginated_response(serializer.data)
|
|
|
|
serializer = WebhookDeliveryReadSerializer(
|
|
queryset, many=True, context={"request": request}
|
|
)
|
|
|
|
return Response(serializer.data)
|
|
|
|
@extend_schema(
|
|
summary="Method return a specific delivery for a specific webhook",
|
|
responses={"200": WebhookDeliveryReadSerializer},
|
|
)
|
|
@action(
|
|
detail=True,
|
|
methods=["GET"],
|
|
url_path=r"deliveries/(?P<delivery_id>\d+)",
|
|
serializer_class=WebhookDeliveryReadSerializer,
|
|
)
|
|
def retrieve_delivery(self, request, pk, delivery_id):
|
|
self.get_object()
|
|
queryset = WebhookDelivery.objects.get(webhook_id=pk, id=delivery_id)
|
|
serializer = WebhookDeliveryReadSerializer(
|
|
queryset, context={"request": request}
|
|
)
|
|
return Response(serializer.data)
|
|
|
|
@extend_schema(summary="Method redeliver a specific webhook delivery",
|
|
request=None,
|
|
responses={200: None}
|
|
)
|
|
@action(
|
|
detail=True,
|
|
methods=["POST"],
|
|
url_path=r"deliveries/(?P<delivery_id>\d+)/redelivery",
|
|
serializer_class=None
|
|
)
|
|
def redelivery(self, request, pk, delivery_id):
|
|
delivery = WebhookDelivery.objects.get(webhook_id=pk, id=delivery_id)
|
|
signal_redelivery.send(sender=self, data=delivery.request)
|
|
|
|
# Questionable: should we provide a body for this response?
|
|
return Response({})
|
|
|
|
@extend_schema(
|
|
summary="Method send ping webhook",
|
|
request=None,
|
|
responses={"200": WebhookDeliveryReadSerializer},
|
|
)
|
|
@action(
|
|
detail=True, methods=["POST"], serializer_class=WebhookDeliveryReadSerializer
|
|
)
|
|
def ping(self, request, pk):
|
|
instance = self.get_object()
|
|
serializer = WebhookReadSerializer(instance, context={"request": request})
|
|
|
|
delivery = signal_ping.send(sender=self, serializer=serializer)[0][1]
|
|
serializer = WebhookDeliveryReadSerializer(
|
|
delivery, context={"request": request}
|
|
)
|
|
return Response(serializer.data)
|