Fix subcloud-group endpoint routing conflict

Pecan default routing algorithm fails to find the requested resource
when the requested URL contains keywords that match the name of any
method declared inside of a pecan controller decorated with the
@expose(generic=True, ...) or @index.when() decorators.

This commit implements a custom routing through the use of pecan's
_route() method, where the routing is redirected to the generic index
method, allowing the keywords to be correctly passed as arguments to
the correct method handler based on the request method (GET, POST,
PATCH, DELETE, etc.).

Test plan:
PASS: Execute all of the subcloud-group sub-commands (add, delete, list,
list-subclouds, show and update) with the following group names: get,
post, patch, delete, index, _validate_name, _route and foo.

Closes-Bug: #1996507

Signed-off-by: Gustavo Herzmann <gustavo.herzmann@windriver.com>
Change-Id: I98a22f2bf1da205b68ee4199fb339776dd3d8dde
This commit is contained in:
Gustavo Herzmann
2022-11-14 11:05:52 -03:00
parent 28d0c8ecfb
commit 00f5a53611
2 changed files with 62 additions and 1 deletions

View File

@@ -15,8 +15,11 @@
# under the License.
#
import abc
from pecan import expose
from pecan import request
import six
import dcmanager.common.context as k_context
@@ -51,3 +54,61 @@ def extract_credentials_for_policy():
context_paras[key] = environ.get(val)
context_paras['roles'] = context_paras['roles'].split(',')
return context_paras
def _get_pecan_data(obj):
return getattr(obj, "_pecan", {})
def _is_exposed(obj):
return getattr(obj, "exposed", False)
def _is_generic(obj):
data = _get_pecan_data(obj)
return "generic" in data.keys()
def _is_generic_handler(obj):
data = _get_pecan_data(obj)
return "generic_handler" in data.keys()
@six.add_metaclass(abc.ABCMeta)
class GenericPathController(object):
"""A controller that allows path parameters to be equal to handler names.
The _route method provides a custom route resolution that checks if the
next object is marked as generic or a generic handler, pointing to the
generic index method in case it is. Pecan will properly handle the rest
of the routing process by redirecting it to the proper method function
handler (GET, POST, PATCH, DELETE, etc.).
Useful when part of the URL contains path parameters that might have
the same name as an already defined exposed controller method.
Requires the definition of an index method with the generator:
@expose(generic=True, ...)
Does not support nested subcontrollers.
"""
RESERVED_NAMES = ("_route", "_default", "_lookup")
@abc.abstractmethod
def index(self):
pass
@expose()
def _route(self, remainder, request):
next_url_part, rest = remainder[0], remainder[1:]
next_obj = getattr(self, next_url_part, None)
is_generic = _is_generic(next_obj) or _is_generic_handler(next_obj)
is_reserved_name = next_url_part in self.__class__.RESERVED_NAMES
if _is_exposed(next_obj) and not is_generic and not is_reserved_name:
# A non-generic exposed method with a non-reserved name
return next_obj, rest
else:
return self.index, remainder

View File

@@ -49,7 +49,7 @@ MIN_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS = 1
MAX_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS = 500
class SubcloudGroupsController(object):
class SubcloudGroupsController(restcomm.GenericPathController):
def __init__(self):
super(SubcloudGroupsController, self).__init__()