🚩(back) use existing no websocket feature flag

An already existing feature flag
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY was used bu the frontend
application to disable or not the edition for a user not connected to
the websocket. We want to reuse it in the backend application to disable
or not the no websocket feature.
This commit is contained in:
Manuel Raynaud
2025-07-04 10:56:24 +02:00
parent 118804e810
commit 9a8f952210
5 changed files with 90 additions and 11 deletions

View File

@@ -60,6 +60,7 @@ COLLABORATION_API_URL=http://y-provider-development:4444/collaboration/api/
COLLABORATION_BACKEND_BASE_URL=http://app-dev:8000
COLLABORATION_SERVER_ORIGIN=http://localhost:3000
COLLABORATION_SERVER_SECRET=my-secret
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY=true
COLLABORATION_WS_URL=ws://localhost:4444/collaboration/ws/
DJANGO_SERVER_TO_SERVER_API_TOKENS=server-api-token

View File

@@ -32,7 +32,6 @@ from rest_framework import filters, status, viewsets
from rest_framework import response as drf_response
from rest_framework.permissions import AllowAny
from rest_framework.throttling import UserRateThrottle
from sentry_sdk import capture_exception
from core import authentication, enums, models
from core.services.ai_services import AIService
@@ -682,7 +681,10 @@ class DocumentViewSet(
def perform_update(self, serializer):
"""Check rules about collaboration."""
if serializer.validated_data.get("websocket", False):
if (
serializer.validated_data.get("websocket", False)
or not settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY
):
return super().perform_update(serializer)
if self._can_user_edit_document(serializer.instance.id, set_cache=True):
@@ -701,10 +703,14 @@ class DocumentViewSet(
"""Check if the current user can edit the document."""
document = self.get_object()
return drf.response.Response(
{"can_edit": self._can_user_edit_document(document.id)}
can_edit = (
True
if not settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY
else self._can_user_edit_document(document.id)
)
return drf.response.Response({"can_edit": can_edit})
@drf.decorators.action(
detail=False,
methods=["get"],

View File

@@ -11,16 +11,38 @@ from core import factories
pytestmark = pytest.mark.django_db
def test_api_documents_can_edit_anonymous():
"""Anonymous users can not edit documents."""
document = factories.DocumentFactory()
@responses.activate
@pytest.mark.parametrize("ws_not_connected_ready_only", [True, False])
@pytest.mark.parametrize("role", ["editor", "reader"])
def test_api_documents_can_edit_anonymous(settings, ws_not_connected_ready_only, role):
"""Anonymous users can edit documents when link_role is editor."""
document = factories.DocumentFactory(link_reach="public", link_role=role)
client = APIClient()
session_key = client.session.session_key
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = ws_not_connected_ready_only
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
)
ws_resp = responses.get(endpoint_url, json={"count": 0, "exists": False})
response = client.get(f"/api/v1.0/documents/{document.id!s}/can-edit/")
assert response.status_code == 401
if role == "reader":
assert response.status_code == 401
else:
assert response.status_code == 200
assert response.json() == {"can_edit": True}
assert ws_resp.call_count == (1 if ws_not_connected_ready_only else 0)
@responses.activate
def test_api_documents_can_edit_authenticated_no_websocket(settings):
@pytest.mark.parametrize("ws_not_connected_ready_only", [True, False])
def test_api_documents_can_edit_authenticated_no_websocket(
settings, ws_not_connected_ready_only
):
"""
A user not connected to the websocket and no other user have already updated the document,
the document can be updated.
@@ -34,6 +56,7 @@ def test_api_documents_can_edit_authenticated_no_websocket(settings):
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = ws_not_connected_ready_only
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -49,7 +72,7 @@ def test_api_documents_can_edit_authenticated_no_websocket(settings):
assert response.status_code == 200
assert response.json() == {"can_edit": True}
assert ws_resp.call_count == 1
assert ws_resp.call_count == (1 if ws_not_connected_ready_only else 0)
@responses.activate
@@ -69,6 +92,7 @@ def test_api_documents_can_edit_authenticated_no_websocket_user_already_editing(
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -103,6 +127,7 @@ def test_api_documents_can_edit_no_websocket_other_user_connected_to_websocket(
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -134,6 +159,7 @@ def test_api_documents_can_edit_user_connected_to_websocket(settings):
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -168,6 +194,7 @@ def test_api_documents_can_edit_websocket_server_unreachable_fallback_to_no_webs
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -202,6 +229,7 @@ def test_api_documents_can_edit_websocket_server_unreachable_fallback_to_no_webs
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"

View File

@@ -313,6 +313,7 @@ def test_api_documents_update_authenticated_no_websocket(settings):
new_document_values["websocket"] = False
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -352,6 +353,7 @@ def test_api_documents_update_authenticated_no_websocket_user_already_editing(se
new_document_values["websocket"] = False
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -390,6 +392,7 @@ def test_api_documents_update_no_websocket_other_user_connected_to_websocket(set
new_document_values["websocket"] = False
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -427,6 +430,7 @@ def test_api_documents_update_user_connected_to_websocket(settings):
new_document_values["websocket"] = False
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -466,6 +470,7 @@ def test_api_documents_update_websocket_server_unreachable_fallback_to_no_websoc
new_document_values["websocket"] = False
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -506,6 +511,7 @@ def test_api_documents_update_websocket_server_unreachable_fallback_to_no_websoc
new_document_values["websocket"] = False
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = True
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
@@ -562,6 +568,44 @@ def test_api_documents_update_force_websocket_param_to_true(settings):
assert ws_resp.call_count == 0
@responses.activate
def test_api_documents_update_feature_flag_disabled(settings):
"""
When the feature flag is disabled, the document should be updated without any check.
"""
user = factories.UserFactory(with_owned_document=True)
client = APIClient()
client.force_login(user)
session_key = client.session.session_key
document = factories.DocumentFactory(users=[(user, "editor")])
new_document_values = serializers.DocumentSerializer(
instance=factories.DocumentFactory()
).data
new_document_values["websocket"] = False
settings.COLLABORATION_API_URL = "http://example.com/"
settings.COLLABORATION_SERVER_SECRET = "secret-token"
settings.COLLABORATION_WS_NOT_CONNECTED_READY_ONLY = False
endpoint_url = (
f"{settings.COLLABORATION_API_URL}get-connections/"
f"?room={document.id}&sessionKey={session_key}"
)
ws_resp = responses.get(endpoint_url, status=500)
assert cache.get(f"docs:no-websocket:{document.id}") is None
response = client.put(
f"/api/v1.0/documents/{document.id!s}/",
new_document_values,
format="json",
)
assert response.status_code == 200
assert cache.get(f"docs:no-websocket:{document.id}") is None
assert ws_resp.call_count == 0
@pytest.mark.parametrize("via", VIA)
def test_api_documents_update_administrator_or_owner_of_another(via, mock_user_teams):
"""

View File

@@ -6,7 +6,7 @@ export const CONFIG = {
AI_FEATURE_ENABLED: true,
CRISP_WEBSITE_ID: null,
COLLABORATION_WS_URL: 'ws://localhost:4444/collaboration/ws/',
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: false,
COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
ENVIRONMENT: 'development',
FRONTEND_CSS_URL: null,
FRONTEND_HOMEPAGE_FEATURE_ENABLED: true,