Explorar o código

Synchronize Event Namespace (#1370)

Elijah Ahianyo hai 1 ano
pai
achega
4505279d5d
Modificáronse 4 ficheiros con 51 adicións e 3 borrados
  1. 8 1
      reflex/app.py
  2. 15 0
      reflex/config.py
  3. 2 1
      reflex/constants.py
  4. 26 1
      tests/test_config.py

+ 8 - 1
reflex/app.py

@@ -97,6 +97,9 @@ class App(Base):
         Args:
             *args: Args to initialize the app with.
             **kwargs: Kwargs to initialize the app with.
+
+        Raises:
+            ValueError: If the event namespace is not provided in the config.
         """
         super().__init__(*args, **kwargs)
 
@@ -128,9 +131,13 @@ class App(Base):
 
         # Create the socket app. Note event endpoint constant replaces the default 'socket.io' path.
         self.socket_app = ASGIApp(self.sio, socketio_path="")
+        namespace = config.get_event_namespace()
+
+        if not namespace:
+            raise ValueError("event namespace must be provided in the config.")
 
         # Create the event namespace and attach the main app. Not related to any paths.
-        self.event_namespace = EventNamespace("/event", self)
+        self.event_namespace = EventNamespace(namespace, self)
 
         # Register the event namespace with the socket.
         self.sio.register_namespace(self.event_namespace)

+ 15 - 0
reflex/config.py

@@ -206,6 +206,9 @@ class Config(Base):
     # Whether to enable or disable nextJS gzip compression.
     next_compression: bool = True
 
+    # The event namespace for ws connection
+    event_namespace: Optional[str] = constants.EVENT_NAMESPACE
+
     def __init__(self, *args, **kwargs):
         """Initialize the config values.
 
@@ -251,6 +254,18 @@ class Config(Base):
             except AttributeError:
                 pass
 
+    def get_event_namespace(self) -> Optional[str]:
+        """Get the websocket event namespace.
+
+        Returns:
+            The namespace for websocket.
+        """
+        if self.event_namespace:
+            return f'/{self.event_namespace.strip("/")}'
+
+        event_url = constants.Endpoint.EVENT.get_url()
+        return urllib.parse.urlsplit(event_url).path
+
 
 def get_config() -> Config:
     """Get the app config.

+ 2 - 1
reflex/constants.py

@@ -197,7 +197,8 @@ OLD_CONFIG_FILE = f"pcconfig{PY_EXT}"
 PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
 # Token expiration time in seconds.
 TOKEN_EXPIRATION = 60 * 60
-
+# The event namespace for websocket
+EVENT_NAMESPACE = get_value("EVENT_NAMESPACE")
 
 # Env modes
 class Env(str, Enum):

+ 26 - 1
tests/test_config.py

@@ -5,7 +5,7 @@ import pytest
 
 import reflex as rx
 from reflex import constants
-from reflex.config import DBConfig
+from reflex.config import DBConfig, get_config
 from reflex.constants import get_value
 
 
@@ -133,3 +133,28 @@ def test_get_value(monkeypatch, key, value, expected_value_type_in_config):
     casted_value = get_value(key, type_=expected_value_type_in_config)
 
     assert isinstance(casted_value, expected_value_type_in_config)
+
+
+@pytest.mark.parametrize(
+    "kwargs, expected",
+    [
+        ({"app_name": "test_app", "api_url": "http://example.com"}, "/event"),
+        ({"app_name": "test_app", "api_url": "http://example.com/api"}, "/api/event"),
+        ({"app_name": "test_app", "event_namespace": "/event"}, "/event"),
+        ({"app_name": "test_app", "event_namespace": "event"}, "/event"),
+        ({"app_name": "test_app", "event_namespace": "event/"}, "/event"),
+    ],
+)
+def test_event_namespace(mocker, kwargs, expected):
+    """Test the event namespace.
+
+    Args:
+        mocker: The pytest mock object.
+        kwargs: The Config kwargs.
+        expected: Expected namespace
+    """
+    conf = rx.Config(**kwargs)
+    mocker.patch("reflex.config.get_config", return_value=conf)
+
+    config = get_config()
+    assert config.get_event_namespace() == expected