Giter Site home page Giter Site logo

spec-first / connexion Goto Github PK

View Code? Open in Web Editor NEW
4.4K 84.0 744.0 16.04 MB

Connexion is a modern Python web framework that makes spec-first and api-first development easy.

Home Page: https://connexion.readthedocs.io/en/latest/

License: Apache License 2.0

Python 100.00%
python openapi api-rest swagger flask-extensions microservices web api-first

connexion's Introduction

coveralls PyPI version License GitHub Workflow Status Coveralls

Explore the docs ยป


Connexion is a modern Python web framework that makes spec-first and api-first development easy. You describe your API in an OpenAPI (or Swagger) specification with as much detail as you want and Connexion will guarantee that it works as you specified.

It works either standalone, or in combination with any ASGI or WSGI-compatible framework!


๐Ÿ“ข Connexion 3 was recently released! Read about the changes here ยป

โœจ Features

Connexion provides the following functionality based on your specification:

  • ๐Ÿš Automatic route registration, no @route decorators needed
  • ๐Ÿ”’ Authentication, split from your application logic
  • ๐Ÿ”Ž Request and response validation of headers, parameters, and body
  • ๐Ÿ“ฌ Parameter parsing and injection, no request object needed
  • ๐Ÿ“จ Response serialization, you can return regular Python objects
  • ๐Ÿ“บ A Swagger UI console with live documentation and โ€˜try it outโ€™ feature
  • ๐Ÿงฉ Pluggability, in all dimensions

Connexion also helps you write your OpenAPI specification and develop against it by providing a command line interface which lets you test and mock your specification.

   connexion run openapi.yaml

(back to top)

๐Ÿซถ Sponsors

Sponsors help us dedicate time to maintain Connexion. Want to help?

Explore the options ยป

(back to top)

๐Ÿชค Why Connexion

With Connexion, you write the spec first. Connexion then calls your Python code, handling the mapping from the specification to the code. This incentivizes you to write the specification so that all of your developers can understand what your API does, even before you write a single line of code.

If multiple teams depend on your APIs, you can use Connexion to easily send them the documentation of your API. This guarantees that your API will follow the specification that you wrote. This is a different process from the one offered by most frameworks, which generate a specification after you've written the code. Some disadvantages of generating specifications based on code is that they often end up lacking details or mix your documentation with the implementation logic of your application.

(back to top)

โš’๏ธ How to Use

Installation

You can install connexion using pip:

    $ pip install connexion

Connexion provides 'extras' with optional dependencies to unlock additional features:

  • swagger-ui: Enables a Swagger UI console for your application.
  • uvicorn: Enables to run the your application using app.run() for development instead of using an external ASGI server.
  • flask: Enables the FlaskApp to build applications compatible with the Flask ecosystem.

You can install them as follows:

    $ pip install connexion[swagger-ui]
    $ pip install connexion[swagger-ui,uvicorn].

(back to top)

Creating your application

Connexion can be used either as a standalone application or as a middleware wrapping an existing ASGI (or WSGI) application written using a different framework. The standalone application can be built using either the AsyncApp or FlaskApp.

  • The AsyncApp is a lightweight application with native asynchronous support. Use it if you are starting a new project and have no specific reason to use one of the other options.

        from connexion import AsyncApp
    
        app = AsyncApp(__name__)
  • The FlaskApp leverages the Flask framework, which is useful if you're migrating from connexion 2.X or you want to leverage the Flask ecosystem.

        from connexion import FlaskApp
    
        app = FlaskApp(__name__)
  • The ConnexionMiddleware can be wrapped around any existing ASGI or WSGI application. Use it if you already have an application written in a different framework and want to add functionality provided by connexion

        from asgi_framework import App
        from connexion import ConnexionMiddleware
    
        app = App(__name__)
        app = ConnexionMiddleware(app)

(back to top)

Registering an API

While you can register individual routes on your application, Connexion really shines when you register an API defined by an OpenAPI (or Swagger) specification. The operation described in your specification is automatically linked to your Python view function via the operationId

