7
7
from django import template
8
8
from django .http import HttpRequest
9
9
from django .urls import NoReverseMatch , reverse
10
+ from reactpy .core .types import ComponentConstructor
10
11
11
12
from reactpy_django import config , models
12
13
from reactpy_django .exceptions import (
15
16
InvalidHostError ,
16
17
)
17
18
from reactpy_django .types import ComponentParamData
18
- from reactpy_django .utils import check_component_args , func_has_args
19
+ from reactpy_django .utils import validate_component_args
19
20
20
21
try :
21
22
RESOLVED_WEB_MODULES_PATH = reverse ("reactpy:web_modules" , args = ["/" ]).strip ("/" )
@@ -55,60 +56,52 @@ def component(
55
56
</body>
56
57
</html>
57
58
"""
58
-
59
- # Determine the host
60
59
request : HttpRequest | None = context .get ("request" )
61
60
perceived_host = (request .get_host () if request else "" ).strip ("/" )
62
61
host = (
63
62
host
64
63
or (next (config .REACTPY_DEFAULT_HOSTS ) if config .REACTPY_DEFAULT_HOSTS else "" )
65
64
).strip ("/" )
66
-
67
- # Check if this this component needs to rendered by the current ASGI app
68
- use_current_app = not host or host .startswith (perceived_host )
69
-
70
- # Create context variables
65
+ is_local = not host or host .startswith (perceived_host )
71
66
uuid = uuid4 ().hex
72
67
class_ = kwargs .pop ("class" , "" )
73
- kwargs .pop ("key" , "" ) # `key` is effectively useless for the root node
74
-
75
- # Fail if user has a method in their host
76
- if host . find ( "://" ) != - 1 :
77
- protocol = host . split ( "://" )[ 0 ]
78
- msg = (
79
- f"Invalid host provided to component. Contains a protocol ' { protocol } ://'."
80
- )
81
- _logger . error ( msg )
82
- return failure_context (dotted_path , InvalidHostError ( msg ) )
83
-
84
- # Fetch the component if needed
85
- if use_current_app :
68
+ kwargs .pop ("key" , "" ) # `key` is useless for the root node
69
+ component_has_args = args or kwargs
70
+ user_component : ComponentConstructor | None = None
71
+
72
+ # Validate the host
73
+ if host and config . REACTPY_DEBUG_MODE :
74
+ try :
75
+ validate_host ( host )
76
+ except InvalidHostError as e :
77
+ return failure_context (dotted_path , e )
78
+
79
+ # Fetch the component
80
+ if is_local :
86
81
user_component = config .REACTPY_REGISTERED_COMPONENTS .get (dotted_path )
87
82
if not user_component :
88
83
msg = f"Component '{ dotted_path } ' is not registered as a root component. "
89
84
_logger .error (msg )
90
85
return failure_context (dotted_path , ComponentDoesNotExistError (msg ))
91
86
92
- # Store the component's args/kwargs in the database, if needed
93
- # These will be fetched by the websocket consumer later
94
- try :
95
- if use_current_app :
96
- check_component_args (user_component , * args , ** kwargs )
97
- if func_has_args (user_component ):
98
- save_component_params (args , kwargs , uuid )
99
- # Can't guarantee args will match up if the component is rendered by a different app.
100
- # So, we just store any provided args/kwargs in the database.
101
- elif args or kwargs :
102
- save_component_params (args , kwargs , uuid )
103
- except Exception as e :
104
- if isinstance (e , ComponentParamError ):
87
+ # Validate the component
88
+ if is_local and config .REACTPY_DEBUG_MODE :
89
+ try :
90
+ validate_component_args (user_component , * args , ** kwargs )
91
+ except ComponentParamError as e :
105
92
_logger .error (str (e ))
106
- else :
93
+ return failure_context (dotted_path , e )
94
+
95
+ # Store args & kwargs in the database (fetched by our websocket later)
96
+ if component_has_args :
97
+ try :
98
+ save_component_params (args , kwargs , uuid )
99
+ except Exception as e :
107
100
_logger .exception (
108
101
"An unknown error has occurred while saving component params for '%s'." ,
109
102
dotted_path ,
110
103
)
111
- return failure_context (dotted_path , e )
104
+ return failure_context (dotted_path , e )
112
105
113
106
# Return the template rendering context
114
107
return {
@@ -117,7 +110,9 @@ def component(
117
110
"reactpy_host" : host or perceived_host ,
118
111
"reactpy_url_prefix" : config .REACTPY_URL_PREFIX ,
119
112
"reactpy_reconnect_max" : config .REACTPY_RECONNECT_MAX ,
120
- "reactpy_component_path" : f"{ dotted_path } /{ uuid } /" ,
113
+ "reactpy_component_path" : f"{ dotted_path } /{ uuid } /"
114
+ if component_has_args
115
+ else f"{ dotted_path } /" ,
121
116
"reactpy_resolved_web_modules_path" : RESOLVED_WEB_MODULES_PATH ,
122
117
}
123
118
@@ -136,3 +131,13 @@ def save_component_params(args, kwargs, uuid):
136
131
model = models .ComponentSession (uuid = uuid , params = pickle .dumps (params ))
137
132
model .full_clean ()
138
133
model .save ()
134
+
135
+
136
+ def validate_host (host : str ):
137
+ if "://" in host :
138
+ protocol = host .split ("://" )[0 ]
139
+ msg = (
140
+ f"Invalid host provided to component. Contains a protocol '{ protocol } ://'."
141
+ )
142
+ _logger .error (msg )
143
+ raise InvalidHostError (msg )
0 commit comments