From fc8afa68b896ddb1cab195fe1bf02bd7413be908 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 20 Sep 2021 21:58:47 -0700
Subject: [PATCH 01/39] propogate websocket down
---
README.md | 6 ++----
src/django_idom/websocket_consumer.py | 2 +-
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index aa57be42..6927c121 100644
--- a/README.md
+++ b/README.md
@@ -147,8 +147,8 @@ ultimately be referenced by name in `your-template.html`. `your-template.html`.
import idom
@idom.component
-def Hello(greeting_recipient): # component names are camelcase by convention
- return Header(f"Hello {greeting_recipient}!")
+def Hello(websocket, greeting_recipient): # component names are camelcase by convention
+ return idom.html.header(f"Hello {greeting_recipient}!")
```
## `example_app/templates/your-template.html`
@@ -165,8 +165,6 @@ idom_component module_name.ComponentName param_1="something" param_2="something-
In context this will look a bit like the following...
```jinja
-
-{% load static %}
{% load idom %}
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 31f1aa38..2071fee4 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -48,7 +48,7 @@ async def _run_dispatch_loop(self):
component_kwargs = json.loads(query_dict.get("kwargs", "{}"))
try:
- component_instance = component_constructor(**component_kwargs)
+ component_instance = component_constructor(self, **component_kwargs)
except Exception:
_logger.exception(
f"Failed to construct component {component_constructor} "
From 4ec52f572571259865ba23f1cb216bb68830c782 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 20 Sep 2021 22:33:20 -0700
Subject: [PATCH 02/39] update tests
---
tests/test_app/components.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/test_app/components.py b/tests/test_app/components.py
index f242b9f1..d3452efb 100644
--- a/tests/test_app/components.py
+++ b/tests/test_app/components.py
@@ -2,12 +2,12 @@
@idom.component
-def HelloWorld():
+def HelloWorld(websocket):
return idom.html.h1({"id": "hello-world"}, "Hello World!")
@idom.component
-def Button():
+def Button(websocket):
count, set_count = idom.hooks.use_state(0)
return idom.html.div(
idom.html.button(
@@ -22,7 +22,7 @@ def Button():
@idom.component
-def ParametrizedComponent(x, y):
+def ParametrizedComponent(websocket, x, y):
total = x + y
return idom.html.h1({"id": "parametrized-component", "data-value": total}, total)
@@ -32,5 +32,5 @@ def ParametrizedComponent(x, y):
@idom.component
-def SimpleBarChart():
+def SimpleBarChart(websocket):
return VictoryBar()
From 839e8d7d8d105aad0b6cd62107f9daf07bce51e4 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 00:24:26 -0700
Subject: [PATCH 03/39] add websocket login
---
README.md | 9 ++++++---
src/django_idom/websocket_consumer.py | 13 +++++++++++++
tests/test_app/asgi.py | 3 ++-
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 6927c121..d3787c14 100644
--- a/README.md
+++ b/README.md
@@ -75,14 +75,17 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_app.settings")
# Fetch ASGI application before importing dependencies that require ORM models.
http_asgi_app = get_asgi_application()
+from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
application = ProtocolTypeRouter(
{
"http": http_asgi_app,
- "websocket": URLRouter(
- # add a path for IDOM's websocket
- [IDOM_WEBSOCKET_PATH]
+ "websocket": AuthMiddlewareStack(
+ URLRouter(
+ # add a path for IDOM's websocket
+ [IDOM_WEBSOCKET_PATH]
+ )
),
}
)
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 2071fee4..1cd77212 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -5,6 +5,8 @@
from typing import Any
from urllib.parse import parse_qsl
+from channels.auth import login
+from channels.db import database_sync_to_async as convert_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from idom.core.dispatcher import dispatch_single_view
from idom.core.layout import Layout, LayoutEvent
@@ -23,6 +25,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
async def connect(self) -> None:
await super().connect()
+
+ user = self.scope.get("user")
+ if user and user.is_authenticated:
+ try:
+ await login(self.scope, user)
+ await convert_to_async(self.scope["session"].save)()
+ except Exception:
+ _logger.exception("IDOM websocket authentication has failed!")
+ elif user == None:
+ _logger.warning(f"IDOM websocket is missing AuthMiddlewareStack!")
+
self._idom_dispatcher_future = asyncio.ensure_future(self._run_dispatch_loop())
async def disconnect(self, code: int) -> None:
diff --git a/tests/test_app/asgi.py b/tests/test_app/asgi.py
index dcb8112c..a4e34e6d 100644
--- a/tests/test_app/asgi.py
+++ b/tests/test_app/asgi.py
@@ -19,12 +19,13 @@
# Fetch ASGI application before importing dependencies that require ORM models.
http_asgi_app = get_asgi_application()
+from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter # noqa: E402
application = ProtocolTypeRouter(
{
"http": http_asgi_app,
- "websocket": URLRouter([IDOM_WEBSOCKET_PATH]),
+ "websocket": AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH])),
}
)
From c1b443eb118dfe616f164b0008f7a62c8708d325 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 00:24:35 -0700
Subject: [PATCH 04/39] add admin view to test app
---
tests/test_app/urls.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests/test_app/urls.py b/tests/test_app/urls.py
index cd4f3da0..1ea0c99b 100644
--- a/tests/test_app/urls.py
+++ b/tests/test_app/urls.py
@@ -17,6 +17,7 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
+from django.contrib import admin
from django.urls import path
from django_idom import IDOM_WEB_MODULES_PATH
@@ -24,4 +25,8 @@
from .views import base_template
-urlpatterns = [path("", base_template), IDOM_WEB_MODULES_PATH]
+urlpatterns = [
+ path("", base_template),
+ IDOM_WEB_MODULES_PATH,
+ path("admin/", admin.site.urls),
+]
From 79ab0bc383785e0aae8e84705456425015c3c817 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 00:29:08 -0700
Subject: [PATCH 05/39] fix styling error
---
src/django_idom/websocket_consumer.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 1cd77212..39aaac6a 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -33,7 +33,7 @@ async def connect(self) -> None:
await convert_to_async(self.scope["session"].save)()
except Exception:
_logger.exception("IDOM websocket authentication has failed!")
- elif user == None:
+ elif user is None:
_logger.warning(f"IDOM websocket is missing AuthMiddlewareStack!")
self._idom_dispatcher_future = asyncio.ensure_future(self._run_dispatch_loop())
From bf6783a87e3868d190d748ec62d07e31c52f6195 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 00:32:02 -0700
Subject: [PATCH 06/39] more styling
---
src/django_idom/websocket_consumer.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 39aaac6a..7b585a26 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -34,7 +34,7 @@ async def connect(self) -> None:
except Exception:
_logger.exception("IDOM websocket authentication has failed!")
elif user is None:
- _logger.warning(f"IDOM websocket is missing AuthMiddlewareStack!")
+ _logger.warning("IDOM websocket is missing AuthMiddlewareStack!")
self._idom_dispatcher_future = asyncio.ensure_future(self._run_dispatch_loop())
From dda8b58d78cc68b1d60ffbe16de9ad4f455ee993 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 00:35:27 -0700
Subject: [PATCH 07/39] last styling error i swear
---
tests/test_app/asgi.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_app/asgi.py b/tests/test_app/asgi.py
index a4e34e6d..740ddb4f 100644
--- a/tests/test_app/asgi.py
+++ b/tests/test_app/asgi.py
@@ -19,7 +19,7 @@
# Fetch ASGI application before importing dependencies that require ORM models.
http_asgi_app = get_asgi_application()
-from channels.auth import AuthMiddlewareStack
+from channels.auth import AuthMiddlewareStack # noqa: E402
from channels.routing import ProtocolTypeRouter, URLRouter # noqa: E402
From 05a3d22ffbe71bea121c36860cf6bb2dd26abe0c Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:11:13 -0700
Subject: [PATCH 08/39] fix spelling error
---
src/django_idom/websocket_consumer.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 7b585a26..31826cfb 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -54,7 +54,7 @@ async def _run_dispatch_loop(self):
try:
component_constructor = IDOM_REGISTERED_COMPONENTS[view_id]
except KeyError:
- _logger.warning(f"Uknown IDOM view ID {view_id!r}")
+ _logger.warning(f"Unknown IDOM view ID {view_id!r}")
return
query_dict = dict(parse_qsl(self.scope["query_string"].decode()))
From ef9895b53839677a2b94b9d518e2f85e0808676e Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:14:08 -0700
Subject: [PATCH 09/39] remove unneeded sys import check
---
src/django_idom/templatetags/idom.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py
index ba44878c..30d7a290 100644
--- a/src/django_idom/templatetags/idom.py
+++ b/src/django_idom/templatetags/idom.py
@@ -36,9 +36,6 @@ def idom_component(_component_id_, **kwargs):
def _register_component(full_component_name: str) -> None:
module_name, component_name = full_component_name.rsplit(".", 1)
- if module_name in sys.modules:
- module = sys.modules[module_name]
- else:
try:
module = import_module(module_name)
except ImportError as error:
From a4d439c01233c7a5278857497dd7d1152fa85665 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:20:21 -0700
Subject: [PATCH 10/39] reconnecting WS (broken)
---
src/django_idom/config.py | 1 +
src/django_idom/templates/idom/component.html | 3 +-
src/django_idom/templatetags/idom.py | 14 ++++---
src/js/src/index.js | 38 +++++++++++--------
4 files changed, 33 insertions(+), 23 deletions(-)
diff --git a/src/django_idom/config.py b/src/django_idom/config.py
index 9b2d9f0a..bde409ef 100644
--- a/src/django_idom/config.py
+++ b/src/django_idom/config.py
@@ -10,6 +10,7 @@
IDOM_BASE_URL = getattr(settings, "IDOM_BASE_URL", "_idom/")
IDOM_WEBSOCKET_URL = IDOM_BASE_URL + "websocket/"
IDOM_WEB_MODULES_URL = IDOM_BASE_URL + "web_module/"
+IDOM_WS_RECONNECT = getattr(settings, "IDOM_WS_RECONNECT", True)
_CACHES = getattr(settings, "CACHES", {})
if _CACHES:
diff --git a/src/django_idom/templates/idom/component.html b/src/django_idom/templates/idom/component.html
index 84ec6963..b1f57757 100644
--- a/src/django_idom/templates/idom/component.html
+++ b/src/django_idom/templates/idom/component.html
@@ -7,7 +7,8 @@
mountPoint,
"{{ idom_websocket_url }}",
"{{ idom_web_modules_url }}",
+ parseInt("{% if idom_ws_reconnect %}604800{% else %}0{% endif %}"),
"{{ idom_component_id }}",
"{{ idom_component_params }}"
);
-
+
\ No newline at end of file
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py
index 30d7a290..d53e4416 100644
--- a/src/django_idom/templatetags/idom.py
+++ b/src/django_idom/templatetags/idom.py
@@ -10,6 +10,7 @@
IDOM_REGISTERED_COMPONENTS,
IDOM_WEB_MODULES_URL,
IDOM_WEBSOCKET_URL,
+ IDOM_WS_RECONNECT,
)
@@ -27,6 +28,7 @@ def idom_component(_component_id_, **kwargs):
"class": class_,
"idom_websocket_url": IDOM_WEBSOCKET_URL,
"idom_web_modules_url": IDOM_WEB_MODULES_URL,
+ "idom_ws_reconnect": IDOM_WS_RECONNECT,
"idom_mount_uuid": uuid4().hex,
"idom_component_id": _component_id_,
"idom_component_params": urlencode({"kwargs": json_kwargs}),
@@ -36,12 +38,12 @@ def idom_component(_component_id_, **kwargs):
def _register_component(full_component_name: str) -> None:
module_name, component_name = full_component_name.rsplit(".", 1)
- try:
- module = import_module(module_name)
- except ImportError as error:
- raise RuntimeError(
- f"Failed to import {module_name!r} while loading {component_name!r}"
- ) from error
+ try:
+ module = import_module(module_name)
+ except ImportError as error:
+ raise RuntimeError(
+ f"Failed to import {module_name!r} while loading {component_name!r}"
+ ) from error
try:
component = getattr(module, component_name)
diff --git a/src/js/src/index.js b/src/js/src/index.js
index bddf2a55..4253bf0b 100644
--- a/src/js/src/index.js
+++ b/src/js/src/index.js
@@ -4,28 +4,34 @@ import { mountLayoutWithWebSocket } from "idom-client-react";
let LOCATION = window.location;
let WS_PROTOCOL = "";
if (LOCATION.protocol == "https:") {
- WS_PROTOCOL = "wss://";
+ WS_PROTOCOL = "wss://";
} else {
- WS_PROTOCOL = "ws://";
+ WS_PROTOCOL = "ws://";
}
let WS_ENDPOINT_URL = WS_PROTOCOL + LOCATION.host + "/";
export function mountViewToElement(
- mountPoint,
- idomWebsocketUrl,
- idomWebModulesUrl,
- viewId,
- queryParams
+ mountPoint,
+ idomWebsocketUrl,
+ idomWebModulesUrl,
+ maxReconnectTimeout,
+ viewId,
+ queryParams
) {
- const fullWebsocketUrl =
- WS_ENDPOINT_URL + idomWebsocketUrl + viewId + "/?" + queryParams;
+ const fullWebsocketUrl =
+ WS_ENDPOINT_URL + idomWebsocketUrl + viewId + "/?" + queryParams;
- const fullWebModulesUrl = LOCATION.origin + "/" + idomWebModulesUrl
- const loadImportSource = (source, sourceType) => {
- return import(
- sourceType == "NAME" ? `${fullWebModulesUrl}${source}` : source
- );
- };
+ const fullWebModulesUrl = LOCATION.origin + "/" + idomWebModulesUrl;
+ const loadImportSource = (source, sourceType) => {
+ return import(
+ sourceType == "NAME" ? `${fullWebModulesUrl}${source}` : source
+ );
+ };
- mountLayoutWithWebSocket(mountPoint, fullWebsocketUrl, loadImportSource);
+ mountLayoutWithWebSocket(
+ mountPoint,
+ fullWebsocketUrl,
+ loadImportSource,
+ maxReconnectTimeout
+ );
}
From f17ba96bab56b424c920f4bb93bdacff334d0919 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:30:45 -0700
Subject: [PATCH 11/39] prevent duplicate component registration fix #5
---
src/django_idom/templatetags/idom.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py
index d53e4416..86996a05 100644
--- a/src/django_idom/templatetags/idom.py
+++ b/src/django_idom/templatetags/idom.py
@@ -1,5 +1,5 @@
+import functools
import json
-import sys
from importlib import import_module
from urllib.parse import urlencode
from uuid import uuid4
@@ -35,6 +35,7 @@ def idom_component(_component_id_, **kwargs):
}
+@functools.cache
def _register_component(full_component_name: str) -> None:
module_name, component_name = full_component_name.rsplit(".", 1)
From aa10d8cbb3a5fa87230f946c97615bf60c013988 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:38:17 -0700
Subject: [PATCH 12/39] allow any host to access this test app
---
tests/test_app/settings.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_app/settings.py b/tests/test_app/settings.py
index ac7d6ab9..29eadffa 100644
--- a/tests/test_app/settings.py
+++ b/tests/test_app/settings.py
@@ -26,7 +26,7 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
From 756b80b312515c7d9308cf536f3d530e7a3e7060 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:41:19 -0700
Subject: [PATCH 13/39] cache -> lru_cache
---
src/django_idom/templatetags/idom.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py
index 86996a05..acee8330 100644
--- a/src/django_idom/templatetags/idom.py
+++ b/src/django_idom/templatetags/idom.py
@@ -35,7 +35,7 @@ def idom_component(_component_id_, **kwargs):
}
-@functools.cache
+@functools.lru_cache
def _register_component(full_component_name: str) -> None:
module_name, component_name = full_component_name.rsplit(".", 1)
From 2786a3bc8a195fca7402ca5075feec711529f66b Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:43:30 -0700
Subject: [PATCH 14/39] py 3.7 compartibility
---
src/django_idom/templatetags/idom.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py
index acee8330..0fcb60f5 100644
--- a/src/django_idom/templatetags/idom.py
+++ b/src/django_idom/templatetags/idom.py
@@ -35,7 +35,7 @@ def idom_component(_component_id_, **kwargs):
}
-@functools.lru_cache
+@functools.lru_cache(maxsize=None)
def _register_component(full_component_name: str) -> None:
module_name, component_name = full_component_name.rsplit(".", 1)
From 2c1adb9a099bb2022b01c1ecbe133c087ef2a54e Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Tue, 21 Sep 2021 23:50:27 -0700
Subject: [PATCH 15/39] memory efficient way of preventing reregistration
---
src/django_idom/templatetags/idom.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py
index 0fcb60f5..e0742791 100644
--- a/src/django_idom/templatetags/idom.py
+++ b/src/django_idom/templatetags/idom.py
@@ -1,4 +1,3 @@
-import functools
import json
from importlib import import_module
from urllib.parse import urlencode
@@ -35,8 +34,10 @@ def idom_component(_component_id_, **kwargs):
}
-@functools.lru_cache(maxsize=None)
def _register_component(full_component_name: str) -> None:
+ if full_component_name in IDOM_REGISTERED_COMPONENTS:
+ return
+
module_name, component_name = full_component_name.rsplit(".", 1)
try:
From e50ca058b0b3cfb7790a789b1e0385b6677e6389 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Wed, 22 Sep 2021 01:10:28 -0700
Subject: [PATCH 16/39] Limit developer control the websocket
---
src/django_idom/websocket_consumer.py | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 31826cfb..30fcb86b 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -2,7 +2,8 @@
import asyncio
import json
import logging
-from typing import Any
+from dataclasses import dataclass
+from typing import Any, Callable
from urllib.parse import parse_qsl
from channels.auth import login
@@ -17,6 +18,12 @@
_logger = logging.getLogger(__name__)
+@dataclass
+class WebSocketConnection:
+ scope: dict
+ disconnect: Callable
+
+
class IdomAsyncWebSocketConsumer(AsyncJsonWebsocketConsumer):
"""Communicates with the browser to perform actions on-demand."""
@@ -36,6 +43,9 @@ async def connect(self) -> None:
elif user is None:
_logger.warning("IDOM websocket is missing AuthMiddlewareStack!")
+ # Limit developer control this websocket
+ self.socket = WebSocketConnection(self.scope, self.disconnect)
+
self._idom_dispatcher_future = asyncio.ensure_future(self._run_dispatch_loop())
async def disconnect(self, code: int) -> None:
@@ -61,7 +71,7 @@ async def _run_dispatch_loop(self):
component_kwargs = json.loads(query_dict.get("kwargs", "{}"))
try:
- component_instance = component_constructor(self, **component_kwargs)
+ component_instance = component_constructor(self.socket, **component_kwargs)
except Exception:
_logger.exception(
f"Failed to construct component {component_constructor} "
From bbb4649b30427bcfbdf59ab64d0d0d1dae2b465b Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Wed, 22 Sep 2021 01:27:46 -0700
Subject: [PATCH 17/39] fix readme filename (idom.py -> components,py)
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index d3787c14..2e3dd0e7 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ your_project/
├── urls.py
└── example_app/
├── __init__.py
- ├── idom.py
+ ├── components.py
├── templates/
│ └── your-template.html
└── urls.py
From 88dfe6563b61b185cc5c4542a899ad9364d8e1ea Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Wed, 22 Sep 2021 01:27:51 -0700
Subject: [PATCH 18/39] format readme
---
README.md | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 2e3dd0e7..c18681dd 100644
--- a/README.md
+++ b/README.md
@@ -20,14 +20,13 @@ interfaces in pure Python.
-
-
# Install Django IDOM
```bash
@@ -215,10 +214,10 @@ urlpatterns = [
If you plan to make code changes to this repository, you'll need to install the
following dependencies first:
-- [NPM](https://docs.npmjs.com/try-the-latest-stable-version-of-npm) for
- installing and managing Javascript
-- [ChromeDriver](https://chromedriver.chromium.org/downloads) for testing with
- [Selenium](https://www.seleniumhq.org/)
+- [NPM](https://docs.npmjs.com/try-the-latest-stable-version-of-npm) for
+ installing and managing Javascript
+- [ChromeDriver](https://chromedriver.chromium.org/downloads) for testing with
+ [Selenium](https://www.seleniumhq.org/)
Once done, you should clone this repository:
@@ -229,9 +228,9 @@ cd django-idom
Then, by running the command below you can:
-- Install an editable version of the Python code
+- Install an editable version of the Python code
-- Download, build, and install Javascript dependencies
+- Download, build, and install Javascript dependencies
```bash
pip install -e . -r requirements.txt
From a4b8f56c4ede81d01ac2a03f554d6dabe301e01c Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Wed, 22 Sep 2021 01:33:00 -0700
Subject: [PATCH 19/39] simplify render example
---
README.md | 8 ++------
tests/test_app/views.py | 5 ++---
2 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index c18681dd..59c2397f 100644
--- a/README.md
+++ b/README.md
@@ -184,15 +184,11 @@ You can then serve `your-template.html` from a view just
[like any other](https://docs.djangoproject.com/en/3.2/intro/tutorial03/#write-views-that-actually-do-something).
```python
-from django.http import HttpResponse
-from django.template import loader
-
+from django.shortcuts import render
def your_view(request):
context = {}
- return HttpResponse(
- loader.get_template("your-template.html").render(context, request)
- )
+ return render(request, "your-template.html", context)
```
## `example_app/urls.py`
diff --git a/tests/test_app/views.py b/tests/test_app/views.py
index 248235a7..11874908 100644
--- a/tests/test_app/views.py
+++ b/tests/test_app/views.py
@@ -1,7 +1,6 @@
-from django.http import HttpResponse
-from django.template import loader
+from django.shortcuts import render
def base_template(request):
context = {}
- return HttpResponse(loader.get_template("base.html").render(context, request))
+ return render(request, "base.html", context)
From 4bcbfdd5768a343f04509cf5fe5fc170c09a773e Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Wed, 22 Sep 2021 01:38:57 -0700
Subject: [PATCH 20/39] fix settings anchor link
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 59c2397f..e8f94abf 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ order to create ASGI websockets within Django. Then, we will add a path for IDOM
websocket consumer using `IDOM_WEBSOCKET_PATH`.
_Note: If you wish to change the route where this websocket is served from, see the
-available [settings](#settings.py)._
+available [settings](#settingspy)._
```python
From cfc07cd20dc8754f0667762954775b7d2be0ea8e Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Wed, 22 Sep 2021 01:42:44 -0700
Subject: [PATCH 21/39] add IDOM_WS_RECONNECT to readme
---
README.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/README.md b/README.md
index e8f94abf..c8dcd0a2 100644
--- a/README.md
+++ b/README.md
@@ -113,6 +113,9 @@ IDOM_BASE_URL: str = "_idom/"
# Only applies when not using Django's caching framework (see below).
IDOM_WEB_MODULE_LRU_CACHE_SIZE: int | None = None
+# Enable or disable websocket reconnection attempts (Default: True)
+IDOM_WS_RECONNECT: bool = True
+
# Configure a cache for loading JS files
CACHES = {
# Configure a cache for loading JS files for IDOM
From 3afbed55b5ce8f573e4b3cc0a17a83d55c0efe06 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Wed, 22 Sep 2021 13:59:34 -0700
Subject: [PATCH 22/39] add session middleware
---
README.md | 7 ++-----
tests/test_app/asgi.py | 5 ++++-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index c8dcd0a2..2082862b 100644
--- a/README.md
+++ b/README.md
@@ -80,11 +80,8 @@ from channels.routing import ProtocolTypeRouter, URLRouter
application = ProtocolTypeRouter(
{
"http": http_asgi_app,
- "websocket": AuthMiddlewareStack(
- URLRouter(
- # add a path for IDOM's websocket
- [IDOM_WEBSOCKET_PATH]
- )
+ "websocket": SessionMiddlewareStack(
+ AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH]))
),
}
)
diff --git a/tests/test_app/asgi.py b/tests/test_app/asgi.py
index 740ddb4f..2b711cc1 100644
--- a/tests/test_app/asgi.py
+++ b/tests/test_app/asgi.py
@@ -21,11 +21,14 @@
from channels.auth import AuthMiddlewareStack # noqa: E402
from channels.routing import ProtocolTypeRouter, URLRouter # noqa: E402
+from channels.sessions import SessionMiddlewareStack # noqa: E402
application = ProtocolTypeRouter(
{
"http": http_asgi_app,
- "websocket": AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH])),
+ "websocket": SessionMiddlewareStack(
+ AuthMiddlewareStack(URLRouter([IDOM_WEBSOCKET_PATH]))
+ ),
}
)
From 9e4b13e3f7ec2b54bc07cae5e03aef85d0e90785 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 27 Sep 2021 02:31:58 -0700
Subject: [PATCH 23/39] change disconnect to close
---
src/django_idom/websocket_consumer.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 30fcb86b..76cc30c7 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -3,7 +3,7 @@
import json
import logging
from dataclasses import dataclass
-from typing import Any, Callable
+from typing import Any, Callable, Coroutine, Optional
from urllib.parse import parse_qsl
from channels.auth import login
@@ -21,7 +21,7 @@
@dataclass
class WebSocketConnection:
scope: dict
- disconnect: Callable
+ close: Callable[[Optional[int]], Coroutine[Any, Any, None]]
class IdomAsyncWebSocketConsumer(AsyncJsonWebsocketConsumer):
@@ -44,7 +44,7 @@ async def connect(self) -> None:
_logger.warning("IDOM websocket is missing AuthMiddlewareStack!")
# Limit developer control this websocket
- self.socket = WebSocketConnection(self.scope, self.disconnect)
+ self.socket = WebSocketConnection(self.scope, self.close)
self._idom_dispatcher_future = asyncio.ensure_future(self._run_dispatch_loop())
From d625ee05fb871669fc7a0895b3578e07f9076040 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 27 Sep 2021 02:33:51 -0700
Subject: [PATCH 24/39] add view ID to WebSocketConnection
---
src/django_idom/websocket_consumer.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/django_idom/websocket_consumer.py b/src/django_idom/websocket_consumer.py
index 76cc30c7..53e954f8 100644
--- a/src/django_idom/websocket_consumer.py
+++ b/src/django_idom/websocket_consumer.py
@@ -22,6 +22,7 @@
class WebSocketConnection:
scope: dict
close: Callable[[Optional[int]], Coroutine[Any, Any, None]]
+ view_id: str
class IdomAsyncWebSocketConsumer(AsyncJsonWebsocketConsumer):
@@ -33,6 +34,8 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
async def connect(self) -> None:
await super().connect()
+ self.view_id = self.scope["url_route"]["kwargs"]["view_id"]
+
user = self.scope.get("user")
if user and user.is_authenticated:
try:
@@ -44,7 +47,7 @@ async def connect(self) -> None:
_logger.warning("IDOM websocket is missing AuthMiddlewareStack!")
# Limit developer control this websocket
- self.socket = WebSocketConnection(self.scope, self.close)
+ self.socket = WebSocketConnection(self.scope, self.close, self.view_id)
self._idom_dispatcher_future = asyncio.ensure_future(self._run_dispatch_loop())
@@ -59,12 +62,11 @@ async def receive_json(self, content: Any, **kwargs: Any) -> None:
await self._idom_recv_queue.put(LayoutEvent(**content))
async def _run_dispatch_loop(self):
- view_id = self.scope["url_route"]["kwargs"]["view_id"]
try:
- component_constructor = IDOM_REGISTERED_COMPONENTS[view_id]
+ component_constructor = IDOM_REGISTERED_COMPONENTS[self.view_id]
except KeyError:
- _logger.warning(f"Unknown IDOM view ID {view_id!r}")
+ _logger.warning(f"Unknown IDOM view ID {self.view_id!r}")
return
query_dict = dict(parse_qsl(self.scope["query_string"].decode()))
From b36ac3773090712ef719d08ebe867a35bafc7bf6 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 27 Sep 2021 02:37:30 -0700
Subject: [PATCH 25/39] change tab width to 2
---
src/js/src/index.js | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/src/js/src/index.js b/src/js/src/index.js
index 4253bf0b..8f17c0d4 100644
--- a/src/js/src/index.js
+++ b/src/js/src/index.js
@@ -4,34 +4,34 @@ import { mountLayoutWithWebSocket } from "idom-client-react";
let LOCATION = window.location;
let WS_PROTOCOL = "";
if (LOCATION.protocol == "https:") {
- WS_PROTOCOL = "wss://";
+ WS_PROTOCOL = "wss://";
} else {
- WS_PROTOCOL = "ws://";
+ WS_PROTOCOL = "ws://";
}
let WS_ENDPOINT_URL = WS_PROTOCOL + LOCATION.host + "/";
export function mountViewToElement(
- mountPoint,
- idomWebsocketUrl,
- idomWebModulesUrl,
- maxReconnectTimeout,
- viewId,
- queryParams
+ mountPoint,
+ idomWebsocketUrl,
+ idomWebModulesUrl,
+ maxReconnectTimeout,
+ viewId,
+ queryParams
) {
- const fullWebsocketUrl =
- WS_ENDPOINT_URL + idomWebsocketUrl + viewId + "/?" + queryParams;
+ const fullWebsocketUrl =
+ WS_ENDPOINT_URL + idomWebsocketUrl + viewId + "/?" + queryParams;
- const fullWebModulesUrl = LOCATION.origin + "/" + idomWebModulesUrl;
- const loadImportSource = (source, sourceType) => {
- return import(
- sourceType == "NAME" ? `${fullWebModulesUrl}${source}` : source
- );
- };
+ const fullWebModulesUrl = LOCATION.origin + "/" + idomWebModulesUrl;
+ const loadImportSource = (source, sourceType) => {
+ return import(
+ sourceType == "NAME" ? `${fullWebModulesUrl}${source}` : source
+ );
+ };
- mountLayoutWithWebSocket(
- mountPoint,
- fullWebsocketUrl,
- loadImportSource,
- maxReconnectTimeout
- );
+ mountLayoutWithWebSocket(
+ mountPoint,
+ fullWebsocketUrl,
+ loadImportSource,
+ maxReconnectTimeout
+ );
}
From 7336b50127a4d107e24a0216cf733e6d4d0f6b20 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 27 Sep 2021 02:48:26 -0700
Subject: [PATCH 26/39] prettier format
---
README.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 2082862b..189638a5 100644
--- a/README.md
+++ b/README.md
@@ -110,8 +110,8 @@ IDOM_BASE_URL: str = "_idom/"
# Only applies when not using Django's caching framework (see below).
IDOM_WEB_MODULE_LRU_CACHE_SIZE: int | None = None
-# Enable or disable websocket reconnection attempts (Default: True)
-IDOM_WS_RECONNECT: bool = True
+# Max amount of time for client to attempt to reconnect. 0 disables reconnection.
+IDOM_WS_RECONNECT_TIMEOUT: int = 604800
# Configure a cache for loading JS files
CACHES = {
@@ -210,10 +210,10 @@ urlpatterns = [
If you plan to make code changes to this repository, you'll need to install the
following dependencies first:
-- [NPM](https://docs.npmjs.com/try-the-latest-stable-version-of-npm) for
- installing and managing Javascript
-- [ChromeDriver](https://chromedriver.chromium.org/downloads) for testing with
- [Selenium](https://www.seleniumhq.org/)
+- [NPM](https://docs.npmjs.com/try-the-latest-stable-version-of-npm) for
+ installing and managing Javascript
+- [ChromeDriver](https://chromedriver.chromium.org/downloads) for testing with
+ [Selenium](https://www.seleniumhq.org/)
Once done, you should clone this repository:
@@ -224,9 +224,9 @@ cd django-idom
Then, by running the command below you can:
-- Install an editable version of the Python code
+- Install an editable version of the Python code
-- Download, build, and install Javascript dependencies
+- Download, build, and install Javascript dependencies
```bash
pip install -e . -r requirements.txt
From 7648256e9a93d8ff0950d18f999cd6420e1bc6c5 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 27 Sep 2021 02:49:21 -0700
Subject: [PATCH 27/39] IDOM_WS_RECONNECT -> IDOM_WS_RECONNECT_TIMEOUT
---
src/django_idom/config.py | 2 +-
src/django_idom/templates/idom/component.html | 2 +-
src/django_idom/templatetags/idom.py | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/django_idom/config.py b/src/django_idom/config.py
index bde409ef..86c3418e 100644
--- a/src/django_idom/config.py
+++ b/src/django_idom/config.py
@@ -10,7 +10,7 @@
IDOM_BASE_URL = getattr(settings, "IDOM_BASE_URL", "_idom/")
IDOM_WEBSOCKET_URL = IDOM_BASE_URL + "websocket/"
IDOM_WEB_MODULES_URL = IDOM_BASE_URL + "web_module/"
-IDOM_WS_RECONNECT = getattr(settings, "IDOM_WS_RECONNECT", True)
+IDOM_WS_RECONNECT_TIMEOUT = getattr(settings, "IDOM_WS_RECONNECT_TIMEOUT", 604800)
_CACHES = getattr(settings, "CACHES", {})
if _CACHES:
diff --git a/src/django_idom/templates/idom/component.html b/src/django_idom/templates/idom/component.html
index b1f57757..8d4b181a 100644
--- a/src/django_idom/templates/idom/component.html
+++ b/src/django_idom/templates/idom/component.html
@@ -7,7 +7,7 @@
mountPoint,
"{{ idom_websocket_url }}",
"{{ idom_web_modules_url }}",
- parseInt("{% if idom_ws_reconnect %}604800{% else %}0{% endif %}"),
+ "{{ idom_ws_reconnect_timeout }}",
"{{ idom_component_id }}",
"{{ idom_component_params }}"
);
diff --git a/src/django_idom/templatetags/idom.py b/src/django_idom/templatetags/idom.py
index e0742791..1b3de383 100644
--- a/src/django_idom/templatetags/idom.py
+++ b/src/django_idom/templatetags/idom.py
@@ -9,7 +9,7 @@
IDOM_REGISTERED_COMPONENTS,
IDOM_WEB_MODULES_URL,
IDOM_WEBSOCKET_URL,
- IDOM_WS_RECONNECT,
+ IDOM_WS_RECONNECT_TIMEOUT,
)
@@ -27,7 +27,7 @@ def idom_component(_component_id_, **kwargs):
"class": class_,
"idom_websocket_url": IDOM_WEBSOCKET_URL,
"idom_web_modules_url": IDOM_WEB_MODULES_URL,
- "idom_ws_reconnect": IDOM_WS_RECONNECT,
+ "idom_ws_reconnect_timeout": IDOM_WS_RECONNECT_TIMEOUT,
"idom_mount_uuid": uuid4().hex,
"idom_component_id": _component_id_,
"idom_component_params": urlencode({"kwargs": json_kwargs}),
From 238769309553ab8529e8ff30c1d91f213b40aaab Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Mon, 27 Sep 2021 02:52:30 -0700
Subject: [PATCH 28/39] format base.html
---
tests/test_app/templates/base.html | 37 +++++++++++++++---------------
1 file changed, 18 insertions(+), 19 deletions(-)
diff --git a/tests/test_app/templates/base.html b/tests/test_app/templates/base.html
index 1ad73c4a..e774403c 100644
--- a/tests/test_app/templates/base.html
+++ b/tests/test_app/templates/base.html
@@ -1,23 +1,22 @@
{% load static %} {% load idom %}
-