Skip to content

Commit

Permalink
Merge pull request #24 from BenjamenMeyer/enhancement_regex_uri_matching
Browse files Browse the repository at this point in the history
Enhancement: Basic Regex pattern matching for service URLs
  • Loading branch information
BenjamenMeyer committed Mar 26, 2015
2 parents f3b8174 + b84637c commit 37af652
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 10 deletions.
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ cover-package=stackinabox
cover-erase=1
cover-inclusive=true
cover-branches=true
cover-min-percentage=80
cover-min-percentage=83
51 changes: 42 additions & 9 deletions stackinabox/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@
logger = logging.getLogger(__name__)


class RouteAlreadyRegisteredError(Exception):
class StackInABoxServiceErrors(Exception):
pass


class RouteAlreadyRegisteredError(StackInABoxServiceErrors):
pass


class InvalidRouteRegexError(StackInABoxServiceErrors):
pass


Expand Down Expand Up @@ -43,15 +51,40 @@ def __init__(self, name):
.format(self.__id, self.name))

@staticmethod
def __get_service_regex(base_url, service_url):
regex = '^{0}{1}$'.format('', service_url)
logger.debug('StackInABoxService: {0} + {1} -> {2}'
.format(base_url, service_url, regex))
return re.compile(regex)
def __is_regex(uri):
regex_type = type(re.compile(''))
return isinstance(uri, regex_type)

@staticmethod
def __get_service_url(url, base_url):
return url[len(base_url):]
def validate_regex(regex):
# The regex generated by stackinabox starts with ^
# and ends with $. Enforce that the provided regex does the same.

if regex.pattern.startswith('^') is False:
logger.debug('StackInABoxService: Pattern must start with ^')
raise InvalidRouteRegexError('Pattern must start with ^')

if regex.pattern.endswith('$') is False:
logger.debug('StackInABoxService: Pattern must end with $')
raise InvalidRouteRegexError('Pattern must end with $')

@staticmethod
def __get_service_regex(base_url, service_url):
# if the specified service_url is already a regex
# then just use. Otherwise create what we need
if StackInABoxService.__is_regex(service_url):
logger.debug('StackInABoxService: Received regex {0} for use...'
.format(service_url.pattern))

# Validate the regex against StackInABoxService requirement
StackInABoxService.validate_regex(service_url)

return service_url
else:
regex = '^{0}{1}$'.format('', service_url)
logger.debug('StackInABoxService: {0} + {1} -> {2}'
.format(base_url, service_url, regex))
return re.compile(regex)

@property
def base_url(self):
Expand Down Expand Up @@ -136,6 +169,6 @@ def register(self, method, uri, call_back):
.format(self.name, method))
self.routes[uri]['handlers'][method] = call_back
else:
RouteAlreadyRegisteredError(
raise RouteAlreadyRegisteredError(
'Service ({0}): Route {1} already registered'
.format(self.name, uri))
10 changes: 10 additions & 0 deletions stackinabox/tests/test_httpretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,13 @@ def test_basic(self):
'alice=bob&joe=jane')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json(), expected_result)

res = requests.get('http://localhost/advanced/1234567890')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.text, 'okay')

res = requests.get('http://localhost/advanced/_234567890')
self.assertEqual(res.status_code, 500)

res = requests.put('http://localhost/advanced/h')
self.assertEqual(res.status_code, 500)
10 changes: 10 additions & 0 deletions stackinabox/tests/test_requests_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,16 @@ def test_basic(self):
self.assertEqual(res.status_code, 200)
self.assertEqual(res.json(), expected_result)

res = self.session.get('http://localhost/advanced/1234567890')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.text, 'okay')

res = self.session.get('http://localhost/advanced/_234567890')
self.assertEqual(res.status_code, 500)

res = self.session.put('http://localhost/advanced/h')
self.assertEqual(res.status_code, 500)

def test_context_requests_mock(self):
with stackinabox.util_requests_mock.activate():
stackinabox.util_requests_mock.requests_mock_registration(
Expand Down
10 changes: 10 additions & 0 deletions stackinabox/tests/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ def run():
assert res.status_code == 200
assert res.json() == expected_result

res = requests.get('http://localhost/advanced/1234567890')
assert res.status_code == 200
assert res.text == 'okay'

res = requests.get('http://localhost/advanced/_234567890')
assert res.status_code == 500

res = requests.put('http://localhost/advanced/h')
assert res.status_code == 500

StackInABox.reset_services()

responses.mock.stop()
Expand Down
63 changes: 63 additions & 0 deletions stackinabox/tests/test_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import re
import unittest


from stackinabox.services.service import *


class TestServiceRegex(unittest.TestCase):

def setUp(self):
super(TestServiceRegex, self).setUp()

def tearDown(self):
super(TestServiceRegex, self).tearDown()

def test_stackinabox_service_regex(self):

positive_cases = [
re.compile('^/$')
]

negative_cases = [
re.compile('^/'),
re.compile('/$')
]

for case in positive_cases:
StackInABoxService.validate_regex(case)

for case in negative_cases:
with self.assertRaises(InvalidRouteRegexError):
StackInABoxService.validate_regex(case)


class AnotherAdvancedService(StackInABoxService):

def __init__(self):
super(AnotherAdvancedService, self).__init__('aas')
self.register(StackInABoxService.GET, '/',
AnotherAdvancedService.first_handler)

def first_handler(self, request, uri, headers):
return (200, headers, 'hello')

def second_handler(self, request, uri, headers):
return (200, headers, 'howdy')


class TestServiceRouteRegistration(unittest.TestCase):

def setUp(self):
super(TestServiceRouteRegistration, self).setUp()

def tearDown(self):
super(TestServiceRouteRegistration, self).tearDown()

def test_bad_registration(self):

service = AnotherAdvancedService()

with self.assertRaises(RouteAlreadyRegisteredError):
service.register(StackInABoxService.GET, '/',
AnotherAdvancedService.second_handler)
7 changes: 7 additions & 0 deletions stackinabox/tests/utils/services.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import re

import six

Expand All @@ -16,6 +17,9 @@ def __init__(self):
AdvancedService.alternate_handler)
self.register(StackInABoxService.GET, '/g',
AdvancedService.query_handler)
self.register(StackInABoxService.GET,
re.compile('^/\d+$'),
AdvancedService.regex_handler)

def handler(self, request, uri, headers):
return (200, headers, 'Hello')
Expand All @@ -41,3 +45,6 @@ def query_handler(self, request, uri, headers):
else:
logger.debug('No query string')
return (200, headers, 'Where did you go?')

def regex_handler(self, request, uri, headers):
return (200, headers, 'okay')

0 comments on commit 37af652

Please sign in to comment.