From c91c443b0861493cdc8ecff7722220e731e848b3 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Mon, 1 Jun 2026 16:24:27 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=88(backend)=20capture=20an=20event=20?= =?UTF-8?q?when=20a=20document=20is=20moved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a document is moved we want to capture an event. The position and target_document_id is added to the sent properties. --- src/backend/core/api/viewsets.py | 10 +++ .../documents/test_api_documents_move.py | 73 +++++++++++++++---- src/backend/core/utils/analytics.py | 1 + 3 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index e6eab55ef..0ee3c4269 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -1076,6 +1076,16 @@ class DocumentViewSet( defaults={"role": models.RoleChoices.OWNER}, ) + posthog_capture( + PosthogEventName.DOC_MOVED, + user, + { + "position": position, + "targeted_document_id": str(target_document_id), + }, + document=document, + ) + return drf.response.Response( {"message": "Document moved successfully."}, status=status.HTTP_200_OK ) diff --git a/src/backend/core/tests/documents/test_api_documents_move.py b/src/backend/core/tests/documents/test_api_documents_move.py index ebab88c20..aef95d78a 100644 --- a/src/backend/core/tests/documents/test_api_documents_move.py +++ b/src/backend/core/tests/documents/test_api_documents_move.py @@ -3,6 +3,7 @@ Test moving documents within the document tree via an detail action API endpoint """ import random +from unittest import mock from uuid import uuid4 from django.utils import timezone @@ -11,6 +12,7 @@ import pytest from rest_framework.test import APIClient from core import enums, factories, models +from core.utils.analytics import PosthogEventName pytestmark = pytest.mark.django_db @@ -144,10 +146,11 @@ def test_api_documents_move_authenticated_target_roles_mocked( models.DocumentAccess.objects.create(document=target, user=user, role=target_role) target_children = factories.DocumentFactory.create_batch(2, parent=target) - response = client.post( - f"/api/v1.0/documents/{document.id!s}/move/", - data={"target_document_id": str(target.id), "position": position}, - ) + with mock.patch("core.api.viewsets.posthog_capture") as mock_capture: + response = client.post( + f"/api/v1.0/documents/{document.id!s}/move/", + data={"target_document_id": str(target.id), "position": position}, + ) document.refresh_from_db() @@ -161,6 +164,14 @@ def test_api_documents_move_authenticated_target_roles_mocked( assert response.status_code == 200 assert response.json() == {"message": "Document moved successfully."} + # The move should be tracked in PostHog + mock_capture.assert_called_once_with( + PosthogEventName.DOC_MOVED, + user, + {"position": position, "targeted_document_id": str(target.id)}, + document=document, + ) + match position: case "first-child": assert list(target.get_children()) == [document, *target_children] @@ -207,6 +218,9 @@ def test_api_documents_move_authenticated_target_roles_mocked( ) assert document.is_root() is True + # No event should be tracked when the move is not permitted + mock_capture.assert_not_called() + def test_api_documents_move_authenticated_no_owner_user_and_team(): """ @@ -226,15 +240,24 @@ def test_api_documents_move_authenticated_no_owner_user_and_team(): child = factories.DocumentFactory(parent=document) target = factories.DocumentFactory() - response = client.post( - f"/api/v1.0/documents/{document.id!s}/move/", - data={"target_document_id": str(target.id), "position": "first-sibling"}, - ) + with mock.patch("core.api.viewsets.posthog_capture") as mock_capture: + response = client.post( + f"/api/v1.0/documents/{document.id!s}/move/", + data={"target_document_id": str(target.id), "position": "first-sibling"}, + ) assert response.status_code == 200 assert response.json() == {"message": "Document moved successfully."} assert list(target.get_siblings()) == [document, parent, target] + # The move should be tracked in PostHog + mock_capture.assert_called_once_with( + PosthogEventName.DOC_MOVED, + user, + {"position": "first-sibling", "targeted_document_id": str(target.id)}, + document=document, + ) + document.refresh_from_db() assert list(document.get_children()) == [child] @@ -260,15 +283,24 @@ def test_api_documents_move_authenticated_no_owner_same_user(): child = factories.DocumentFactory(parent=document) target = factories.DocumentFactory() - response = client.post( - f"/api/v1.0/documents/{document.id!s}/move/", - data={"target_document_id": str(target.id), "position": "first-sibling"}, - ) + with mock.patch("core.api.viewsets.posthog_capture") as mock_capture: + response = client.post( + f"/api/v1.0/documents/{document.id!s}/move/", + data={"target_document_id": str(target.id), "position": "first-sibling"}, + ) assert response.status_code == 200 assert response.json() == {"message": "Document moved successfully."} assert list(target.get_siblings()) == [document, parent, target] + # The move should be tracked in PostHog + mock_capture.assert_called_once_with( + PosthogEventName.DOC_MOVED, + user, + {"position": "first-sibling", "targeted_document_id": str(target.id)}, + document=document, + ) + document.refresh_from_db() assert list(document.get_children()) == [child] assert document.accesses.count() == 2 @@ -293,15 +325,24 @@ def test_api_documents_move_authenticated_no_owner_same_team(): child = factories.DocumentFactory(parent=document) target = factories.DocumentFactory() - response = client.post( - f"/api/v1.0/documents/{document.id!s}/move/", - data={"target_document_id": str(target.id), "position": "first-sibling"}, - ) + with mock.patch("core.api.viewsets.posthog_capture") as mock_capture: + response = client.post( + f"/api/v1.0/documents/{document.id!s}/move/", + data={"target_document_id": str(target.id), "position": "first-sibling"}, + ) assert response.status_code == 200 assert response.json() == {"message": "Document moved successfully."} assert list(target.get_siblings()) == [document, parent, target] + # The move should be tracked in PostHog + mock_capture.assert_called_once_with( + PosthogEventName.DOC_MOVED, + user, + {"position": "first-sibling", "targeted_document_id": str(target.id)}, + document=document, + ) + document.refresh_from_db() assert list(document.get_children()) == [child] diff --git a/src/backend/core/utils/analytics.py b/src/backend/core/utils/analytics.py index 335ac267b..b65460eb3 100644 --- a/src/backend/core/utils/analytics.py +++ b/src/backend/core/utils/analytics.py @@ -21,6 +21,7 @@ class PosthogEventName(StrEnum): DOC_IMPORTED = "doc_imported" DOC_FAVORITED = "doc_favorited" DOC_AI_ACTION = "doc_ai_action" + DOC_MOVED = "doc_moved" # DocumentAccess DOC_ACCESS_CREATED = "doc_access_created"