dev: webhooks

This commit is contained in:
pablohashescobar 2023-11-23 17:58:45 +05:30
parent 2e513af4f3
commit e275740a45
11 changed files with 97 additions and 37 deletions

View File

@ -12,6 +12,6 @@ from .issue import (
IssueExpandSerializer,
)
from .state import StateLiteSerializer, StateSerializer
from .cycle import CycleSerializer, CycleIssueSerializer
from .module import ModuleSerializer, ModuleIssueSerializer
from .cycle import CycleSerializer, CycleIssueSerializer, CycleLiteSerializer
from .module import ModuleSerializer, ModuleIssueSerializer, ModuleLiteSerializer
from .inbox import InboxIssueSerializer

View File

@ -47,3 +47,10 @@ class CycleIssueSerializer(BaseSerializer):
"project",
"cycle",
]
class CycleLiteSerializer(BaseSerializer):
class Meta:
model = Cycle
fields = "__all__"

View File

@ -19,8 +19,9 @@ from plane.db.models import (
ProjectMember,
)
from .base import BaseSerializer
from .cycle import CycleSerializer
from .module import ModuleSerializer
from .cycle import CycleSerializer, CycleLiteSerializer
from .module import ModuleSerializer, ModuleLiteSerializer
class IssueSerializer(BaseSerializer):
assignees = serializers.ListField(
@ -312,10 +313,30 @@ class IssueActivitySerializer(BaseSerializer):
]
class IssueExpandSerializer(BaseSerializer):
class CycleIssueSerializer(BaseSerializer):
cycle = CycleSerializer(read_only=True)
cycle = CycleSerializer(source="issue_cycle.cycle", many=True)
module = ModuleSerializer(source="issue_module.module", many=True)
class Meta:
fields = [
"cycle",
]
class ModuleIssueSerializer(BaseSerializer):
module = ModuleSerializer(read_only=True)
class Meta:
fields = [
"module",
]
class IssueExpandSerializer(BaseSerializer):
# Serialize the related cycle. It's a OneToOne relation.
cycle = CycleLiteSerializer(source="issue_cycle.cycle", read_only=True)
# Serialize the related module. It's a OneToOne relation.
module = ModuleLiteSerializer(source="issue_module.module", read_only=True)
class Meta:
model = Issue

View File

@ -21,7 +21,6 @@ class ModuleSerializer(BaseSerializer):
write_only=True,
required=False,
)
is_favorite = serializers.BooleanField(read_only=True)
total_issues = serializers.IntegerField(read_only=True)
cancelled_issues = serializers.IntegerField(read_only=True)
completed_issues = serializers.IntegerField(read_only=True)
@ -154,3 +153,10 @@ class ModuleLinkSerializer(BaseSerializer):
{"error": "URL already exists for this Issue"}
)
return ModuleLink.objects.create(**validated_data)
class ModuleLiteSerializer(BaseSerializer):
class Meta:
model = Module
fields = "__all__"

View File

@ -52,7 +52,7 @@ class WebhookMixin:
# Push the object to delay
send_webhook.delay(
event=self.webhook_event,
event_data=response.data,
payload=response.data,
kw=self.kwargs,
action=self.request.method,
slug=self.workspace_slug,

View File

@ -311,6 +311,7 @@ class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView):
serializer_class = CycleIssueSerializer
model = CycleIssue
webhook_event = "cycle_issue"
bulk = True
permission_classes = [
ProjectEntityPermission,
]

View File

@ -43,27 +43,28 @@ class TimezoneMixin:
class WebhookMixin:
webhook_event = None
bulk = False
def finalize_response(self, request, response, *args, **kwargs):
response = super().finalize_response(request, response, *args, **kwargs)
# Check for the case should webhook be sent
if (
self.webhook_event
and self.request.method in ["POST", "PATCH"]
and self.request.method in ["POST", "PATCH", "DELETE"]
and response.status_code in [200, 201, 204]
):
# Get the id
object_id = (
response.data.get("id") if isinstance(response.data, dict) else None
)
# Push the object to delay
send_webhook.delay(
event=self.webhook_event,
event_id=object_id,
payload=response.data,
kw=self.kwargs,
action=self.request.method,
slug=self.workspace_slug,
bulk=self.bulk,
)
return response
return response
class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):