run.py

   def post_greeting(name: str, greeting: str):  # Paramaeters are automatically unpacked
       return f"{greeting} {name}", 200          # Responses are automatically serialized

   app.add_api("openapi.yaml")

openapi.yaml

   ...
   paths:
     /greeting/{name}:
       post:
         operationId: run.post_greeting
         responses:
           200:
             content:
               text/plain:
                 schema:
                   type: string
         parameters:
           - name: name
             in: path
             required: true
             schema:
               type: string
           - name: greeting
             in: query
             required: true
             schema:
               type: string

(back to top)

Running your application

If you installed connexion using connexion[uvicorn], you can run it using the run method. This is only recommended for development:

    app.run()

In production, run your application using an ASGI server such as uvicorn. If you defined your app in a python module called run.py, you can run it as follows:

    $ uvicorn run:app

Or with gunicorn:

    $ gunicorn -k uvicorn.workers.UvicornWorker run:app

Now you're able to run and use Connexion!

See the examples folder for more examples.

(back to top)

๐Ÿ“œ Changes

A full changelog is maintained on the GitHub releases page.

(back to top)

๐Ÿคฒ Contributing

We welcome your ideas, issues, and pull requests. Just follow the usual/standard GitHub practices.

For easy development, install connexion using poetry with all extras, and install the pre-commit hooks to automatically run black formatting and static analysis checks.

    pip install poetry
    poetry install --all-extras
    pre-commit install

You can find out more about how Connexion works and where to apply your changes by having a look at our architecture.

Unless you explicitly state otherwise in advance, any non trivial contribution intentionally submitted for inclusion in this project by you to the steward of this repository shall be under the terms and conditions of Apache License 2.0 written below, without any additional copyright information, terms or conditions.

(back to top)

๐Ÿ™ Thanks

We'd like to thank all of Connexion's contributors for working on this project, Swagger/OpenAPI for their support, and Zalando for originally developing and releasing Connexion.

๐Ÿ“š Recommended Resources

About the advantages of working spec-first:

Tools to help you work spec-first:

connexion's People

Contributors

31z4 avatar cansik avatar cclauss avatar cyprien-g avatar ddefisher avatar dfeinzeig avatar dimbleby avatar dtkav avatar dutradda avatar fp-dsem avatar hjacobs avatar ibigpapa avatar ioggstream avatar jbryan avatar jfinkhaeuser avatar jmcs avatar kleijnweb avatar krzysztof-indyk avatar lappleapple avatar leenabhegade avatar menudoproblema avatar mvalkon avatar nmusatti avatar phoracek avatar rafaelcaricio avatar robbesneyders avatar ruwann avatar tdpsk avatar tuxlife avatar wilsonge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

connexion's Issues

List as body parameter fails in validation

As I saw you modified body parameter handling in version 0.10.2 #59. Now the problem is that lists cause an error because you try to call items on a list object which fails.

It looks like you just handle dicts (JSON Objects) as body parameters (code):

        # Add body parameters
        for key, value in body_parameters.items():
            if key not in arguments:
                logger.debug("Body parameter '%s' not in function arguments", key)

My swagger config for this route looks like this:

  /provenance/:
    post:
      tags:
        - INGEST
      description: creates a new provenance if it does not exist
      operationId: propertyservice.routes.provenance.create
      parameters:
        - name: provenance_data
          in: body
          description: provenance_data
          required: true
          schema:
            type: array
            items:
              type: string
      responses:
        200:
          description: OK

And this is what I send with the POST request as body:

["test2"]

Full error message

INFO:werkzeug:127.0.0.1 - - [25/Sep/2015 13:52:47] "POST /provenance/ HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/Library/Python/2.7/site-packages/flask_cors/extension.py", line 110, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Library/Python/2.7/site-packages/flask_cors/extension.py", line 110, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Library/Python/2.7/site-packages/connexion/decorators/validation.py", line 160, in wrapper
    response = function(*args, **kwargs)
  File "/Library/Python/2.7/site-packages/connexion/decorators/validation.py", line 271, in wrapper
    response = function(*args, **kwargs)
  File "/Library/Python/2.7/site-packages/connexion/decorators/produces.py", line 120, in wrapper
    data, status_code = self.get_data_status_code(function(*args, **kwargs))
  File "/Library/Python/2.7/site-packages/connexion/decorators/parameter.py", line 57, in wrapper
    for key, value in body_parameters.items():
