summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2017-10-26 01:08:11 +0000
committerGerrit Code Review <review@openstack.org>2017-10-26 01:08:11 +0000
commit5edf040277a9ffeb4b0c9aa416c2a4239bba1d0a (patch)
tree8b11b4b3ceb2266affb3870a47a39b2eb33c34ae
parentb726187ec416aa8b470ce73ef06610064855f3ce (diff)
parentbe10840d86b820c7bd91c43e5d12b21fdcd24ad8 (diff)
Merge "More sophisticated extension matching"
-rw-r--r--mixmatch/extend/base.py59
-rw-r--r--mixmatch/extend/name_routing.py6
-rw-r--r--mixmatch/proxy.py1
-rw-r--r--mixmatch/tests/unit/test_extend.py91
-rw-r--r--requirements.txt1
5 files changed, 86 insertions, 72 deletions
diff --git a/mixmatch/extend/base.py b/mixmatch/extend/base.py
index 315a105..5953c52 100644
--- a/mixmatch/extend/base.py
+++ b/mixmatch/extend/base.py
@@ -12,61 +12,28 @@
12# License for the specific language governing permissions and limitations 12# License for the specific language governing permissions and limitations
13# under the License. 13# under the License.
14 14
15from routes import mapper
16
15 17
16class Extension(object): 18class Extension(object):
17 ROUTES = [] 19 ROUTES = []
18 OPTS = [] 20 OPTS = []
19 21
20 def matches(self, request): 22 def matches(self, request):
21 for route in self.ROUTES: 23 route_map = mapper.Mapper()
22 if route.match(request): 24 for (path, methods) in self.ROUTES:
23 return True 25 conditions = None if not methods else {'method': methods}
24 return False 26
27 route_map.connect(path.strip('/'),
28 action=self,
29 conditions=conditions)
30
31 match = route_map.match(url=request.path.strip('/'),
32 environ=request.environ)
33 return bool(match)
25 34
26 def handle_request(self, request): 35 def handle_request(self, request):
27 pass 36 pass
28 37
29 def handle_response(self, response): 38 def handle_response(self, response):
30 pass 39 pass
31
32
33class Route(object):
34 def __init__(self, service=None, version=None, method=None, action=None):
35 self.service = service
36 self.version = version
37 self.method = method
38 self.action = action
39
40 def _match_service(self, service):
41 if self.service:
42 return self.service == service
43 return True
44
45 def _match_version(self, version):
46 if self.version:
47 return self.version == version
48 return True
49
50 def _match_method(self, method):
51 if self.method:
52 return self.method == method
53 return True
54
55 def _match_action(self, action):
56 if self.action is None:
57 return True
58 elif action is None:
59 return False
60 elif len(self.action) != len(action):
61 return False
62 else:
63 for i in range(len(self.action)):
64 if self.action[i] != action[i]:
65 return False
66 return True
67
68 def match(self, request):
69 return (self._match_service(request.service) and
70 self._match_version(request.version) and
71 self._match_method(request.method) and
72 self._match_action(request.action))
diff --git a/mixmatch/extend/name_routing.py b/mixmatch/extend/name_routing.py
index f35a681..a841a3a 100644
--- a/mixmatch/extend/name_routing.py
+++ b/mixmatch/extend/name_routing.py
@@ -20,10 +20,8 @@ from oslo_serialization import jsonutils
20class NameRouting(base.Extension): 20class NameRouting(base.Extension):
21 21
22 ROUTES = [ 22 ROUTES = [
23 base.Route(service='volume', version=None, 23 ('/volume/{version}/{project_id}/volumes', ['POST']),
24 action=['volumes'], method='POST'), 24 ('/image/{version}/images', ['POST'])
25 base.Route(service='image', version=None,
26 action=['images'], method='POST'),
27 ] 25 ]
28 26
29 @staticmethod 27 @staticmethod
diff --git a/mixmatch/proxy.py b/mixmatch/proxy.py
index 73cee77..856ac7b 100644
--- a/mixmatch/proxy.py
+++ b/mixmatch/proxy.py
@@ -98,6 +98,7 @@ class RequestDetails(object):
98 # NOTE(jfreud): if chunked transfer, body must be accessed through 98 # NOTE(jfreud): if chunked transfer, body must be accessed through
99 # utilities found in mixmatch.session 99 # utilities found in mixmatch.session
100 self.body = request.data 100 self.body = request.data
101 self.environ = request.environ
101 102
102 103
103class RequestHandler(object): 104class RequestHandler(object):
diff --git a/mixmatch/tests/unit/test_extend.py b/mixmatch/tests/unit/test_extend.py
index 297900f..5d1d8fc 100644
--- a/mixmatch/tests/unit/test_extend.py
+++ b/mixmatch/tests/unit/test_extend.py
@@ -1,4 +1,4 @@
1# Copyright 2016 Massachusetts Open Cloud 1# Copyright 2017 Massachusetts Open Cloud
2# 2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may 3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain 4# not use this file except in compliance with the License. You may obtain
@@ -11,31 +11,78 @@
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations 12# License for the specific language governing permissions and limitations
13# under the License. 13# under the License.
14
14from testtools import testcase 15from testtools import testcase
15from mixmatch.extend.base import Route 16
17from mixmatch.extend import base
18
19
20class FakeRequest(object):
21 def __init__(self, path, method):
22 self.path = path
23 self.full_path = path
24 self.environ = {'REQUEST_METHOD': method}
16 25
17 26
18class TestRoutes(testcase.TestCase): 27class TestRoutes(testcase.TestCase):
19 def setUp(self): 28 def setUp(self):
20 super(TestRoutes, self).setUp() 29 super(TestRoutes, self).setUp()
30 self.ext = base.Extension()
31
32 def assertMatches(self, request):
33 self.assertTrue(self.ext.matches(request))
34
35 def assertDoesntMatch(self, request):
36 self.assertFalse(self.ext.matches(request))
37
38 def test_no_routes_doesnt_match(self):
39 self.ext.ROUTES = []
40 self.assertDoesntMatch(FakeRequest('service/version', 'GET'))
41
42 def test_simple_routes(self):
43 self.ext.ROUTES = [('service', [])]
44 self.assertMatches(FakeRequest('service', 'GET'))
45 self.assertMatches(FakeRequest('service/', 'GET'))
46 self.assertDoesntMatch(FakeRequest('not-service', 'GET'))
47
48 self.ext.ROUTES = [('service', []), ('not-service', [])]
49 self.assertMatches(FakeRequest('service', 'GET'))
50 self.assertMatches(FakeRequest('not-service', 'GET'))
51
52 self.ext.ROUTES = [('service', ['GET'])]
53 self.assertMatches(FakeRequest('service', 'GET'))
54 self.assertDoesntMatch(FakeRequest('service', 'POST'))
55
56 self.ext.ROUTES = [('service', ['GET', 'POST'])]
57 self.assertMatches(FakeRequest('service', 'GET'))
58 self.assertMatches(FakeRequest('service', 'POST'))
59
60 def test_wildcard_routes(self):
61 self.ext.ROUTES = [('service/{version}', [])]
62 self.assertMatches(FakeRequest('service/v1', 'GET'))
63 self.assertMatches(FakeRequest('service/v2', 'GET'))
64 self.assertDoesntMatch(FakeRequest('service', 'GET'))
65 self.assertDoesntMatch(FakeRequest('service/', 'GET'))
66
67 self.ext.ROUTES = [('service/{version}/resource', [])]
68 self.assertMatches(FakeRequest('service/v1/resource', 'GET'))
69 self.assertDoesntMatch(FakeRequest('service/v1', 'GET'))
70 self.assertDoesntMatch(FakeRequest('service/v1/', 'GET'))
71
72 self.ext.ROUTES = [('service/{version}/resource/{resource_id}', [])]
73 self.assertMatches(FakeRequest('service/v1/resource/123', 'GET'))
74 self.assertDoesntMatch(FakeRequest('service/v1/resource/', 'GET'))
75 self.assertDoesntMatch(FakeRequest('service/v1/resource', 'GET'))
21 76
22 def test_match_action_True(self): 77 self.ext.ROUTES = [
23 testRouteT = Route(None, None, None, [123, 'test']) 78 ('service/{version}/resource/{resource_id}/action', [])
24 self.assertEqual(testRouteT._match_action([123, 'test']), True) 79 ]
25 80 self.assertMatches(
26 def test_match_action_False(self): 81 FakeRequest('service/v1/resource/123/action', 'GET')
27 testRouteF = Route(None, None, None, [123, 'test']) 82 )
28 self.assertEqual(testRouteF._match_action([12, 'test']), False) 83 self.assertDoesntMatch(
29 84 FakeRequest('service/v1/resource/123', 'GET')
30 def test_match_action_Different_Length(self): 85 )
31 routeLen3 = Route(None, None, None, [123, 'test', None]) 86 self.assertDoesntMatch(
32 routeLen2 = Route(None, None, None, [123, 'test']) 87 FakeRequest('service/v1/resource/123/action/other', 'GET')
33 self.assertEqual(routeLen3._match_action([123, 'test']), False) 88 )
34 self.assertEqual(routeLen2._match_action([123, 'test', None]), False)
35
36 def test_match_action_None(self):
37 routeNone = Route(None, None, None, None)
38 testRoute = Route(None, None, None, [123])
39 self.assertEqual(routeNone._match_action([123]), True)
40 self.assertEqual(testRoute._match_action(None), False)
41 self.assertEqual(routeNone._match_action(None), True)
diff --git a/requirements.txt b/requirements.txt
index d66e1d3..33972fc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,3 +18,4 @@ python-keystoneclient>=3.8.0 # Apache-2.0
18requests>=2.14.2 # Apache-2.0 18requests>=2.14.2 # Apache-2.0
19six>=1.9.0 # MIT 19six>=1.9.0 # MIT
20stevedore>=1.20.0 # Apache-2.0 20stevedore>=1.20.0 # Apache-2.0
21Routes>=2.3.1 # MIT