diff --git a/integration_tests/base_routes.py b/integration_tests/base_routes.py index a271fb268..e4088db4b 100644 --- a/integration_tests/base_routes.py +++ b/integration_tests/base_routes.py @@ -25,6 +25,7 @@ current_file_path = pathlib.Path(__file__).parent.resolve() jinja_template = JinjaTemplate(os.path.join(current_file_path, "templates")) +jinja_template.set_robyn(app) # ===== Websockets ===== diff --git a/integration_tests/templates/test.html b/integration_tests/templates/test.html index 00b2d0a1e..87aa59be5 100644 --- a/integration_tests/templates/test.html +++ b/integration_tests/templates/test.html @@ -8,6 +8,6 @@ -

{{framework}} 🤝 {{templating_engine}}

+

{{framework}} 🤝 {{templating_engine}} Testing get_function_url

diff --git a/integration_tests/test_get_requests.py b/integration_tests/test_get_requests.py index 8afa15af9..6d6d2fbdf 100644 --- a/integration_tests/test_get_requests.py +++ b/integration_tests/test_get_requests.py @@ -40,6 +40,7 @@ def check_response(r: Response): assert r.text.startswith("\n\n") assert "Jinja2" in r.text assert "Robyn" in r.text + assert 'Testing get_function_url' in r.text check_response(get(f"/{function_type}/template")) diff --git a/robyn/templating.py b/robyn/templating.py index 5cdff73e0..1494aba30 100644 --- a/robyn/templating.py +++ b/robyn/templating.py @@ -1,22 +1,83 @@ from abc import ABC, abstractmethod +from typing import Callable, List from jinja2 import Environment, FileSystemLoader -from robyn import status_codes +from robyn import Robyn, status_codes +from robyn.router import Route from .robyn import Headers, Response +def get_param_filled_url(url: str, kwdict: dict | None = None) -> str: + """fill the :params in the url + + Args: + url (str): typically comes from the route + kwdict (dict): the **kwargs as a dict + + Returns: + str: _description_modified url (if there are elements in kwdict, otherwise unchanged) + """ + if kwdict is not None: + for k, v in zip(kwdict.keys(), kwdict.values()): + url = url.replace(f":{k}", f"{v}") + + return url + + class TemplateInterface(ABC): def __init__(self): ... @abstractmethod def render_template(self, *args, **kwargs) -> Response: ... + @abstractmethod + def set_robyn(self, robyn: Robyn) -> None: ... + + @abstractmethod + def get_function_url(self, function_name: str, route_type: str = "GET", **kwargs) -> str: ... + class JinjaTemplate(TemplateInterface): - def __init__(self, directory, encoding="utf-8", followlinks=False): - self.env = Environment(loader=FileSystemLoader(searchpath=directory, encoding=encoding, followlinks=followlinks)) + def __init__(self, directory, encoding="utf-8", followlinks=False) -> None: + self.env: Environment = Environment(loader=FileSystemLoader(searchpath=directory, encoding=encoding, followlinks=followlinks)) + self.add_function_to_globals("get_function_url", self.get_function_url) + self.robyn: Robyn | None = None + + def add_function_to_globals(self, name: str, func: Callable): + """ + Add a global function to a Jinja environment. + """ + self.env.globals[name] = func + + def set_robyn(self, robyn: Robyn) -> None: + """ + The get_function_url needs to have access to the list of routes stored in the apps Robyn object + + Args: + robyn (Robyn): The top instance of the Robyn class for this app. + """ + self.robyn = robyn + + def get_function_url(self, function_name: str, route_type: str = "GET", **kwargs) -> str: + """Creates a link to an endpoint function name + + Returns: + str: the url for the function + """ + + if self.robyn is None: + return "get_function_url needs set_robyn" + + routes: List[Route] = self.robyn.router.get_routes() + for r in routes: + if r.function.handler.__name__ == function_name and str(r.route_type) == f"HttpMethod.{route_type}": + if len(kwargs) > 0: + return get_param_filled_url(r.route, kwargs) + return r.route + + return "route not found in Robyn router" def render_template(self, template_name, **kwargs) -> Response: rendered_template = self.env.get_template(template_name).render(**kwargs) diff --git a/unit_tests/test_get_function_url.py b/unit_tests/test_get_function_url.py new file mode 100644 index 000000000..e27689dfc --- /dev/null +++ b/unit_tests/test_get_function_url.py @@ -0,0 +1,74 @@ +from robyn import Robyn +from robyn.templating import JinjaTemplate + + +def h(request): + return "Hello" + + +def get_hello(request): + return "get_Hello" + + +def post_hello(request): + return "post_Hello" + + +def put_hello(request): + return "put_Hello" + + +def delete_hello(request): + return "delete_Hello" + + +def patch_hello(request): + return "patch_Hello" + + +def options_hello(request): + return "options_Hello" + + +def head_hello(request): + return "head_Hello" + + +def test_get_function_url(): + app = Robyn(__file__) + app.add_route("GET", "/", h) + app.add_route("GET", "/get_hello", get_hello) + app.add_route("POST", "/post_hello", post_hello) + app.add_route("PUT", "/put_hello", put_hello) + app.add_route("DELETE", "/delete_hello", delete_hello) + app.add_route("PATCH", "/patch_hello", patch_hello) + app.add_route("OPTIONS", "/options_hello", options_hello) + app.add_route("HEAD", "/head_hello", head_hello) + + jinja_template = JinjaTemplate(".", "templates", "utf-8") + jinja_template.set_robyn(app) + + assert jinja_template.get_function_url("h") == "/" + assert jinja_template.get_function_url("get_hello") == "/get_hello" + assert jinja_template.get_function_url("get_hello", "GET") == "/get_hello" + assert jinja_template.get_function_url("post_hello", "POST") == "/post_hello" + assert jinja_template.get_function_url("put_hello", "PUT") == "/put_hello" + assert jinja_template.get_function_url("delete_hello", "DELETE") == "/delete_hello" + assert jinja_template.get_function_url("patch_hello", "PATCH") == "/patch_hello" + assert jinja_template.get_function_url("options_hello", "OPTIONS") == "/options_hello" + assert jinja_template.get_function_url("head_hello", "HEAD") == "/head_hello" + + +def get_hello_param(request): + return "get_Hello_param" + + +def test_get_function_url_with_params() -> None: + app = Robyn(__file__) + app.add_route("GET", "/get_hello/:id", get_hello_param) + + jinja_template = JinjaTemplate(".", "templates", "utf-8") + jinja_template.set_robyn(app) + + url: str = jinja_template.get_function_url("get_hello_param", "GET", id=42) + assert url == "/get_hello/42", f"Param filled url|{url}|" diff --git a/unit_tests/test_get_param_filled_url.py b/unit_tests/test_get_param_filled_url.py new file mode 100644 index 000000000..aeb24492f --- /dev/null +++ b/unit_tests/test_get_param_filled_url.py @@ -0,0 +1,29 @@ +from robyn.templating import get_param_filled_url + + +def test_get_param_filled_url_42() -> None: + d42: dict = {"id": 42} + assert get_param_filled_url("/get_hello/:id", d42) == "/get_hello/42" + assert get_param_filled_url("/:id", d42) == "/42" + assert get_param_filled_url("/get_hello/:idmore", d42) == "/get_hello/42more" + assert get_param_filled_url("/get_hello/:id/", d42) == "/get_hello/42/" + assert get_param_filled_url("/get_hello/:id/more", d42) == "/get_hello/42/more" + + +def test_get_param_filled_url_2s() -> None: + d42: dict = {"id": 42, "s": "str"} + assert get_param_filled_url("/get_hello/:id/:s", d42) == "/get_hello/42/str" + assert get_param_filled_url("/:id/:s", d42) == "/42/str" + assert get_param_filled_url("/get_hello/:id:smore", d42) == "/get_hello/42strmore" + assert get_param_filled_url("/get_hello/:id/:s/", d42) == "/get_hello/42/str/" + assert get_param_filled_url("/get_hello/:id/:s/more", d42) == "/get_hello/42/str/more" + assert get_param_filled_url("/get_hello/:s/:id/:s/more", d42) == "/get_hello/str/42/str/more" + + +def test_get_param_filled_url() -> None: + assert get_param_filled_url("/get_hello/:id/:s") == "/get_hello/:id/:s" + assert get_param_filled_url("/:id/:s") == "/:id/:s" + assert get_param_filled_url("/get_hello/:id:smore") == "/get_hello/:id:smore" + assert get_param_filled_url("/get_hello/:id/:s/") == "/get_hello/:id/:s/" + assert get_param_filled_url("/get_hello/:id/:s/more") == "/get_hello/:id/:s/more" + assert get_param_filled_url("/get_hello/:s/:id/:s/more") == "/get_hello/:s/:id/:s/more"