diff options
Diffstat (limited to 'cvp/opnfv_testapi/tornado_swagger')
-rw-r--r-- | cvp/opnfv_testapi/tornado_swagger/README.md | 273 | ||||
-rw-r--r-- | cvp/opnfv_testapi/tornado_swagger/__init__.py | 8 | ||||
-rw-r--r-- | cvp/opnfv_testapi/tornado_swagger/handlers.py | 38 | ||||
-rw-r--r-- | cvp/opnfv_testapi/tornado_swagger/settings.py | 25 | ||||
-rw-r--r-- | cvp/opnfv_testapi/tornado_swagger/swagger.py | 291 | ||||
-rw-r--r-- | cvp/opnfv_testapi/tornado_swagger/views.py | 134 |
6 files changed, 0 insertions, 769 deletions
diff --git a/cvp/opnfv_testapi/tornado_swagger/README.md b/cvp/opnfv_testapi/tornado_swagger/README.md deleted file mode 100644 index d815f216..00000000 --- a/cvp/opnfv_testapi/tornado_swagger/README.md +++ /dev/null @@ -1,273 +0,0 @@ -# tornado-swagger - -## What is tornado-swagger? -tornado is a wrapper for tornado which enables swagger-ui support. - -In essense, you just need to wrap the Api instance and add a few python decorators to -get full swagger support.http://swagger.io/ - - -## How to use: - - -```python -from tornado.web import RequestHandler, HTTPError -from tornado_swagger import swagger - -swagger.docs() - -# You may decorate your operation with @swagger.operation and use docs to inform information -class ItemNoParamHandler(GenericApiHandler): - @swagger.operation(nickname='create') - def post(self): - """ - @param body: create test results for a item. - @type body: L{Item} - @return 200: item is created. - @raise 400: invalid input - """ - -# Operations not decorated with @swagger.operation do not get added to the swagger docs - -class ItemNoParamHandler(GenericApiHandler): - def options(self): - """ - I'm not visible in the swagger docs - """ - pass - - -# Then you use swagger.Application instead of tornado.web.Application -# and do other operations as usual - -def make_app(): - return swagger.Application([ - (r"/items", ItemNoParamHandler), - (r"/items/([^/]+)", ItemHandler), - (r"/items/([^/]+)/cases/([^/]+)", ItemOptionParamHandler), - ]) - -# You define models like this: -@swagger.model -class Item: - """ - @descriptin: - This is an example of a model class that has parameters in its constructor - and the fields in the swagger spec are derived from the parameters to __init__. - @notes: - In this case we would have property1, property2 as required parameters - and property3 as optional parameter. - @property property3: Item decription - @ptype property3: L{PropertySubclass} - """ - def __init__(self, property1, property2=None): - self.property1 = property1 - self.property2 = property2 - -# Swagger json: - "models": { - "Item": { - "description": "A description...", - "id": "Item", - "required": [ - "property1", - ], - "properties": [ - "property1": { - "type": "string" - }, - "property2": { - "type": "string" - "default": null - } - ] - } - } - -# If you declare an __init__ method with meaningful arguments -# then those args could be used to deduce the swagger model fields. -# just as shown above - -# if you declare an @property in docs, this property property2 will also be used -# to deduce the swagger model fields -class Item: - """ - @property property3: Item description - """ - def __init__(self, property1, property2): - self.property1 = property1 - self.property2 = property2 - -# Swagger json: - "models": { - "Item": { - "description": "A description...", - "id": "Item", - "required": [ - "property1", - ], - "properties": [ - "property1": { - "type": "string" - }, - "property2": { - "type": "string" - } - "property3": { - "type": "string" - } - ] - } - } - -# if you declare an argument with @ptype, the type of this argument will be specified -# rather than the default 'string' -class Item: - """ - @ptype property3: L{PropertySubclass} - """ - def __init__(self, property1, property2, property3=None): - self.property1 = property1 - self.property2 = property2 - self.property3 = property3 - -# Swagger json: - "models": { - "Item": { - "description": "A description...", - "id": "Item", - "required": [ - "property1", - ], - "properties": [ - "property1": { - "type": "string" - }, - "property2": { - "type": "string" - }, - "property3": { - "type": "PropertySubclass" - "default": null - } - ] - } - } - -# if you want to declare an list property, you can do it like this: -class Item: - """ - @ptype property3: L{PropertySubclass} - @ptype property4: C{list} of L{PropertySubclass} - """ - def __init__(self, property1, property2, property3, property4=None): - self.property1 = property1 - self.property2 = property2 - self.property3 = property3 - self.property4 = property4 - -# Swagger json: - "models": { - "Item": { - "description": "A description...", - "id": "Item", - "required": [ - "property1", - ], - "properties": [ - "property1": { - "type": "string" - }, - "property2": { - "type": "string" - }, - "property3": { - "type": "PropertySubclass" - "default": null - }, - "property4": { - "default": null, - "items": { - "type": "PropertySubclass"}, - "type": "array" - } - } - ] - } - } - -# if it is a query: -class ItemQueryHandler(GenericApiHandler): - @swagger.operation(nickname='query') - def get(self): - """ - @param property1: - @type property1: L{string} - @in property1: query - @required property1: False - - @param property2: - @type property2: L{string} - @in property2: query - @required property2: True - @rtype: L{Item} - - @notes: GET /item?property1=1&property2=1 - """ - -# Swagger json: - "apis": [ - { - "operations": [ - { - "parameters": [ - { - "name": "property1", - "dataType": "string", - "paramType": "query", - "description": "" - }, - { - "name": "property2", - "dataType": "string", - "paramType": "query", - "required": true, - "description": "" - } - ], - "responseClass": "Item", - "notes": null, - "responseMessages": [], - "summary": null, - "httpMethod": "GET", - "nickname": "query" - } - ], - "path": "/item", - "description": null - }, - .... - ] -``` - -# Running and testing - -Now run your tornado app - -``` -python main.py -``` - -And visit: - -``` -curl http://ip:port/swagger/spec -``` - -access to web -``` -http://ip:port/swagger/spec.html -``` - -# Passing more metadata to swagger -customized arguments used in creating the 'swagger.docs' object will be supported later diff --git a/cvp/opnfv_testapi/tornado_swagger/__init__.py b/cvp/opnfv_testapi/tornado_swagger/__init__.py deleted file mode 100644 index 363bc388..00000000 --- a/cvp/opnfv_testapi/tornado_swagger/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -############################################################################## -# Copyright (c) 2016 ZTE Corporation -# feng.xiaowei@zte.com.cn -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## diff --git a/cvp/opnfv_testapi/tornado_swagger/handlers.py b/cvp/opnfv_testapi/tornado_swagger/handlers.py deleted file mode 100644 index e39a9f63..00000000 --- a/cvp/opnfv_testapi/tornado_swagger/handlers.py +++ /dev/null @@ -1,38 +0,0 @@ -############################################################################## -# Copyright (c) 2016 ZTE Corporation -# feng.xiaowei@zte.com.cn -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## -import tornado.web - -from opnfv_testapi.tornado_swagger import settings -from opnfv_testapi.tornado_swagger import views - - -def swagger_handlers(): - prefix = settings.docs_settings.get('swagger_prefix', '/swagger') - if prefix[-1] != '/': - prefix += '/' - - def _path(suffix): - return prefix + suffix - return [ - tornado.web.URLSpec( - _path(r'spec.html$'), - views.SwaggerUIHandler, - settings.docs_settings, - name=settings.API_DOCS_NAME), - tornado.web.URLSpec( - _path(r'resources.json$'), - views.SwaggerResourcesHandler, - settings.docs_settings, - name=settings.RESOURCE_LISTING_NAME), - tornado.web.URLSpec( - _path(r'APIs$'), - views.SwaggerApiHandler, - settings.docs_settings, - name=settings.API_DECLARATION_NAME), - ] diff --git a/cvp/opnfv_testapi/tornado_swagger/settings.py b/cvp/opnfv_testapi/tornado_swagger/settings.py deleted file mode 100644 index 28422611..00000000 --- a/cvp/opnfv_testapi/tornado_swagger/settings.py +++ /dev/null @@ -1,25 +0,0 @@ -############################################################################## -# Copyright (c) 2016 ZTE Corporation -# feng.xiaowei@zte.com.cn -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - -API_DOCS_NAME = 'swagger-api-docs' -RESOURCE_LISTING_NAME = 'swagger-resource-listing' -API_DECLARATION_NAME = 'swagger-api-declaration' - -docs_settings = { - 'base_url': '', - 'static_path': '', - 'swagger_prefix': '/swagger', - 'api_version': 'v1.0', - 'swagger_version': '1.2', - 'api_key': '', - 'enabled_methods': ['get', 'post', 'put', 'patch', 'delete'], - 'exclude_namespaces': [], -} - -models = [] diff --git a/cvp/opnfv_testapi/tornado_swagger/swagger.py b/cvp/opnfv_testapi/tornado_swagger/swagger.py deleted file mode 100644 index 83f389a6..00000000 --- a/cvp/opnfv_testapi/tornado_swagger/swagger.py +++ /dev/null @@ -1,291 +0,0 @@ -############################################################################## -# Copyright (c) 2016 ZTE Corporation -# feng.xiaowei@zte.com.cn -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## -from HTMLParser import HTMLParser -from functools import wraps -import inspect - -import epydoc.markup -import tornado.web - -from opnfv_testapi.tornado_swagger import handlers -from opnfv_testapi.tornado_swagger import settings - - -class EpytextParser(HTMLParser): - a_text = False - - def __init__(self, tag): - HTMLParser.__init__(self) - self.tag = tag - self.data = None - - def handle_starttag(self, tag, attr): - if tag == self.tag: - self.a_text = True - - def handle_endtag(self, tag): - if tag == self.tag: - self.a_text = False - - def handle_data(self, data): - if self.a_text: - self.data = data - - def get_data(self): - return self.data - - -class DocParser(object): - def __init__(self): - self.notes = None - self.summary = None - self.responseClass = None - self.responseMessages = [] - self.params = {} - self.properties = {} - - def parse_docstring(self, text): - if text is None: - return - - errors = [] - doc = epydoc.markup.parse(text, markup='epytext', errors=errors) - _, fields = doc.split_fields(errors) - - for field in fields: - tag = field.tag() - arg = field.arg() - body = field.body() - self._get_parser(tag)(arg=arg, body=body) - return doc - - def _get_parser(self, tag): - parser = { - 'param': self._parse_param, - 'type': self._parse_type, - 'in': self._parse_in, - 'required': self._parse_required, - 'rtype': self._parse_rtype, - 'property': self._parse_property, - 'ptype': self._parse_ptype, - 'return': self._parse_return, - 'raise': self._parse_return, - 'notes': self._parse_notes, - 'description': self._parse_description, - } - return parser.get(tag, self._not_supported) - - def _parse_param(self, **kwargs): - arg = kwargs.get('arg', None) - body = self._get_body(**kwargs) - self.params.setdefault(arg, {}).update({ - 'name': arg, - 'description': body, - }) - - if 'paramType' not in self.params[arg]: - self.params[arg]['paramType'] = 'query' - - def _parse_type(self, **kwargs): - arg = kwargs.get('arg', None) - body = self._get_body(**kwargs) - self.params.setdefault(arg, {}).update({ - 'name': arg, - 'dataType': body - }) - - def _parse_in(self, **kwargs): - arg = kwargs.get('arg', None) - body = self._get_body(**kwargs) - self.params.setdefault(arg, {}).update({ - 'name': arg, - 'paramType': body - }) - - def _parse_required(self, **kwargs): - arg = kwargs.get('arg', None) - body = self._get_body(**kwargs) - self.params.setdefault(arg, {}).update({ - 'name': arg, - 'required': False if body in ['False', 'false'] else True - }) - - def _parse_rtype(self, **kwargs): - body = self._get_body(**kwargs) - self.responseClass = body - - def _parse_property(self, **kwargs): - arg = kwargs.get('arg', None) - self.properties.setdefault(arg, {}).update({ - 'type': 'string' - }) - - def _parse_ptype(self, **kwargs): - arg = kwargs.get('arg', None) - code = self._parse_epytext_para('code', **kwargs) - link = self._parse_epytext_para('link', **kwargs) - if code is None: - self.properties.setdefault(arg, {}).update({ - 'type': link - }) - elif code == 'list': - self.properties.setdefault(arg, {}).update({ - 'type': 'array', - 'items': {'type': link} - }) - - def _parse_return(self, **kwargs): - arg = kwargs.get('arg', None) - body = self._get_body(**kwargs) - self.responseMessages.append({ - 'code': arg, - 'message': body - }) - - def _parse_notes(self, **kwargs): - body = self._get_body(**kwargs) - self.notes = self._sanitize_doc(body) - - def _parse_description(self, **kwargs): - body = self._get_body(**kwargs) - self.summary = self._sanitize_doc(body) - - def _not_supported(self, **kwargs): - pass - - @staticmethod - def _sanitize_doc(comment): - return comment.replace('\n', '<br/>') if comment else comment - - @staticmethod - def _get_body(**kwargs): - body = kwargs.get('body', None) - return body.to_plaintext(None).strip() if body else body - - @staticmethod - def _parse_epytext_para(tag, **kwargs): - def _parse_epytext(tag, body): - epytextParser = EpytextParser(tag) - epytextParser.feed(str(body)) - data = epytextParser.get_data() - epytextParser.close() - return data - - body = kwargs.get('body', None) - return _parse_epytext(tag, body) if body else body - - -class model(DocParser): - def __init__(self, *args, **kwargs): - super(model, self).__init__() - self.args = args - self.kwargs = kwargs - self.required = [] - self.cls = None - - def __call__(self, *args): - if self.cls: - return self.cls - - cls = args[0] - self._parse_model(cls) - - return cls - - def _parse_model(self, cls): - self.id = cls.__name__ - self.cls = cls - if '__init__' in dir(cls): - self._parse_args(cls.__init__) - self.parse_docstring(inspect.getdoc(cls)) - settings.models.append(self) - - def _parse_args(self, func): - argspec = inspect.getargspec(func) - argspec.args.remove("self") - defaults = {} - if argspec.defaults: - defaults = list(zip(argspec.args[-len(argspec.defaults):], - argspec.defaults)) - required_args_count = len(argspec.args) - len(defaults) - for arg in argspec.args[:required_args_count]: - self.required.append(arg) - self.properties.setdefault(arg, {'type': 'string'}) - for arg, default in defaults: - self.properties.setdefault(arg, { - 'type': 'string', - "default": default - }) - - -class operation(DocParser): - def __init__(self, nickname='apis', **kwds): - super(operation, self).__init__() - self.nickname = nickname - self.func = None - self.func_args = [] - self.kwds = kwds - - def __call__(self, *args, **kwds): - if self.func: - return self.func(*args, **kwds) - - func = args[0] - self._parse_operation(func) - - @wraps(func) - def __wrapper__(*in_args, **in_kwds): - return self.func(*in_args, **in_kwds) - - __wrapper__.rest_api = self - return __wrapper__ - - def _parse_operation(self, func): - self.func = func - - self.__name__ = func.__name__ - self._parse_args(func) - self.parse_docstring(inspect.getdoc(self.func)) - - def _parse_args(self, func): - argspec = inspect.getargspec(func) - argspec.args.remove("self") - - defaults = [] - if argspec.defaults: - defaults = argspec.args[-len(argspec.defaults):] - - for arg in argspec.args: - if arg in defaults: - required = False - else: - required = True - self.params.setdefault(arg, { - 'name': arg, - 'required': required, - 'paramType': 'path', - 'dataType': 'string' - }) - self.func_args = argspec.args - - -def docs(**opts): - settings.docs_settings.update(opts) - - -class Application(tornado.web.Application): - def __init__(self, app_handlers=None, - default_host="", - transforms=None, - **settings): - super(Application, self).__init__( - handlers.swagger_handlers() + app_handlers, - default_host, - transforms, - **settings) diff --git a/cvp/opnfv_testapi/tornado_swagger/views.py b/cvp/opnfv_testapi/tornado_swagger/views.py deleted file mode 100644 index 79399970..00000000 --- a/cvp/opnfv_testapi/tornado_swagger/views.py +++ /dev/null @@ -1,134 +0,0 @@ -############################################################################## -# Copyright (c) 2016 ZTE Corporation -# feng.xiaowei@zte.com.cn -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## -import inspect -import json - -import tornado.template -import tornado.web - -from opnfv_testapi.tornado_swagger import settings - - -def json_dumps(obj, pretty=False): - return json.dumps(obj, - sort_keys=True, - indent=4, - separators=(',', ': ')) if pretty else json.dumps(obj) - - -class SwaggerUIHandler(tornado.web.RequestHandler): - def initialize(self, **kwargs): - self.static_path = kwargs.get('static_path') - self.base_url = kwargs.get('base_url') - - def get_template_path(self): - return self.static_path - - def get(self): - resource_url = self.reverse_url(settings.RESOURCE_LISTING_NAME) - discovery_url = self.base_url + resource_url - self.render('swagger/index.html', discovery_url=discovery_url) - - -class SwaggerResourcesHandler(tornado.web.RequestHandler): - def initialize(self, **kwargs): - self.api_version = kwargs.get('api_version') - self.swagger_version = kwargs.get('swagger_version') - self.base_url = kwargs.get('base_url') - self.exclude_namespaces = kwargs.get('exclude_namespaces') - - def get(self): - self.set_header('content-type', 'application/json') - resources = { - 'apiVersion': self.api_version, - 'swaggerVersion': self.swagger_version, - 'basePath': self.base_url, - 'apis': [{ - 'path': self.reverse_url(settings.API_DECLARATION_NAME), - 'description': 'Restful APIs Specification' - }] - } - - self.finish(json_dumps(resources, self.get_arguments('pretty'))) - - -class SwaggerApiHandler(tornado.web.RequestHandler): - def initialize(self, **kwargs): - self.api_version = kwargs.get('api_version') - self.swagger_version = kwargs.get('swagger_version') - self.base_url = kwargs.get('base_url') - - def get(self): - self.set_header('content-type', 'application/json') - apis = self.find_api(self.application.handlers) - if apis is None: - raise tornado.web.HTTPError(404) - - specs = { - 'apiVersion': self.api_version, - 'swaggerVersion': self.swagger_version, - 'basePath': self.base_url, - 'resourcePath': '/', - 'produces': ["application/json"], - 'apis': [self.__get_api_spec__(path, spec, operations) - for path, spec, operations in apis], - 'models': self.__get_models_spec(settings.models) - } - self.finish(json_dumps(specs, self.get_arguments('pretty'))) - - def __get_models_spec(self, models): - models_spec = {} - for model in models: - models_spec.setdefault(model.id, self.__get_model_spec(model)) - return models_spec - - @staticmethod - def __get_model_spec(model): - return { - 'description': model.summary, - 'id': model.id, - 'notes': model.notes, - 'properties': model.properties, - 'required': model.required - } - - @staticmethod - def __get_api_spec__(path, spec, operations): - return { - 'path': path, - 'description': spec.handler_class.__doc__, - 'operations': [{ - 'httpMethod': api.func.__name__.upper(), - 'nickname': api.nickname, - 'parameters': api.params.values(), - 'summary': api.summary, - 'notes': api.notes, - 'responseClass': api.responseClass, - 'responseMessages': api.responseMessages, - } for api in operations] - } - - @staticmethod - def find_api(host_handlers): - def get_path(url, args): - return url % tuple(['{%s}' % arg for arg in args]) - - def get_operations(cls): - return [member.rest_api - for (_, member) in inspect.getmembers(cls) - if hasattr(member, 'rest_api')] - - for host, handlers in host_handlers: - for spec in handlers: - for (_, mbr) in inspect.getmembers(spec.handler_class): - if inspect.ismethod(mbr) and hasattr(mbr, 'rest_api'): - path = get_path(spec._path, mbr.rest_api.func_args) - operations = get_operations(spec.handler_class) - yield path, spec, operations - break |