AttributeError: 'list' object has no attribute 'items'

JSONEncoder datetime.date encoding fails

The default function of the JSONEncoder does only handle datetime.datetime types in a special way. So if you insert a datetime.date the method fails.

Would be great if you could extend the default function with a special treatment for datetime.date objects:

def default(self, o):
    if isinstance(o, datetime.datetime):
        # only supports UTC timestamps
        return o.isoformat('T') + 'Z'

    if isinstance(o, datetime.date):
        return o.isoformat()
    return json.JSONEncoder.default(self, o)

This converts the date into a W3C conform string format.

Reconsider logging client errors (HTTP status 4xx) with "ERROR" log level

Connexion is currently logging some client failures (HTTP 4xx) as errors (logging.error or logging.exception), we should reconsider/discuss this.

Client errors should not spam the server log file. We could (depending on the severity) consider INFO or WARN log levels.

grep -nr logger.error connexion
connexion/decorators/security.py:57: logger.error("... No auth provided. Aborting with 401.")
connexion/decorators/security.py:74: logger.error("... User scopes (%s) don't include one of the allowed scopes (%s). Aborting with 401.",
connexion/decorators/validation.py:132: logger.error("Unrecognized collectionFormat, cannot validate: %s", col_fmt)
connexion/decorators/validation.py:194: logger.error("'%s' is not a '%s'", data, expected_type_name)
connexion/decorators/validation.py:211: logger.error("Missing parameter '%s'", required_key, extra=log_extra)

unorderable types with `default` response

When you have an operation response like this:

responses:
  200:
    description: "pet response"
    schema:
      type: "array"
      items:
        $ref: "#/definitions/pet"
  default:
    description: "unexpected error"
    schema:
      $ref: "#/definitions/errorModel"

the mix of integers and strings (200 and default) seems to cause jsonify to blow up when loading from {{basePath}}/swagger.json:

ERROR:__main__:Exception on /api/swagger.json [GET]
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.5/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.5/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python3.5/site-packages/connexion/api.py", line 143, in <lambda>
    self.blueprint.add_url_rule('/swagger.json', endpoint_name, lambda: flask.jsonify(self.specification))
  File "/usr/local/lib/python3.5/site-packages/flask/json.py", line 238, in jsonify
    indent=indent),
  File "/usr/local/lib/python3.5/site-packages/flask/json.py", line 126, in dumps
    rv = _json.dumps(obj, **kwargs)
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/__init__.py", line 237, in dumps
    **kw).encode(obj)
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 201, in encode
    chunks = list(chunks)
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 429, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 403, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 403, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 403, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 403, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/json/encoder.py", line 352, in _iterencode_dict
    items = sorted(dct.items(), key=lambda kv: kv[0])
TypeError: unorderable types: str() < int()

Tornado dependency should be optional

As there are now three HTTP server options (Werkzeug, Tornado, gevent), we should make Tornado an optional dependency --- I'm using gevent myself most of the time (easy monkey patching supporting the requests library).

JSON References for Parameters Not Supported

Hello all,

If you are using references in a parameter list an error is thrown: body_parameters = [parameter for parameter in self.parameters if parameter['in'] == 'body']

An example of using a reference is included in the first definition of my swagger file:

Full Swagger def:

# Example YAML to get you started quickly.
# Be aware that YAML has indentation based scoping.
# Code completion support is available so start typing for available options.
swagger: '2.0'

# This is your document metadata
info:
  version: "0.0.1"
  title: Babel 

parameters:
  paper_id:
    name: paper_id
    description: Publisher assigned identifier of a paper
    type: string
    in: path
    required: true
  publisher:
    in: path
    name: publisher
    description: Publisher to perform this operation on
    type: string
    required: true
    enum:
    - aminer
    - arxiv
    - dblp 
    - jstor
    - mas
    - plos
    - pubmed


