diff --git a/pype/services/rest_api/base_class.py b/pype/services/rest_api/base_class.py index 5355c5886c..8a3c9d3704 100644 --- a/pype/services/rest_api/base_class.py +++ b/pype/services/rest_api/base_class.py @@ -7,7 +7,7 @@ from .lib import ( ) -def route(path, url_prefix="", methods=[]): +def route(path, url_prefix="", methods=[], strict_match=False): """Decorator that register callback and all its attributes. Callback is registered to Singleton RestApiFactory. @@ -15,8 +15,10 @@ def route(path, url_prefix="", methods=[]): :type path: str :param url_prefix: Specify prefix of path, defaults to "/". :type url_prefix: str, list, optional - :param methods: Specify request method (GET, POST, PUT, UPDATE, DELETE) when callback will be triggered, defaults to ["GET"] + :param methods: Specify request method (GET, POST, PUT, etc.) when callback will be triggered, defaults to ["GET"] :type methods: list, str, optional + :param strict_match: Decides if callback can handle both single and multiple entities (~/projects/ && ~/projects/), defaults to False. + :type strict_match: bool `path` may include dynamic keys that will be stored to object which can be obtained in callback. @@ -35,7 +37,9 @@ def route(path, url_prefix="", methods=[]): callback. """ def decorator(callback): - RestApiFactory.register_route(path, callback, url_prefix, methods) + RestApiFactory.register_route( + path, callback, url_prefix, methods, strict_match + ) callback.restapi = True return callback return decorator @@ -84,12 +88,14 @@ class RestApi: It is possible to use decorators in another class only when object, of class where decorators are, is registered to RestApiFactory. """ - def route(path, url_prefix="", methods=[]): - return route(path, url_prefix, methods) + def route(path, url_prefix="", methods=[], strict_match=False): + return route(path, url_prefix, methods, strict_match) @classmethod - def register_route(cls, callback, path, url_prefix="", methods=[]): - return route(path, methods, url_prefix)(callback) + def register_route( + cls, callback, path, url_prefix="", methods=[], strict_match=False + ): + return route(path, methods, url_prefix, strict_match)(callback) @classmethod def register_statics(cls, url_prefix, dir_path): diff --git a/pype/services/rest_api/lib/factory.py b/pype/services/rest_api/lib/factory.py index 3c9f84cc85..a07a8262c1 100644 --- a/pype/services/rest_api/lib/factory.py +++ b/pype/services/rest_api/lib/factory.py @@ -47,7 +47,7 @@ def prepare_fullpath(path, prefix): return fullpath -def prepare_regex_from_path(full_path): +def prepare_regex_from_path(full_path, strict_match): """Prepare regex based on set path. When registered path do not contain dynamic keys regex is not set. @@ -68,8 +68,9 @@ def prepare_regex_from_path(full_path): for key in all_founded_keys: replacement = "(?P{}\w+)".format(key) keys.append(key.replace("<", "").replace(">", "")) - if full_path.endswith(key): - replacement = "?{}?".format(replacement) + if not strict_match: + if full_path.endswith(key): + replacement = "?{}?".format(replacement) regex_path = regex_path.replace(key, replacement) regex_path = "^{}$".format(regex_path) @@ -227,7 +228,7 @@ class _RestApiFactory: self.unprocessed_statics.index(item) ) - def register_route(self, path, callback, url_prefix, methods): + def register_route(self, path, callback, url_prefix, methods, strict_match): log.debug("Registering callback for item \"{}\"".format( callback.__qualname__ )) @@ -235,7 +236,8 @@ class _RestApiFactory: "path": path, "callback": callback, "url_prefix": url_prefix, - "methods": methods + "methods": methods, + "strict_match": strict_match } self.unprocessed_routes.append(route) @@ -260,7 +262,9 @@ class _RestApiFactory: methods = prepare_methods(route["methods"], callback) url_prefix = prepare_prefix(route["url_prefix"]) fullpath = prepare_fullpath(route["path"], url_prefix) - regex, regex_keys = prepare_regex_from_path(fullpath) + regex, regex_keys = prepare_regex_from_path( + fullpath, route["strict_match"] + ) callback_info = prepare_callback_info(callback) for method in methods: diff --git a/pype/services/rest_api/rest_api.py b/pype/services/rest_api/rest_api.py index 2df666b45a..8dadbd524a 100644 --- a/pype/services/rest_api/rest_api.py +++ b/pype/services/rest_api/rest_api.py @@ -24,7 +24,7 @@ class RestApiServer: or created object, with used decorator, is registered with `register_obj`. .. code-block:: python - @route("/username", url_prefix="/api", methods=["get"]) + @route("/username", url_prefix="/api", methods=["get"], strict_match=False) def get_username(): return {"username": getpass.getuser()} @@ -51,7 +51,7 @@ class RestApiServer: "Proj2": {"proj_data": []}, } - @route("/projects/", url_prefix="/api", methods=["get"]) + @route("/projects/", url_prefix="/api", methods=["get"], strict_match=False) def get_projects(request_info): project_name = request_info.url_data["project_name"] if not project_name: @@ -64,7 +64,7 @@ class RestApiServer: .. code-block:: python from rest_api import abort - @route("/projects/", url_prefix="/api", methods=["get"]) + @route("/projects/", url_prefix="/api", methods=["get"], strict_match=False) def get_projects(request_info): project_name = request_info.url_data["project_name"] if not project_name: @@ -75,6 +75,11 @@ class RestApiServer: abort(404, "Project \"{}\".format(project_name) was not found") return project + `strict_match` allows to handle not only specific entity but all entity types. + E.g. "/projects/" with set `strict_match` to False will handle also + "/projects" or "/projects/" path. It is necessary to set `strict_match` to + True when should handle only single entity. + Callback may return many types. For more information read docstring of `_handle_callback_result` defined in handler. """ @@ -102,8 +107,12 @@ class RestApiServer: self.qaction = qaction self.failed_icon = failed_icon - def register_callback(self, path, callback, url_prefix="", methods=[]): - RestApiFactory.register_route(path, callback, url_prefix, methods) + def register_callback( + self, path, callback, url_prefix="", methods=[], strict_match=False + ): + RestApiFactory.register_route( + path, callback, url_prefix, methods, strict_match + ) # route(path, url_prefix, methods)(callback) def register_statics(self, url_prefix, dir_path):