diff --git a/apps/api/plane/app/views/view/base.py b/apps/api/plane/app/views/view/base.py index c56ea7657c..863b165eb0 100644 --- a/apps/api/plane/app/views/view/base.py +++ b/apps/api/plane/app/views/view/base.py @@ -129,6 +129,18 @@ class WorkspaceViewViewSet(BaseViewSet): # membership in the workspace could therefore read any global view in it # by id. issue_view = self.get_queryset().filter(pk=pk).first() + + # get_queryset() is scoped to the URL workspace, so None means either no + # such view or one belonging to a workspace this URL does not name. + # Serializing None yields a hollow object — every field null or empty — + # returned as 200, and enqueues a recent-visit for an entity that does + # not exist. Answer 404, as the other retrieve endpoints do. + if issue_view is None: + return Response( + {"error": "The required object does not exist."}, + status=status.HTTP_404_NOT_FOUND, + ) + serializer = IssueViewSerializer(issue_view) recent_visited_task.delay( slug=slug, diff --git a/apps/api/plane/tests/contract/app/test_undecorated_action_authz_app.py b/apps/api/plane/tests/contract/app/test_undecorated_action_authz_app.py index 8f5b4495de..3f40932707 100644 --- a/apps/api/plane/tests/contract/app/test_undecorated_action_authz_app.py +++ b/apps/api/plane/tests/contract/app/test_undecorated_action_authz_app.py @@ -249,6 +249,20 @@ class TestWorkspaceViewRetrieveRequiresMembership: ) assert str(response.data["id"]) == str(workspace_view.id) + @pytest.mark.django_db + def test_missing_view_is_a_404_not_an_empty_200(self, session_client, workspace): + """A member asking for a view id that does not exist must get 404. + + get_queryset() is scoped to the URL workspace, so this also covers a real + view id belonging to a different workspace. + """ + url = WORKSPACE_VIEW_DETAIL_URL.format(slug=workspace.slug, pk=uuid4()) + response = session_client.get(url) + + assert response.status_code == status.HTTP_404_NOT_FOUND, ( + f"Got {response.status_code}: {getattr(response, 'data', None)!r}" + ) + @pytest.mark.django_db def test_workspace_guest_can_read_a_global_view(self, workspace, workspace_view): """Positive control: the role set matches list(), which permits guests."""