# Describe your paths here
paths:
  /recommendation/{publisher}/{paper_id}:
    get:
      description: Generates recommendations for `paper_id`
      operationId: app.echo
      produces:
        - application/json
      parameters:
        - $ref: '#/parameters/paper_id'
        - $ref: '#/parameters/publisher'
        - in: query
          name: limit
          default: 5
          maximum: 10
          minimum: 1
          required: false
          type: integer
          description: Maximum number of recommendations to return
        - in: query
          name: algorithm
          required: false
          type: string
          description: Algorithm to generate recommendations with. If not provided a random algorithm will be used.
          enum:
          - ef_expert
          - ef_classic
        - in: query
          name: client_id
          required: false
          type: string
          description: Identifier provided by the platform to clients to track client usage. If you have one please use it.
      # Expected responses for this operation:
      responses:
        # Response code
        200:
          description: Successful response
          schema:
            $ref: "#/definitions/Recommendations"

  /search:
    get:
      description: Searches metadata for known papers
      produces:
        - application/json
      parameters:
        - in: query
          name: query
          description: Query text
          required: true
          type: string
        - in: query
          name: publishers
          description: Publishers to restrict search to.
          type: array
          items:
            type: string
          collectionFormat: multi
        - in: query
          name: start
          description: For pagination. Use ``next`` value from a previous call.
          type: integer
      responses:
        "200":
          description: Search results
          schema: 
            $ref: "#/definitions/SearchResults"

  /metadata/{publisher}/{paper_id}:
    get:
      description: Returns metadata for a specific paper
      produces:
        - application/json
      parameters:
        - $ref: '#/parameters/paper_id'
        - $ref: '#/parameters/publisher'
      responses:
        "200":
          description: Metadata
        "400":
          description: No metadata found 

definitions:
  Recommendation:
    properties:
      paper_id:
        type: string
      publisher:
        type: string
  Recommendations:
    properties:
      transaction_id:
        type: string
        description: An identifier for this transaction. Use this when calling /feedback
      results:
        type: array
        items:
          $ref: '#/definitions/Recommendation'
  SearchResult:
    properties:
      paper_id:
        type: string
        description: ID of paper
      publisher:
        type: string
        description: Publisher name
      score:
        type: number
        description: Relevancy score. Higher is better.
  SearchResults:
    properties:
      count:
        type: integer
        description: Number of search results
      next:
        type: integer
        description: Value to provide for pagination
      start:
        type: integer
        description: Start value for this result.
      results:
        type: array
        items:
          $ref: '#/definitions/SearchResult'

Full Error

ERROR:connexion.api:Failed to add operation for GET /recommendation/<publisher>/<paper_id>
Traceback (most recent call last):
  File "/Users/iwsmith/bin/loopy/lib/python2.7/site-packages/connexion/api.py", line 120, in add_paths
    self.add_operation(method, path, endpoint)
  File "/Users/iwsmith/bin/loopy/lib/python2.7/site-packages/connexion/api.py", line 105, in add_operation
    self.blueprint.add_url_rule(path, operation.endpoint_name, operation.function, methods=[method])
  File "/Users/iwsmith/bin/loopy/lib/python2.7/site-packages/connexion/operation.py", line 128, in function
    validation_decorator = self.__validation_decorator
  File "/Users/iwsmith/bin/loopy/lib/python2.7/site-packages/connexion/operation.py", line 223, in __validation_decorator
    if self.body_schema:
  File "/Users/iwsmith/bin/loopy/lib/python2.7/site-packages/connexion/operation.py", line 89, in body_schema
    body_parameters = [parameter for parameter in self.parameters if parameter['in'] == 'body']

Please let me know if there is anything else I can do to help with this issue.

Thanks!

Expose Request Metrics as JSON endpoint

