forked from github/plane
[WEB - 1122] fix: webhook for issues, issue comments, projects, cycles and modules. (#4330)
* dev: update webhook logic for issues * dev: update issue webhooks for cycle and module * dev: webhook for comment * dev: issue attachment webhooks * dev: add logging * dev: add inbox issue webhooks * dev: update the webhook send task * dev: project webhooks for api * dev: webhooks update for projects, cycles and modules * dev: fix webhook on cycle and module create from external apis
This commit is contained in:
parent
fb74875cde
commit
f1fda4ae4a
@ -1,9 +1,13 @@
|
||||
# Module improts
|
||||
from .base import BaseSerializer
|
||||
from .issue import IssueExpandSerializer
|
||||
from plane.db.models import InboxIssue
|
||||
|
||||
|
||||
class InboxIssueSerializer(BaseSerializer):
|
||||
|
||||
issue_detail = IssueExpandSerializer(read_only=True, source="issue")
|
||||
|
||||
class Meta:
|
||||
model = InboxIssue
|
||||
fields = "__all__"
|
||||
|
@ -19,7 +19,6 @@ from rest_framework.views import APIView
|
||||
# Module imports
|
||||
from plane.api.middleware.api_authentication import APIKeyAuthentication
|
||||
from plane.api.rate_limit import ApiKeyRateThrottle
|
||||
from plane.bgtasks.webhook_task import send_webhook
|
||||
from plane.utils.exception_logger import log_exception
|
||||
from plane.utils.paginator import BasePaginator
|
||||
|
||||
@ -38,40 +37,6 @@ class TimezoneMixin:
|
||||
timezone.deactivate()
|
||||
|
||||
|
||||
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", "DELETE"]
|
||||
and response.status_code in [200, 201, 204]
|
||||
):
|
||||
url = request.build_absolute_uri()
|
||||
parsed_url = urlparse(url)
|
||||
# Extract the scheme and netloc
|
||||
scheme = parsed_url.scheme
|
||||
netloc = parsed_url.netloc
|
||||
# Push the object to delay
|
||||
send_webhook.delay(
|
||||
event=self.webhook_event,
|
||||
payload=response.data,
|
||||
kw=self.kwargs,
|
||||
action=self.request.method,
|
||||
slug=self.workspace_slug,
|
||||
bulk=self.bulk,
|
||||
current_site=f"{scheme}://{netloc}",
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
|
||||
authentication_classes = [
|
||||
APIKeyAuthentication,
|
||||
|
@ -5,6 +5,7 @@ import json
|
||||
from django.core import serializers
|
||||
from django.db.models import Count, F, Func, OuterRef, Q, Sum
|
||||
from django.utils import timezone
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import status
|
||||
@ -26,10 +27,11 @@ from plane.db.models import (
|
||||
)
|
||||
from plane.utils.analytics_plot import burndown_plot
|
||||
|
||||
from .base import BaseAPIView, WebhookMixin
|
||||
from .base import BaseAPIView
|
||||
from plane.bgtasks.webhook_task import model_activity
|
||||
|
||||
|
||||
class CycleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class CycleAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset automatically provides `list`, `create`, `retrieve`,
|
||||
`update` and `destroy` actions related to cycle.
|
||||
@ -277,6 +279,16 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
project_id=project_id,
|
||||
owned_by=request.user,
|
||||
)
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="cycle",
|
||||
model_id=str(serializer.data["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=None,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
return Response(
|
||||
serializer.data, status=status.HTTP_201_CREATED
|
||||
)
|
||||
@ -295,6 +307,11 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
cycle = Cycle.objects.get(
|
||||
workspace__slug=slug, project_id=project_id, pk=pk
|
||||
)
|
||||
|
||||
current_instance = json.dumps(
|
||||
CycleSerializer(cycle).data, cls=DjangoJSONEncoder
|
||||
)
|
||||
|
||||
if cycle.archived_at:
|
||||
return Response(
|
||||
{"error": "Archived cycle cannot be edited"},
|
||||
@ -344,6 +361,17 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
serializer.save()
|
||||
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="cycle",
|
||||
model_id=str(serializer.data["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=current_instance,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@ -515,7 +543,7 @@ class CycleArchiveUnarchiveAPIEndpoint(BaseAPIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class CycleIssueAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset automatically provides `list`, `create`,
|
||||
and `destroy` actions related to cycle issues.
|
||||
|
@ -154,6 +154,13 @@ class InboxIssueAPIEndpoint(BaseAPIView):
|
||||
state=state,
|
||||
)
|
||||
|
||||
# create an inbox issue
|
||||
inbox_issue = InboxIssue.objects.create(
|
||||
inbox_id=inbox.id,
|
||||
project_id=project_id,
|
||||
issue=issue,
|
||||
source=request.data.get("source", "in-app"),
|
||||
)
|
||||
# Create an Issue Activity
|
||||
issue_activity.delay(
|
||||
type="issue.activity.created",
|
||||
@ -163,14 +170,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
|
||||
project_id=str(project_id),
|
||||
current_instance=None,
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
)
|
||||
|
||||
# create an inbox issue
|
||||
inbox_issue = InboxIssue.objects.create(
|
||||
inbox_id=inbox.id,
|
||||
project_id=project_id,
|
||||
issue=issue,
|
||||
source=request.data.get("source", "in-app"),
|
||||
inbox=str(inbox_issue.id),
|
||||
)
|
||||
|
||||
serializer = InboxIssueSerializer(inbox_issue)
|
||||
@ -260,6 +260,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
|
||||
cls=DjangoJSONEncoder,
|
||||
),
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
inbox=(inbox_issue.id),
|
||||
)
|
||||
issue_serializer.save()
|
||||
else:
|
||||
@ -327,6 +328,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=False,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
inbox=str(inbox_issue.id),
|
||||
)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
@ -48,11 +48,10 @@ from plane.db.models import (
|
||||
ProjectMember,
|
||||
)
|
||||
|
||||
from .base import BaseAPIView, WebhookMixin
|
||||
from .base import BaseAPIView
|
||||
|
||||
|
||||
|
||||
class WorkspaceIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class WorkspaceIssueAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset provides `retrieveByIssueId` on workspace level
|
||||
|
||||
@ -60,12 +59,9 @@ class WorkspaceIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
|
||||
model = Issue
|
||||
webhook_event = "issue"
|
||||
permission_classes = [
|
||||
ProjectEntityPermission
|
||||
]
|
||||
permission_classes = [ProjectEntityPermission]
|
||||
serializer_class = IssueSerializer
|
||||
|
||||
|
||||
@property
|
||||
def project__identifier(self):
|
||||
return self.kwargs.get("project__identifier", None)
|
||||
@ -91,7 +87,9 @@ class WorkspaceIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
.order_by(self.kwargs.get("order_by", "-created_at"))
|
||||
).distinct()
|
||||
|
||||
def get(self, request, slug, project__identifier=None, issue__identifier=None):
|
||||
def get(
|
||||
self, request, slug, project__identifier=None, issue__identifier=None
|
||||
):
|
||||
if issue__identifier and project__identifier:
|
||||
issue = Issue.issue_objects.annotate(
|
||||
sub_issues_count=Issue.issue_objects.filter(
|
||||
@ -100,7 +98,11 @@ class WorkspaceIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
.values("count")
|
||||
).get(workspace__slug=slug, project__identifier=project__identifier, sequence_id=issue__identifier)
|
||||
).get(
|
||||
workspace__slug=slug,
|
||||
project__identifier=project__identifier,
|
||||
sequence_id=issue__identifier,
|
||||
)
|
||||
return Response(
|
||||
IssueSerializer(
|
||||
issue,
|
||||
@ -110,7 +112,8 @@ class WorkspaceIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
class IssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
|
||||
class IssueAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset automatically provides `list`, `create`, `retrieve`,
|
||||
`update` and `destroy` actions related to issue.
|
||||
@ -652,7 +655,7 @@ class IssueLinkAPIEndpoint(BaseAPIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class IssueCommentAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class IssueCommentAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset automatically provides `list`, `create`, `retrieve`,
|
||||
`update` and `destroy` actions related to comments of the particular issue.
|
||||
|
@ -5,6 +5,7 @@ import json
|
||||
from django.core import serializers
|
||||
from django.db.models import Count, F, Func, OuterRef, Prefetch, Q
|
||||
from django.utils import timezone
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import status
|
||||
@ -28,10 +29,11 @@ from plane.db.models import (
|
||||
Project,
|
||||
)
|
||||
|
||||
from .base import BaseAPIView, WebhookMixin
|
||||
from .base import BaseAPIView
|
||||
from plane.bgtasks.webhook_task import model_activity
|
||||
|
||||
|
||||
class ModuleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class ModuleAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset automatically provides `list`, `create`, `retrieve`,
|
||||
`update` and `destroy` actions related to module.
|
||||
@ -163,6 +165,16 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
serializer.save()
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="module",
|
||||
model_id=str(serializer.data["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=None,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
module = Module.objects.get(pk=serializer.data["id"])
|
||||
serializer = ModuleSerializer(module)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
@ -172,6 +184,11 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
module = Module.objects.get(
|
||||
pk=pk, project_id=project_id, workspace__slug=slug
|
||||
)
|
||||
|
||||
current_instance = json.dumps(
|
||||
ModuleSerializer(module).data, cls=DjangoJSONEncoder
|
||||
)
|
||||
|
||||
if module.archived_at:
|
||||
return Response(
|
||||
{"error": "Archived module cannot be edited"},
|
||||
@ -204,6 +221,18 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
)
|
||||
serializer.save()
|
||||
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="module",
|
||||
model_id=str(serializer.data["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=current_instance,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@ -260,7 +289,7 @@ class ModuleAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class ModuleIssueAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset automatically provides `list`, `create`, `retrieve`,
|
||||
`update` and `destroy` actions related to module issues.
|
||||
|
@ -1,7 +1,11 @@
|
||||
# Python imports
|
||||
import json
|
||||
|
||||
# Django imports
|
||||
from django.db import IntegrityError
|
||||
from django.db.models import Exists, F, Func, OuterRef, Prefetch, Q, Subquery
|
||||
from django.utils import timezone
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import status
|
||||
@ -23,11 +27,11 @@ from plane.db.models import (
|
||||
State,
|
||||
Workspace,
|
||||
)
|
||||
|
||||
from .base import BaseAPIView, WebhookMixin
|
||||
from plane.bgtasks.webhook_task import model_activity
|
||||
from .base import BaseAPIView
|
||||
|
||||
|
||||
class ProjectAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class ProjectAPIEndpoint(BaseAPIView):
|
||||
"""Project Endpoints to create, update, list, retrieve and delete endpoint"""
|
||||
|
||||
serializer_class = ProjectSerializer
|
||||
@ -236,6 +240,17 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
.filter(pk=serializer.data["id"])
|
||||
.first()
|
||||
)
|
||||
# Model activity
|
||||
model_activity.delay(
|
||||
model_name="project",
|
||||
model_id=str(project.id),
|
||||
requested_data=request.data,
|
||||
current_instance=None,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
serializer = ProjectSerializer(project)
|
||||
return Response(
|
||||
serializer.data, status=status.HTTP_201_CREATED
|
||||
@ -265,7 +280,9 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
try:
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
project = Project.objects.get(pk=pk)
|
||||
|
||||
current_instance = json.dumps(
|
||||
ProjectSerializer(project).data, cls=DjangoJSONEncoder
|
||||
)
|
||||
if project.archived_at:
|
||||
return Response(
|
||||
{"error": "Archived project cannot be updated"},
|
||||
@ -303,6 +320,17 @@ class ProjectAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
.filter(pk=serializer.data["id"])
|
||||
.first()
|
||||
)
|
||||
|
||||
model_activity.delay(
|
||||
model_name="project",
|
||||
model_id=str(project.id),
|
||||
requested_data=request.data,
|
||||
current_instance=current_instance,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
serializer = ProjectSerializer(project)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(
|
||||
|
@ -442,7 +442,7 @@ class IssueLinkSerializer(BaseSerializer):
|
||||
raise serializers.ValidationError("Invalid URL format.")
|
||||
|
||||
# Check URL scheme
|
||||
if not value.startswith(('http://', 'https://')):
|
||||
if not value.startswith(("http://", "https://")):
|
||||
raise serializers.ValidationError("Invalid URL scheme.")
|
||||
|
||||
return value
|
||||
|
@ -30,7 +30,7 @@ from .user.base import (
|
||||
|
||||
from .oauth import OauthEndpoint
|
||||
|
||||
from .base import BaseAPIView, BaseViewSet, WebhookMixin
|
||||
from .base import BaseAPIView, BaseViewSet
|
||||
|
||||
from .workspace.base import (
|
||||
WorkSpaceViewSet,
|
||||
|
@ -19,7 +19,6 @@ from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
# Module imports
|
||||
from plane.bgtasks.webhook_task import send_webhook
|
||||
from plane.utils.exception_logger import log_exception
|
||||
from plane.utils.paginator import BasePaginator
|
||||
|
||||
@ -38,35 +37,6 @@ class TimezoneMixin:
|
||||
timezone.deactivate()
|
||||
|
||||
|
||||
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", "DELETE"]
|
||||
and response.status_code in [200, 201, 204]
|
||||
):
|
||||
# Push the object to delay
|
||||
send_webhook.delay(
|
||||
event=self.webhook_event,
|
||||
payload=response.data,
|
||||
kw=self.kwargs,
|
||||
action=self.request.method,
|
||||
slug=self.workspace_slug,
|
||||
bulk=self.bulk,
|
||||
current_site=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
|
||||
model = None
|
||||
|
||||
|
@ -20,6 +20,7 @@ from django.db.models import (
|
||||
)
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils import timezone
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import status
|
||||
@ -47,10 +48,11 @@ from plane.db.models import (
|
||||
from plane.utils.analytics_plot import burndown_plot
|
||||
|
||||
# Module imports
|
||||
from .. import BaseAPIView, BaseViewSet, WebhookMixin
|
||||
from .. import BaseAPIView, BaseViewSet
|
||||
from plane.bgtasks.webhook_task import model_activity
|
||||
|
||||
|
||||
class CycleViewSet(WebhookMixin, BaseViewSet):
|
||||
class CycleViewSet(BaseViewSet):
|
||||
serializer_class = CycleSerializer
|
||||
model = Cycle
|
||||
webhook_event = "cycle"
|
||||
@ -412,6 +414,17 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
|
||||
)
|
||||
.first()
|
||||
)
|
||||
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="cycle",
|
||||
model_id=str(cycle["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=None,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
return Response(cycle, status=status.HTTP_201_CREATED)
|
||||
return Response(
|
||||
serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
||||
@ -434,6 +447,11 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
|
||||
{"error": "Archived cycle cannot be updated"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
current_instance = json.dumps(
|
||||
CycleSerializer(cycle).data, cls=DjangoJSONEncoder
|
||||
)
|
||||
|
||||
request_data = request.data
|
||||
|
||||
if (
|
||||
@ -487,6 +505,18 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
|
||||
"assignee_ids",
|
||||
"status",
|
||||
).first()
|
||||
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="cycle",
|
||||
model_id=str(cycle["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=current_instance,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
return Response(cycle, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
@ -23,7 +23,7 @@ from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
# Module imports
|
||||
from .. import BaseViewSet, WebhookMixin
|
||||
from .. import BaseViewSet
|
||||
from plane.app.serializers import (
|
||||
IssueSerializer,
|
||||
CycleIssueSerializer,
|
||||
@ -40,7 +40,7 @@ from plane.bgtasks.issue_activites_task import issue_activity
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
from plane.utils.user_timezone_converter import user_timezone_converter
|
||||
|
||||
class CycleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
class CycleIssueViewSet(BaseViewSet):
|
||||
serializer_class = CycleIssueSerializer
|
||||
model = CycleIssue
|
||||
|
||||
@ -254,6 +254,7 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
update_cycle_issue_activity = []
|
||||
# Iterate over each cycle_issue in cycle_issues
|
||||
for cycle_issue in cycle_issues:
|
||||
old_cycle_id = cycle_issue.cycle_id
|
||||
# Update the cycle_issue's cycle_id
|
||||
cycle_issue.cycle_id = cycle_id
|
||||
# Add the modified cycle_issue to the records_to_update list
|
||||
@ -261,7 +262,7 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
# Record the update activity
|
||||
update_cycle_issue_activity.append(
|
||||
{
|
||||
"old_cycle_id": str(cycle_issue.cycle_id),
|
||||
"old_cycle_id": str(old_cycle_id),
|
||||
"new_cycle_id": str(cycle_id),
|
||||
"issue_id": str(cycle_issue.issue_id),
|
||||
}
|
||||
|
@ -251,6 +251,16 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
inbox_id = Inbox.objects.filter(
|
||||
workspace__slug=slug, project_id=project_id
|
||||
).first()
|
||||
# create an inbox issue
|
||||
inbox_issue = InboxIssue.objects.create(
|
||||
inbox_id=inbox_id.id,
|
||||
project_id=project_id,
|
||||
issue_id=serializer.data["id"],
|
||||
source=request.data.get("source", "in-app"),
|
||||
)
|
||||
# Create an Issue Activity
|
||||
issue_activity.delay(
|
||||
type="issue.activity.created",
|
||||
@ -262,16 +272,7 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=True,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
inbox_id = Inbox.objects.filter(
|
||||
workspace__slug=slug, project_id=project_id
|
||||
).first()
|
||||
# create an inbox issue
|
||||
inbox_issue = InboxIssue.objects.create(
|
||||
inbox_id=inbox_id.id,
|
||||
project_id=project_id,
|
||||
issue_id=serializer.data["id"],
|
||||
source=request.data.get("source", "in-app"),
|
||||
inbox=str(inbox_issue.id),
|
||||
)
|
||||
inbox_issue = (
|
||||
InboxIssue.objects.select_related("issue")
|
||||
@ -339,7 +340,24 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
# Get issue data
|
||||
issue_data = request.data.pop("issue", False)
|
||||
if bool(issue_data):
|
||||
issue = Issue.objects.get(
|
||||
issue = Issue.objects.annotate(
|
||||
label_ids=Coalesce(
|
||||
ArrayAgg(
|
||||
"labels__id",
|
||||
distinct=True,
|
||||
filter=~Q(labels__id__isnull=True),
|
||||
),
|
||||
Value([], output_field=ArrayField(UUIDField())),
|
||||
),
|
||||
assignee_ids=Coalesce(
|
||||
ArrayAgg(
|
||||
"assignees__id",
|
||||
distinct=True,
|
||||
filter=~Q(assignees__id__isnull=True),
|
||||
),
|
||||
Value([], output_field=ArrayField(UUIDField())),
|
||||
),
|
||||
).get(
|
||||
pk=inbox_issue.issue_id,
|
||||
workspace__slug=slug,
|
||||
project_id=project_id,
|
||||
@ -379,6 +397,7 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=True,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
inbox=str(inbox_issue.id),
|
||||
)
|
||||
issue_serializer.save()
|
||||
else:
|
||||
@ -444,6 +463,7 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=False,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
inbox=(inbox_issue.id),
|
||||
)
|
||||
|
||||
inbox_issue = (
|
||||
@ -480,7 +500,8 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
output_field=ArrayField(UUIDField()),
|
||||
),
|
||||
),
|
||||
).first()
|
||||
)
|
||||
.first()
|
||||
)
|
||||
serializer = InboxIssueDetailSerializer(inbox_issue).data
|
||||
return Response(serializer, status=status.HTTP_200_OK)
|
||||
|
@ -53,7 +53,7 @@ from plane.utils.issue_filters import issue_filters
|
||||
from plane.utils.user_timezone_converter import user_timezone_converter
|
||||
|
||||
# Module imports
|
||||
from .. import BaseAPIView, BaseViewSet, WebhookMixin
|
||||
from .. import BaseAPIView, BaseViewSet
|
||||
|
||||
|
||||
class IssueListEndpoint(BaseAPIView):
|
||||
@ -249,7 +249,7 @@ class IssueListEndpoint(BaseAPIView):
|
||||
return Response(issues, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class IssueViewSet(WebhookMixin, BaseViewSet):
|
||||
class IssueViewSet(BaseViewSet):
|
||||
def get_serializer_class(self):
|
||||
return (
|
||||
IssueCreateSerializer
|
||||
|
@ -11,7 +11,7 @@ from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
# Module imports
|
||||
from .. import BaseViewSet, WebhookMixin
|
||||
from .. import BaseViewSet
|
||||
from plane.app.serializers import (
|
||||
IssueCommentSerializer,
|
||||
CommentReactionSerializer,
|
||||
@ -25,7 +25,7 @@ from plane.db.models import (
|
||||
from plane.bgtasks.issue_activites_task import issue_activity
|
||||
|
||||
|
||||
class IssueCommentViewSet(WebhookMixin, BaseViewSet):
|
||||
class IssueCommentViewSet(BaseViewSet):
|
||||
serializer_class = IssueCommentSerializer
|
||||
model = IssueComment
|
||||
webhook_event = "issue_comment"
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Python imports
|
||||
import json
|
||||
|
||||
# Django Imports
|
||||
from django.contrib.postgres.aggregates import ArrayAgg
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db.models import (
|
||||
@ -17,14 +18,14 @@ from django.db.models import (
|
||||
Value,
|
||||
)
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
# Django Imports
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
# Module imports
|
||||
from plane.app.permissions import (
|
||||
ProjectEntityPermission,
|
||||
ProjectLitePermission,
|
||||
@ -49,13 +50,11 @@ from plane.db.models import (
|
||||
)
|
||||
from plane.utils.analytics_plot import burndown_plot
|
||||
from plane.utils.user_timezone_converter import user_timezone_converter
|
||||
from plane.bgtasks.webhook_task import model_activity
|
||||
from .. import BaseAPIView, BaseViewSet
|
||||
|
||||
|
||||
# Module imports
|
||||
from .. import BaseAPIView, BaseViewSet, WebhookMixin
|
||||
|
||||
|
||||
class ModuleViewSet(WebhookMixin, BaseViewSet):
|
||||
class ModuleViewSet(BaseViewSet):
|
||||
model = Module
|
||||
permission_classes = [
|
||||
ProjectEntityPermission,
|
||||
@ -238,6 +237,16 @@ class ModuleViewSet(WebhookMixin, BaseViewSet):
|
||||
"updated_at",
|
||||
)
|
||||
).first()
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="module",
|
||||
model_id=str(module["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=None,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
datetime_fields = ["created_at", "updated_at"]
|
||||
module = user_timezone_converter(
|
||||
module, datetime_fields, request.user.user_timezone
|
||||
@ -422,6 +431,9 @@ class ModuleViewSet(WebhookMixin, BaseViewSet):
|
||||
|
||||
def partial_update(self, request, slug, project_id, pk):
|
||||
module = self.get_queryset().filter(pk=pk)
|
||||
current_instance = json.dumps(
|
||||
ModuleSerializer(module).data, cls=DjangoJSONEncoder
|
||||
)
|
||||
|
||||
if module.first().archived_at:
|
||||
return Response(
|
||||
@ -464,6 +476,18 @@ class ModuleViewSet(WebhookMixin, BaseViewSet):
|
||||
"created_at",
|
||||
"updated_at",
|
||||
).first()
|
||||
|
||||
# Send the model activity
|
||||
model_activity.delay(
|
||||
model_name="module",
|
||||
model_id=str(module["id"]),
|
||||
requested_data=request.data,
|
||||
current_instance=current_instance,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
datetime_fields = ["created_at", "updated_at"]
|
||||
module = user_timezone_converter(
|
||||
module, datetime_fields, request.user.user_timezone
|
||||
|
@ -16,7 +16,7 @@ from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
# Module imports
|
||||
from .. import BaseViewSet, WebhookMixin
|
||||
from .. import BaseViewSet
|
||||
from plane.app.serializers import (
|
||||
ModuleIssueSerializer,
|
||||
IssueSerializer,
|
||||
@ -33,7 +33,7 @@ from plane.bgtasks.issue_activites_task import issue_activity
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
from plane.utils.user_timezone_converter import user_timezone_converter
|
||||
|
||||
class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
class ModuleIssueViewSet(BaseViewSet):
|
||||
serializer_class = ModuleIssueSerializer
|
||||
model = ModuleIssue
|
||||
webhook_event = "module_issue"
|
||||
|
@ -1,5 +1,6 @@
|
||||
# Python imports
|
||||
import boto3
|
||||
import json
|
||||
|
||||
# Django imports
|
||||
from django.db import IntegrityError
|
||||
@ -14,6 +15,7 @@ from django.db.models import (
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
# Third Party imports
|
||||
from rest_framework.response import Response
|
||||
@ -22,7 +24,7 @@ from rest_framework import serializers
|
||||
from rest_framework.permissions import AllowAny
|
||||
|
||||
# Module imports
|
||||
from plane.app.views.base import BaseViewSet, BaseAPIView, WebhookMixin
|
||||
from plane.app.views.base import BaseViewSet, BaseAPIView
|
||||
from plane.app.serializers import (
|
||||
ProjectSerializer,
|
||||
ProjectListSerializer,
|
||||
@ -50,9 +52,10 @@ from plane.db.models import (
|
||||
Issue,
|
||||
)
|
||||
from plane.utils.cache import cache_response
|
||||
from plane.bgtasks.webhook_task import model_activity
|
||||
|
||||
|
||||
class ProjectViewSet(WebhookMixin, BaseViewSet):
|
||||
class ProjectViewSet(BaseViewSet):
|
||||
serializer_class = ProjectListSerializer
|
||||
model = Project
|
||||
webhook_event = "project"
|
||||
@ -334,6 +337,17 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
||||
.filter(pk=serializer.data["id"])
|
||||
.first()
|
||||
)
|
||||
|
||||
model_activity.delay(
|
||||
model_name="project",
|
||||
model_id=str(project.id),
|
||||
requested_data=request.data,
|
||||
current_instance=None,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
serializer = ProjectListSerializer(project)
|
||||
return Response(
|
||||
serializer.data, status=status.HTTP_201_CREATED
|
||||
@ -364,7 +378,9 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
|
||||
project = Project.objects.get(pk=pk)
|
||||
|
||||
current_instance = json.dumps(
|
||||
ProjectSerializer(project).data, cls=DjangoJSONEncoder
|
||||
)
|
||||
if project.archived_at:
|
||||
return Response(
|
||||
{"error": "Archived projects cannot be updated"},
|
||||
@ -402,6 +418,16 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
||||
.filter(pk=serializer.data["id"])
|
||||
.first()
|
||||
)
|
||||
|
||||
model_activity.delay(
|
||||
model_name="project",
|
||||
model_id=str(project.id),
|
||||
requested_data=request.data,
|
||||
current_instance=current_instance,
|
||||
actor_id=request.user.id,
|
||||
slug=slug,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
serializer = ProjectListSerializer(project)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(
|
||||
|
@ -31,6 +31,7 @@ from plane.db.models import (
|
||||
)
|
||||
from plane.settings.redis import redis_instance
|
||||
from plane.utils.exception_logger import log_exception
|
||||
from plane.bgtasks.webhook_task import webhook_activity
|
||||
|
||||
|
||||
# Track Changes in name
|
||||
@ -1296,7 +1297,7 @@ def create_issue_vote_activity(
|
||||
IssueActivity(
|
||||
issue_id=issue_id,
|
||||
actor_id=actor_id,
|
||||
verb="created",
|
||||
verb="updated",
|
||||
old_value=None,
|
||||
new_value=requested_data.get("vote"),
|
||||
field="vote",
|
||||
@ -1365,7 +1366,7 @@ def create_issue_relation_activity(
|
||||
IssueActivity(
|
||||
issue_id=issue_id,
|
||||
actor_id=actor_id,
|
||||
verb="created",
|
||||
verb="updated",
|
||||
old_value="",
|
||||
new_value=f"{issue.project.identifier}-{issue.sequence_id}",
|
||||
field=requested_data.get("relation_type"),
|
||||
@ -1380,7 +1381,7 @@ def create_issue_relation_activity(
|
||||
IssueActivity(
|
||||
issue_id=related_issue,
|
||||
actor_id=actor_id,
|
||||
verb="created",
|
||||
verb="updated",
|
||||
old_value="",
|
||||
new_value=f"{issue.project.identifier}-{issue.sequence_id}",
|
||||
field=(
|
||||
@ -1606,6 +1607,7 @@ def issue_activity(
|
||||
subscriber=True,
|
||||
notification=False,
|
||||
origin=None,
|
||||
inbox=None,
|
||||
):
|
||||
try:
|
||||
issue_activities = []
|
||||
@ -1692,6 +1694,41 @@ def issue_activity(
|
||||
except Exception as e:
|
||||
log_exception(e)
|
||||
|
||||
for activity in issue_activities_created:
|
||||
webhook_activity.delay(
|
||||
event=(
|
||||
"issue_comment"
|
||||
if activity.field == "comment"
|
||||
else "inbox_issue" if inbox else "issue"
|
||||
),
|
||||
event_id=(
|
||||
activity.issue_comment_id
|
||||
if activity.field == "comment"
|
||||
else inbox if inbox else activity.issue_id
|
||||
),
|
||||
verb=activity.verb,
|
||||
field=(
|
||||
"description"
|
||||
if activity.field == "comment"
|
||||
else activity.field
|
||||
),
|
||||
old_value=(
|
||||
activity.old_value
|
||||
if activity.old_value != ""
|
||||
else None
|
||||
),
|
||||
new_value=(
|
||||
activity.new_value
|
||||
if activity.new_value != ""
|
||||
else None
|
||||
),
|
||||
actor_id=activity.actor_id,
|
||||
current_site=origin,
|
||||
slug=activity.workspace.slug,
|
||||
old_identifier=activity.old_identifier,
|
||||
new_identifier=activity.new_identifier,
|
||||
)
|
||||
|
||||
if notification:
|
||||
notifications.delay(
|
||||
type=type,
|
||||
|
@ -25,6 +25,8 @@ from plane.api.serializers import (
|
||||
ModuleIssueSerializer,
|
||||
ModuleSerializer,
|
||||
ProjectSerializer,
|
||||
UserLiteSerializer,
|
||||
InboxIssueSerializer,
|
||||
)
|
||||
from plane.db.models import (
|
||||
Cycle,
|
||||
@ -37,6 +39,7 @@ from plane.db.models import (
|
||||
User,
|
||||
Webhook,
|
||||
WebhookLog,
|
||||
InboxIssue,
|
||||
)
|
||||
from plane.license.utils.instance_value import get_email_configuration
|
||||
from plane.utils.exception_logger import log_exception
|
||||
@ -49,6 +52,8 @@ SERIALIZER_MAPPER = {
|
||||
"cycle_issue": CycleIssueSerializer,
|
||||
"module_issue": ModuleIssueSerializer,
|
||||
"issue_comment": IssueCommentSerializer,
|
||||
"user": UserLiteSerializer,
|
||||
"inbox_issue": InboxIssueSerializer,
|
||||
}
|
||||
|
||||
MODEL_MAPPER = {
|
||||
@ -59,6 +64,8 @@ MODEL_MAPPER = {
|
||||
"cycle_issue": CycleIssue,
|
||||
"module_issue": ModuleIssue,
|
||||
"issue_comment": IssueComment,
|
||||
"user": User,
|
||||
"inbox_issue": InboxIssue,
|
||||
}
|
||||
|
||||
|
||||
@ -179,64 +186,6 @@ def webhook_task(self, webhook, slug, event, event_data, action, current_site):
|
||||
return
|
||||
|
||||
|
||||
@shared_task()
|
||||
def send_webhook(event, payload, kw, action, slug, bulk, current_site):
|
||||
try:
|
||||
webhooks = Webhook.objects.filter(workspace__slug=slug, is_active=True)
|
||||
|
||||
if event == "project":
|
||||
webhooks = webhooks.filter(project=True)
|
||||
|
||||
if event == "issue":
|
||||
webhooks = webhooks.filter(issue=True)
|
||||
|
||||
if event == "module" or event == "module_issue":
|
||||
webhooks = webhooks.filter(module=True)
|
||||
|
||||
if event == "cycle" or event == "cycle_issue":
|
||||
webhooks = webhooks.filter(cycle=True)
|
||||
|
||||
if event == "issue_comment":
|
||||
webhooks = webhooks.filter(issue_comment=True)
|
||||
|
||||
if webhooks:
|
||||
if action in ["POST", "PATCH"]:
|
||||
if bulk and event in ["cycle_issue", "module_issue"]:
|
||||
return
|
||||
else:
|
||||
event_data = [
|
||||
get_model_data(
|
||||
event=event,
|
||||
event_id=(
|
||||
payload.get("id")
|
||||
if isinstance(payload, dict)
|
||||
else kw.get("pk")
|
||||
),
|
||||
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,
|
||||
current_site=current_site,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
if settings.DEBUG:
|
||||
print(e)
|
||||
log_exception(e)
|
||||
return
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_webhook_deactivation_email(
|
||||
webhook_id, receiver_id, current_site, reason
|
||||
@ -294,3 +243,240 @@ def send_webhook_deactivation_email(
|
||||
except Exception as e:
|
||||
log_exception(e)
|
||||
return
|
||||
|
||||
|
||||
@shared_task(
|
||||
bind=True,
|
||||
autoretry_for=(requests.RequestException,),
|
||||
retry_backoff=600,
|
||||
max_retries=5,
|
||||
retry_jitter=True,
|
||||
)
|
||||
def webhook_send_task(
|
||||
self,
|
||||
webhook,
|
||||
slug,
|
||||
event,
|
||||
event_data,
|
||||
action,
|
||||
current_site,
|
||||
activity,
|
||||
):
|
||||
try:
|
||||
webhook = Webhook.objects.get(id=webhook, workspace__slug=slug)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Autopilot",
|
||||
"X-Plane-Delivery": str(uuid.uuid4()),
|
||||
"X-Plane-Event": event,
|
||||
}
|
||||
|
||||
# # Your secret key
|
||||
event_data = (
|
||||
json.loads(json.dumps(event_data, cls=DjangoJSONEncoder))
|
||||
if event_data is not None
|
||||
else None
|
||||
)
|
||||
|
||||
activity = (
|
||||
json.loads(json.dumps(activity, cls=DjangoJSONEncoder))
|
||||
if activity is not None
|
||||
else None
|
||||
)
|
||||
|
||||
action = {
|
||||
"POST": "create",
|
||||
"PATCH": "update",
|
||||
"PUT": "update",
|
||||
"DELETE": "delete",
|
||||
}.get(action, action)
|
||||
|
||||
payload = {
|
||||
"event": event,
|
||||
"action": action,
|
||||
"webhook_id": str(webhook.id),
|
||||
"workspace_id": str(webhook.workspace_id),
|
||||
"data": event_data,
|
||||
"activity": activity,
|
||||
}
|
||||
|
||||
# Use HMAC for generating signature
|
||||
if webhook.secret_key:
|
||||
hmac_signature = hmac.new(
|
||||
webhook.secret_key.encode("utf-8"),
|
||||
json.dumps(payload).encode("utf-8"),
|
||||
hashlib.sha256,
|
||||
)
|
||||
signature = hmac_signature.hexdigest()
|
||||
headers["X-Plane-Signature"] = signature
|
||||
|
||||
# Send the webhook event
|
||||
response = requests.post(
|
||||
webhook.url,
|
||||
headers=headers,
|
||||
json=payload,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
# Log the webhook request
|
||||
WebhookLog.objects.create(
|
||||
workspace_id=str(webhook.workspace_id),
|
||||
webhook_id=str(webhook.id),
|
||||
event_type=str(event),
|
||||
request_method=str(action),
|
||||
request_headers=str(headers),
|
||||
request_body=str(payload),
|
||||
response_status=str(response.status_code),
|
||||
response_headers=str(response.headers),
|
||||
response_body=str(response.text),
|
||||
retry_count=str(self.request.retries),
|
||||
)
|
||||
|
||||
except requests.RequestException as e:
|
||||
# Log the failed webhook request
|
||||
WebhookLog.objects.create(
|
||||
workspace_id=str(webhook.workspace_id),
|
||||
webhook_id=str(webhook.id),
|
||||
event_type=str(event),
|
||||
request_method=str(action),
|
||||
request_headers=str(headers),
|
||||
request_body=str(payload),
|
||||
response_status=500,
|
||||
response_headers="",
|
||||
response_body=str(e),
|
||||
retry_count=str(self.request.retries),
|
||||
)
|
||||
# Retry logic
|
||||
if self.request.retries >= self.max_retries:
|
||||
Webhook.objects.filter(pk=webhook.id).update(is_active=False)
|
||||
if webhook:
|
||||
# send email for the deactivation of the webhook
|
||||
send_webhook_deactivation_email(
|
||||
webhook_id=webhook.id,
|
||||
receiver_id=webhook.created_by_id,
|
||||
reason=str(e),
|
||||
current_site=current_site,
|
||||
)
|
||||
return
|
||||
raise requests.RequestException()
|
||||
|
||||
except Exception as e:
|
||||
if settings.DEBUG:
|
||||
print(e)
|
||||
log_exception(e)
|
||||
return
|
||||
|
||||
|
||||
@shared_task
|
||||
def webhook_activity(
|
||||
event,
|
||||
verb,
|
||||
field,
|
||||
old_value,
|
||||
new_value,
|
||||
actor_id,
|
||||
slug,
|
||||
current_site,
|
||||
event_id,
|
||||
old_identifier,
|
||||
new_identifier,
|
||||
):
|
||||
try:
|
||||
webhooks = Webhook.objects.filter(workspace__slug=slug, is_active=True)
|
||||
|
||||
if event == "project":
|
||||
webhooks = webhooks.filter(project=True)
|
||||
|
||||
if event == "issue":
|
||||
webhooks = webhooks.filter(issue=True)
|
||||
|
||||
if event == "module" or event == "module_issue":
|
||||
webhooks = webhooks.filter(module=True)
|
||||
|
||||
if event == "cycle" or event == "cycle_issue":
|
||||
webhooks = webhooks.filter(cycle=True)
|
||||
|
||||
if event == "issue_comment":
|
||||
webhooks = webhooks.filter(issue_comment=True)
|
||||
|
||||
for webhook in webhooks:
|
||||
webhook_send_task.delay(
|
||||
webhook=webhook.id,
|
||||
slug=slug,
|
||||
event=event,
|
||||
event_data=get_model_data(
|
||||
event=event,
|
||||
event_id=event_id,
|
||||
),
|
||||
action=verb,
|
||||
current_site=current_site,
|
||||
activity={
|
||||
"field": field,
|
||||
"new_value": new_value,
|
||||
"old_value": old_value,
|
||||
"actor": get_model_data(event="user", event_id=actor_id),
|
||||
"old_identifier": old_identifier,
|
||||
"new_identifier": new_identifier,
|
||||
},
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
if settings.DEBUG:
|
||||
print(e)
|
||||
log_exception(e)
|
||||
return
|
||||
|
||||
|
||||
@shared_task
|
||||
def model_activity(
|
||||
model_name,
|
||||
model_id,
|
||||
requested_data,
|
||||
current_instance,
|
||||
actor_id,
|
||||
slug,
|
||||
origin=None,
|
||||
):
|
||||
"""Function takes in two json and computes differences between keys of both the json"""
|
||||
if current_instance is None:
|
||||
webhook_activity.delay(
|
||||
event=model_name,
|
||||
verb="created",
|
||||
field=None,
|
||||
old_value=None,
|
||||
new_value=None,
|
||||
actor_id=actor_id,
|
||||
slug=slug,
|
||||
current_site=origin,
|
||||
event_id=model_id,
|
||||
old_identifier=None,
|
||||
new_identifier=None,
|
||||
)
|
||||
return
|
||||
|
||||
# Load the current instance
|
||||
current_instance = (
|
||||
json.loads(current_instance) if current_instance is not None else None
|
||||
)
|
||||
|
||||
# Loop through all keys in requested data and check the current value and requested value
|
||||
for key in requested_data:
|
||||
current_value = current_instance.get(key, None)
|
||||
requested_value = requested_data.get(key, None)
|
||||
if current_value != requested_value:
|
||||
webhook_activity.delay(
|
||||
event=model_name,
|
||||
verb="updated",
|
||||
field=key,
|
||||
old_value=current_value,
|
||||
new_value=requested_value,
|
||||
actor_id=actor_id,
|
||||
slug=slug,
|
||||
current_site=origin,
|
||||
event_id=model_id,
|
||||
old_identifier=None,
|
||||
new_identifier=None,
|
||||
)
|
||||
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user