From 9e012b93c0a741ec1b40af7e5ea1b5e7cf48e5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Santos?= Date: Thu, 30 Apr 2015 10:13:32 +0200 Subject: [PATCH] Jsonify operations that only return json --- .codevalidatorrc | 2 +- connexion/api.py | 37 ++++++++++++++++++++++++++++++++++--- connexion/app.py | 4 +++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/.codevalidatorrc b/.codevalidatorrc index 09af642..8852b83 100644 --- a/.codevalidatorrc +++ b/.codevalidatorrc @@ -21,7 +21,7 @@ "*pom.xml": ["xml", "pomdesc"], "*.pp": ["utf8", "nobom", "notabs", "nocr", "notrailingws", "puppet"], "*.properties": ["utf8", "nobom", "notabs", "nocr", "notrailingws"], - "*.py": ["utf8", "nobom", "notabs", "nocr", "notrailingws", "pyflakes", "pep8"], + "*.py": ["utf8", "nobom", "notabs", "nocr", "notrailingws", "pep8"], "*.rst": ["utf8", "nobom", "notabs", "nocr", "notrailingws"], "* *": ["invalidpath"], "*.sh": ["utf8", "nobom", "notabs", "nocr", "notrailingws"], diff --git a/connexion/api.py b/connexion/api.py index 569a171..1f40031 100644 --- a/connexion/api.py +++ b/connexion/api.py @@ -1,5 +1,7 @@ +import functools import logging import pathlib +import types import flask import yaml @@ -9,6 +11,16 @@ import connexion.utils as utils logger = logging.getLogger('connexion.api') +def jsonify(function: types.FunctionType) -> types.FunctionType: + """ + Decorator to jsonify the return value of the wrapped function + """ + @functools.wraps(function) + def wrapper(*args, **kwargs): + return flask.jsonify(function(*args, **kwargs)) + return wrapper + + class Api: """ Single API that corresponds to a flask blueprint @@ -19,28 +31,47 @@ class Api: with swagger_yaml_path.open() as swagger_yaml: self.specification = yaml.load(swagger_yaml) + # https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#fixed-fields + # TODO Validate yaml # TO_DOC: # If base_url is not on provided then we try to read it from the swagger.yaml or use / by default if base_url is None: - self.base_url = self.specification.get('basePath', '/') + self.base_url = self.specification.get('basePath', '/') # type: dict else: self.base_url = base_url self.specification['basePath'] = base_url + # A list of MIME types the APIs can produce. This is global to all APIs but can be overridden on specific + # API calls. + self.produces = self.specification.get('produces', []) # type: List[str] + + # Create blueprint and enpoints self.blueprint = self.create_blueprint() self.add_swagger_json() self.add_paths() - def add_endpoint(self, method: str, path: str, endpoint: dict): + def add_endpoint(self, method: str, path: str, operation: dict): """ Adds one endpoint to the api. """ - operation_id = endpoint['operationId'] + # https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#fixed-fields-5 + # From Spec: A friendly name for the operation. The id MUST be unique among all operations described in the API. + # Tools and libraries MAY use the operation id to uniquely identify an operation. + # In connexion: Used to identify the module and function + operation_id = operation['operationId'] + + # From Spec: A list of MIME types the operation can produce. This overrides the produces definition at the + # Swagger Object. An empty value MAY be used to clear the global definition. + # In connexion: if produces == ['application/json'] then the function return value s jsonified + produces = operation['produces'] if 'produces' in operation else self.produces + returns_json = produces == ['application/json'] logger.debug('... adding %s -> %s', method.upper(), operation_id) endpoint_name = utils.flaskify_endpoint(operation_id) function = utils.get_function_from_name(operation_id) + if returns_json: + function = jsonify(function) # TODO wrap function with json.dumps if produces is ['application/json'] self.blueprint.add_url_rule(path, endpoint_name, function, methods=[method]) diff --git a/connexion/app.py b/connexion/app.py index 791bc9d..078a61b 100644 --- a/connexion/app.py +++ b/connexion/app.py @@ -7,10 +7,10 @@ import tornado.httpserver import tornado.ioloop import connexion.api -import connexion.utils as utils logger = logging.getLogger('api') + class App: def __init__(self, import_name: str, port: int=5000, specification_dir: pathlib.Path=''): @@ -44,3 +44,5 @@ class App: http_server.listen(self.port) logger.info('Listening on http://127.0.0.1:{port}/'.format(port=self.port)) tornado.ioloop.IOLoop.instance().start() + +# TODO: default and changeable json errors