Connexion could (optionally) expose request metrics via a dedicated "/metrics" endpoint as JSON.
The format could be like Dropwizard Metrics (http://metrics.dropwizard.io/3.1.0/manual/servlets/) or Prometheus.

Example metrics JSON (Dropwizard format):

{
  "version": "3.0.0",
  "gauges": {},
  "counters": {},
  "histograms": {},
  "meters": {},
  "timers": {
    "connexion.response.200.GET.pets": {
      "count": 1917,
      "max": 1125,
      "mean": 386.16423467091,
      "min": 295,
      "p50": 383,
      "p75": 383,
      "p95": 383,
      "p98": 460,
      "p99": 460,
      "p999": 460,
      "stddev": 15.285876814113,
      "m15_rate": 0.0027894332885165,
      "m1_rate": 3.7570008143941E-5,
      "m5_rate": 0.0016195023085788,
      "mean_rate": 0.0031567415804972,
      "duration_units": "milliseconds",
      "rate_units": "calls/second"
    },
    "connexion.response.200.GET.pets.{pet_id}": {
      "count": 392373,
      "max": 627,
      "mean": 219.38202968217,
      "min": 163,
      "p50": 218,
      "p75": 224,
      "p95": 249,
      "p98": 265,
      "p99": 425,
      "p999": 425,
      "stddev": 30.77609293132,
      "m15_rate": 0.69320705677888,
      "m1_rate": 0.67804789230544,
      "m5_rate": 0.71816217263666,
      "mean_rate": 0.64605322610366,
      "duration_units": "milliseconds",
      "rate_units": "calls/second"
    }
  }
}

Swagger paths without operationId property should not crash connexion

To ease incremental development, Connexion should not fail if one of the operationId properties is missing.

$ python3 hack.py
Traceback (most recent call last):
File "hack.py", line 4, in
app.add_api('swagger.yaml')
File "/usr/local/lib/python3.4/dist-packages/connexion/app.py", line 81, in add_api
api = connexion.api.Api(yaml_path, base_path, arguments, swagger_ui)
File "/usr/local/lib/python3.4/dist-packages/connexion/api.py", line 73, in init
self.add_paths()
File "/usr/local/lib/python3.4/dist-packages/connexion/api.py", line 106, in add_paths
self.add_operation(method, path, endpoint)
File "/usr/local/lib/python3.4/dist-packages/connexion/api.py", line 90, in add_operation
security_definitions=self.security_definitions)
File "/usr/local/lib/python3.4/dist-packages/connexion/operation.py", line 52, in init
self.operation_id = operation['operationId']
KeyError: 'operationId'

Prioritisation of similar paths

I'm currently working on an api where we have two paths which looks very similar. The only difference is the last part of the paths. In one path there is a string parameter at then end and in the other one there is a fixed path name:

http://myapi.com/user/{userid}
http://myapi.com/user/insert

So I would suggest that if I call the second path, the operation of the second path should be called. But when I try it out, connexion takes the userid path operation with the parameter insert.

I couldn't find something in the swagger specifications about this, but for swagger, the two paths are not similar. Otherwise the swagger definition would not be valid:

Swagger API

swagger: '2.0'
info:
  version: 1.0.0
  title: 'My API'
paths:
  /user/insert:
    get:
      description: Hello World
      responses:
          200:
            description: OK

  /user/{userid}:
    get:
      description: Hello World
      parameters:
        - name: userid
          in: path
          description: Size of array
          required: true
          type: string
      responses:
          200:
            description: OK

I know the API design might not be the best one, but my question is, if this is swagger valid and if yes, how does connexion prioritize this?

Return api from App.add_api ?

Hi,

I promise not to flood you with spammy pull requests, but would you be willing to accept another small change? I need access to the Swagger spec in other parts of my application. Now I could parse the yaml spec again myself, but seeing as you are already doing the Jinja2 parametrization, it makes sense to me to do this in just one place.

The processed spec is stored in Api.specification. Would you mind if I got App.add_api to return the generated api so that I can then get hold of the Swagger spec? I could then use this in the view parts of my application.

Does that sound a reasonable change to you guys? Or is there a better way to get hold of the Swagger specification in the view functions?

