mirror of
https://github.com/makeplane/plane.git
synced 2026-07-09 20:10:06 +02:00
fix: Issues created or updated via REST API send no notifications or emails (#9307)
* fix: send notifications when work items are created or updated via the REST API * test: rename contract test file to test_issue_notifications.py
This commit is contained in:
@@ -479,6 +479,8 @@ class IssueListCreateAPIEndpoint(BaseAPIView):
|
||||
project_id=str(project_id),
|
||||
current_instance=None,
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=True,
|
||||
origin=base_host(request=request, is_app=True),
|
||||
)
|
||||
|
||||
# Send the model activity
|
||||
@@ -638,6 +640,8 @@ class IssueDetailAPIEndpoint(BaseAPIView):
|
||||
project_id=str(project_id),
|
||||
current_instance=current_instance,
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=True,
|
||||
origin=base_host(request=request, is_app=True),
|
||||
)
|
||||
# Send the model activity for webhook dispatch
|
||||
model_activity.delay(
|
||||
@@ -696,6 +700,8 @@ class IssueDetailAPIEndpoint(BaseAPIView):
|
||||
project_id=str(project_id),
|
||||
current_instance=None,
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=True,
|
||||
origin=base_host(request=request, is_app=True),
|
||||
)
|
||||
# Send the model activity for webhook dispatch
|
||||
model_activity.delay(
|
||||
@@ -781,6 +787,8 @@ class IssueDetailAPIEndpoint(BaseAPIView):
|
||||
project_id=str(project_id),
|
||||
current_instance=current_instance,
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=True,
|
||||
origin=base_host(request=request, is_app=True),
|
||||
)
|
||||
# Send the model activity for webhook dispatch
|
||||
model_activity.delay(
|
||||
|
||||
135
apps/api/plane/tests/contract/api/test_issue_notifications.py
Normal file
135
apps/api/plane/tests/contract/api/test_issue_notifications.py
Normal file
@@ -0,0 +1,135 @@
|
||||
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from rest_framework import status
|
||||
|
||||
from plane.db.models import Issue, Project, ProjectMember, State, User
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def project(db, workspace, create_user):
|
||||
"""Create a test project with the user as an admin member and a default state."""
|
||||
project = Project.objects.create(
|
||||
name="Test Project",
|
||||
identifier="TP",
|
||||
workspace=workspace,
|
||||
created_by=create_user,
|
||||
)
|
||||
ProjectMember.objects.create(
|
||||
project=project,
|
||||
member=create_user,
|
||||
role=20, # Admin role
|
||||
is_active=True,
|
||||
)
|
||||
# A default state is required to create work items through the API
|
||||
State.objects.create(
|
||||
name="Backlog",
|
||||
color="#000000",
|
||||
group="backlog",
|
||||
default=True,
|
||||
project=project,
|
||||
workspace=workspace,
|
||||
created_by=create_user,
|
||||
)
|
||||
return project
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_issue(db, project, workspace, create_user):
|
||||
"""Create an existing work item to update/assign in tests."""
|
||||
return Issue.objects.create(
|
||||
name="Existing Issue",
|
||||
project=project,
|
||||
workspace=workspace,
|
||||
created_by=create_user,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def assignee_user(db):
|
||||
"""Create a second user that can be assigned to a work item."""
|
||||
user = User.objects.create(
|
||||
email="assignee@plane.so",
|
||||
username="assignee-user",
|
||||
first_name="Assignee",
|
||||
last_name="User",
|
||||
)
|
||||
user.set_password("assignee-password")
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.mark.contract
|
||||
class TestIssueNotificationContract:
|
||||
"""
|
||||
Contract: creating and updating/assigning a work item through the external
|
||||
REST API (``/api/v1/...``) must trigger notifications, i.e. ``issue_activity``
|
||||
is dispatched with ``notification=True`` so subscribers and assignees are
|
||||
notified the same way the web app does. See makeplane/plane#9306.
|
||||
"""
|
||||
|
||||
def get_list_url(self, workspace_slug, project_id):
|
||||
"""Helper to build the work item list/create endpoint URL."""
|
||||
return f"/api/v1/workspaces/{workspace_slug}/projects/{project_id}/issues/"
|
||||
|
||||
def get_detail_url(self, workspace_slug, project_id, issue_id):
|
||||
"""Helper to build the work item detail endpoint URL."""
|
||||
return f"/api/v1/workspaces/{workspace_slug}/projects/{project_id}/issues/{issue_id}/"
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_issue_triggers_notification(self, api_key_client, workspace, project):
|
||||
"""Creating a work item via the external API dispatches a notifying activity."""
|
||||
url = self.get_list_url(workspace.slug, project.id)
|
||||
|
||||
with patch("plane.api.views.issue.issue_activity") as mock_issue_activity:
|
||||
response = api_key_client.post(url, {"name": "New Issue"}, format="json")
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert Issue.objects.filter(name="New Issue").exists()
|
||||
|
||||
mock_issue_activity.delay.assert_called_once()
|
||||
kwargs = mock_issue_activity.delay.call_args.kwargs
|
||||
assert kwargs["type"] == "issue.activity.created"
|
||||
assert kwargs["notification"] is True
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_issue_triggers_notification(self, api_key_client, workspace, project, create_issue):
|
||||
"""Updating a work item via the external API dispatches a notifying activity."""
|
||||
url = self.get_detail_url(workspace.slug, project.id, create_issue.id)
|
||||
|
||||
with patch("plane.api.views.issue.issue_activity") as mock_issue_activity:
|
||||
response = api_key_client.patch(url, {"name": "Renamed Issue"}, format="json")
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
create_issue.refresh_from_db()
|
||||
assert create_issue.name == "Renamed Issue"
|
||||
|
||||
mock_issue_activity.delay.assert_called_once()
|
||||
kwargs = mock_issue_activity.delay.call_args.kwargs
|
||||
assert kwargs["type"] == "issue.activity.updated"
|
||||
assert kwargs["notification"] is True
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_assign_issue_triggers_notification(self, api_key_client, workspace, project, create_issue, assignee_user):
|
||||
"""Assigning a work item via the external API dispatches a notifying activity."""
|
||||
ProjectMember.objects.create(
|
||||
project=project,
|
||||
member=assignee_user,
|
||||
role=15, # Member role
|
||||
is_active=True,
|
||||
)
|
||||
url = self.get_detail_url(workspace.slug, project.id, create_issue.id)
|
||||
|
||||
with patch("plane.api.views.issue.issue_activity") as mock_issue_activity:
|
||||
response = api_key_client.patch(url, {"assignees": [str(assignee_user.id)]}, format="json")
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
mock_issue_activity.delay.assert_called_once()
|
||||
kwargs = mock_issue_activity.delay.call_args.kwargs
|
||||
assert kwargs["type"] == "issue.activity.updated"
|
||||
assert kwargs["notification"] is True
|
||||
Reference in New Issue
Block a user