Merge "More sophisticated extension matching"
This commit is contained in:
commit
5edf040277
|
@ -12,61 +12,28 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from routes import mapper
|
||||
|
||||
|
||||
class Extension(object):
|
||||
ROUTES = []
|
||||
OPTS = []
|
||||
|
||||
def matches(self, request):
|
||||
for route in self.ROUTES:
|
||||
if route.match(request):
|
||||
return True
|
||||
return False
|
||||
route_map = mapper.Mapper()
|
||||
for (path, methods) in self.ROUTES:
|
||||
conditions = None if not methods else {'method': methods}
|
||||
|
||||
route_map.connect(path.strip('/'),
|
||||
action=self,
|
||||
conditions=conditions)
|
||||
|
||||
match = route_map.match(url=request.path.strip('/'),
|
||||
environ=request.environ)
|
||||
return bool(match)
|
||||
|
||||
def handle_request(self, request):
|
||||
pass
|
||||
|
||||
def handle_response(self, response):
|
||||
pass
|
||||
|
||||
|
||||
class Route(object):
|
||||
def __init__(self, service=None, version=None, method=None, action=None):
|
||||
self.service = service
|
||||
self.version = version
|
||||
self.method = method
|
||||
self.action = action
|
||||
|
||||
def _match_service(self, service):
|
||||
if self.service:
|
||||
return self.service == service
|
||||
return True
|
||||
|
||||
def _match_version(self, version):
|
||||
if self.version:
|
||||
return self.version == version
|
||||
return True
|
||||
|
||||
def _match_method(self, method):
|
||||
if self.method:
|
||||
return self.method == method
|
||||
return True
|
||||
|
||||
def _match_action(self, action):
|
||||
if self.action is None:
|
||||
return True
|
||||
elif action is None:
|
||||
return False
|
||||
elif len(self.action) != len(action):
|
||||
return False
|
||||
else:
|
||||
for i in range(len(self.action)):
|
||||
if self.action[i] != action[i]:
|
||||
return False
|
||||
return True
|
||||
|
||||
def match(self, request):
|
||||
return (self._match_service(request.service) and
|
||||
self._match_version(request.version) and
|
||||
self._match_method(request.method) and
|
||||
self._match_action(request.action))
|
||||
|
|
|
@ -20,10 +20,8 @@ from oslo_serialization import jsonutils
|
|||
class NameRouting(base.Extension):
|
||||
|
||||
ROUTES = [
|
||||
base.Route(service='volume', version=None,
|
||||
action=['volumes'], method='POST'),
|
||||
base.Route(service='image', version=None,
|
||||
action=['images'], method='POST'),
|
||||
('/volume/{version}/{project_id}/volumes', ['POST']),
|
||||
('/image/{version}/images', ['POST'])
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -98,6 +98,7 @@ class RequestDetails(object):
|
|||
# NOTE(jfreud): if chunked transfer, body must be accessed through
|
||||
# utilities found in mixmatch.session
|
||||
self.body = request.data
|
||||
self.environ = request.environ
|
||||
|
||||
|
||||
class RequestHandler(object):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2016 Massachusetts Open Cloud
|
||||
# Copyright 2017 Massachusetts Open Cloud
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
|
@ -11,31 +11,78 @@
|
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from testtools import testcase
|
||||
from mixmatch.extend.base import Route
|
||||
|
||||
from mixmatch.extend import base
|
||||
|
||||
|
||||
class FakeRequest(object):
|
||||
def __init__(self, path, method):
|
||||
self.path = path
|
||||
self.full_path = path
|
||||
self.environ = {'REQUEST_METHOD': method}
|
||||
|
||||
|
||||
class TestRoutes(testcase.TestCase):
|
||||
def setUp(self):
|
||||
super(TestRoutes, self).setUp()
|
||||
self.ext = base.Extension()
|
||||
|
||||
def test_match_action_True(self):
|
||||
testRouteT = Route(None, None, None, [123, 'test'])
|
||||
self.assertEqual(testRouteT._match_action([123, 'test']), True)
|
||||
def assertMatches(self, request):
|
||||
self.assertTrue(self.ext.matches(request))
|
||||
|
||||
def test_match_action_False(self):
|
||||
testRouteF = Route(None, None, None, [123, 'test'])
|
||||
self.assertEqual(testRouteF._match_action([12, 'test']), False)
|
||||
def assertDoesntMatch(self, request):
|
||||
self.assertFalse(self.ext.matches(request))
|
||||
|
||||
def test_match_action_Different_Length(self):
|
||||
routeLen3 = Route(None, None, None, [123, 'test', None])
|
||||
routeLen2 = Route(None, None, None, [123, 'test'])
|
||||
self.assertEqual(routeLen3._match_action([123, 'test']), False)
|
||||
self.assertEqual(routeLen2._match_action([123, 'test', None]), False)
|
||||
def test_no_routes_doesnt_match(self):
|
||||
self.ext.ROUTES = []
|
||||
self.assertDoesntMatch(FakeRequest('service/version', 'GET'))
|
||||
|
||||
def test_match_action_None(self):
|
||||
routeNone = Route(None, None, None, None)
|
||||
testRoute = Route(None, None, None, [123])
|
||||
self.assertEqual(routeNone._match_action([123]), True)
|
||||
self.assertEqual(testRoute._match_action(None), False)
|
||||
self.assertEqual(routeNone._match_action(None), True)
|
||||
def test_simple_routes(self):
|
||||
self.ext.ROUTES = [('service', [])]
|
||||
self.assertMatches(FakeRequest('service', 'GET'))
|
||||
self.assertMatches(FakeRequest('service/', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('not-service', 'GET'))
|
||||
|
||||
self.ext.ROUTES = [('service', []), ('not-service', [])]
|
||||
self.assertMatches(FakeRequest('service', 'GET'))
|
||||
self.assertMatches(FakeRequest('not-service', 'GET'))
|
||||
|
||||
self.ext.ROUTES = [('service', ['GET'])]
|
||||
self.assertMatches(FakeRequest('service', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('service', 'POST'))
|
||||
|
||||
self.ext.ROUTES = [('service', ['GET', 'POST'])]
|
||||
self.assertMatches(FakeRequest('service', 'GET'))
|
||||
self.assertMatches(FakeRequest('service', 'POST'))
|
||||
|
||||
def test_wildcard_routes(self):
|
||||
self.ext.ROUTES = [('service/{version}', [])]
|
||||
self.assertMatches(FakeRequest('service/v1', 'GET'))
|
||||
self.assertMatches(FakeRequest('service/v2', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('service', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('service/', 'GET'))
|
||||
|
||||
self.ext.ROUTES = [('service/{version}/resource', [])]
|
||||
self.assertMatches(FakeRequest('service/v1/resource', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('service/v1', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('service/v1/', 'GET'))
|
||||
|
||||
self.ext.ROUTES = [('service/{version}/resource/{resource_id}', [])]
|
||||
self.assertMatches(FakeRequest('service/v1/resource/123', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('service/v1/resource/', 'GET'))
|
||||
self.assertDoesntMatch(FakeRequest('service/v1/resource', 'GET'))
|
||||
|
||||
self.ext.ROUTES = [
|
||||
('service/{version}/resource/{resource_id}/action', [])
|
||||
]
|
||||
self.assertMatches(
|
||||
FakeRequest('service/v1/resource/123/action', 'GET')
|
||||
)
|
||||
self.assertDoesntMatch(
|
||||
FakeRequest('service/v1/resource/123', 'GET')
|
||||
)
|
||||
self.assertDoesntMatch(
|
||||
FakeRequest('service/v1/resource/123/action/other', 'GET')
|
||||
)
|
||||
|
|
|
@ -18,3 +18,4 @@ python-keystoneclient>=3.8.0 # Apache-2.0
|
|||
requests>=2.14.2 # Apache-2.0
|
||||
six>=1.9.0 # MIT
|
||||
stevedore>=1.20.0 # Apache-2.0
|
||||
Routes>=2.3.1 # MIT
|
||||
|
|
Loading…
Reference in New Issue