mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
dev: update webhook logic for issues
This commit is contained in:
parent
aa09ec7cd4
commit
fe68e14d8b
@ -52,8 +52,7 @@ from plane.db.models import (
|
||||
from .base import BaseAPIView, WebhookMixin
|
||||
|
||||
|
||||
|
||||
class WorkspaceIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
class WorkspaceIssueAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
This viewset provides `retrieveByIssueId` on workspace level
|
||||
|
||||
@ -61,12 +60,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)
|
||||
@ -92,7 +88,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(
|
||||
@ -101,7 +99,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,
|
||||
@ -111,7 +113,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.
|
||||
|
@ -249,6 +249,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
|
||||
@ -256,7 +257,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),
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ from plane.db.models import (
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
|
||||
# Module imports
|
||||
from .. import BaseAPIView, BaseViewSet, WebhookMixin
|
||||
from .. import BaseAPIView, BaseViewSet
|
||||
|
||||
|
||||
class IssueListEndpoint(BaseAPIView):
|
||||
@ -244,7 +244,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
|
||||
|
@ -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
|
||||
@ -1692,6 +1693,19 @@ def issue_activity(
|
||||
except Exception as e:
|
||||
log_exception(e)
|
||||
|
||||
for activity in issue_activities_created:
|
||||
webhook_activity.delay(
|
||||
event="issue",
|
||||
event_id=activity.issue_id,
|
||||
verb=activity.verb,
|
||||
field=activity.field,
|
||||
old_value=activity.old_value,
|
||||
new_value=activity.new_value,
|
||||
actor_id=activity.actor_id,
|
||||
current_site=origin,
|
||||
slug=activity.workspace.slug,
|
||||
)
|
||||
|
||||
if notification:
|
||||
notifications.delay(
|
||||
type=type,
|
||||
|
@ -294,3 +294,169 @@ 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
|
||||
)
|
||||
|
||||
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,
|
||||
):
|
||||
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_id": actor_id,
|
||||
},
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user