Skip to content

Commit de30ede

Browse files
committed
more consistent verbiage
1 parent 939f642 commit de30ede

File tree

6 files changed

+54
-31
lines changed

6 files changed

+54
-31
lines changed

src/reactpy_django/auth/components.py

+29-23
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import asyncio
44
import contextlib
55
from logging import getLogger
6-
from typing import TYPE_CHECKING
6+
from typing import TYPE_CHECKING, Any
77
from uuid import uuid4
88

99
from django.urls import reverse
1010
from reactpy import component, hooks, html
1111

1212
from reactpy_django.javascript_components import HttpRequest
13-
from reactpy_django.models import SwitchSession
13+
from reactpy_django.models import SynchronizeSession
1414

1515
if TYPE_CHECKING:
1616
from django.contrib.sessions.backends.base import SessionBase
@@ -19,38 +19,38 @@
1919

2020

2121
@component
22-
def session_manager(child):
22+
def session_manager(child: Any):
2323
"""This component can force the client (browser) to switch HTTP sessions,
2424
making it match the websocket session.
2525
2626
Used to force persistent authentication between Django's websocket and HTTP stack."""
2727
from reactpy_django import config
2828

29-
switch_sessions, set_switch_sessions = hooks.use_state(False)
29+
synchronize_requested, set_synchronize_requested = hooks.use_state(False)
3030
_, set_rerender = hooks.use_state(uuid4)
3131
uuid_ref = hooks.use_ref(str(uuid4()))
3232
uuid = uuid_ref.current
3333
scope = hooks.use_connection().scope
3434

3535
@hooks.use_effect(dependencies=[])
3636
def setup_asgi_scope():
37-
"""Store a trigger function in websocket scope so that ReactPy-Django's hooks can command a session synchronization."""
37+
"""Store trigger functions in the websocket scope so that ReactPy-Django's hooks can command
38+
any relevant actions."""
3839
scope.setdefault("reactpy", {})
3940
scope["reactpy"]["synchronize_session"] = synchronize_session
4041
scope["reactpy"]["rerender"] = rerender
4142

42-
@hooks.use_effect(dependencies=[switch_sessions])
43-
async def synchronize_session_timeout():
44-
"""Ensure that the ASGI scope is available to this component.
45-
This effect will automatically be cancelled if the session is successfully
46-
switched (via dependencies=[switch_sessions])."""
47-
if switch_sessions:
43+
@hooks.use_effect(dependencies=[synchronize_requested])
44+
async def synchronize_session_watchdog():
45+
"""This effect will automatically be cancelled if the session is successfully
46+
switched (via effect dependencies)."""
47+
if synchronize_requested:
4848
await asyncio.sleep(config.REACTPY_AUTH_TIMEOUT + 0.1)
4949
await asyncio.to_thread(
5050
_logger.warning,
5151
f"Client did not switch sessions within {config.REACTPY_AUTH_TIMEOUT} (REACTPY_AUTH_TIMEOUT) seconds.",
5252
)
53-
set_switch_sessions(False)
53+
set_synchronize_requested(False)
5454

5555
async def synchronize_session():
5656
"""Entrypoint where the server will command the client to switch HTTP sessions
@@ -60,37 +60,43 @@ async def synchronize_session():
6060
if not session or not session.session_key:
6161
return
6262

63-
# Delete any sessions currently associated with this UUID
64-
with contextlib.suppress(SwitchSession.DoesNotExist):
65-
obj = await SwitchSession.objects.aget(uuid=uuid)
63+
# Delete any sessions currently associated with this UUID, which also resets
64+
# the SynchronizeSession validity time.
65+
# This exists to fix scenarios where...
66+
# 1) The developer manually rotates the session key.
67+
# 2) A component tree requests multiple logins back-to-back before they finish.
68+
# 3) A login is requested, but the server failed to respond to the HTTP request.
69+
with contextlib.suppress(SynchronizeSession.DoesNotExist):
70+
obj = await SynchronizeSession.objects.aget(uuid=uuid)
6671
await obj.adelete()
6772

6873
# Begin the process of synchronizing HTTP and websocket sessions
69-
obj = await SwitchSession.objects.acreate(uuid=uuid, session_key=session.session_key)
74+
obj = await SynchronizeSession.objects.acreate(uuid=uuid, session_key=session.session_key)
7075
await obj.asave()
71-
set_switch_sessions(True)
76+
set_synchronize_requested(True)
7277

7378
async def synchronize_session_callback(status_code: int, response: str):
7479
"""This callback acts as a communication bridge, allowing the client to notify the server
75-
of the status of session switch command."""
76-
set_switch_sessions(False)
80+
of the status of session switch."""
81+
set_synchronize_requested(False)
7782
if status_code >= 300 or status_code < 200:
7883
await asyncio.to_thread(
7984
_logger.warning,
8085
f"Client returned unexpected HTTP status code ({status_code}) while trying to sychronize sessions.",
8186
)
8287

