diff options
author | Sean Dague <sean@dague.net> | 2016-01-11 12:33:37 -0500 |
---|---|---|
committer | Sean Dague <sean@dague.net> | 2016-01-13 11:46:28 -0500 |
commit | 3c24bc4273d78376c56c09c5c653b351882f6d6e (patch) | |
tree | ea113b32315b68b4a5fff36070345319e13b5052 | |
parent | 920e4084c55bc0e4b97b7bb9ee8f81782dee515c (diff) | |
download | routes-3c24bc4273d78376c56c09c5c653b351882f6d6e.tar.gz |
Add support for ``requirements`` to mapper.resource
This adds support for ``requirements`` option to mapper.resource,
which makes it possible to restrict matching in urls (most often
useful for capturing variables with path_prefix).
In OpenStack Nova we've used the prefix_path on Mapper.resource to
specify additional variables we want to capture (specifically
{project_id}). Project_id is a uuid. When trying to restrict
project_id to only valid uuid format a couple of issues were exposed.
- #1 '/{project_id:[a-f0-9]{32}}/...' builds an incorrect regex
because of the nested {}
- #2 the preferred method that works on connect() to pass
requirements doesn't work here (requirements are reset to only an
id match)
That leaves us with having to build a custom project_id match with 32
[a-f0-9] strings appended for every resource added to get the support
we need without effectively vendoring our own version of Mapper.resource.
This small change to allow requirements to pass through would make it
possible to get this tighter validation with much smaller regexes.
-rw-r--r-- | CHANGELOG.rst | 3 | ||||
-rw-r--r-- | routes/mapper.py | 18 | ||||
-rw-r--r-- | tests/test_functional/test_resources.py | 15 |
3 files changed, 36 insertions, 0 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c50d3f1..7433724 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Routes Changelog Release 2.3 (**dev**) ===================== +* Add support for the ``requirements`` option when using + mapper.resource to create routes. PR #57. Patch by Sean Dague. + * Concatenation fix when using submappers with path prefixes. Multiple submappers combined the path prefix inside the controller argument in non-obvious ways. The controller argument will now be properly carried diff --git a/routes/mapper.py b/routes/mapper.py index 7ca9ee9..382c01d 100644 --- a/routes/mapper.py +++ b/routes/mapper.py @@ -982,6 +982,22 @@ class Mapper(SubMapperParent): # GET /category/7/message/1 # has named route "category_message" + ``requirements`` + + A dictionary that restricts the matching of a + variable. Can be used when matching variables with path_prefix. + + Example:: + + map.resource('message', 'messages', + path_prefix='{project_id}/', + requirements={"project_id": R"\d+"}) + # POST /01234/message + # success, project_id is set to "01234" + # POST /foo/message + # 404 not found, won't be matched by this route + + ``parent_resource`` A ``dict`` containing information about the parent resource, for creating a nested resource. It should contain @@ -1105,6 +1121,8 @@ class Mapper(SubMapperParent): '_parent_resource': parent_resource, '_filter': kwargs.get('_filter') } + if 'requirements' in kwargs: + options['requirements'] = kwargs['requirements'] def requirements_for(meth): """Returns a new dict to be used for all route creation as the diff --git a/tests/test_functional/test_resources.py b/tests/test_functional/test_resources.py index 06bebcd..0855cd5 100644 --- a/tests/test_functional/test_resources.py +++ b/tests/test_functional/test_resources.py @@ -85,6 +85,21 @@ class TestResourceGeneration(unittest.TestCase): eq_('/messages/new/preview', url_for('category_preview_new_message')) assert_raises(Exception, url_for, 'category_preview_new_message', method='get') + def test_resources_with_requirements(self): + m = Mapper() + m.resource('message', 'messages', path_prefix='/{project_id}/{user_id}/', + requirements={'project_id': r'[0-9a-f]{4}', 'user_id': r'\d+'}) + options = dict(controller='messages', project_id='cafe', user_id='123') + self._assert_restful_routes(m, options, path_prefix='cafe/123/') + + # in addition to the positive tests we need to guarantee we + # are not matching when the requirements don't match. + eq_({'action': u'create', 'project_id': u'cafe', 'user_id': u'123', 'controller': u'messages'}, + m.match('/cafe/123/messages')) + eq_(None, m.match('/extensions/123/messages')) + eq_(None, m.match('/b0a3/123b/messages')) + eq_(None, m.match('/foo/bar/messages')) + class TestResourceRecognition(unittest.TestCase): def test_resource(self): |