From e36366b293f557f7973c7cbfd8916873057f6015 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Tue, 10 Mar 2026 16:44:59 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(backend)=20create=20a=20link=5Ftra?= =?UTF-8?q?ce=20record=20for=20onboarded=20documents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a user is created, we created accesses to a list of onboarding documents. Doing this have side effect on the proximity search feature. Instead of creating access, we should create link_reach --- CHANGELOG.md | 4 + src/backend/core/models.py | 19 +++-- .../core/tests/test_models_invitations.py | 2 +- src/backend/core/tests/test_models_users.py | 74 +++++++++++-------- 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7682efc..b249b4fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to - ♿(frontend) localize LaGaufre label fallback in Docs #1979 +### Fixed + +- 🐛(backend) create a link_trace record for on-boarding documents + ## [v4.7.0] - 2026-03-09 ### Added diff --git a/src/backend/core/models.py b/src/backend/core/models.py index ac9ae2c6..322b8c42 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -227,11 +227,11 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin): def _handle_onboarding_documents_access(self): """ If the user is new and there are documents configured to be given to new users, - give access to these documents and pin them as favorites for the user. + create link traces to these documents and pin them as favorites for the user. """ if settings.USER_ONBOARDING_DOCUMENTS: onboarding_document_ids = set(settings.USER_ONBOARDING_DOCUMENTS) - onboarding_accesses = [] + onboarding_link_traces = [] favorite_documents = [] for document_id in onboarding_document_ids: try: @@ -243,16 +243,20 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin): ) continue - onboarding_accesses.append( - DocumentAccess( - user=self, document=document, role=RoleChoices.READER + if document.link_reach == LinkReachChoices.RESTRICTED: + logger.warning( + "Onboarding on a restricted document is not allowed. Must be public or " + "connected. Restricted document: %s", + document_id, ) - ) + continue + + onboarding_link_traces.append(LinkTrace(user=self, document=document)) favorite_documents.append( DocumentFavorite(user=self, document_id=document_id) ) - DocumentAccess.objects.bulk_create(onboarding_accesses) + LinkTrace.objects.bulk_create(onboarding_link_traces) DocumentFavorite.objects.bulk_create(favorite_documents) def _duplicate_onboarding_sandbox_document(self): @@ -264,7 +268,6 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin): sandbox_id = settings.USER_ONBOARDING_SANDBOX_DOCUMENT try: template_document = Document.objects.get(id=sandbox_id) - except Document.DoesNotExist: logger.warning( "Onboarding sandbox document with id %s does not exist. Skipping.", diff --git a/src/backend/core/tests/test_models_invitations.py b/src/backend/core/tests/test_models_invitations.py index 4bd538a2..56cf546c 100644 --- a/src/backend/core/tests/test_models_invitations.py +++ b/src/backend/core/tests/test_models_invitations.py @@ -79,7 +79,7 @@ def test_models_invitations_is_expired(): assert expired_invitation.is_expired is True -def test_models_invitationd_new_userd_convert_invitations_to_accesses(): +def test_models_invitations_new_user_convert_invitations_to_accesses(): """ Upon creating a new user, invitations linked to the email should be converted to accesses and then deleted. diff --git a/src/backend/core/tests/test_models_users.py b/src/backend/core/tests/test_models_users.py index 33cac6bd..f0f6b01c 100644 --- a/src/backend/core/tests/test_models_users.py +++ b/src/backend/core/tests/test_models_users.py @@ -89,24 +89,19 @@ def test_models_users_handle_onboarding_documents_access_empty_setting(): assert models.DocumentAccess.objects.filter(user=user).count() == 0 -def test_models_users_handle_onboarding_documents_access_with_single_document(): +def test_models_users_handle_onboarding_document_link_trace_with_single_document(): """ When USER_ONBOARDING_DOCUMENTS has a valid document ID, - an access should be created for the new user with the READER role. + a LinkTrace should be created for the new user. The document should be pinned as a favorite for the user. """ - document = factories.DocumentFactory() + document = factories.DocumentFactory(link_reach=models.LinkReachChoices.PUBLIC) with override_settings(USER_ONBOARDING_DOCUMENTS=[str(document.id)]): user = factories.UserFactory() - assert ( - models.DocumentAccess.objects.filter(user=user, document=document).count() == 1 - ) - - access = models.DocumentAccess.objects.get(user=user, document=document) - assert access.role == models.RoleChoices.READER + assert models.LinkTrace.objects.filter(user=user, document=document).count() == 1 user_favorites = models.DocumentFavorite.objects.filter(user=user) assert user_favorites.count() == 1 @@ -121,9 +116,15 @@ def test_models_users_handle_onboarding_documents_access_with_multiple_documents All accesses should have the READER role. All documents should be pinned as favorites for the user. """ - document1 = factories.DocumentFactory(title="Document 1") - document2 = factories.DocumentFactory(title="Document 2") - document3 = factories.DocumentFactory(title="Document 3") + document1 = factories.DocumentFactory( + title="Document 1", link_reach=models.LinkReachChoices.PUBLIC + ) + document2 = factories.DocumentFactory( + title="Document 2", link_reach=models.LinkReachChoices.AUTHENTICATED + ) + document3 = factories.DocumentFactory( + title="Document 3", link_reach=models.LinkReachChoices.PUBLIC + ) with override_settings( USER_ONBOARDING_DOCUMENTS=[ @@ -134,15 +135,12 @@ def test_models_users_handle_onboarding_documents_access_with_multiple_documents ): user = factories.UserFactory() - user_accesses = models.DocumentAccess.objects.filter(user=user) - assert user_accesses.count() == 3 + link_traces = models.LinkTrace.objects.filter(user=user) + assert link_traces.count() == 3 - assert models.DocumentAccess.objects.filter(user=user, document=document1).exists() - assert models.DocumentAccess.objects.filter(user=user, document=document2).exists() - assert models.DocumentAccess.objects.filter(user=user, document=document3).exists() - - for access in user_accesses: - assert access.role == models.RoleChoices.READER + assert models.LinkTrace.objects.filter(user=user, document=document1).exists() + assert models.LinkTrace.objects.filter(user=user, document=document2).exists() + assert models.LinkTrace.objects.filter(user=user, document=document3).exists() user_favorites = models.DocumentFavorite.objects.filter(user=user) assert user_favorites.count() == 3 @@ -166,7 +164,7 @@ def test_models_users_handle_onboarding_documents_access_with_invalid_document_i call_args = mock_logger.warning.call_args assert "Onboarding document with id" in call_args[0][0] - assert models.DocumentAccess.objects.filter(user=user).count() == 0 + assert models.LinkTrace.objects.filter(user=user).count() == 0 def test_models_users_handle_onboarding_documents_access_duplicate_prevention(): @@ -174,16 +172,27 @@ def test_models_users_handle_onboarding_documents_access_duplicate_prevention(): If the same document is listed multiple times in USER_ONBOARDING_DOCUMENTS, it should only create one access (or handle duplicates gracefully). """ - document = factories.DocumentFactory() + document = factories.DocumentFactory(link_reach=models.LinkReachChoices.PUBLIC) with override_settings( USER_ONBOARDING_DOCUMENTS=[str(document.id), str(document.id)] ): user = factories.UserFactory() - user_accesses = models.DocumentAccess.objects.filter(user=user, document=document) + link_traces = models.LinkTrace.objects.filter(user=user, document=document) - assert user_accesses.count() >= 1 + assert link_traces.count() == 1 + + +def test_models_users_handle_onboarding_documents_on_restricted_document_is_not_allowed(): + """On-boarding document can be used when restricted""" + + document = factories.DocumentFactory(link_reach=models.LinkReachChoices.RESTRICTED) + settings.USER_ONBOARDING_DOCUMENTS = [str(document.id)] + + user = factories.UserFactory() + + assert not models.LinkTrace.objects.filter(user=user, document=document).exists() @override_settings(USER_ONBOARDING_SANDBOX_DOCUMENT=None) @@ -272,7 +281,9 @@ def test_models_users_duplicate_onboarding_sandbox_document_integration_with_oth Verify that sandbox creation works alongside other onboarding methods. """ template_document = factories.DocumentFactory(title="Getting started with Docs") - onboarding_doc = factories.DocumentFactory(title="Onboarding Document") + onboarding_doc = factories.DocumentFactory( + title="Onboarding Document", link_reach=models.LinkReachChoices.AUTHENTICATED + ) with override_settings( USER_ONBOARDING_SANDBOX_DOCUMENT=str(template_document.id), @@ -284,11 +295,10 @@ def test_models_users_duplicate_onboarding_sandbox_document_integration_with_oth creator=user, title="Getting started with Docs" ).first() - user_accesses = models.DocumentAccess.objects.filter(user=user) - assert user_accesses.count() == 2 + assert models.DocumentAccess.objects.filter(user=user).count() == 1 + assert models.LinkTrace.objects.filter(user=user).count() == 1 - sandbox_access = user_accesses.get(document=sandbox_doc) - onboarding_access = user_accesses.get(document=onboarding_doc) - - assert sandbox_access.role == models.RoleChoices.OWNER - assert onboarding_access.role == models.RoleChoices.READER + assert models.DocumentAccess.objects.filter( + document=sandbox_doc, user=user, role=models.RoleChoices.OWNER + ).exists() + assert models.LinkTrace.objects.filter(document=onboarding_doc, user=user).exists()