8388
async def rerender():
84-
"""Force a rerender of the entire component tree."""
89+
"""Event that can force a rerender of the entire component tree."""
8590
set_rerender(uuid4())
8691

87-
# Switch sessions using a client side HttpRequest component, if needed
92+
# If needed, synchronize sessions by configuring all relevant session cookies.
93+
# This is achieved by commanding the client to perform a HTTP request to our session manager endpoint.
8894
http_request = None
89-
if switch_sessions:
95+
if synchronize_requested:
9096
http_request = HttpRequest(
9197
{
9298
"method": "GET",
93-
"url": reverse("reactpy:switch_session", args=[uuid]),
99+
"url": reverse("reactpy:session_manager", args=[uuid]),
94100
"body": None,
95101
"callback": synchronize_session_callback,
96102
},

src/reactpy_django/checks.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def reactpy_warnings(app_configs, **kwargs):
3838
try:
3939
reverse("reactpy:web_modules", kwargs={"file": "example"})
4040
reverse("reactpy:view_to_iframe", kwargs={"dotted_path": "example"})
41-
reverse("reactpy:switch_session", args=[str(uuid4())])
41+
reverse("reactpy:session_manager", args=[str(uuid4())])
4242
except Exception:
4343
warnings.append(
4444
Warning(

src/reactpy_django/http/urls.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
),
1818
path(
1919
"session/<uuid:uuid>",
20-
views.switch_session,
21-
name="switch_session",
20+
views.session_manager,
21+
name="session_manager",
2222
),
2323
]

src/reactpy_django/http/views.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,17 @@ async def view_to_iframe(request: HttpRequest, dotted_path: str) -> HttpResponse
4444
return response
4545

4646

47-
async def switch_session(request: HttpRequest, uuid: str) -> HttpResponse:
48-
"""Switches the client's active session.
47+
async def session_manager(request: HttpRequest, uuid: str) -> HttpResponse:
48+
"""Switches the client's active session to match ReactPy.
4949
5050
This view exists because ReactPy is rendered via WebSockets, and browsers do not
5151
allow active WebSocket connections to modify HTTP cookies. Django's authentication
5252
design requires HTTP cookies to persist state changes.
5353
"""
54-
from reactpy_django.models import SwitchSession
54+
from reactpy_django.models import SynchronizeSession
5555

5656
# Find out what session the client wants to switch
57-
data = await SwitchSession.objects.aget(uuid=uuid)
57+
data = await SynchronizeSession.objects.aget(uuid=uuid)
5858

5959
# CHECK: Session has expired?
6060
if data.expired:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 5.1.4 on 2024-12-25 00:18
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('reactpy_django', '0008_rename_authsession_switchsession'),
10+
]
11+
12+
operations = [
13+
migrations.RenameModel(
14+
old_name='SwitchSession',
15+
new_name='SynchronizeSession',
16+
),
17+
]

src/reactpy_django/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ComponentSession(models.Model):
2020
last_accessed = models.DateTimeField(auto_now=True)
2121

2222

23-
class SwitchSession(models.Model):
23+
class SynchronizeSession(models.Model):
2424
"""A model for stores any relevant data needed to force Django's HTTP session to
2525
match the websocket session.
2626

0 commit comments

Comments
 (0)