More sophisticated extension matching
Rewrote the extension matching code to work with variables. Now using the Routes library. Change-Id: I361e6e597421bc71ed2cf9ed82516ad8b7bd3a8c
This commit is contained in:
parent
3a8a1f4872
commit
be10840d86
|
@ -12,61 +12,28 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from routes import mapper
|
||||||
|
|
||||||
|
|
||||||
class Extension(object):
|
class Extension(object):
|
||||||
ROUTES = []
|
ROUTES = []
|
||||||
OPTS = []
|
OPTS = []
|
||||||
|
|
||||||
def matches(self, request):
|
def matches(self, request):
|
||||||
for route in self.ROUTES:
|
route_map = mapper.Mapper()
|
||||||
if route.match(request):
|
for (path, methods) in self.ROUTES:
|
||||||
return True
|
conditions = None if not methods else {'method': methods}
|
||||||
return False
|
|
||||||
|
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):
|
def handle_request(self, request):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def handle_response(self, response):
|
def handle_response(self, response):
|
||||||
pass
|
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):
|
class NameRouting(base.Extension):
|
||||||
|
|
||||||
ROUTES = [
|
ROUTES = [
|
||||||
base.Route(service='volume', version=None,
|
('/volume/{version}/{project_id}/volumes', ['POST']),
|
||||||
action=['volumes'], method='POST'),
|
('/image/{version}/images', ['POST'])
|
||||||
base.Route(service='image', version=None,
|
|
||||||
action=['images'], method='POST'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -98,6 +98,7 @@ class RequestDetails(object):
|
||||||
# NOTE(jfreud): if chunked transfer, body must be accessed through
|
# NOTE(jfreud): if chunked transfer, body must be accessed through
|
||||||
# utilities found in mixmatch.session
|
# utilities found in mixmatch.session
|
||||||
self.body = request.data
|
self.body = request.data
|
||||||
|
self.environ = request.environ
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(object):
|
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
|
# 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
|
# 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
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from testtools import testcase
|
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):
|
class TestRoutes(testcase.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestRoutes, self).setUp()
|
super(TestRoutes, self).setUp()
|
||||||
|
self.ext = base.Extension()
|
||||||
|
|
||||||
def test_match_action_True(self):
|
def assertMatches(self, request):
|
||||||
testRouteT = Route(None, None, None, [123, 'test'])
|
self.assertTrue(self.ext.matches(request))
|
||||||
self.assertEqual(testRouteT._match_action([123, 'test']), True)
|
|
||||||
|
|
||||||
def test_match_action_False(self):
|
def assertDoesntMatch(self, request):
|
||||||
testRouteF = Route(None, None, None, [123, 'test'])
|
self.assertFalse(self.ext.matches(request))
|
||||||
self.assertEqual(testRouteF._match_action([12, 'test']), False)
|
|
||||||
|
|
||||||
def test_match_action_Different_Length(self):
|
def test_no_routes_doesnt_match(self):
|
||||||
routeLen3 = Route(None, None, None, [123, 'test', None])
|
self.ext.ROUTES = []
|
||||||
routeLen2 = Route(None, None, None, [123, 'test'])
|
self.assertDoesntMatch(FakeRequest('service/version', 'GET'))
|
||||||
self.assertEqual(routeLen3._match_action([123, 'test']), False)
|
|
||||||
self.assertEqual(routeLen2._match_action([123, 'test', None]), False)
|
|
||||||
|
|
||||||
def test_match_action_None(self):
|
def test_simple_routes(self):
|
||||||
routeNone = Route(None, None, None, None)
|
self.ext.ROUTES = [('service', [])]
|
||||||
testRoute = Route(None, None, None, [123])
|
self.assertMatches(FakeRequest('service', 'GET'))
|
||||||
self.assertEqual(routeNone._match_action([123]), True)
|
self.assertMatches(FakeRequest('service/', 'GET'))
|
||||||
self.assertEqual(testRoute._match_action(None), False)
|
self.assertDoesntMatch(FakeRequest('not-service', 'GET'))
|
||||||
self.assertEqual(routeNone._match_action(None), True)
|
|
||||||
|
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
|
requests>=2.14.2 # Apache-2.0
|
||||||
six>=1.9.0 # MIT
|
six>=1.9.0 # MIT
|
||||||
stevedore>=1.20.0 # Apache-2.0
|
stevedore>=1.20.0 # Apache-2.0
|
||||||
|
Routes>=2.3.1 # MIT
|
||||||
|
|
Loading…
Reference in New Issue