Browse Source

Merge "More sophisticated extension matching"

Zuul 1 year ago
parent
commit
5edf040277
5 changed files with 86 additions and 72 deletions
  1. 13
    46
      mixmatch/extend/base.py
  2. 2
    4
      mixmatch/extend/name_routing.py
  3. 1
    0
      mixmatch/proxy.py
  4. 69
    22
      mixmatch/tests/unit/test_extend.py
  5. 1
    0
      requirements.txt

+ 13
- 46
mixmatch/extend/base.py View File

@@ -12,61 +12,28 @@
12 12
 #   License for the specific language governing permissions and limitations
13 13
 #   under the License.
14 14
 
15
+from routes import mapper
16
+
15 17
 
16 18
 class Extension(object):
17 19
     ROUTES = []
18 20
     OPTS = []
19 21
 
20 22
     def matches(self, request):
21
-        for route in self.ROUTES:
22
-            if route.match(request):
23
-                return True
24
-        return False
23
+        route_map = mapper.Mapper()
24
+        for (path, methods) in self.ROUTES:
25
+            conditions = None if not methods else {'method': methods}
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 35
     def handle_request(self, request):
27 36
         pass
28 37
 
29 38
     def handle_response(self, response):
30 39
         pass
31
-
32
-
33
-class 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))

+ 2
- 4
mixmatch/extend/name_routing.py View File

@@ -20,10 +20,8 @@ from oslo_serialization import jsonutils
20 20
 class NameRouting(base.Extension):
21 21
 
22 22
     ROUTES = [
23
-        base.Route(service='volume', version=None,
24
-                   action=['volumes'], method='POST'),
25
-        base.Route(service='image', version=None,
26
-                   action=['images'], method='POST'),
23
+        ('/volume/{version}/{project_id}/volumes', ['POST']),
24
+        ('/image/{version}/images', ['POST'])
27 25
     ]
28 26
 
29 27
     @staticmethod

+ 1
- 0
mixmatch/proxy.py View File

@@ -98,6 +98,7 @@ class RequestDetails(object):
98 98
         # NOTE(jfreud): if chunked transfer, body must be accessed through
99 99
         # utilities found in mixmatch.session
100 100
         self.body = request.data
101
+        self.environ = request.environ
101 102
 
102 103
 
103 104
 class RequestHandler(object):

+ 69
- 22
mixmatch/tests/unit/test_extend.py View File

@@ -1,4 +1,4 @@
1
-#   Copyright 2016 Massachusetts Open Cloud
1
+#   Copyright 2017 Massachusetts Open Cloud
2 2
 #
3 3
 #   Licensed under the Apache License, Version 2.0 (the "License"); you may
4 4
 #   not use this file except in compliance with the License. You may obtain
@@ -11,31 +11,78 @@
11 11
 #   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 12
 #   License for the specific language governing permissions and limitations
13 13
 #   under the License.
14
+
14 15
 from testtools import testcase
15
-from mixmatch.extend.base import Route
16
+
17
+from mixmatch.extend import base
18
+
19
+
20
+class 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
 
18 27
 class TestRoutes(testcase.TestCase):
19 28
     def setUp(self):
20 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):
23
-        testRouteT = Route(None, None, None, [123, 'test'])
24
-        self.assertEqual(testRouteT._match_action([123, 'test']), True)
25
-
26
-    def test_match_action_False(self):
27
-        testRouteF = Route(None, None, None, [123, 'test'])
28
-        self.assertEqual(testRouteF._match_action([12, 'test']), False)
29
-
30
-    def test_match_action_Different_Length(self):
31
-        routeLen3 = Route(None, None, None, [123, 'test', None])
32
-        routeLen2 = Route(None, None, None, [123, 'test'])
33
-        self.assertEqual(routeLen3._match_action([123, 'test']), False)
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)
77
+        self.ext.ROUTES = [
78
+            ('service/{version}/resource/{resource_id}/action', [])
79
+        ]
80
+        self.assertMatches(
81
+            FakeRequest('service/v1/resource/123/action', 'GET')
82
+        )
83
+        self.assertDoesntMatch(
84
+            FakeRequest('service/v1/resource/123', 'GET')
85
+        )
86
+        self.assertDoesntMatch(
87
+            FakeRequest('service/v1/resource/123/action/other', 'GET')
88
+        )

+ 1
- 0
requirements.txt View File

@@ -18,3 +18,4 @@ python-keystoneclient>=3.8.0 # Apache-2.0
18 18
 requests>=2.14.2 # Apache-2.0
19 19
 six>=1.9.0 # MIT
20 20
 stevedore>=1.20.0 # Apache-2.0
21
+Routes>=2.3.1  # MIT

Loading…
Cancel
Save