Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion apps/api/plane/api/views/personal_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,20 @@ def post(self, request, slug):
serializer.save()
issue = Issue.objects.get(pk=serializer.data["id"])

# `create_issue_activity` (issue_activities_task.py) gates the
# assignee tracking branch on `requested_data["assignee_ids"]`
# being present, but the API IssueSerializer uses `assignees` as
# its write key. Without this alias the assignee IssueActivity
# row is never written, so `dispatch_lark_for_activities` has
# nothing to fan out and Lark DMs never fire for assignees on
# personal-task creation.
activity_payload = dict(payload)
if "assignees" in activity_payload and "assignee_ids" not in activity_payload:
activity_payload["assignee_ids"] = activity_payload["assignees"]

issue_activity.delay(
type="issue.activity.created",
requested_data=json.dumps(payload, cls=DjangoJSONEncoder),
requested_data=json.dumps(activity_payload, cls=DjangoJSONEncoder),
actor_id=str(request.user.id),
issue_id=str(issue.id),
project_id=str(project.id),
Expand Down
35 changes: 35 additions & 0 deletions apps/api/plane/tests/contract/api/test_personal_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""Contract tests for the personal-tasks token endpoint
(``/api/v1/workspaces/{slug}/personal-tasks/``)."""

import json
import uuid

import pytest
Expand Down Expand Up @@ -397,6 +398,40 @@ def test_patch_triggers_model_activity_for_webhook_fanout(
assert kwargs["current_instance"] is not None
assert kwargs["slug"] == workspace_with_owner.slug

@pytest.mark.django_db
def test_post_requested_data_includes_assignee_ids_for_lark_gate(
self, system_api_client, workspace_with_owner, owner_user, mocker
):
"""The real Lark fan-out path: ``create_issue_activity``
(issue_activities_task.py:581) only calls ``track_assignees``
when ``requested_data.get('assignee_ids') is not None``. Our
API serializer uses the ``assignees`` key, so the issue_activity
payload MUST mirror it under ``assignee_ids`` or the assignee
IssueActivity row is never emitted and dispatch_lark_for_activities
sees nothing to DM.
"""
spy = mocker.patch("plane.api.views.personal_task.issue_activity.delay")
response = system_api_client.post(
_personal_tasks_url(workspace_with_owner.slug),
data={
"owner": str(owner_user.id),
"name": "Notify me on Lark",
"external_source": "task-manager-v1",
"external_id": "lark-gate-1",
},
format="json",
HTTP_HOST="task.example.com",
)
assert response.status_code == status.HTTP_201_CREATED, response.content
assert spy.called, "issue_activity.delay was not invoked"
requested_data = json.loads(spy.call_args.kwargs["requested_data"])
assert "assignee_ids" in requested_data, (
"issue_activity.requested_data is missing 'assignee_ids' — "
"create_issue_activity will skip track_assignees and no Lark "
"DM will fire."
)
assert requested_data["assignee_ids"] == [str(owner_user.id)]

@pytest.mark.django_db
def test_idempotent_409_reuse_does_not_trigger_model_activity(
self, system_api_client, workspace_with_owner, owner_user, mocker
Expand Down