From 44ca3f02bc35a9e54ffa6c3587cd08500b6c7d7c Mon Sep 17 00:00:00 2001 From: Henit Chobisa Date: Wed, 30 Apr 2025 22:33:17 +0530 Subject: [PATCH] feat: webhook activity payload reverted (#3094) * chore: revert webhook activity task * fix: channel id --- .../plane/bgtasks/issue_activities_task.py | 356 ++++-------------- apiserver/plane/bgtasks/webhook_task.py | 303 ++++++++------- .../handle-issue-webhook.ts | 5 +- 3 files changed, 228 insertions(+), 436 deletions(-) diff --git a/apiserver/plane/bgtasks/issue_activities_task.py b/apiserver/plane/bgtasks/issue_activities_task.py index 84ad886ff4..2113819797 100644 --- a/apiserver/plane/bgtasks/issue_activities_task.py +++ b/apiserver/plane/bgtasks/issue_activities_task.py @@ -8,12 +8,11 @@ from celery import shared_task # Django imports from django.core.serializers.json import DjangoJSONEncoder from django.utils import timezone -from django.db.models import Q - -# Module imports from plane.app.serializers import IssueActivitySerializer from plane.bgtasks.notification_task import notifications + +# Module imports from plane.db.models import ( CommentReaction, Cycle, @@ -28,14 +27,11 @@ from plane.db.models import ( State, User, EstimatePoint, - IssueType, ) -from plane.db.models.workspace import Workspace from plane.settings.redis import redis_instance from plane.utils.exception_logger import log_exception from plane.bgtasks.webhook_task import webhook_activity from plane.utils.issue_relation_mapper import get_inverse_relation -from plane.utils.uuid import is_valid_uuid # Track Changes in name @@ -92,27 +88,6 @@ def track_description( ): last_activity.created_at = timezone.now() last_activity.save(update_fields=["created_at"]) - - # Get the current origin from redis - ri = redis_instance() - origin = ri.get(str(issue_id)) - - workspace = Workspace.objects.get(pk=workspace_id) - - if workspace: - webhook_activity.delay( - event="issue", - event_id=issue_id, - verb="updated", - field="description", - old_value=None, - new_value=None, - current_site=origin, - actor_id=actor_id, - slug=workspace.slug, - old_identifier=last_activity.old_identifier, - new_identifier=last_activity.new_identifier, - ) else: issue_activities.append( IssueActivity( @@ -218,16 +193,9 @@ def track_state( issue_activities, epoch, ): - current_state_id = current_instance.get("state_id") or current_instance.get("state") - requested_state_id = requested_data.get("state_id") or requested_data.get("state") - - if current_state_id != requested_state_id: - # check if the current state - if not is_valid_uuid(requested_state_id): - return - - new_state = State.objects.get(pk=requested_state_id) - old_state = State.objects.get(pk=current_state_id) + if current_instance.get("state_id") != requested_data.get("state_id"): + new_state = State.objects.get(pk=requested_data.get("state_id", None)) + old_state = State.objects.get(pk=current_instance.get("state_id", None)) issue_activities.append( IssueActivity( @@ -338,10 +306,6 @@ def track_labels( # Set of newly added labels for added_label in added_labels: - # validate uuids - if not is_valid_uuid(added_label): - continue - label = Label.objects.get(pk=added_label) issue_activities.append( IssueActivity( @@ -362,10 +326,6 @@ def track_labels( # Set of dropped labels for dropped_label in dropped_labels: - # validate uuids - if not is_valid_uuid(dropped_label): - continue - label = Label.objects.get(pk=dropped_label) issue_activities.append( IssueActivity( @@ -412,10 +372,6 @@ def track_assignees( bulk_subscribers = [] for added_asignee in added_assignees: - # validate uuids - if not is_valid_uuid(added_asignee): - continue - assignee = User.objects.get(pk=added_asignee) issue_activities.append( IssueActivity( @@ -449,10 +405,6 @@ def track_assignees( ) for dropped_assignee in dropped_assginees: - # validate uuids - if not is_valid_uuid(dropped_assignee): - continue - assignee = User.objects.get(pk=dropped_assignee) issue_activities.append( IssueActivity( @@ -496,12 +448,6 @@ def track_estimate_points( if requested_data.get("estimate_point") is not None else None ) - - if new_estimate is None: - field = "estimate_" + old_estimate.estimate.type - else: - field = "estimate_" + new_estimate.estimate.type - issue_activities.append( IssueActivity( issue_id=issue_id, @@ -519,7 +465,7 @@ def track_estimate_points( ), old_value=old_estimate.value if old_estimate else None, new_value=new_estimate.value if new_estimate else None, - field=field, + field="estimate_point", project_id=project_id, workspace_id=workspace_id, comment="updated the estimate point to ", @@ -609,7 +555,7 @@ def track_closed_to( ) -def track_type( +def create_issue_activity( requested_data, current_instance, issue_id, @@ -619,60 +565,31 @@ def track_type( issue_activities, epoch, ): - new_type_id = requested_data.get("type_id") - old_type_id = current_instance.get("type_id") - - if new_type_id != old_type_id: - verb = "updated" if new_type_id else "deleted" - old_type_id = ( - IssueType.objects.filter(pk=old_type_id).first() if old_type_id else None - ) - new_type_id = ( - IssueType.objects.filter(pk=new_type_id).first() if new_type_id else None - ) - - issue_activities.append( - IssueActivity( - issue_id=issue_id, - actor_id=actor_id, - verb=verb, - old_value=old_type_id.name if old_type_id else None, - new_value=new_type_id.name if new_type_id else None, - field="type", - project_id=project_id, - workspace_id=workspace_id, - comment="", - old_identifier=old_type_id.id if old_type_id else None, - new_identifier=new_type_id.id if new_type_id else None, - epoch=epoch, - ) - ) - - -def create_epic_activity( - requested_data, - current_instance, - issue_id, - project_id, - workspace_id, - actor_id, - issue_activities, - epoch, -): - epic = Issue.objects.get(pk=issue_id) + issue = Issue.objects.get(pk=issue_id) issue_activity = IssueActivity.objects.create( issue_id=issue_id, project_id=project_id, workspace_id=workspace_id, - comment="created the epic", + comment="created the issue", verb="created", - field="epic", actor_id=actor_id, epoch=epoch, ) - issue_activity.created_at = epic.created_at - issue_activity.actor_id = epic.created_by_id + issue_activity.created_at = issue.created_at + issue_activity.actor_id = issue.created_by_id issue_activity.save(update_fields=["created_at", "actor_id"]) + requested_data = json.loads(requested_data) if requested_data is not None else None + if requested_data.get("assignee_ids") is not None: + track_assignees( + requested_data, + current_instance, + issue_id, + project_id, + workspace_id, + actor_id, + issue_activities, + epoch, + ) def update_issue_activity( @@ -690,7 +607,6 @@ def update_issue_activity( "parent_id": track_parent, "priority": track_priority, "state_id": track_state, - "state": track_state, "description_html": track_description, "target_date": track_target_date, "start_date": track_start_date, @@ -699,7 +615,6 @@ def update_issue_activity( "estimate_point": track_estimate_points, "archived_at": track_archive_at, "closed_to": track_closed_to, - "type_id": track_type, } requested_data = json.loads(requested_data) if requested_data is not None else None @@ -875,15 +790,14 @@ def create_cycle_issue_activity( issue_id=updated_record.get("issue_id"), actor_id=actor_id, verb="updated", - old_value=old_cycle.name if old_cycle else "", - new_value=new_cycle.name if new_cycle else "", + old_value=old_cycle.name, + new_value=new_cycle.name, field="cycles", project_id=project_id, workspace_id=workspace_id, - comment=f"""updated cycle from {old_cycle.name if old_cycle else ""} - to {new_cycle.name if new_cycle else ""}""", - old_identifier=old_cycle.id if old_cycle else None, - new_identifier=new_cycle.id if new_cycle else None, + comment=f"updated cycle from {old_cycle.name} to {new_cycle.name}", + old_identifier=old_cycle.id, + new_identifier=new_cycle.id, epoch=epoch, ) ) @@ -937,7 +851,7 @@ def delete_cycle_issue_activity( issues = requested_data.get("issues") for issue in issues: current_issue = Issue.objects.filter(pk=issue).first() - if current_issue: + if issue: current_issue.updated_at = timezone.now() current_issue.save(update_fields=["updated_at"]) issue_activities.append( @@ -979,11 +893,11 @@ def create_module_issue_activity( actor_id=actor_id, verb="created", old_value="", - new_value=module.name if module else "", + new_value=module.name, field="modules", project_id=project_id, workspace_id=workspace_id, - comment=f"added module {module.name if module else ''}", + comment=f"added module {module.name}", new_identifier=requested_data.get("module_id"), epoch=epoch, ) @@ -1499,7 +1413,7 @@ def delete_issue_relation_activity( ), project_id=project_id, workspace_id=workspace_id, - comment=f"deleted {requested_data.get('relation_type')} relation", + comment=f'deleted {requested_data.get("relation_type")} relation', old_identifier=requested_data.get("related_issue"), epoch=epoch, ) @@ -1635,160 +1549,6 @@ def create_intake_activity( ) -def create_issue_activity( - requested_data, - current_instance, - issue_id, - project_id, - workspace_id, - actor_id, - issue_activities, - epoch, -): - issue = Issue.objects.get(pk=issue_id) - issue_activity = IssueActivity.objects.create( - issue_id=issue_id, - project_id=project_id, - workspace_id=workspace_id, - comment="created the issue", - verb="created", - actor_id=actor_id, - epoch=epoch, - ) - - issue_activity.created_at = issue.created_at - issue_activity.actor_id = issue.created_by_id - issue_activity.save(update_fields=["created_at", "actor_id"]) - requested_data = json.loads(requested_data) if requested_data is not None else None - if requested_data.get("assignee_ids") is not None: - track_assignees( - requested_data, - current_instance, - issue_id, - project_id, - workspace_id, - actor_id, - issue_activities, - epoch, - ) - - -def create_customer_activity( - requested_data, - current_instance, - issue_id, - project_id, - workspace_id, - actor_id, - issue_activities, - epoch, -): - requested_data = json.loads(requested_data) if requested_data is not None else None - - if requested_data.get("customer_request_id") is not None: - field = "customer_request" - elif requested_data.get("customer_request_id") is None: - field = "customer" - - issue_activities.append( - IssueActivity( - issue_id=issue_id, - project_id=project_id, - workspace_id=workspace_id, - actor_id=actor_id, - verb="created", - comment="linked to the customer", - new_value=requested_data.get("name"), - old_value=None, - field=field, - new_identifier=requested_data.get("customer_id"), - epoch=epoch, - ) - ) - - -def delete_customer_activity( - requested_data, - current_instance, - issue_id, - project_id, - workspace_id, - actor_id, - issue_activities, - epoch, -): - requested_data = json.loads(requested_data) if requested_data is not None else None - - if requested_data.get("customer_request_id") is not None: - field = "customer_request" - elif requested_data.get("customer_request_id") is None: - field = "customer" - - issue_activities.append( - IssueActivity( - issue_id=issue_id, - project_id=project_id, - workspace_id=workspace_id, - actor_id=actor_id, - verb="deleted", - comment="removed from customer", - new_value=None, - old_value=requested_data.get("name"), - field=field, - old_identifier=requested_data.get("customer_id"), - epoch=epoch, - ) - ) - - -def convert_epic_to_work_item_activity( - requested_data, - current_instance, - issue_id, - project_id, - workspace_id, - actor_id, - issue_activities, - epoch, -): - issue_activities.append( - IssueActivity( - issue_id=issue_id, - project_id=project_id, - workspace_id=workspace_id, - comment="converted the epic to work item", - field="epic", - verb="converted", - actor_id=actor_id, - epoch=epoch, - ) - ) - - -def convert_work_item_to_epic_activity( - requested_data, - current_instance, - issue_id, - project_id, - workspace_id, - actor_id, - issue_activities, - epoch, -): - issue_activities.append( - IssueActivity( - issue_id=issue_id, - project_id=project_id, - workspace_id=workspace_id, - comment="converted the work item to epic", - field="work_item", - verb="converted", - actor_id=actor_id, - epoch=epoch, - ) - ) - - # Receive message from room group @shared_task def issue_activity( @@ -1807,29 +1567,22 @@ def issue_activity( try: issue_activities = [] - # check if project_id is valid - if not is_valid_uuid(str(project_id)): - return - project = Project.objects.get(pk=project_id) workspace_id = project.workspace_id if issue_id is not None: - issue = ( - Issue.objects.filter(pk=issue_id) - .filter(Q(type__isnull=True) | Q(type__is_epic=False)) - .first() - ) - if origin and issue: + if origin: ri = redis_instance() # set the request origin in redis ri.set(str(issue_id), origin, ex=600) + issue = Issue.objects.filter(pk=issue_id).first() if issue: try: issue.updated_at = timezone.now() issue.save(update_fields=["updated_at"]) except Exception: pass + ACTIVITY_MAPPER = { "issue.activity.created": create_issue_activity, "issue.activity.updated": update_issue_activity, @@ -1858,11 +1611,6 @@ def issue_activity( "issue_draft.activity.updated": update_draft_issue_activity, "issue_draft.activity.deleted": delete_draft_issue_activity, "intake.activity.created": create_intake_activity, - "epic.activity.created": create_epic_activity, - "work_item.activity.converted": convert_epic_to_work_item_activity, - "epic.activity.converted": convert_work_item_to_epic_activity, - "customer.activity.created": create_customer_activity, - "customer.activity.deleted": delete_customer_activity, } func = ACTIVITY_MAPPER.get(type) @@ -1880,6 +1628,40 @@ def issue_activity( # Save all the values to database issue_activities_created = IssueActivity.objects.bulk_create(issue_activities) + # Post the updates to segway for integrations and webhooks + if len(issue_activities_created): + for activity in issue_activities_created: + webhook_activity.delay( + event=( + "issue_comment" + if activity.field == "comment" + else "intake_issue" + if intake + else "issue" + ), + event_id=( + activity.issue_comment_id + if activity.field == "comment" + else intake + if intake + 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( diff --git a/apiserver/plane/bgtasks/webhook_task.py b/apiserver/plane/bgtasks/webhook_task.py index 0bcfd2693a..c1ea01a4dc 100644 --- a/apiserver/plane/bgtasks/webhook_task.py +++ b/apiserver/plane/bgtasks/webhook_task.py @@ -5,7 +5,6 @@ import logging import uuid import requests -from typing import Any, Dict, List, Optional, Union # Third party imports from celery import shared_task @@ -71,89 +70,150 @@ MODEL_MAPPER = { } -logger = logging.getLogger("plane.worker") - - -def get_model_data( - event: str, event_id: Union[str, List[str]], many: bool = False -) -> Dict[str, Any]: - """ - Retrieve and serialize model data based on the event type. - - Args: - event (str): The type of event/model to retrieve data for - event_id (Union[str, List[str]]): The ID or list of IDs of the model instance(s) - many (bool): Whether to retrieve multiple instances - - Returns: - Dict[str, Any]: Serialized model data - - Raises: - ValueError: If serializer is not found for the event - ObjectDoesNotExist: If model instance is not found - """ +def get_model_data(event, event_id, many=False): model = MODEL_MAPPER.get(event) - if model is None: - raise ValueError(f"Model not found for event: {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, many=many).data + +@shared_task( + bind=True, + autoretry_for=(requests.RequestException,), + retry_backoff=600, + max_retries=5, + retry_jitter=True, +) +def webhook_task(self, webhook, slug, event, event_data, action, current_site): try: - if many: - queryset = model.objects.filter(pk__in=event_id) - else: - queryset = model.objects.get(pk=event_id) + webhook = Webhook.objects.get(id=webhook, workspace__slug=slug) - serializer = SERIALIZER_MAPPER.get(event) - if serializer is None: - raise ValueError(f"Serializer not found for event: {event}") + headers = { + "Content-Type": "application/json", + "User-Agent": "Autopilot", + "X-Plane-Delivery": str(uuid.uuid4()), + "X-Plane-Event": event, + } - return serializer(queryset, many=many).data - except ObjectDoesNotExist: - raise ObjectDoesNotExist(f"No {event} found with id: {event_id}") + # # 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, + } + + # 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=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 Webhook.DoesNotExist: + return + except requests.RequestException as e: + # Log the failed webhook request + WebhookLog.objects.create( + workspace_id=str(webhook.workspace_id), + webhook=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 send_webhook_deactivation_email( - webhook_id: str, receiver_id: str, current_site: str, reason: str -) -> None: - """ - Send an email notification when a webhook is deactivated. +def send_webhook_deactivation_email(webhook_id, receiver_id, current_site, reason): + # Get email configurations + ( + EMAIL_HOST, + EMAIL_HOST_USER, + EMAIL_HOST_PASSWORD, + EMAIL_PORT, + EMAIL_USE_TLS, + EMAIL_USE_SSL, + EMAIL_FROM, + ) = get_email_configuration() + + receiver = User.objects.get(pk=receiver_id) + webhook = Webhook.objects.get(pk=webhook_id) + subject = "Webhook Deactivated" + message = f"Webhook {webhook.url} has been deactivated due to failed requests." + + # Send the mail + context = { + "email": receiver.email, + "message": message, + "webhook_url": f"{current_site}/{str(webhook.workspace.slug)}/settings/webhooks/{str(webhook.id)}", + } + html_content = render_to_string( + "emails/notifications/webhook-deactivate.html", context + ) + text_content = strip_tags(html_content) - Args: - webhook_id (str): ID of the deactivated webhook - receiver_id (str): ID of the user to receive the notification - current_site (str): Current site URL - reason (str): Reason for webhook deactivation - """ try: - ( - EMAIL_HOST, - EMAIL_HOST_USER, - EMAIL_HOST_PASSWORD, - EMAIL_PORT, - EMAIL_USE_TLS, - EMAIL_USE_SSL, - EMAIL_FROM, - ) = get_email_configuration() - - receiver = User.objects.get(pk=receiver_id) - webhook = Webhook.objects.get(pk=webhook_id) - - # Get the webhook payload - subject = "Webhook Deactivated" - message = f"Webhook {webhook.url} has been deactivated due to failed requests." - - # Send the mail - context = { - "email": receiver.email, - "message": message, - "webhook_url": f"{current_site}/{str(webhook.workspace.slug)}/settings/webhooks/{str(webhook.id)}", - } - html_content = render_to_string( - "emails/notifications/webhook-deactivate.html", context - ) - text_content = strip_tags(html_content) - - # Set the email connection connection = get_connection( host=EMAIL_HOST, port=int(EMAIL_PORT), @@ -163,7 +223,6 @@ def send_webhook_deactivation_email( use_ssl=EMAIL_USE_SSL == "1", ) - # Create the email message msg = EmailMultiAlternatives( subject=subject, body=text_content, @@ -173,10 +232,11 @@ def send_webhook_deactivation_email( ) msg.attach_alternative(html_content, "text/html") msg.send() - logger.info("Email sent successfully.") + logging.getLogger("plane").info("Email sent successfully.") + return except Exception as e: log_exception(e) - logger.error(f"Failed to send email: {e}") + return @shared_task( @@ -187,29 +247,10 @@ def send_webhook_deactivation_email( retry_jitter=True, ) def webhook_send_task( - self, - webhook_id: str, - slug: str, - event: str, - event_data: Optional[Dict[str, Any]], - action: str, - current_site: str, - activity: Optional[Dict[str, Any]], -) -> None: - """ - Send webhook notifications to configured endpoints. - - Args: - webhook (str): Webhook ID - slug (str): Workspace slug - event (str): Event type - event_data (Optional[Dict[str, Any]]): Event data to be sent - action (str): HTTP method/action - current_site (str): Current site URL - activity (Optional[Dict[str, Any]]): Activity data - """ + self, webhook, slug, event, event_data, action, current_site, activity +): try: - webhook = Webhook.objects.get(id=webhook_id, workspace__slug=slug) + webhook = Webhook.objects.get(id=webhook, workspace__slug=slug) headers = { "Content-Type": "application/json", @@ -256,12 +297,7 @@ def webhook_send_task( ) signature = hmac_signature.hexdigest() headers["X-Plane-Signature"] = signature - except Exception as e: - log_exception(e) - logger.error(f"Failed to send webhook: {e}") - return - try: # Send the webhook event response = requests.post(webhook.url, headers=headers, json=payload, timeout=30) @@ -278,7 +314,7 @@ def webhook_send_task( response_body=str(response.text), retry_count=str(self.request.retries), ) - logger.info(f"Webhook {webhook.id} sent successfully") + except requests.RequestException as e: # Log the failed webhook request WebhookLog.objects.create( @@ -293,13 +329,12 @@ def webhook_send_task( response_body=str(e), retry_count=str(self.request.retries), ) - logger.error(f"Webhook {webhook.id} failed with error: {e}") # 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.delay( + send_webhook_deactivation_email( webhook_id=webhook.id, receiver_id=webhook.created_by_id, reason=str(e), @@ -309,50 +344,26 @@ def webhook_send_task( raise requests.RequestException() except Exception as e: + if settings.DEBUG: + print(e) log_exception(e) return @shared_task def webhook_activity( - event: str, - verb: str, - field: Optional[str], - old_value: Any, - new_value: Any, - actor_id: str | uuid.UUID, - slug: str, - current_site: str, - event_id: str | uuid.UUID, - old_identifier: Optional[str], - new_identifier: Optional[str], -) -> None: - """ - Process and send webhook notifications for various activities in the system. - - This task filters relevant webhooks based on the event type and sends notifications - to all active webhooks for the workspace. - - Args: - event (str): Type of event (project, issue, module, cycle, issue_comment) - verb (str): Action performed (created, updated, deleted) - field (Optional[str]): Name of the field that was changed - old_value (Any): Previous value of the field - new_value (Any): New value of the field - actor_id (str | uuid.UUID): ID of the user who performed the action - slug (str): Workspace slug - current_site (str): Current site URL - event_id (str | uuid.UUID): ID of the event object - old_identifier (Optional[str]): Previous identifier if any - new_identifier (Optional[str]): New identifier if any - - Returns: - None - - Note: - The function silently returns on ObjectDoesNotExist exceptions to handle - race conditions where objects might have been deleted. - """ + 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) @@ -373,7 +384,7 @@ def webhook_activity( for webhook in webhooks: webhook_send_task.delay( - webhook_id=webhook.id, + webhook=webhook.id, slug=slug, event=event, event_data=( diff --git a/silo/src/apps/slack/worker/plane-webhook-handlers/handle-issue-webhook.ts b/silo/src/apps/slack/worker/plane-webhook-handlers/handle-issue-webhook.ts index 4b164fa036..30d407a0e0 100644 --- a/silo/src/apps/slack/worker/plane-webhook-handlers/handle-issue-webhook.ts +++ b/silo/src/apps/slack/worker/plane-webhook-handlers/handle-issue-webhook.ts @@ -7,7 +7,6 @@ const ignoredFieldUpdates = ["description", "attachment"]; export const handleIssueWebhook = async (payload: PlaneWebhookPayload) => { const activities = await getActivities(payload); - // Ideally we won't hit this case, but just in case if (activities.length === 0) { return; @@ -24,14 +23,14 @@ export const handleIssueWebhook = async (payload: PlaneWebhookPayload) => { const message = createSlackBlocksFromActivity(activities); const entityData = entityConnection.entity_data as any; - const channel = entityData.channel.id; + const channel = entityData.channel || entityData.channel.id; const messageTs = entityData.message.ts; if (message.length === 0) { return; } - await slackService.sendThreadMessage(channel, messageTs, { + const response = await slackService.sendThreadMessage(channel, messageTs, { text: "Work Item Updated", blocks: message, });