api: work around Routes cutting off suffix from resource id

Routes allows for auxiliary format suffix. Sadly it doesn't distinguish
between an actual format suffix (.json) and any other suffix that may be
part of the id. (like for first.second resource tag). To work this
behavior around, we will reattach the 'format' suffix if it is not of a
supported format (json only at the time of writing).

This of course leaves a corner case where there is a tag where .json is
a part of its id. This seems to be a reasonable balance to leave it
unfixed, because an alternative would probably be not backwards
compatible.

Closes-Bug: #1694897
Change-Id: I271107150166f0ee680faaa2e3ca6044cf4e8d4f
This commit is contained in:
Ihar Hrachyshka 2017-06-01 21:08:20 +00:00
parent 2979468011
commit 527468be33
2 changed files with 19 additions and 1 deletions

View File

@ -74,8 +74,22 @@ def Resource(controller, faults=None, deserializers=None, serializers=None,
if request.body:
args['body'] = deserializer.deserialize(request.body)['body']
method = getattr(controller, action)
# Routes library is dumb and cuts off everything after last dot (.)
# as format. At the same time, it doesn't enforce format suffix,
# which combined makes it impossible to pass a 'id' with dots
# included (the last section after the last dot is lost). This is
# important for some API extensions like tags where the id is
# really a tag name that can contain special characters.
#
# To work around the Routes behaviour, we will attach the suffix
# back to id if it's not one of supported formats (atm json only).
# This of course won't work for the corner case of a tag name that
# actually ends with '.json', but there seems to be no better way
# to tackle it without breaking API backwards compatibility.
if fmt is not None and fmt not in format_types:
args['id'] = '.'.join([args['id'], fmt])
method = getattr(controller, action)
result = method(request=request, **args)
except Exception as e:
mapped_exc = api_common.convert_exception_to_http_exc(e, faults,

View File

@ -44,6 +44,10 @@ class TagTestJSON(base.BaseAdminNetworkTest):
self.client.update_tag(self.resource, self.res_id, 'red')
self._get_and_compare_tags(['red', 'blue', 'green'])
# add a tag with a dot
self.client.update_tag(self.resource, self.res_id, 'black.or.white')
self._get_and_compare_tags(['red', 'blue', 'green', 'black.or.white'])
# replace tags
tags = ['red', 'yellow', 'purple']
res_body = self.client.update_tags(self.resource, self.res_id, tags)