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:
Kristi Nikolla 2017-10-20 17:56:44 -04:00
parent 3a8a1f4872
commit be10840d86
5 changed files with 83 additions and 69 deletions

View File

@ -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))

View File

@ -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

View File

@ -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):

View File

@ -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')
)

View File

@ -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