Package: lcdp-connexion
ConnexionConnexion is a framework that automagically handles HTTP requests based onOpenAPI Specification(formerly known as Swagger Spec) of your API described inYAML format. Connexion allows you to
write an OpenAPI specification, then maps the endpoints to your Python functions; this makes it
unique, as many tools generate the specification based on your Python code. You can describe your
REST API in as much detail as you want; then Connexion guarantees that it will work as you
specified.We built Connexion this way in order to:simplify the development processconfirm expectations about what your API will look likeConnexion Features:Validates requests and endpoint parameters automatically, based on
your specificationProvides a Web Swagger Console UI so that the users of your API can
have live documentation and even call your API’s endpoints
through itHandles OAuth 2 token-based authenticationSupports API versioningSupports automatic serialization of payloads. If your
specification defines that an endpoint returns JSON, Connexion will
automatically serialize the return value for you and set the right
content type in the HTTP header.Why ConnexionWith 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 that offered by frameworks such asHug, which generates a specificationafteryou’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 code logic of your application.Other Sources/MentionsZalando RESTful API guidelines withAPI FirstConnexion listed onSwagger’s websiteBlog post:Crafting effective Microservices in PythonNew in Connexion 2.0:App and Api options must be provided through the “options” argument (old_style_optionshave been removed).You must specify a form content-type in ‘consumes’ in order to consume form data.TheOperationinterface has been formalized in theAbstractOperationclass.TheOperationclass has been renamed toSwagger2Operation.Array parameter deserialization now follows the Swagger 2.0 spec more closely.
In situations when a query parameter is passed multiple times, and the collectionFormat is either csv or pipes, the right-most value will be used.
For example,?q=1,2,3&q=4,5,6will result inq = [4, 5, 6].
The old behavior is available by setting the collectionFormat tomulti, or by importingdecorators.uri_parsing.AlwaysMultiURIParserand passingparser_class=AlwaysMultiURIParserto your Api.The spec validator library has changed fromswagger-spec-validatortoopenapi-spec-validator.Errors that previously raisedSwaggerValidationErrornow raise theInvalidSpecificationexception.
All spec validation errors should be wrapped withInvalidSpecification.Support for nullable/x-nullable, readOnly and writeOnly/x-writeOnly has been added to the standard json schema validator.Custom validators can now be specified on api level (instead of app level).Added support for basic authentication and apikey authenticationIf unsupported security requirements are defined orx-tokenInfoFunc/x-tokenInfoUrlis missing, connexion now denies requests instead of allowing access without security-check.Accessingconnexion.request.user/flask.request.useris no longer supported, useconnexion.context['user']insteadHow to UsePrerequisitesPython 3.6+Installing ItIn your command line, type:$pipinstallconnexionRunning ItPlace your API YAML inside a folder in the root
path of your application (e.gswagger/). Then run:importconnexionapp=connexion.App(__name__,specification_dir='swagger/')app.add_api('my_api.yaml')app.run(port=8080)See theConnexion Pet Store Example Applicationfor a sample
specification.Now you’re able to run and use Connexion!OAuth 2 Authentication and AuthorizationConnexion supports one of the three OAuth 2 handling methods. (See
“TODO” below.) With Connexion, the API security definitionmustinclude a ‘x-tokenInfoUrl’ or ‘x-tokenInfoFunc (or setTOKENINFO_URLorTOKENINFO_FUNCenv var respectively). ‘x-tokenInfoUrl’ must contain an
URL to validate and get thetoken informationand ‘x-tokenInfoFunc must
contain a reference to a function used to obtain the token info. When both ‘x-tokenInfoUrl’
and ‘x-tokenInfoFunc’ are used, Connexion will prioritize the function method. Connexion expects to
receive the OAuth token in theAuthorizationheader field in the
format described inRFC 6750section 2.1. This aspect
represents a significant difference from the usual OAuth flow.Dynamic Rendering of Your SpecificationConnexion usesJinja2to allow specification parameterization through theargumentsparameter. You can define specification arguments for the application either globally (via theconnexion.Appconstructor) or for each specific API (via theconnexion.App#add_apimethod):app=connexion.App(__name__,specification_dir='swagger/',arguments={'global':'global_value'})app.add_api('my_api.yaml',arguments={'api_local':'local_value'})app.run(port=8080)When a value is provided both globally and on the API, the API value will take precedence.Endpoint Routing to Your Python ViewsConnexion uses theoperationIdfrom eachOperation Objectto
identify which Python function should handle each URL.Explicit Routing:paths:/hello_world:post:operationId:myapp.api.hello_worldIf you provide this path in your specification POST requests tohttp://MYHOST/hello_world, it will be handled by the functionhello_worldin themyapp.apimodule. Optionally, you can includex-swagger-router-controller(orx-openapi-router-controller) in your
operation definition, makingoperationIdrelative:paths:/hello_world:post:x-swagger-router-controller:myapp.apioperationId:hello_worldKeep in mind that Connexion follows howHTTP methods work in Flaskand therefore HEAD requests will be handled by theoperationIdspecified under GET in the specification. If both methods are supported,connexion.request.methodcan be used to determine which request was made.Automatic RoutingTo customize this behavior, Connexion can use alternativeResolvers–for example,RestyResolver. TheRestyResolverwill compose anoperationIdbased on the path and HTTP method of
the endpoints in your specification:fromconnexion.resolverimportRestyResolverapp=connexion.App(__name__)app.add_api('swagger.yaml',resolver=RestyResolver('api'))paths:/:get:# Implied operationId: api.get/foo:get:# Implied operationId: api.foo.searchpost:# Implied operationId: api.foo.post'/foo/{id}':get:# Implied operationId: api.foo.getput:# Implied operationId: api.foo.putcopy:# Implied operationId: api.foo.copydelete:# Implied operationId: api.foo.deleteRestyResolverwill give precedence to anyoperationIdencountered in the specification. It will also respectx-router-controller. You can import and extendconnexion.resolver.Resolverto implement your ownoperationId(and function) resolution algorithm.Automatic Parameter HandlingConnexion automatically maps the parameters defined in your endpoint specification to arguments of your Python views as named parameters, and, whenever possible, with value casting. Simply define the endpoint’s parameters with the same names as your views arguments.As an example, say you have an endpoint specified as:paths:/foo:get:operationId:api.foo_getparameters:-name:messagedescription:Some message.in:querytype:stringrequired:trueAnd the view function:# api.py filedeffoo_get(message):# do somethingreturn'You send the message:{}'.format(message),200In this example, Connexion automatically recognizes that your view
function expects an argument namedmessageand assigns the value
of the endpoint parametermessageto your view function.NoteIn the OpenAPI 3.x.x spec, the requestBody does not have a name.
By default it will be passed in as ‘body’. You can optionally
provide the x-body-name parameter in your requestBody schema
to override the name of the parameter that will be passed to your
handler function.WarningWhen you define a parameter at your endpoint asnotrequired, and
this argument does not have default value in your Python view, you will get
a “missing positional argument” exception whenever you call this endpoint
WITHOUT the parameter. Provide a default value for a named argument or use**kwargsdict.Type castingWhenever possible, Connexion will try to parse your argument values and
do type casting to related Python native values. The current
available type castings are:OpenAPI TypePython TypeintegerintstringstrnumberfloatbooleanboolarraylistnullNoneobjectdictIf you use thearraytype In the Swagger definition, you can define thecollectionFormatso that it won’t be recognized. Connexion currently
supports collection formats “pipes” and “csv”. The default format is “csv”.Connexion is opinionated about how the URI is parsed forarraytypes.
The default behavior for query parameters that have been defined multiple
times is to use the right-most value. For example, if you provide a URI with
the the query string?letters=a,b,c&letters=d,e,f, connexion will setletters = ['d', 'e', 'f'].You can override this behavior by specifying the URI parser in the app or
api options.fromconnexion.decorators.uri_parsingimportAlwaysMultiURIParseroptions={'uri_parser_class':AlwaysMultiURIParser}app=connexion.App(__name__,specification_dir='swagger/',options=options)You can implement your own URI parsing behavior by inheriting fromconnexion.decorators.uri_parsing.AbstractURIParser.There are a handful of URI parsers included with connection.OpenAPIURIParser
default: OpenAPI 3.0This parser adheres to the OpenAPI 3.x.x spec, and uses thestyleparameter. Query parameters are parsed from left to right, so if a query
parameter is defined twice, then the right-most definition will take
precedence. For example, if you provided a URI with the query string?letters=a,b,c&letters=d,e,f, andstyle: simple, then connexion
will setletters = ['d', 'e', 'f']. For additional information seeOpenAPI 3.0 Style Values.Swagger2URIParser
default: OpenAPI 2.0This parser adheres to the Swagger 2.0 spec, and will only join together
multiple instance of the same query parameter if thecollectionFormatis set tomulti. Query parameters are parsed from left to right, so
if a query parameter is defined twice, then the right-most definition
wins. For example, if you provided a URI with the query string?letters=a,b,c&letters=d,e,f, andcollectionFormat: csv, then
connexion will setletters = ['d', 'e', 'f']FirstValueURIParserThis parser behaves like the Swagger2URIParser, except that it prefers
the first defined value. For example, if you provided a URI with the query
string?letters=a,b,c&letters=d,e,fandcollectionFormat: csvhen connexion will setletters = ['a', 'b', 'c']AlwaysMultiURIParserThis parser is backwards compatible with Connexion 1.x. It joins together
multiple instances of the same query parameter.Parameter validationConnexion can apply strict parameter validation for query and form data
parameters.  When this is enabled, requests that include parameters not defined
in the swagger spec return a 400 error.  You can enable it when adding the API
to your application:app.add_api('my_apy.yaml',strict_validation=True)API Versioning and basePathSetting a base path is useful for versioned APIs. An example of
a base path would be the1.0inhttp://MYHOST/1.0/hello_world.If you are using OpenAPI 3.x.x, you set your base URL path in the
servers block of the specification. You can either specify a full
URL, or just a relative path.servers:-url:https://MYHOST/1.0description:full url example-url:/1.0description:relative path examplepaths:...If you are using OpenAPI 2.0, you can define abasePathon the top level
of your OpenAPI 2.0 specification.basePath:/1.0paths:...If you don’t want to include the base path in your specification, you
can provide it when adding the API to your application:app.add_api('my_api.yaml',base_path='/1.0')Swagger JSONConnexion makes the OpenAPI/Swagger specification in JSON format
available from eitherswagger.json(for OpenAPI 2.0) oropenapi.json(for OpenAPI 3.x.x) at the base path of the API.
For example, if your base path was1.0, then your spec would be
available at/1.0/openapi.json.You can disable serving the spec JSON at the application level:options={"serve_spec":False}app=connexion.App(__name__,specification_dir='openapi/',options=options)app.add_api('my_api.yaml')You can also disable it at the API level:options={"serve_spec":False}app=connexion.App(__name__,specification_dir='openapi/')app.add_api('my_api.yaml',options=options)HTTPS SupportWhen specifying HTTPS as the scheme in the API YAML file, all the URIs
in the served Swagger UI are HTTPS endpoints. The problem: The default
server that runs 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 when using Connexion?One way,described by Flask, looks like this:fromOpenSSLimportSSLcontext=SSL.Context(SSL.SSLv23_METHOD)context.use_privatekey_file('yourserver.key')context.use_certificate_file('yourserver.crt')app.run(host='127.0.0.1',port='12344',debug=False/True,ssl_context=context)However, Connexion doesn’t provide an ssl_context parameter. This is
because Flask doesn’t, either–but it uses**kwargsto send the
parameters to the underlyingwerkzeugserver.The Swagger UI ConsoleThe Swagger UI for an API is available through pip extras.
You can install it withpip installconnexion[swagger-ui].
It will be served up at{base_path}/ui/wherebase_pathis the
base path of the API.You can disable the Swagger UI at the application level:app=connexion.App(__name__,specification_dir='openapi/',options={"swagger_ui":False})app.add_api('my_api.yaml')You can also disable it at the API level:app=connexion.App(__name__,specification_dir='openapi/')app.add_api('my_api.yaml',options={"swagger_ui":False})If necessary, you can explicitly specify the path to the directory with
swagger-ui to not use the connexion[swagger-ui] distro.
In order to do this, you should specify the following option:options={'swagger_path':'/path/to/swagger_ui/'}app=connexion.App(__name__,specification_dir='openapi/',options=options)If you wish to provide your own swagger-ui distro, note that connexion
expects a jinja2 file calledswagger_ui/index.j2in order to load the
correctswagger.jsonby default. Yourindex.j2file can use theopenapi_spec_urljinja variable for this purpose:const ui = SwaggerUIBundle({ url: "{{ openapi_spec_url }}"})Additionally, if you wish to use swagger-ui-3.x.x, it is also provided by
installing connexion[swagger-ui], and can be enabled like this:fromswagger_ui_bundleimportswagger_ui_3_pathoptions={'swagger_path':swagger_ui_3_path}app=connexion.App(__name__,specification_dir='swagger/',options=options)Server BackendBy default Connexion uses theFlaskserver. For asynchronous
applications, you can also useTornadoas the HTTP server. To do
this, set your server totornado:importconnexionapp=connexion.App(__name__,specification_dir='swagger/')app.run(server='tornado',port=8080)You can use the Flask WSGI app with any WSGI container, e.g.using
Flask with uWSGI(this is common):app=connexion.App(__name__,specification_dir='swagger/')application=app.app# expose global WSGI application objectYou can use theaiohttpframework as server backend as well:importconnexionapp=connexion.AioHttpApp(__name__,specification_dir='swagger/')app.run(port=8080)NoteAlso check aiohttp handlerexamples.Set up and run the installation code:$sudopip3installuwsgi$uwsgi--http:8080-wapp-p16# use 16 worker processesSee theuWSGI documentationfor more information.DocumentationAdditional information is available atConnexion’s Documentation Page.ChangesA full changelog is maintained on theGitHub releases page.Contributing to Connexion/TODOsWe welcome your ideas, issues, and pull requests. Just follow the
usual/standard GitHub practices.You can find out more about how Connexion works and where to apply your changes by having a look
at ourARCHITECTURE.rst.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 (Zalando SE, Berlin) shall be under the
terms and conditions of Apache License 2.0 written below, without any
additional copyright information, terms or conditions.TODOsIf you’d like to become a more consistent contributor to Connexion, we’d love your help working on
these we have a list ofissues where we are looking for contributions.ThanksWe’d like to thank all of Connexion’s contributors for working on this
project, and to Swagger/OpenAPI for their support.LicenseCopyright 2015 Zalando SELicensed 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 athttp://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.