View File

@ -502,7 +502,10 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
class CycleIssueViewSet(WebhookMixin, BaseViewSet):
serializer_class = CycleIssueSerializer
model = CycleIssue
webhook_event = "cycle_issue"
bulk = True
permission_classes = [
ProjectEntityPermission,
]

View File

@ -287,6 +287,8 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
serializer_class = ModuleIssueSerializer
model = ModuleIssue
webhook_event = "module_issue"
bulk = True
filterset_fields = [
"issue__labels__id",

View File

@ -55,11 +55,14 @@ MODEL_MAPPER = {
}
def get_model_data(event, event_id, many=True):
def get_model_data(event, event_id, many=False):
model = MODEL_MAPPER.get(event)
if many:
queryset = model.objects.filter(pk__in=event_id)
else:
queryset = model.objects.get(pk=event_id)
serializer = SERIALIZER_MAPPER.get(event)
return serializer(queryset).data
return serializer(queryset, many=many).data
@shared_task(
@ -89,11 +92,11 @@ def webhook_task(self, webhook, slug, event, event_data, action):
# Use HMAC for generating signature
if webhook.secret_key:
event_data_json = json.dumps(event_data) if event_data is not None else '{}'
event_data_json = json.dumps(event_data) if event_data is not None else "{}"
hmac_signature = hmac.new(
webhook.secret_key.encode("utf-8"),
event_data_json.encode("utf-8"),
hashlib.sha256
hashlib.sha256,
)
signature = hmac_signature.hexdigest()
headers["X-Plane-Signature"] = signature
@ -157,7 +160,6 @@ def webhook_task(self, webhook, slug, event, event_data, action):
raise requests.RequestException()
except Exception as e:
print(e)
if settings.DEBUG:
print(e)
capture_exception(e)
@ -165,7 +167,7 @@ def webhook_task(self, webhook, slug, event, event_data, action):
@shared_task()
def send_webhook(event, event_data, kw, action, slug, bulk):
def send_webhook(event, payload, kw, action, slug, bulk):
try:
webhooks = Webhook.objects.filter(workspace__slug=slug, is_active=True)
@ -184,22 +186,39 @@ def send_webhook(event, event_data, kw, action, slug, bulk):
if event == "issue_comment":
webhooks = webhooks.filter(issue_comment=True)
if webhooks:
if action in ["POST", "PATCH"]:
if bulk:
event_data= []
if event in ["cycle_issue", "module_issue"]:
pass
if bulk and event in ["cycle_issue", "module_issue"]:
event_data = IssueExpandSerializer(
Issue.objects.filter(
pk__in=[
str(event.get("issue")) for event in payload
]
).prefetch_related("issue_cycle", "issue_module"), many=True
).data
event = "issue"
action = "PATCH"
else:
event_id = event_data.get("id") if isinstance(event_data, dict) else None
event_data = [get_model_data(event=event, event_id=event_id, many=isinstance(event_id, list))]
event_data = [
get_model_data(
event=event,
event_id=payload.get("id") if isinstance(payload, dict) else None,
many=False,
)
]
if action == "DELETE":
event_data = [{"id": kw.get("pk")}]
for webhook in webhooks:
for data in event_data:
webhook_task.delay(webhook=webhook.id, slug=slug, event=event, event_data=data, action=action,)
webhook_task.delay(
webhook=webhook.id,
slug=slug,
event=event,
event_data=data,
action=action,
)
except Exception as e:
if settings.DEBUG:

View File

@ -51,9 +51,9 @@ class Module(ProjectBaseModel):
def save(self, *args, **kwargs):
if self._state.adding:
smallest_sort_order = Module.objects.filter(
project=self.project
).aggregate(smallest=models.Min("sort_order"))["smallest"]
smallest_sort_order = Module.objects.filter(project=self.project).aggregate(
smallest=models.Min("sort_order")
)["smallest"]
if smallest_sort_order is not None:
self.sort_order = smallest_sort_order - 10000