Make it Python 2 compatible

It will be really useful to be compatible with Python 2.x and to support Flask decorators for app routing. I think the later is related to #20

Cross-origin resource sharing support

Is it planned to build CORS into connexion? The only way I found was to use the app (flask) attribute of the app (connexion) and flask-cors:

import connexion
from flask.ext.cors import CORS

app = connexion.App(__name__, port=8080, specification_dir='swagger/')
app.add_api('test.yaml')

# add CORS support
CORS(app.app)

app.run()

HTTPS support

When specifying {{https}} as the scheme in the API YAML file, all the URIs in the Swagger UI that is served are HTTPS endpoints. The problem is that the default server that is running is a "normal" HTTP server. This means that the Swagger UI cannot be used to play with the API.

What is the correct way to start a HTTPS server using connexion?

Custom jsonfier

Instead of using flask's jsonifier use python json module to serialize response as it is more flexible.

Expose WSGI interface

This bug is actually part bug, part feature request/enhancement, depending on how one wants to approach it.

Problem

The Connexion app doesn't expose a WSGI interface, and as such cannot be hosted by Nginx/Apache/other WSGI compliant servers.

Solution 1

Instead of wrapping the Flask application object, make Connexion a Flask extension. I am not sure how much work this would involve, but is likely a breaking change.

Solution 2

Expose the WSGI interface. Note that simply adding a __call__ method isn't sufficient; though the application starts everything is being pushed to Flask, not Connexion. Effectively this results in bypassing Connexion, so it doesn't really solve the problem. I don't know what is involved in exposing the full WSGI interface, but I suspect it is non-trivial.

Just as an example of what won't work, I tried this fix:

    def __call__(self, environ, start_response):
        """
        Provide the shortcut to the underlying WSGI interface
        """
        return self.app(environ, start_response)

on the Connexion.app class. This is meant to mirror what Flask does, but as noted above ends up bypassing Connexion all together.

How to add headers to the Response object?

Hi guys,

I'm loving this project and hoping to make some contributions. One thing I'm unclear on though, is once I have routed the request to the function listed in operationId, is there any way that I can modify the response object before it is sent to the client?

For example, I would like to add a Location header to my 201 response when a resource is successfully created. I can create a Response object myself and return that - but then I seem to lose the work that you have done - for example setting the Content-Type according to the Swagger spec. It may well be that I am doing something wrong!

Can you help me? Is there a way for the function that I implement to modify the response?

many thanks in advance :)

Add support for multiple implementations in operationId

To make it possible to use the same swagger.yaml file, make it possible to specify several operationId entry points. Like:

operationId:
  connexion:
    app.get_users
  swagger1st:
    get_users

Or maybe even to somehow pin the project to enable not per-swagger-first-library, but per-project distinction.

Python3 swagger.json generation error

Hi ! I've been testing the generator and works perfectly on Python 2 but I get this error when asking for the swagger.json if I run it with python3. I'm not sure how I can help to get this working, but please let me know, I'll gladly help on this .

Thanks in advance

Traceback (most recent call last):
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.4/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.4/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.4/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python3.4/site-packages/connexion/api.py", line 141, in <lambda>
    self.blueprint.add_url_rule('/swagger.json', endpoint_name, lambda: flask.jsonify(self.specification))
  File "/usr/local/lib/python3.4/site-packages/flask/json.py", line 238, in jsonify
    indent=indent),
  File "/usr/local/lib/python3.4/site-packages/flask/json.py", line 126, in dumps
    rv = _json.dumps(obj, **kwargs)
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/__init__.py", line 237, in dumps
    **kw).encode(obj)
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/encoder.py", line 194, in encode
    chunks = list(chunks)
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/encoder.py", line 422, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/encoder.py", line 396, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/encoder.py", line 396, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/encoder.py", line 396, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/encoder.py", line 396, in _iterencode_dict
    yield from chunks
  File "/usr/local/Cellar/python3/3.4.3_2/Frameworks/Python.framework/Versions/3.4/lib/python3.4/json/encoder.py", line 345, in _iterencode_dict
    items = sorted(dct.items(), key=lambda kv: kv[0])
