kiloeyes/kiloeyes/common/resource_api.py

116 lines
4.3 KiB
Python
Executable File

# Copyright 2013 IBM Corp
#
# 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import falcon
from falcon import api_helpers
from oslo_log import log
RESOURCE_METHOD_FLAG = 'fab05a04-b861-4651-bd0c-9cb3eb9a6088'
LOG = log.getLogger(__name__)
class Restify(object):
def __init__(self, path='', method='GET'):
if not path:
raise Exception('Path has to be specified.')
if method.upper() not in falcon.HTTP_METHODS:
raise Exception('Invalid request method.')
self.path = path
self.method = method.upper()
def __call__(self, func):
setattr(func, RESOURCE_METHOD_FLAG, self)
return func
class ResourceAPI(falcon.API):
def add_route(self, uri_template, 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(None, ExampleResource())
Args:
uri_template (url pattern): the url pattern which a client will
post a request against. If none, ResourceAPI will
automatically look up the decorated methods.
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 = {}
try:
if uri_template:
super(ResourceAPI, self).add_route(uri_template, resource)
else:
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 = (
api_helpers.compile_uri_template(flag.path))
map = (template, {})
path_maps[flag.path] = map
new_method = api_helpers._wrap_with_hooks(
self._before, self._after, method)
map[1][flag.method] = new_method
for item in path_maps:
self._routes.insert(0, (path_maps[item][0],
path_maps[item][1]))
except Exception:
LOG.exception('Error occurred while adding the resource')
LOG.debug(self._routes)