def test_one_field(self): fields, pattern = routing.compile_uri_template('/{name}') assert fields == set(['name']) result = pattern.match('/Kelsier') assert result assert result.groupdict() == {'name': 'Kelsier'} fields, pattern = routing.compile_uri_template('/character/{name}') assert fields == set(['name']) result = pattern.match('/character/Kelsier') assert result assert result.groupdict() == {'name': 'Kelsier'} fields, pattern = routing.compile_uri_template( '/character/{name}/profile') assert fields == set(['name']) assert not pattern.match('/character') assert not pattern.match('/character/Kelsier') assert not pattern.match('/character/Kelsier/') result = pattern.match('/character/Kelsier/profile') assert result assert result.groupdict() == {'name': 'Kelsier'}
def test_one_field(self): fields, pattern = routing.compile_uri_template('/{name}') assert fields == set(['name']) result = pattern.match('/Kelsier') assert result assert result.groupdict() == {'name': 'Kelsier'} fields, pattern = routing.compile_uri_template('/character/{name}') assert fields == set(['name']) result = pattern.match('/character/Kelsier') assert result assert result.groupdict() == {'name': 'Kelsier'} fields, pattern = routing.compile_uri_template('/character/{name}/profile') assert fields == set(['name']) assert not pattern.match('/character') assert not pattern.match('/character/Kelsier') assert not pattern.match('/character/Kelsier/') result = pattern.match('/character/Kelsier/profile') assert result assert result.groupdict() == {'name': 'Kelsier'}
def test_one_field(self): fields, pattern = routing.compile_uri_template('/{name}') self.assertEqual(fields, set(['name'])) result = pattern.match('/Kelsier') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name': 'Kelsier'}) fields, pattern = routing.compile_uri_template('/character/{name}') self.assertEqual(fields, set(['name'])) result = pattern.match('/character/Kelsier') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name': 'Kelsier'}) fields, pattern = routing.compile_uri_template( '/character/{name}/profile') self.assertEqual(fields, set(['name'])) self.assertFalse(pattern.match('/character')) self.assertFalse(pattern.match('/character/Kelsier')) self.assertFalse(pattern.match('/character/Kelsier/')) result = pattern.match('/character/Kelsier/profile') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name': 'Kelsier'})
def test_one_field(self): fields, pattern = routing.compile_uri_template('/{name}') self.assertEqual(fields, set(['name'])) result = pattern.match('/Kelsier') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name': 'Kelsier'}) fields, pattern = routing.compile_uri_template('/character/{name}') self.assertEqual(fields, set(['name'])) result = pattern.match('/character/Kelsier') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name': 'Kelsier'}) fields, pattern = routing.compile_uri_template('/character/{name}/profile') self.assertEqual(fields, set(['name'])) self.assertFalse(pattern.match('/character')) self.assertFalse(pattern.match('/character/Kelsier')) self.assertFalse(pattern.match('/character/Kelsier/')) result = pattern.match('/character/Kelsier/profile') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name': 'Kelsier'})
def load_spec_swagger(self, swagger_spec): # a Swagger spec (typically filenamed api-docs.json) defines a number of API "operations" # each operationId is (meant to be) unique and defines the operation's HTTP method & a URI template # # the routing table here is a dict that allows lookup of a Swagger operationId and its URI template # when an HTTP request comes in, we work out which Swagger operationId it maps to # this is done by doing a regular expression match of the inbound URL to each URI template until we # find the correct Swagger operationId # you can add multiple Swagger specs but be aware that a duplicate operationId will overwrite existing # this function sets up the routing table by parsing the Swagger spec JSON log.info("Loading swagger spec into routing table") # param swagger_spec is a JSON document try: swagger_spec = json.loads(swagger_spec) except: raise Exception("Unable to parse the Swagger spec JSON document.") try: for k in swagger_spec['paths'].keys(): for http_method in swagger_spec['paths'][k].keys(): uri_fields, uri_template = compile_uri_template('/' + http_method.lower() + '/3.0' + k) self.routing_templates.append(repr(uri_template)) operationId = swagger_spec['paths'][k][http_method]['operationId'] self.routing_table[operationId] = {'uri_fields': uri_fields, 'uri_template': uri_template} if operationId not in self.operation_handlers.keys(): log.warning('In Swagger spec but not in operation_handlers: {}'.format(operationId)) except: raise Exception("Unable to build routing table from provided Swagger spec.")
def test_three_fields(self): fields, pattern = routing.compile_uri_template('/{a}/{b}/x/{c}') assert fields == set('abc') result = pattern.match('/one/2/x/3') assert result assert result.groupdict() == {'a': 'one', 'b': '2', 'c': '3'}
def test_malformed_field(self): fields, pattern = routing.compile_uri_template('/{a}/{1b}/x/{c}') self.assertEqual(fields, set('ac')) result = pattern.match('/one/{1b}/x/3') self.assertTrue(result) self.assertEqual(result.groupdict(), {'a': 'one', 'c': '3'})
def test_malformed_field(self): fields, pattern = routing.compile_uri_template('/{a}/{1b}/x/{c}') assert fields == set('ac') result = pattern.match('/one/{1b}/x/3') assert result assert result.groupdict() == {'a': 'one', 'c': '3'}
def test_one_field_with_digits(self): fields, pattern = routing.compile_uri_template('/{name123}') self.assertEqual(fields, set(['name123'])) result = pattern.match('/Kelsier') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name123': 'Kelsier'})
def test_one_field_with_digits(self): fields, pattern = routing.compile_uri_template('/{name123}') assert fields == set(['name123']) result = pattern.match('/Kelsier') assert result assert result.groupdict() == {'name123': 'Kelsier'}
def test_three_fields(self): fields, pattern = routing.compile_uri_template('/{a}/{b}/x/{c}') self.assertEqual(fields, set('abc')) result = pattern.match('/one/2/x/3') self.assertTrue(result) self.assertEqual(result.groupdict(), {'a': 'one', 'b': '2', 'c': '3'})
def test_no_fields(self, path): fields, pattern = routing.compile_uri_template(path) assert not fields assert not pattern.match(path[:-1]) result = pattern.match(path) assert result assert not result.groupdict()
def test_root(self): fields, pattern = routing.compile_uri_template('/') assert not fields assert not pattern.match('/x') result = pattern.match('/') assert result assert not result.groupdict()
def test_no_fields(self, path): fields, pattern = routing.compile_uri_template(path) self.assertFalse(fields) self.assertFalse(pattern.match(path[:-1])) result = pattern.match(path) self.assertTrue(result) self.assertFalse(result.groupdict())
def test_two_fields(self, postfix): path = '/book/{id}/characters/{name}' + postfix fields, pattern = routing.compile_uri_template(path) self.assertEqual(fields, set(['name', 'id'])) result = pattern.match('/book/0765350386/characters/Vin') self.assertTrue(result) self.assertEqual(result.groupdict(), {'name': 'Vin', 'id': '0765350386'})
def test_root(self): fields, pattern = routing.compile_uri_template('/') self.assertFalse(fields) self.assertFalse(pattern.match('/x')) result = pattern.match('/') self.assertTrue(result) self.assertFalse(result.groupdict())
def test_two_fields(self, postfix): path = '/book/{book_id}/characters/{n4m3}' + postfix fields, pattern = routing.compile_uri_template(path) assert fields == set(['n4m3', 'book_id']) result = pattern.match('/book/0765350386/characters/Vin') assert result assert result.groupdict() == {'n4m3': 'Vin', 'book_id': '0765350386'}
def test_two_fields(self, postfix): path = '/book/{book_id}/characters/{n4m3}' + postfix fields, pattern = routing.compile_uri_template(path) self.assertEqual(fields, set(['n4m3', 'book_id'])) result = pattern.match('/book/0765350386/characters/Vin') self.assertTrue(result) self.assertEqual(result.groupdict(), {'n4m3': 'Vin', 'book_id': '0765350386'})
def load_methods(self, method, method_content, path, swagger_spec): uri_fields, uri_regex = compile_uri_template( '/' + method.lower() + swagger_spec['basePath'] + path) self.specs[uri_regex] = {'uri_fields': uri_fields} for attribute, attribute_content in method_content.items(): if attribute in ['beforeId', 'afterId', 'operationId', 'validationId', 'exceptionId', 'finalId']: attribute_content = self.load_handler(attribute_content) self.load_attributes(attribute, attribute_content, swagger_spec, uri_regex) self.specs[uri_regex]['path'] = path
def add_route(self, uri_template, resource): """Associates a templatized URI path with a resource. A resource is an instance of a class that defines various on_* "responder" methods, one for each HTTP method the resource allows. For example, to support GET, simply define an `on_get` responder. If a client requests an unsupported method, Falcon will respond with "405 Method not allowed". Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also ``add_sink``). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. """ uri_fields, path_template = routing.compile_uri_template(uri_template) method_map = routing.create_http_method_map(resource, uri_fields, self._before, self._after) # Insert at the head of the list in case we get duplicate # adds (will cause the last one to win). self._routes.insert(0, (path_template, method_map, resource))
def add_route(self, uri_template, resource): """Associates a templatized URI path with a resource. A resource is an instance of a class that defines various on_* "responder" methods, one for each HTTP method the resource allows. For example, to support GET, simply define an `on_get` responder. If a client requests an unsupported method, Falcon will respond with "405 Method not allowed". Responders must always define at least two arguments to receive request and response objects, respectively. For example:: def on_post(self, req, resp): pass In addition, if the route's template contains field expressions, any responder that desires to receive requests for that route must accept arguments named after the respective field names defined in the template. A field expression consists of a bracketed field name. For example, given the following template:: /user/{name} A PUT request to "/user/kgriffs" would be routed to:: def on_put(self, req, resp, name): pass Args: uri_template (str): A templatized URI. Care must be taken to ensure the template does not mask any sink patterns, if any are registered (see also `add_sink`). resource (instance): Object which represents a REST resource. Falcon will pass "GET" requests to on_get, "PUT" requests to on_put, etc. If any HTTP methods are not supported by your resource, simply don't define the corresponding request handlers, and Falcon will do the right thing. """ uri_fields, path_template = routing.compile_uri_template(uri_template) method_map = routing.create_http_method_map( resource, uri_fields, self._before, self._after) # Insert at the head of the list in case we get duplicate # adds (will cause the last one to win). self._routes.insert(0, (path_template, method_map, resource))
def load_spec_swagger(self, swagger_spec): # a Swagger spec (typically filenamed api-docs.json) defines a number of API "operations" # each operationId is (meant to be) unique and defines the operation's HTTP method & a URI template # # the routing table here is a dict that allows lookup of a Swagger operationId and its URI template # when an HTTP request comes in, we work out which Swagger operationId it maps to # this is done by doing a regular expression match of the inbound URL to each URI template until we # find the correct Swagger operationId # you can add multiple Swagger specs but be aware that a duplicate operationId will overwrite existing # this function sets up the routing table by parsing the Swagger spec JSON log.info("Loading swagger spec into routing table") # param swagger_spec is a JSON document try: swagger_spec = json.loads(swagger_spec) except: raise Exception("Unable to parse the Swagger spec JSON document.") try: version = swagger_spec['info']['version'] except KeyError: version = '3.0' pass try: basePath = swagger_spec['basePath'] except KeyError: basePath = '' for k in swagger_spec['paths'].keys(): for http_method in swagger_spec['paths'][k].keys(): if http_method == 'parameters': continue uri_fields, uri_template = compile_uri_template( '/' + http_method.lower() + basePath + '/' + version + k) self.routing_templates.append(repr(uri_template)) operationId = swagger_spec['paths'][k][http_method][ 'operationId'] self.routing_table[operationId] = { 'uri_fields': uri_fields, 'uri_template': uri_template } if operationId not in self.operation_handlers.keys(): log.warning( 'In Swagger spec but not in operation_handlers: {}'. format(operationId))
def test_one_field_with_prefixed_digits(self): fields, pattern = routing.compile_uri_template('/{37signals}') self.assertEqual(fields, set()) result = pattern.match('/s2n') self.assertFalse(result)
def test_template_may_not_contain_double_slash(self, value): with pytest.raises(ValueError): routing.compile_uri_template(value)
def test_template_must_start_with_slash(self, value): with pytest.raises(ValueError): routing.compile_uri_template(value)
def test_string_type_required(self, value): with pytest.raises(TypeError): routing.compile_uri_template(value)
from .. import utils from .config import PREFIX logging.getLogger("requests").setLevel(logging.WARNING) # TODO don't block on sending updates # TODO async subscription validation # TODO restarting app/server? # TODO subscription expiration? # TODO avoid globals (threading) # TODO list subscription scalability SUBSCRIPTIONS = {} PATTERN_MESSAGES = ( routing.compile_uri_template('/me/mailFolders/{folderid}/messages')[1], 'Message') PATTERN_CONTACTS = ( routing.compile_uri_template('/me/contactFolders/{folderid}/contacts')[1], 'Contact') PATTERN_EVENTS = ( routing.compile_uri_template('/me/calendars/{folderid}/events')[1], 'Event') if PROMETHEUS: SUBSCR_COUNT = Counter('kopano_mfr_total_subscriptions', 'Total number of subscriptions') SUBSCR_ACTIVE = Gauge('kopano_mfr_active_subscriptions', 'Number of active subscriptions', multiprocess_mode='livesum') POST_COUNT = Counter('kopano_mfr_total_webhook_posts',
def test_one_field_with_prefixed_digits(self): fields, pattern = routing.compile_uri_template('/{37signals}') assert fields == set() result = pattern.match('/s2n') assert not result
def add_route(self, resource): """Associates uri patterns with resource methods. A resource is an instance of a class that defines various methods to handle http requests. Use this class to create applications which serve a standard compliant ReSTful API. For example, you may have an API which manage monitoring data, there can be multiple implementations of the API using different technologies. One can use Mongodb, the other can use Cassandra. To make the configuration of the application very easy, each implementation provides a class with set of methods decorated by class Restify, the application can simply using single entry configuration to load different implementations. For example:: class ExampleResource(object): @Restify(path='/path1/', method='post') def func1(self, req, res): pass @Restify(path='/path2/{id}/key/', method='get') def func2(self, req, res, id): pass def func3(self, req, res, id): pass With the above class, the following code will add the class method func1, func2 to handle post and get requests respectively, method func3 won't be added to the routes.:: app.add_route(ExampleResource()) Args: resource (instance): Object which represents an HTTP/REST "resource". Falcon will pass requests to various decorated methods to handle http requests. """ if not resource: raise Exception('Not a valid resource') path_maps = {} for attr in dir(resource): method = getattr(resource, attr) if callable(method) and hasattr(method, RESOURCE_METHOD_FLAG): flag = getattr(method, RESOURCE_METHOD_FLAG) map = path_maps.get(flag.path) if not map: uri_fields, template = ( routing.compile_uri_template(flag.path)) map = (template, {}) path_maps[flag.path] = map new_method = hooks._wrap_with_hooks( self._before, self._after, method, resource) map[1][flag.method] = new_method for item in path_maps: self._routes.insert(0, (path_maps[item][0], path_maps[item][1], resource))
def test_deprecated_warning(self): with pytest.warns( DeprecatedWarning, match='Call to deprecated function compile_uri_template().', ): routing.compile_uri_template('/')