Skip to content

Commit 8210f75

Browse files
committed
Clean up how the return value is returned from the view.
1 parent c8bc1b5 commit 8210f75

File tree

2 files changed

+117
-75
lines changed

2 files changed

+117
-75
lines changed

django_unicorn/message.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import logging
2+
3+
from django.http.response import HttpResponseRedirect
4+
5+
import orjson
6+
7+
from .components import HashUpdate, LocationUpdate
8+
from .errors import UnicornViewError
9+
from .serializer import dumps
10+
from .utils import generate_checksum
11+
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
class ComponentRequest:
17+
"""
18+
Parses, validates, and stores all of the data from the message request.
19+
"""
20+
21+
def __init__(self, request):
22+
self.body = {}
23+
24+
try:
25+
self.body = orjson.loads(request.body)
26+
assert self.body, "Invalid JSON body"
27+
except orjson.JSONDecodeError as e:
28+
raise UnicornViewError("Body could not be parsed") from e
29+
30+
self.data = self.body.get("data")
31+
assert self.data is not None, "Missing data" # data could theoretically be {}
32+
33+
self.id = self.body.get("id")
34+
assert self.id, "Missing component id"
35+
36+
self.key = self.body.get("key", "")
37+
38+
self.validate_checksum()
39+
40+
self.action_queue = self.body.get("actionQueue", [])
41+
42+
def validate_checksum(self):
43+
"""
44+
Validates that the checksum in the request matches the data.
45+
46+
Returns:
47+
Raises `AssertionError` if the checksums don't match.
48+
"""
49+
checksum = self.body.get("checksum")
50+
assert checksum, "Missing checksum"
51+
52+
generated_checksum = generate_checksum(orjson.dumps(self.data))
53+
assert checksum == generated_checksum, "Checksum does not match"
54+
55+
56+
class Return:
57+
def __init__(self, method_name, params=[]):
58+
self.method_name = method_name
59+
self.params = params
60+
self._value = {}
61+
self.redirect = {}
62+
63+
@property
64+
def value(self):
65+
return self._value
66+
67+
@value.setter
68+
def value(self, value):
69+
self._value = value
70+
# TODO: Support a tuple/list return_value which could contain a redirect and value(s)
71+
72+
if value is not None:
73+
if isinstance(value, HttpResponseRedirect):
74+
self.redirect = {
75+
"url": value.url,
76+
}
77+
elif isinstance(value, HashUpdate):
78+
self.redirect = {
79+
"hash": value.hash,
80+
}
81+
elif isinstance(value, LocationUpdate):
82+
self.redirect = {
83+
"url": value.redirect.url,
84+
"refresh": True,
85+
"title": value.title,
86+
}
87+
88+
if self.redirect:
89+
self._value = self.redirect
90+
91+
def get_data(self):
92+
try:
93+
serialized_value = orjson.loads(dumps(self.value))
94+
serialized_params = orjson.loads(dumps(self.params))
95+
96+
return {
97+
"method": self.method_name,
98+
"params": serialized_params,
99+
"value": serialized_value,
100+
}
101+
except Exception as e:
102+
logger.exception(e)
103+
104+
return {}

django_unicorn/views.py

Lines changed: 13 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@
44

55
from django.db.models import Model
66
from django.http import HttpRequest, JsonResponse
7-
from django.http.response import HttpResponseRedirect
87
from django.views.decorators.csrf import csrf_protect
98
from django.views.decorators.http import require_POST
109

1110
import orjson
1211

1312
from .call_method_parser import InvalidKwarg, parse_call_method_name, parse_kwarg
14-
from .components import HashUpdate, LocationUpdate, UnicornField, UnicornView
13+
from .components import UnicornField, UnicornView
1514
from .errors import UnicornViewError
16-
from .serializer import dumps
17-
from .utils import generate_checksum
15+
from .message import ComponentRequest, Return
1816

1917