TypeError: unorderable types: str() < int()

Not including a 'produces' in a swagger definition results in an error

Hi again,

I found out that if you don't include produces section you will get a very odd error:

Traceback (most recent call last):
  File "/Users/iwsmith/anaconda/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/iwsmith/anaconda/lib/python2.7/site-packages/flask/app.py", line 1478, in full_dispatch_request
    response = self.make_response(rv)
  File "/Users/iwsmith/anaconda/lib/python2.7/site-packages/flask/app.py", line 1577, in make_response
    rv = self.response_class.force_type(rv, request.environ)
  File "/Users/iwsmith/anaconda/lib/python2.7/site-packages/werkzeug/wrappers.py", line 841, in force_type
    response = BaseResponse(*_run_wsgi_app(response, environ))
  File "/Users/iwsmith/anaconda/lib/python2.7/site-packages/werkzeug/test.py", line 867, in run_wsgi_app
    app_rv = app(environ, start_response)
TypeError: 'object' object is not callable

The swagger fragment that generated this is below:

/feedback/{transaction_id}:
    put:
      description: Notify Babel of actions taken based on a recommendations
      consumes:
      - application/json
      operationId: app.put_feedback
      parameters:
        - in: path
          name: transaction_id
          type: string
          required: true
        - in: query
          name: action
          type: string
          required: true
          enum:
          - click
          - download
        - $ref: '#/parameters/paper_id_query'
        - $ref: '#/parameters/publisher_query'
        - $ref: '#/parameters/client_id'
      responses:
        "200":
          description: OK

I fixed this by including a produces: -application/json in the definition.

Document response handling

Create a section in the documentation for response handling.
It should include:

  • Serialization (From the current quickstart section)
  • How connexion handles response objects
  • How to return status code
  • After #77 is merged, how to return headers

Token_info in request object

Include token_info information in the request object so that applications can get that information without having to request it again

Paths with "-" do not work

The "flaskify_path" utility function will rewrite all paths with hyphen ("-") to underscore ("_").

Paths containing hyphens should work too:

paths:
  /my-test-path/{param-1}/{param-2}:
     ...

Add /.well-known/schema-discovery route for queries from TWINTIP

many exceptions regularly appeared on our applications, we find they were caused by the queries from TWINTIP (https://docs.stups.io/en/latest/components/twintip.html)

we need to create a dummy response of "/.well-known/schema-discovery" for each applications, most of them are using connexion.

so it will be nice if connexion can handle those TWINTIP queries internally, or provide a template or something to users, so that we can easily fill some text and connexion generate the response internally.

change use of `operationId`

The spec field operationId is used for client generation method names. While connexion uses operationId for routing directly to a controller, it will make for some very ugly client SDK methods.

For example:

operationId: my.controller.default_controller.getUser

Will generate into this for ObjectiveC:

-(NSNumber*) myControllerDefaultControllerGetUserWithCompletionBlock

It would be nice if connexion followed the same strategy as swagger-inflector and swagger-node for locating the routing controller/method.

Specifically, I'd propose the following:

operationId: getUser
x-swagger-router-controller: my.controller.default_controller

Which allows for a friendly name for the client SDK and server controller method, and uses the vendor extensions for the controller location.

Document how to allow long running request not block everything else

Now per default the long running request is blocking all other requests. This is an issue of Flask, but still would make sense to document.

Here is an example from Henning's email:

#!/usr/bin/env python3                                                                                                                                                                                                                       

import gevent.monkey

gevent.monkey.patch_all()  # CHANGED!

import connexion
import flask
from time import sleep

def get_greeting():

    def generator():
        while True:
            yield "beat\n\n"
            sleep(5)

    response = flask.Response(generator(), mimetype='text/plain', status=200)
    return response

if __name__ == '__main__':
    app = connexion.App(__name__, 8080, server='gevent')  # CHANGED!
    app.add_api('helloworld-api.yaml', arguments={'title': 'Hello World Example'})
    app.run()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.