77from django import template
88from django .http import HttpRequest
99from django .urls import NoReverseMatch , reverse
10+ from reactpy .core .types import ComponentConstructor
1011
1112from reactpy_django import config , models
1213from reactpy_django .exceptions import (
1516 InvalidHostError ,
1617)
1718from 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
1920
2021try :
2122 RESOLVED_WEB_MODULES_PATH = reverse ("reactpy:web_modules" , args = ["/" ]).strip ("/" )
@@ -55,60 +56,52 @@ def component(
5556 </body>
5657 </html>
5758 """
58-
59- # Determine the host
6059 request : HttpRequest | None = context .get ("request" )
6160 perceived_host = (request .get_host () if request else "" ).strip ("/" )
6261 host = (
6362 host
6463 or (next (config .REACTPY_DEFAULT_HOSTS ) if config .REACTPY_DEFAULT_HOSTS else "" )
6564 ).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 )
7166 uuid = uuid4 ().hex
7267 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 :
8681 user_component = config .REACTPY_REGISTERED_COMPONENTS .get (dotted_path )
8782 if not user_component :
8883 msg = f"Component '{ dotted_path } ' is not registered as a root component. "
8984 _logger .error (msg )
9085 return failure_context (dotted_path , ComponentDoesNotExistError (msg ))
9186
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 :
10592 _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 :
107100 _logger .exception (
108101 "An unknown error has occurred while saving component params for '%s'." ,
109102 dotted_path ,
110103 )
111- return failure_context (dotted_path , e )
104+ return failure_context (dotted_path , e )
112105
113106 # Return the template rendering context
114107 return {
@@ -117,7 +110,9 @@ def component(
117110 "reactpy_host" : host or perceived_host ,
118111 "reactpy_url_prefix" : config .REACTPY_URL_PREFIX ,
119112 "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 } /" ,
121116 "reactpy_resolved_web_modules_path" : RESOLVED_WEB_MODULES_PATH ,
122117 }
123118
@@ -136,3 +131,13 @@ def save_component_params(args, kwargs, uuid):
136131 model = models .ComponentSession (uuid = uuid , params = pickle .dumps (params ))
137132 model .full_clean ()
138133 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