forked from github/plane
Merge branch 'chore/api_endpoints' of github.com:makeplane/plane into chore/api_endpoints
This commit is contained in:
commit
21b6804ebc
@ -66,7 +66,6 @@ class IssueSerializer(BaseSerializer):
|
||||
project_id=self.context.get("project_id"),
|
||||
is_active=True,
|
||||
member_id__in=data["assignees"],
|
||||
is_active=True,
|
||||
).values_list("member_id", flat=True)
|
||||
|
||||
# Validate labels are from project
|
||||
|
@ -36,28 +36,31 @@ class TimezoneMixin:
|
||||
else:
|
||||
timezone.deactivate()
|
||||
|
||||
|
||||
class WebhookMixin:
|
||||
webhook_event = None
|
||||
|
||||
def finalize_response(self, request, response, *args, **kwargs):
|
||||
response = super().finalize_response(request, response, *args, **kwargs)
|
||||
|
||||
if (
|
||||
self.webhook_event
|
||||
and self.request.method in ["POST", "PATCH", "DELETE"]
|
||||
and self.request.method in ["POST", "PATCH"]
|
||||
and response.status_code in [200, 201, 204]
|
||||
):
|
||||
# Get the id
|
||||
object_id = (
|
||||
response.data.get("id") if isinstance(response.data, dict) else None
|
||||
)
|
||||
|
||||
send_webhook.delay(
|
||||
event=self.webhook_event,
|
||||
event_data=json.dumps(response.data, cls=DjangoJSONEncoder),
|
||||
event_id=object_id,
|
||||
action=self.request.method,
|
||||
slug=self.workspace_slug,
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
|
||||
class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
|
||||
authentication_classes = [
|
||||
APIKeyAuthentication,
|
||||
@ -139,13 +142,13 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
|
||||
response = super().finalize_response(request, response, *args, **kwargs)
|
||||
|
||||
# Add custom headers if they exist in the request META
|
||||
ratelimit_remaining = request.META.get('X-RateLimit-Remaining')
|
||||
ratelimit_remaining = request.META.get("X-RateLimit-Remaining")
|
||||
if ratelimit_remaining is not None:
|
||||
response['X-RateLimit-Remaining'] = ratelimit_remaining
|
||||
response["X-RateLimit-Remaining"] = ratelimit_remaining
|
||||
|
||||
ratelimit_reset = request.META.get('X-RateLimit-Reset')
|
||||
ratelimit_reset = request.META.get("X-RateLimit-Reset")
|
||||
if ratelimit_reset is not None:
|
||||
response['X-RateLimit-Reset'] = ratelimit_reset
|
||||
response["X-RateLimit-Reset"] = ratelimit_reset
|
||||
|
||||
return response
|
||||
|
||||
|
@ -2,15 +2,63 @@ import requests
|
||||
import uuid
|
||||
import hashlib
|
||||
import json
|
||||
import hmac
|
||||
|
||||
# Django imports
|
||||
from django.conf import settings
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
# Third party imports
|
||||
from celery import shared_task
|
||||
from sentry_sdk import capture_exception
|
||||
|
||||
from plane.db.models import Webhook, WebhookLog
|
||||
from plane.db.models import (
|
||||
Webhook,
|
||||
WebhookLog,
|
||||
Project,
|
||||
Issue,
|
||||
Cycle,
|
||||
Module,
|
||||
ModuleIssue,
|
||||
CycleIssue,
|
||||
IssueComment,
|
||||
)
|
||||
from plane.api.serializers import (
|
||||
ProjectSerializer,
|
||||
IssueSerializer,
|
||||
CycleSerializer,
|
||||
ModuleSerializer,
|
||||
CycleIssueSerializer,
|
||||
ModuleIssueSerializer,
|
||||
IssueCommentSerializer,
|
||||
)
|
||||
|
||||
SERIALIZER_MAPPER = {
|
||||
"project": ProjectSerializer,
|
||||
"issue": IssueSerializer,
|
||||
"cycle": CycleSerializer,
|
||||
"module": ModuleSerializer,
|
||||
"cycle_issue": CycleIssueSerializer,
|
||||
"module_issue": ModuleIssueSerializer,
|
||||
"issue_comment": IssueCommentSerializer,
|
||||
}
|
||||
|
||||
MODEL_MAPPER = {
|
||||
"project": Project,
|
||||
"issue": Issue,
|
||||
"cycle": Cycle,
|
||||
"module": Module,
|
||||
"cycle_issue": CycleIssue,
|
||||
"module_issue": ModuleIssue,
|
||||
"issue_comment": IssueComment,
|
||||
}
|
||||
|
||||
|
||||
def get_model_data(event, event_id):
|
||||
model = MODEL_MAPPER.get(event)
|
||||
queryset = model.objects.get(pk=event_id)
|
||||
serializer = SERIALIZER_MAPPER.get(event)
|
||||
return serializer(queryset).data
|
||||
|
||||
|
||||
@shared_task(
|
||||
@ -20,7 +68,7 @@ from plane.db.models import Webhook, WebhookLog
|
||||
max_retries=5,
|
||||
retry_jitter=True,
|
||||
)
|
||||
def webhook_task(self, webhook, slug, event, event_data, action):
|
||||
def webhook_task(self, webhook, slug, event, event_id, action):
|
||||
try:
|
||||
webhook = Webhook.objects.get(id=webhook, workspace__slug=slug)
|
||||
|
||||
@ -31,19 +79,26 @@ def webhook_task(self, webhook, slug, event, event_data, action):
|
||||
"X-Plane-Event": event,
|
||||
}
|
||||
|
||||
# Your secret key
|
||||
event_data = get_model_data(event=event, event_id=event_id)
|
||||
|
||||
# # Your secret key
|
||||
event_data = (
|
||||
json.loads(json.dumps(event_data, cls=DjangoJSONEncoder))
|
||||
if event_data is not None
|
||||
else None
|
||||
)
|
||||
|
||||
# Use HMAC for generating signature
|
||||
if webhook.secret_key:
|
||||
# Concatenate the data and the secret key
|
||||
message = event_data + webhook.secret_key
|
||||
|
||||
# Create a SHA-256 hash of the message
|
||||
sha256 = hashlib.sha256()
|
||||
sha256.update(message.encode("utf-8"))
|
||||
signature = sha256.hexdigest()
|
||||
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
|
||||
)
|
||||
signature = hmac_signature.hexdigest()
|
||||
headers["X-Plane-Signature"] = signature
|
||||
|
||||
event_data = json.loads(event_data) if event_data is not None else None
|
||||
|
||||
action = {
|
||||
"POST": "create",
|
||||
"PATCH": "update",
|
||||
@ -103,6 +158,7 @@ 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)
|
||||
@ -110,7 +166,7 @@ def webhook_task(self, webhook, slug, event, event_data, action):
|
||||
|
||||
|
||||
@shared_task()
|
||||
def send_webhook(event, event_data, action, slug):
|
||||
def send_webhook(event, event_id, action, slug):
|
||||
try:
|
||||
webhooks = Webhook.objects.filter(workspace__slug=slug, is_active=True)
|
||||
|
||||
@ -130,7 +186,7 @@ def send_webhook(event, event_data, action, slug):
|
||||
webhooks = webhooks.filter(issue_comment=True)
|
||||
|
||||
for webhook in webhooks:
|
||||
webhook_task.delay(webhook.id, slug, event, event_data, action)
|
||||
webhook_task.delay(webhook.id, slug, event, event_id, action)
|
||||
|
||||
except Exception as e:
|
||||
if settings.DEBUG:
|
||||
|
Loading…
Reference in New Issue
Block a user