2018
logger = logging.getLogger(__name__)
@@ -180,44 +178,6 @@ def _call_method_name(
180178
return func()
181179

182180

183-
class ComponentRequest:
184-
"""
185-
Parses, validates, and stores all of the data from the message request.
186-
"""
187-
188-
def __init__(self, request):
189-
self.body = {}
190-
191-
try:
192-
self.body = orjson.loads(request.body)
193-
assert self.body, "Invalid JSON body"
194-
except orjson.JSONDecodeError as e:
195-
raise UnicornViewError("Body could not be parsed") from e
196-
197-
self.data = self.body.get("data")
198-
assert self.data is not None, "Missing data" # data could theoretically be {}
199-
200-
self.id = self.body.get("id")
201-
assert self.id, "Missing component id"
202-
203-
self.validate_checksum()
204-
205-
self.action_queue = self.body.get("actionQueue", [])
206-
207-
def validate_checksum(self):
208-
"""
209-
Validates that the checksum in the request matches the data.
210-
211-
Returns:
212-
Raises `AssertionError` if the checksums don't match.
213-
"""
214-
checksum = self.body.get("checksum")
215-
assert checksum, "Missing checksum"
216-
217-
generated_checksum = generate_checksum(orjson.dumps(self.data))
218-
assert checksum == generated_checksum, "Checksum does not match"
219-
220-
221181
@handle_error
222182
@csrf_protect
223183
@require_POST
@@ -258,7 +218,7 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
258218
component.hydrate()
259219

260220
is_reset_called = False
261-
return_value = None
221+
return_data = None
262222

263223
for action in component_request.action_queue:
264224
action_type = action.get("type")
@@ -326,6 +286,7 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
326286
assert call_method_name, "Missing 'name' key for callMethod"
327287

328288
(method_name, params) = parse_call_method_name(call_method_name)
289+
return_data = Return(method_name, params)
329290
setter_method = {}
330291

331292
if "=" in call_method_name:
@@ -341,9 +302,8 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
341302
property_value = setter_method[property_name]
342303

343304
_set_property_value(component, property_name, property_value)
305+
return_data = Return(property_name, [property_value])
344306
else:
345-
(method_name, params) = parse_call_method_name(call_method_name)
346-
347307
if method_name == "$refresh":
348308
# Handle the refresh special action
349309
component = UnicornView.create(
@@ -375,41 +335,16 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
375335
validate_all_fields = True
376336
else:
377337
component.calling(method_name, params)
378-
return_value = _call_method_name(component, method_name, params)
338+
return_data.value = _call_method_name(
339+
component, method_name, params
340+
)
379341
component.called(method_name, params)
380342
else:
381343
raise UnicornViewError(f"Unknown action_type '{action_type}'")
382344

383345
# Re-load frontend context variables to deal with non-serializable properties
384346
component_request.data = orjson.loads(component.get_frontend_context_variables())
385347

386-
return_data = {}
387-
redirect_data = {}
388-
389-
# TODO: Support a tuple/list return_value which could contain a redirect and value(s).
390-
# `return (redirect(...), 1)` -> { return: { 1 } }, redirect: { url: "..." } }
391-
# easier for `HashUpdate`, would need to handle setting last return value after new component loaded
392-
if return_value is not None:
393-
if isinstance(return_value, HttpResponseRedirect):
394-
redirect_data = {
395-
"url": return_value.url,
396-
}
397-
elif isinstance(return_value, HashUpdate):
398-
redirect_data = {
399-
"hash": return_value.hash,
400-
}
401-
elif isinstance(return_value, LocationUpdate):
402-
redirect_data = {
403-
"url": return_value.redirect.url,
404-
"refresh": True,
405-
"title": return_value.title,
406-
}
407-
else:
408-
try:
409-
return_data = orjson.loads(dumps(return_value))
410-
except:
411-
pass
412-
413348
if not is_reset_called:
414349
if validate_all_fields:
415350
component.validate()
@@ -429,8 +364,11 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
429364
"dom": rendered_component,
430365
"data": component_request.data,
431366
"errors": component.errors,
432-
"redirect": redirect_data,
433-
"return": return_data,
434367
}
435368

369+
if return_data:
370+
res.update(
371+
{"redirect": return_data.redirect, "return": return_data.get_data(),}
372+
)
373+
436374
return JsonResponse(res)

0 commit comments

Comments
 (0)