GithubHelp home page GithubHelp logo

python-restx / flask-restx Goto Github PK

View Code? Open in Web Editor NEW
2.1K 2.1K 329.0 1.81 MB

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask

Home Page: https://flask-restx.readthedocs.io/en/latest/

License: Other

Python 99.09% HTML 0.91%
api flask json python rest restful restplus restx swagger

flask-restx's People

Contributors

a-luna avatar ajmaradiaga avatar arajkumar avatar avilaton avatar awiddersheim avatar bdscharf avatar calvin620707 avatar dependabot[bot] avatar dmvass avatar etiennepelletier avatar fixedd avatar foarsitter avatar frol avatar j5awry avatar kashyapm94 avatar leiserfg avatar maho avatar maurerle avatar mdesetty avatar noirbizarre avatar peter-doggart avatar plowman avatar poogles avatar rob-smallshire avatar ryu-cz avatar steadbytes avatar svilgelm avatar tgoodyear avatar tzabal avatar ziirish 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

flask-restx's Issues

Add support to config swagger-ui using app.config

I came across a problem that probably can only be solved by asking you maintainers of this package.

My need: Show model tab by default, instead of example value, in the parameters section in API method documentation.

image

The method that I tried: According docs for Swagger-UI configuration, I can set DEFAULT_MODEL_RENDERING to "model" to make this happen.

Why didn't work: Since flask-restplus doesn't seems to do anything when I set app.config.SWAGGER_UI_DEFAULT_MODEL_RENDERING to "model", I suppose allowing users to set custom configurations of Swagger-UI could be a feature added to flask-restx.

Setting the api representation through the namespace

Code

from flask_restx import Resource, Namespace
from flask_accepts import accepts, responds

from app.main.service.kick_event_service import get_all_kick_events, save_new_kick_event
from app.main.model.kick_event import KickEventSchema

api = Namespace('kick_event', description='kick event related operations')

@api.route('/')
class KickEventResource(Resource):
    @responds(schema=KickEventSchema, many=True)
    @api.produces(["application/vnd.api+json"])
    #@api.representation('application/vnd.api+json') <- no bueno
    def get(self):
        """List all Kick Events"""
        return get_all_kick_events()

Expected Behavior

I am trying to set the content-type of my response header. I am converting my api to JSON-API standard and so I need to use header "content-type:application/vnd.api+json".

Actual Behavior

I saw in the documentation (https://flask-restx.readthedocs.io/en/latest/api.html#flask_restx.Api.representation) that there is the "representation" method which seemed to be what I needed, but adding @api.representation('application/vnd.api+json')
to my code gives me error: "Instance of 'Namespace' has no 'representation' member".

Error Messages

"Instance of 'Namespace' has no 'representation' member".

Environment

  • Python version : 3.8
  • Flask version: 1.1.1
  • Flask-RESTX version: 0.1.0
  • Other installed Flask extensions: flask-accepts

Additional Context

Digging through the source code I can see that the namespace class and the api class are not the same, and that the "representation" method doesn't exist in the namespace class, so that error makes sense... But I would like to know how (if it's possible) to declare my api using a Namespace, and also be able to call the representation function.

I managed to get what I wanted by changing DEFAULT_REPRESENTATIONS = [('application/json', output_json)] to this: DEFAULT_REPRESENTATIONS = [('application/vnd.api+json', output_json)] within the api.py file in the source code for the extension, so now ALL my endpoints have the correct header.. But I'd rather be able to pass it the correct way instead of just overriding the default, if possible.

Wildcard field contains an extra nested level in docs

This might be related to the recent wildcard field changes, e.g. #24.

When using a wildcard field with nested models, the resulting documentation contains three levels: *, <*>, and the nested model, where I would expect just two: The key <*> and the nested model. Example values in the doc also contains an extra nested level.

Please note, the actual output of the marshaled model in get is correct, only the swagger doc is wrong.

Code

from flask import Flask
from flask_restx import Resource, Api, fields

app = Flask(__name__)
api = Api(app)

nested_model = api.model('Nested model', {'nested_string': fields.String()})
wild = fields.Wildcard(fields.Nested(nested_model))
wildcard_model = api.model('Wildcard model', {'*': wild})

@api.route('/hello')
class HelloWorld(Resource):
    @api.marshal_with(wildcard_model)
    def get(self):
        return {
            'foo': {
                'nested_string': 'foo'
            },
            'bar': {
                'nested_string': 'bar'
            }
        }

if __name__ == '__main__':
    app.run(debug=True)

Repro Steps (if applicable)

  1. Run example above
  2. View documentation at http://localhost:5000

Actual model doc

wildcard-model

Expected model doc

Only one level of *, not two.

Actual example value doc

{
  "*": {
    "additionalProp1": {
      "nested_string": "string"
    },
    "additionalProp2": {
      "nested_string": "string"
    },
    "additionalProp3": {
      "nested_string": "string"
    }
  }
}

Expected example value doc

{
  "additionalProp1": {
    "nested_string": "string"
  },
  "additionalProp2": {
    "nested_string": "string"
  },
  "additionalProp3": {
    "nested_string": "string"
  }
}

or

{
  "*": {
    "nested_string": "string"
  }
}

Actual output of get (correct)

{
  "bar": {
    "nested_string": "bar"
  },
  "foo": {
    "nested_string": "foo"
  }
}

Environment

  • Python 3.7
  • Flask 1.1.1
  • Flask-RESTX 0.1.1

Marshal not renaming fields with attribute

Either I am misunderstanding how to use the marshal feature, and the documentation or the following is a bug.

Code

m = api.model('mymodel', {'name': fields.String(attribute='MyName')})
@api.route('/test')
class Test(Resource):
    @api.expect(m)
    def post(self, **kwargs):
        logger.debug(api.payload)
        logger.debug(request.get_json())
        logger.debug(marshal(api.payload, m))
        return api.payload

Output

127.0.0.1 - - [26/Jan/2020 22:57:36] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [26/Jan/2020 22:57:37] "GET /swaggerui/swagger-ui-bundle.js HTTP/1.1" 304 -
127.0.0.1 - - [26/Jan/2020 22:57:37] "GET /swaggerui/swagger-ui.css HTTP/1.1" 304 -
127.0.0.1 - - [26/Jan/2020 22:57:37] "GET /swaggerui/droid-sans.css HTTP/1.1" 304 -
127.0.0.1 - - [26/Jan/2020 22:57:37] "GET /swaggerui/swagger-ui-standalone-preset.js HTTP/1.1" 304 -
127.0.0.1 - - [26/Jan/2020 22:57:37] "GET /swaggerui/favicon-16x16.png HTTP/1.1" 200 -
127.0.0.1 - - [26/Jan/2020 22:57:37] "GET /swagger.json HTTP/1.1" 200 -
[2020-01-26 22:58:09,111] DEBUG in switchvox: {'name': '987'}
[2020-01-26 22:58:09,111] DEBUG in switchvox: {'name': '987'}
[2020-01-26 22:58:09,111] DEBUG in switchvox: {'name': None}

Expected Behavior

I expected that using the fields.String(attribute="...") would cause the rewrite of the data being entered so that instead of receiving {'name': '987'} I would have received {'MyName': '987'}
Either when calling api.payload or request.get_json()

Actual Behavior

No change in field names.

Error Messages/Stack Trace

Does not error, just does not return expected results.

Environment

  • Python 3.7
  • Flask version current
  • Flask-RESTX version current
  • Other installed Flask flask_restplus

Maybe I am calling it wrong, even using marshal() did not return any data. Unfortunately the documentation is not really clear to me around this part.

CI is broken for Coveralls

Repro Steps

  1. Create a PR
  2. The tests run nicely in each environment
  3. But suddenly, something goes wrong...

Expected Behavior

The tests, coveralls, etc. should all pass.

Actual Behavior

Coveralls does not pass

Error Messages/Stack Trace

You can see an example failure here.

Not on TravisCI. You have to provide either repo_token in .coveralls.yml or set the COVERALLS_REPO_TOKEN env var.
Traceback (most recent call last):
  File "/opt/hostedtoolcache/Python/3.6.10/x64/lib/python3.6/site-packages/coveralls/cli.py", line 61, in main
    service_name=options['--service'])
  File "/opt/hostedtoolcache/Python/3.6.10/x64/lib/python3.6/site-packages/coveralls/api.py", line 58, in __init__
    self.ensure_token()
  File "/opt/hostedtoolcache/Python/3.6.10/x64/lib/python3.6/site-packages/coveralls/api.py", line 67, in ensure_token
    self.config_filename))
coveralls.exception.CoverallsException: Not on TravisCI. You have to provide either repo_token in .coveralls.yml or set the COVERALLS_REPO_TOKEN env var.
##[error]Process completed with exit code 1.

Additional Context

  • The probably fix here is to create a Github encrypted secret to match the COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} line in test.yml
  • This is blocking the tests from passing on #35.

Integrate marshmallow

There exists a huge pile of forks etc of the flask_restful family and the one thing that seems to be missing consistently is native support for marshmallow. It would be really great if restx finally integrated that.
The flask-restplus-patched project exists but its based off ancient apispec and so it's not quite there

Flask-RestPlus drop in replacement milestone

After the werkzeug issue #35 that also hit Flask-RestPlus I thought about jumping over to Flask-RestX but i am not sure how stable or close this fork is to the original. I am assuming release 1.0 is the goal for a drop in replacement with only bug fixes and no API breaking changes right? I is safe to move over now?

Email field type?

I think it would be useful to have a email field type, which would be similar to fields.String, but would have a Swagger example value similar to "[email protected]" instead of "string: and would validate that the field contains a properly formatted email address

Feature Request: keyword agument to allow fields to accept nulls

When creating models using fields for use with marshaling responses and validating input with the expect decorator, it would be useful to allow input fields to allow null values.

I managed to extend the fields.String type in order to support this feature in my API, but the code for generating documentation does not handle this properly.

Example class for extending fields to allow null input values when a Model instance is used with the expect decorator:

class NullableStringField(fields.String):
     def schema(self):
        schema = super().schema()
        schema.pop('type')
        enum = schema.pop('enum')
        schema['oneOf'] = []
        schema['oneOf'].append({'type': 'null'})
        schema['oneOf'].append({'type': 'string'})
        if enum is not None:
            schema['oneOf'][-1]['enum'] = enum
        return schema

Swagger documentation with two or more blueprints

Hello! Fist of all, thanks for the awesome project!

So, I work with a flask-restx project with two independent Blueprints, each of them with your own documentation:

bp_mapi = Blueprint('api_mapi', __name__)
api_mapi = Api(bp_mapi,
               title="Harpo Management API",
               version=MAPI_VERSION,
               doc="/mapi/doc",
               prefix='/mapi'
               )
bp_host = Blueprint('api_host', __name__)
api_host = Api(bp_host,
               title="Harpo Host communication API",
               version=MAPI_VERSION,
               doc="/host/doc",
               decorators=[check_host_jwt]
               )

The blueprints are registered in this order:

    app.register_blueprint(bp_host)
    app.register_blueprint(bp_mapi)

The problem is when I try to access /mapi/doc, the project shows the host documentation.

It's an issue or am I forgeting something?

Thanks in advance!

Document/specify request parser PUT body that is not JSON

Question
I'm using the request parser to specify & document an API that expects a (possibly) non-JSON payload in a PUT request, where the Content-Type header specifies the mime type of the body contents. My issue is with the Swagger spec that is generated from the parser arguments.

At the moment, the location parameter to add_argument can only be set to form or json. The first will have the Swagger UI PUT a POST form, instead of a vanilla body, and it sets the Content-Type to application/x-www-form-urlencoded or multipart/form-data in the Swagger API documentation. The second will force the Content-Type to application/json which is not correct either.

Is there a way to have the Swagger UI show that it expects a PUT body without forcing the content type?

Context
Code looks something like this:

blueprint = Blueprint('api', __name__)
api = Api(blueprint)
ns = api.namespace('generate', description='Generator service')

# Register the api blueprint
app.register_blueprint(blueprint, url_prefix='/api')

generate_data_parser = api.parser()
generate_data_parser.add_argument('Content-Type', required=False, default="application/ld+json", help='The MIME type of of the data provided.', location='headers')
# Set location either to json or form, but cannot set to body?
generate_data_parser.add_argument("data", required=True, location="json", help="The data that you want to validate.")
@ns.route('/data', methods=['PUT'], doc={"name": "Generate something cool", "description": "Generate something cool"})
class GenerateData(Resource):

    @ns.expect(generate_data_parser)
    def put(self):
        byte_data = request.data
        mime_type = request.headers.get('Content-Type', "application/ld+json")

        # DO SOMETHING REALLY INTELLIGENT

        return make_response(...)

@api.expect and @api.response decorators don't consistently handle documentation of pre-defined Model from string name

Code

I'm trying to organize my code so that model definitions are shared, in order to reduce clutter, and so that pure API logic ends up in its own files. Here's the structure that I've got right now.

In app.py (the entry point):

from api import api
from flask import Flask

app = Flask(__name__)
api.init_app(app)

In api/__init__.py:

from flask_restx import Api

from .demo import api as demo_ns
from .models import register_models

api = Api()
register_models(api)
api.add_namespace(demo_ns)

In api/models.py:

from flask_restx import fields

models = {
    'My Cool Model': {'my_field': fields.String(description='A cool field!')}
}


def register_models(api):
    for model_name, model_data in models.items():
        api.model(name=model_name, model=model_data)

In api/demo.py:

from flask import json
from flask_restx import Namespace, Resource, Model

api = Namespace('demo', description='Demo and testing endpoints')


@api.route('/hello')
class HelloWorld(Resource):
    @api.expect(Model('My Cool Model'), validate=True)
    @api.response(200, description='Success', model=Model('My Cool Model'))
    def post(self):
        return json.jsonify({'my_field': 'hello!'})

Repro Steps (if applicable)

  1. Run the above code.
  2. Note that the generated documentation does not have a definition for the data returned on a 200 response, but it does have a definition for the data expected in the request.
  3. Change the model parameter in the api.response decorator to [Model('My Cool Model')].
  4. Reload the documentation.
  5. Note that the generated documentation now specifies that a list of objects are returned on a 200 response.

Expected Behavior

Constructing a model object should work the same in both places; if constructing an already-defined model object works for the api.expect decorator, it should also work for the api.response decorator, and vice versa.

Actual Behavior

With the above code, the model will properly render in the documentation for the api.expect decorator, but it will not render for the api.response decorator. However, changing the model parameter in the api.response decorator from Model('My Cool Model') to [Model('My Cool Model')] (i.e. encapsulating it in a list) causes it to render. This is fine for when I want my defined API to produce a list of objects, but not so good for when I want my defined API to produce a single object.

Environment

  • Python version
    Running the python3.7-slim docker image, exact version is below:
Python 3.7.5 (default, Nov 15 2019, 02:40:28) 
[GCC 8.3.0] on linux
  • Flask version: 1.1.1
  • Flask-RESTX version: 0.1.1
  • Other installed Flask extensions: N/A

Additional Context

My current workaround is to include a make_model function in the api/models.py file:

def make_model(api, model_name):
    return api.model(model_name, models[model_name])

This function can then be imported and called in api/demo.py as follows, and produces the expected result in the documentation:

from .models import make_model
# snip
@api.response(200, description='Success', model=make_model(api, 'My Cool Model'))

Using semver

the latest release of restx is 0.1, the pypi package has 0.1.0, the conda-forge (#28) 0.1.0 (it cannot download the package from pypi using 0.1 version).
So I would recommend to use semver for the releases and always three digest, include tags and releases.

The main problem people have with semver, that if there is a backward incompatible changed, they afraid to increase the major number.

Here is a short summary:
Given a version number MAJOR.MINOR.PATCH, increment the:

  • MAJOR version when you make incompatible API changes,
  • MINOR version when you add functionality in a backwards compatible manner, and
  • PATCH version when you make backwards compatible bug fixes.

Error in marshalling can cause a TypeError

Code

from flask_restx import Model, marshal, fields

model = Model('Test', {'field': fields.Float()})

marshal({'field': {'min': '12.1', 'max': '12.2'}}, model)

Expected Behavior

Given the example code I would expect to receive a MarshallingError with a useful error message directing me to where the problem is.

Actual Behavior

I receive a TypeError only telling me the underlying error with no context.

Environment

  • Python version: 3.8
  • Flask version: 1.1.1
  • Flask-RESTX version: 0.1.1

Additional Context

I have a PR incoming for this. I am opening this issue as that is what the CONTRIBUTING guidelines suggest.

Feature request: Models based on dataclasses

I'm interested in this feature, so I'm re-opening this issue for flask-restx (@casparjespersen):

Since Python 3.7 dataclasses was introduced. This is an easy way to define data models:

from dataclasses import dataclass

@dataclass
class Foo:
   name: str
   age: int

@dataclass
class Bar:
   foo: Foo

bar = Bar(Foo("Jane Doe", 42))

There is a growing eco-system around this, e.g. the jsons module that allows (de)serialization of such objects:

import jsons

bar_dict = jsons.dump(bar)
assert isinstance(bar_dict, dict)

bar_again = jsons.load(bar_dict, cls=Bar)
assert isinstance(bar_again, Bar)

It would be interesting to have a discussion on supporting dataclasses as valid model types in flask-restplus, since it is a Python-native data structure (compared to e.g. marshmallow), but it appears (to my knowledge?) to lack validation metadata, etc.

Exception data is returned instead of custom error handle data

Hi,

I'm having an issue with the error handler which does not return to the client the correct data. Here's a simple test case that currently fail:

Code

    def test_errorhandler_for_custom_exception_with_data(self, app, client):
        api = restx.Api(app)

        class CustomException(RuntimeError):
            data = "Foo Bar"

        @api.route('/test/', endpoint='test')
        class TestResource(restx.Resource):
            def get(self):
                raise CustomException('error')

        @api.errorhandler(CustomException)
        def handle_custom_exception(error):
            return {'message': str(error), 'test': 'value'}, 400

        response = client.get('/test/')
        assert response.status_code == 400
        assert response.content_type == 'application/json'

        data = json.loads(response.data.decode('utf8'))
        assert data == {
            'message': 'error',
            'test': 'value',
        }
E       AssertionError: assert 'Foo Bar' == {'message': 'error', 'test': 'value'}

Repro Steps

  1. Register an error handler
  2. Raise an exception having the attribute data (such as Marshmallow ValidationError)
  3. The client gets back the value of the data attribute of the exception, not the one from the error handler

Expected Behavior

Should return to the client the return value of the error handler

Actual Behavior

Output the exception attribute instead

Environment

  • Python version: 3.6 and 3.7
  • Flask version: 1.1.1
  • Flask-RESTX version: 0.1.0

Additional Context

The issue appears when the exception has an attribute data, because in api.py line 655 (handle_error) there's a data = getattr(e, 'data', default_data), so the handler returns the data attribute of the exception e instead of the custom handler data located in default_data

Thanks!

Address Werkzeug, Flask, and Python Versions for flask-restx 0.2.0

Werkzeug 1.0 was released Feb 6th and removes py3.4 support.
Flask requires Werkzeug >= 0.15.0, so a default install from scratch will pull WZ 1.0
Flask has moved onto the 1.X series as of mid-2018 but we "allow" 0.8+

Address the large variance and forward motion of flask and werkzeug.

Flask >= 1.0.2 (1.0 and 1.0.1 had bugs. 1.0.2 stayed stable for ayear)
Werkzeug >= 1.0 (yes, forcing forward compatibility with 1.0 series)

remove py34

Related to #34 and #35

Custom swagger doc using api.doc

Hi, all~ Thanks for this awesome project.

I need to document a request JSON body using custom YAML rather than defining a model every time.
This is what I have tried.

POST_BODY_DOC = """
consumes:
  - application/json
parameters:
  - in: body
    name: user
    description: The user to create.
    schema:
      type: object
      required:
        - userName
      properties:
        userName:
          type: string
        firstName:
          type: string
        lastName:
          type: string
"""

@api .route('/app/demo')
class DemoAPI(Resource):
    @api.doc(**yaml.load(POST_BODY_DOC))
    def post(self):
          .....

Rename flask-restplus -> flask-restx

All occurrences of Flask-RESTPlus, flask-restplus e.t.c. should be replaced with Flaks-RESTX, flask-restx e.t.c. In addition, the Flask-RESTPlus logo should also be removed until a Flask-RESTX logo is created.

Swagger documentation behind a reverse proxy with subpaths

When deploying a service behind a proxy reverse (Traefik) and under a subpath domain (https://api.example.com/service1) swagger.json and swagger-ui sources are not available since flask-restx is looking for them in the root url (https://api-example.com/swagger.json).

My current solution is to set an environment variable for the subpath domain and redefining where the files are located:

    if Config.URL_SUBDOMAIN:
        @apidoc.apidoc.add_app_template_global
        def swagger_static(filename):
            return "{0}/swaggerui/{1}".format(Config.URL_SUBDOMAIN, filename)

    api = Api(app=app, doc='/doc', version=version(), title="API documentation")

    if Config.URL_SUBDOMAIN:
        @api.documentation
        def custom_ui():
            return render_template("swagger-ui.html", title=api.title, specs_url="{}/swagger.json".format(Config.URL_SUBDOMAIN))

With this solution the access to the documentation is available but we can not execute the queries from swagger-ui since it does not now anything about the subpath domain.

Flask-restx and swagger-ui should respect the full url where the api is being served.

Flask-RESTX Models Re-Design

For quite some time there have been significant issues around data models, request
parsing and response marshalling in flask-restx (carried over from
flask-restplus). The most obvious of which is the deprecation warning
about the reqparse module in the documentation that has been in place for far
too long
. These changes have been put off for various reasons which I won't
discuss here, however now the new fork is steadily underway I (and no doubt others) would like
to start addressing this.

Since this digs quite deep into the architecture of flask-restx there will be
significant (and likely breaking) changes required. As such, this issue is to
serve as a discussion around the API we would like to provide and some initial
ideas
of how to best proceed. This is not intended to be the starting point of
hacking something together which makes things worse!

I will set out my current thoughts on the topic, please contribute by adding
more points and expanding on mine with more discussion.

High Level Goals:

  • Uniform API for request parsing and response marshalling
    • e.g. remove the separation between reqparse and models
  • Generate correct and valid Swagger/OpenAPI Specifications
  • Validation of input and output data should conform to the generated
    Swagger/OpenAPI Specifications
    • e.g. If the Swagger/OpenAPI spec considers a value valid, the model should too.
  • Define models using JSON Schema
    • Supported already, but with numerous issues (@j5awry has been battling for some time)
  • OpenAPI 3 support

General Issues/Discussion Points

  • What should the API look like?
    • Continue with the api.marshal , api.doc decorator style?
    • How to define models?
      • Do we force direct usage of another library e.g. Marshmallow or wrap in
        some other API and use the library for the "under the hood" work?
  • Model validation
    • External libraries e.g. Marshmallow
  • Schema Generation
    • External libraries e.g. Marshmallow
  • Backwards compatibility
    • Continue to support reqparse and existing models interface?
    • Swagger 2.0 vs OpenAPI 3.0
      • IMO generating both should be a goal if possible

Resources/Notable Libraries

How to configure package namespaces?

The documentation only shows an example where all related Dog functionality is inside a dog.py module with the api = Namespace('dogs', description='Dogs related operations') declaration at the top.

But what if I wish to have multiple an entire pacakge devoted to a namespace? I was expecting to be able to do it like this:

project
โ”œ-- app.py
โ”œ-- dog_api
    โ”œ-- __init__.py  <---- `api = Namespace('dogs', path='/dogs')`
    โ”œ-- sounds.py <------ `from . import api; @api.route('/sounds/<int:soundid>`
    โ”œ-- foods.py <------ `from . import api; @api.route('/foods/<int:foodid>``
    โ”œ-- breeds.py <------ `from . import api; @api.route('/breeds/<int:breedid>``

The code for app.py would have this:

from .dog_api import api as dog_api


api = Api(app)
api.add_namespace(dog_api, path='/dogs')

Obviously this is example code, but I have a real project set up very similarly to this and it's running into route errors. When running tests it's apparent that the routes aren't being registered in time/properly and trying to access /dogs/sounds/something will cause a 404. I believe it has to do with the order that I'm importing/add_namespaceing.

What should the proper import order/order to use add_namespace be when a namespace represents an entire pacakge?

Werkzeug ImportError

Got an ImportError on ../flask_restx/api.py

Code

from werkzeug import cached_property

Expected Behavior

cached_property is not longer located in werkzeug for werkzeug 1.0.0

Actual Behavior

cached_property is located in werkzeug.utils for werkzeug 1.0.0

Error Messages/Stack Trace

Error: While importing "run", an ImportError was raised:

Traceback (most recent call last):
  File "/home/nasser/.local/lib/python3.6/site-packages/flask/cli.py", line 240, in locate_app
    __import__(module_name)
  File "/home/nasser/me/cs/IslamApp/run.py", line 3, in <module>
    from application import app
  File "/home/nasser/me/cs/IslamApp/application/__init__.py", line 51, in <module>
    module = importlib.import_module(plugin['path'], package='application')
  File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/home/nasser/me/cs/IslamApp/application/api/__init__.py", line 4, in <module>
    from flask_restx import Api
  File "/home/nasser/.local/lib/python3.6/site-packages/flask_restx/__init__.py", line 5, in <module>
    from .api import Api  # noqa
  File "/home/nasser/.local/lib/python3.6/site-packages/flask_restx/api.py", line 28, in <module>
    from werkzeug import cached_property
ImportError: cannot import name 'cached_property'

Environment

  • Python 3.6.9
  • Flask 1.1.1
  • Flask-RESTX 0.1.1
  • flask-sqlalchemy 2.4.1
  • flask-debugtoolbar 0.10.1
  • flask-login 0.5.0
  • flask-migrate 2.5.2
  • flask-wtf 0.14.3

Additional Context

I think it's good to release a version for werkzeug 1.0.0 since it has been release 2020-02-06
This is the blogpost about https://www.palletsprojects.com/blog/werkzeug-1-0-0-released/

wrong url strings generated in rules when adding namespace with path "/"?

Folks,

I've created a new namespace called base_api.
base_api = Namespace("base")

Have added routes to it with @base_api.route(...)

I want this to be the "root" namespace, so I
api.add_namespace(base_api, path="/")

The problem is now that I have two leading slashes in the url strings for the rules generated and not one.
I've tried leaving the path out or setting it to the empty string but it's replaced by the string "base".

I know I could use the api created by doing:
api = Api(...)
in the main module and put stuff into the default namespace, but I prefer to put all my routes in separate files and separate namespaces rather than cluttering up the main app.

Is there an way to get what I prefer, other than leaving off the leading slash on all the routes for the base namespace?

The following total kludge seems to fix it for me, but maybe I'm not understanding how things work too well :)

class CustomApi(Api):                                                                                    
    def ns_urls(self, ns, urls):                                                                         
        def fix(url):                                                                                    
            return url[1:] if url.startswith("//") else url                                              
                                                                                                         
        return [fix(url) for url in super().ns_urls(ns, urls)]     

Thanks :)

-- gyre --

Werkzeug V1.x breaks restx import

***** BEFORE LOGGING AN ISSUE *****

  • Is this something you can debug and fix? Send a pull request! Bug fixes and documentation fixes are welcome.
  • Please check if a similar issue already exists or has been closed before. Seriously, nobody here is getting paid. Help us out and take five minutes to make sure you aren't submitting a duplicate.
  • Please review the guidelines for contributing

Code

from flask_restx import Api

Repro Steps (if applicable)

  1. Use this Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
#flask-restplus = "*"
flask-restx = "*"
flask = "*"

[requires]
python_version = "3.7"
  1. Pipenv install
  2. Attempt to import Api from restx
paul-> python 
Python 3.7.6 (default, Dec 30 2019, 19:38:28) 
[Clang 11.0.0 (clang-1100.0.33.16)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from flask_restx import Api
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/paul/.local/share/virtualenvs/restplus-test-_yVwyWWD/lib/python3.7/site-packages/flask_restx/__init__.py", line 5, in <module>
    from .api import Api  # noqa
  File "/Users/paul/.local/share/virtualenvs/restplus-test-_yVwyWWD/lib/python3.7/site-packages/flask_restx/api.py", line 28, in <module>
    from werkzeug import cached_property
ImportError: cannot import name 'cached_property' from 'werkzeug' (/Users/paul/.local/share/virtualenvs/restplus-test-_yVwyWWD/lib/python3.7/site-packages/werkzeug/__init__.py)
>>> 
  1. Broken!

Expected Behavior

Api should be silently imported

Actual Behavior

Import fails with error below

Error Messages/Stack Trace

Traceback (most recent call last):
File "", line 1, in
File "/Users/paul/.local/share/virtualenvs/restplus-test-_yVwyWWD/lib/python3.7/site-packages/flask_restx/init.py", line 5, in
from .api import Api # noqa
File "/Users/paul/.local/share/virtualenvs/restplus-test-_yVwyWWD/lib/python3.7/site-packages/flask_restx/api.py", line 28, in
from werkzeug import cached_property
ImportError: cannot import name 'cached_property' from 'werkzeug' (/Users/paul/.local/share/virtualenvs/restplus-test-_yVwyWWD/lib/python3.7/site-packages/werkzeug/init.py)

Environment

  • Python version 3.7.6
paul-> pipenv graph
flask-restx==0.1.0
  - aniso8601 [required: >=0.82, installed: 8.0.0]
  - Flask [required: >=0.8, installed: 1.1.1]
    - click [required: >=5.1, installed: 7.0]
    - itsdangerous [required: >=0.24, installed: 1.1.0]
    - Jinja2 [required: >=2.10.1, installed: 2.11.1]
      - MarkupSafe [required: >=0.23, installed: 1.1.1]
    - Werkzeug [required: >=0.15, installed: 1.0.0]
  - jsonschema [required: Any, installed: 3.2.0]
    - attrs [required: >=17.4.0, installed: 19.3.0]
    - importlib-metadata [required: Any, installed: 1.5.0]
      - zipp [required: >=0.5, installed: 2.1.0]
    - pyrsistent [required: >=0.14.0, installed: 0.15.7]
      - six [required: Any, installed: 1.14.0]
    - setuptools [required: Any, installed: 45.1.0]
    - six [required: >=1.11.0, installed: 1.14.0]
  - pytz [required: Any, installed: 2019.3]
  - six [required: >=1.3.0, installed: 1.14.0]
  • Flask version
  • Flask-RESTX version
  • Other installed Flask extensions

Additional Context

This also occurs using flask-restplus. My guess is that the latest Flask breaks both.

Nested fields not working as expected

Dear All,
I am trying to use response marshaling but I get an error. I managed to make the fields/marshal work as indicate in the example - Nested fields

Code

import hashlib, hmac, time
try:
    from urllib.parse import urlencode
except ImportError:
    from urllib import urlencode
import requests
from flask_restx import Namespace, Resource, fields
from dataenhancer.secret.config import ENDPOINT_ROOMS, INDICO_SERVER, API_KEY, SECRET_KEY

namespace = Namespace('indico', description="Contrast information with Indico endpoint")

timing_fields = {}
timing_fields['date'] = fields.String(readOnly=True, description='aa')
timing_fields['tz'] = fields.String(readOnly=True, description='aa')
timing_fields['time'] = fields.String(readOnly=True, description='aa')

booking_fields = {}
booking_fields['url'] = fields.String(readOnly=True, description='aa') 
booking_fields['id'] = fields.String(readOnly=True, description='aa') 
booking_fields['creator'] = fields.String(readOnly=True, description='aa') 
booking_fields['start_dt'] = fields.Nested(timing_fields)
booking_fields['end_dt'] = fields.Nested(timing_fields)

resource_fields = {'vc_room': fields.String(readOnly=True, description='aa')}
resource_fields['event'] = fields.String(readOnly=True, description='aa')
resource_fields['booking'] = fields.Nested(booking_fields)

indico_model = namespace.model('indicoevents', resource_fields)

def _build_indico_request(path, params, api_key=None, secret_key=None, only_public=False, persistent=False):
    items = list(params.items()) if hasattr(params, 'items') else list(params)
    if api_key:
        items.append(('apikey', api_key))
    if only_public:
        items.append(('onlypublic', 'yes'))
    if secret_key:
        if not persistent:
            items.append(('timestamp', str(int(time.time()))))
        items = sorted(items, key=lambda x: x[0].lower())
        url = '%s?%s' % (path, urlencode(items))
        signature = hmac.new(secret_key.encode('utf-8'), url.encode('utf-8'),
                             hashlib.sha1).hexdigest()
        items.append(('signature', signature))
    if not items:
        return path
    return '%s?%s' % (path, urlencode(items))

@namespace.doc('class')
@namespace.route('/<string:office>')
class IndicoEventsEndpoint(Resource):
    #decorators = [require_token]

    @namespace.doc('Get_all_possible_events')
    @namespace.marshal_with(indico_model)
    def get(self, office):
        params = {
            'from': 'today',
            'to': 'today'
        }
        query = _build_indico_request('{}{}.json'.format(ENDPOINT_ROOMS, office), params, api_key=API_KEY, secret_key=SECRET_key,
                                persistent=True)   
        res = requests.get('{}/{}'.format(INDICO_SERVER, query))
        res.raise_for_status()
        return res.json()

I get always the following exception:

Unable to render schema
Traceback (most recent call last):
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/api.py", line 500, in __schema__
    self._schema = Swagger(self).as_dict()
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 202, in as_dict
    **kwargs
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 391, in serialize_resource
    path[method] = self.serialize_operation(doc, method)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 397, in serialize_operation
    'responses': self.responses_for(doc, method) or None,
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 510, in responses_for
    schema = self.serialize_schema(model)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 563, in serialize_schema
    self.register_model(model)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 592, in register_model
    self.register_field(field)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 600, in register_field
    self.register_model(field.nested)
  File "/Users/r/.pyenv/versions/3.6.5/lib/python3.6/site-packages/flask_restx/swagger.py", line 583, in register_model
    if name not in self.api.models:
TypeError: unhashable type: 'dict'
127.0.0.1 - - [04/Feb/2020 23:07:01] "GET /api/v1/swagger.json HTTP/1.1" 500 -
127.0.0.1 - - [04/Feb/2020 23:07:01] "GET /swaggerui/favicon-32x32.png HTTP/1.1" 200 -

By doing:

$ python
Python 3.6.5 (default, Apr 28 2019, 21:42:51) 
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.10.44.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from flask_restx import fields, marshal
>>> import json
>>> timing_fields = {}
timing_fields['date'] = fields.String(readOnly=True, description='aa')
timing_fields['tz'] = fields.String(readOnly=True, description='aa')
timing_fields['time'] = fields.String(readOnly=True, description='aa')

booking_fields = {}
booking_fields['url'] = fields.String(readOnly=True, description='aa') 
booking_fields['id'] = fields.String(readOnly=True, description='aa') 
booking_fields['creator'] = fields.String(readOnly=True, description='aa') 
booking_fields['start_dt'] = fields.Nested(timing_fields)
booking_fields['end_dt'] = fields.Nested(timing_fields)

resource_fields = {'vc_room': fields.String(readOnly=True, description='aa')}
resource_fields['event'] = fields.String(readOnly=True, description='aa')
resource_fields['booking'] = fields.Nested(booking_fields)>>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> 
>>> 
>>> data = {
            "vc_room": "dafdafds",
            "event": "afdasfdsfda",
            "booking": {
                "url": "https://indico-staging.xx.xx/rooms/booking/402627",
                "start_dt": {
                    "date": "2020-02-03",
                    "tz": "None",
                    "time": "13:00:00"
                },
                "end_dt": {
                    "date": "2020-02-03",
                    "tz": "None",
                    "time": "17:00:00"
                },
                "id": 402627,
                "creator": "R"
            }
}... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 
>>> 
>>> 
>>> json.dumps(marshal(data, resource_fields))
'{"vc_room": "dafdafds", "event": "afdasfdsfda", "booking": {"url": "https://indico-staging.cern.ch/rooms/booking/402627", "id": "402627", "creator": "R", "start_dt": {"date": "2020-02-03", "tz": "None", "time": "13:00:00"}, "end_dt": {"date": "2020-02-03", "tz": "None", "time": "17:00:00"}}}'
>>> ^C

Is this a bug? I can provide further evidences or reduce the code to reach this issue, but it looks to me quite a general one.

Running python 3.6.5:
Flask==1.1.1
Flask-Cors==3.0.8
flask-restplus==0.13.0
flask-restx==0.1.0

Apologies if I am missing something trivial.
Thank you.

Using GetHub Actions instead of Travis CI

I would like to make a proposal to use GitHub Actions instead of Travis CI.
In my opinion and from my experience, the GitHub Actions works better and a bit faster than Travis CI.
The most important enhancement is that everything is stored on the GitHub.
GitHub workflows allows to test on Linux, Mac and Windows VMs for free for the open source projects.
All developers can test their code in their forks just pushing a code into a brach, open the actions (in there fork, not here) and see the results.

Examples of the Push and Pull Request checks are here: https://github.com/SVilgelm/flask-restx/pull/3/checks
it's a PR to master branch in my fork.

Example of the release workflow: https://github.com/SVilgelm/flask-restx/runs/410117918?check_suite_focus=true
and the pypi: https://pypi.org/project/flask-restx-svilgelm-test/99.99.99/

So, let's discuss.

Marshal expect with validate true doesn't work with nested field

Hello Folks,

When running a test case against an endpoint I'm getting the following error:

 "Unresolvable JSON pointer: %r" % fragment
jsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer: 'definitions/test_case_params'

Pip freeze:

alembic==1.4.0
aniso8601==8.0.0
astroid==2.3.3
attrs==19.3.0
autopep8==1.5
certifi==2019.11.28
chardet==3.0.4
Click==7.0
Flask==1.1.1
Flask-Cors==3.0.8
Flask-Migrate==2.5.2
flask-restx==0.1.1
Flask-SQLAlchemy==2.4.1
Flask-Testing==0.7.1
gunicorn==20.0.0
idna==2.8
importlib-metadata==1.5.0
isort==4.3.21
itsdangerous==1.1.0
Jinja2==2.11.1
jsonschema==3.2.0
lazy-object-proxy==1.4.3
Mako==1.1.1
MarkupSafe==1.1.1
mccabe==0.6.1
psycopg2-binary==2.8.3
pycodestyle==2.5.0
pylint==2.4.4
pyrsistent==0.15.7
python-dateutil==2.8.1
python-dotenv==0.10.3
python-editor==1.0.4
pytz==2019.3
requests==2.22.0
six==1.14.0
SQLAlchemy==1.3.13
typed-ast==1.4.1
urllib3==1.25.8
Werkzeug==0.16.1
wrapt==1.11.2
zipp==2.2.0

Full traceback:

ERROR: test_test_case_post_success_only_required_params (app.test.test_test_case_controller.TestTestCaseController)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 811, in resolve_fragment
    document = document[part]
KeyError: 'definitions'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/cleivianecosta/apps/geekhunter-dexter/app/test/test_test_case_controller.py", line 124, in test_test_case_post_success_only_required_params
    follow_redirects=True)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 1039, in post
    return self.open(*args, **kw)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/testing.py", line 227, in open
    follow_redirects=follow_redirects,
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 993, in open
    response = self.run_wsgi_app(environ.copy(), buffered=buffered)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 884, in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 1119, in run_wsgi_app
    app_rv = app(environ, start_response)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 2449, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 599, in error_router
    return original_handler(f)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 597, in error_router
    return self.handle_error(e)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 599, in error_router
    return original_handler(f)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 597, in error_router
    return self.handle_error(e)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 339, in wrapper
    resp = resource(*args, **kwargs)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/views.py", line 89, in view
    return self.dispatch_request(*args, **kwargs)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/resource.py", line 42, in dispatch_request
    self.validate_payload(meth)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/resource.py", line 88, in validate_payload
    self.__validate_payload(expect, collection=False)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/resource.py", line 73, in __validate_payload
    expect.validate(data, self.api.refresolver, self.api.format_checker)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/model.py", line 104, in validate
    validator.validate(data)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 352, in validate
    for error in self.iter_errors(*args, **kwargs):
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 328, in iter_errors
    for error in errors:
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/_validators.py", line 286, in properties
    schema_path=property,
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 344, in descend
    for error in self.iter_errors(instance, schema):
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 328, in iter_errors
    for error in errors:
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/_legacy_validators.py", line 55, in items_draft3_draft4
    for error in validator.descend(item, items, path=index):
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 344, in descend
    for error in self.iter_errors(instance, schema):
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 328, in iter_errors
    for error in errors:
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/_validators.py", line 259, in ref
    scope, resolved = validator.resolver.resolve(ref)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 766, in resolve
    return url, self._remote_cache(url)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 781, in resolve_from_url
    return self.resolve_fragment(document, fragment)
  File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 814, in resolve_fragment
    "Unresolvable JSON pointer: %r" % fragment
jsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer: 'definitions/test_case_params'

The endpoint

...

API = Namespace("test_cases", description="test cases related operations")
TEST_CASE_PARAMS = API.model("test_case_params", {
    "question_id": fields.Integer(
        required=True, description="The related question id"
    ),
    "name": fields.String(
        required=True, description="The test case name"
    ),
    "points": fields.Integer(
        required=True, description="How many points is the test worth"
    ),
    "test_input": fields.String(
        required=True, description="The input for the test"
    ),
    "test_output": fields.String(
        required=True, description="The expected output for the test"
    ),
    "is_visible": fields.Boolean(description="True if the test case is an example")
})

TEST_CASES_POST_PARAMS = API.model("test_cases_post_params", {
    "test_cases": fields.List(fields.Nested(TEST_CASE_PARAMS), required=True)
})

...

@API.route("/")
@API.expect(PARSER)
class TestCaseList(Resource):
  @API.expect(TEST_CASES_POST_PARAMS, validate=True)
  @API.marshal_with(LIST_TEST_CASES_MODEL)
  @token_required
  def post(self):
    data = request.json
    test_cases = create_test_cases(data["test_cases"])
    if test_cases is None:
      return {
          "error": "Could not create test_cases at this time"
      }, HTTPStatus.INTERNAL_SERVER_ERROR
    result = {"test_cases": [CreateGetTestCase(
        test_case) for test_case in test_cases]}
    return result, HTTPStatus.OK

The Test Case

def test_test_case_post_success_only_required_params(self):
    """
    Test if the /test-cases/ endpoint is working in case of success
    when only required params are passed
    """
    breakpoint()
    temp_params = {"test_cases": [self.test_case_required_params]}
    response = self.client.post("/test-cases/",
                                headers={
                                    "Content-Type": "application/json",
                                    "Authorization": APP.config["DEFAULT_AUTH_TOKEN"]
                                },
                                data=json.dumps(temp_params),
                                follow_redirects=True)

    self.assertTrue(response.status_code == HTTPStatus.OK)

    output_data = json.loads(response.data)
    for key in self.list_output_keys:
      self.assertTrue(key in output_data)
    self.assertTrue(output_data["error"] is None)

Note that before trying to migrate restplus to restX this was working as expected but on restX this error starts to pop up and if I set on the endpoint validate=False the problem would go away! So I think that there's some kind of problem when restX is trying to validate the params

Add pre-flight requests

Hello,

Whenever I'm making a request using the axes of the vue I have problems due to the pre-flight call, as it sends an Option before doing the GET / POST / ETC

I think it would be interesting to have an annotation that allows me to enable a pre-flight check on my endpoints.

if you had something like this it would be great:

class SignIn (Resource):
ย ย ย ย "" "Sign in User and return jwt token" ""
ย ย ย ย @ api.expect (sign_in)
ย ย ย ย @ api.marshal_with (auth_user, code = 200)
ย ย ย ย @ pre-flight.enabled()
ย ย ย ย def post (self):
ย ย ย ย ย ย ย ย "" "Return Login User" ""
ย ย ย ย ย ย ย ย api_payload = api.payload
ย ย ย ย ย ย ย ย return signin (api_payload)

add workflow for freezing and updating dependencies

ATM we have a combination of frozen, ranges, and "latest" in our requirements files. We also have no workflow for updating dependencies and doing testing. As such, some of our frozen deps are a bit behind, and our ranges may not be up to date. Ex: Flask is currently >0.8 but we may want to start forcing the 1.0 fork

We need an automated way to do the following

  • check dependencies are reasonably up to date
  • test against latest versions of dependencies
  • test ranges of dependencies if we are going to allow specific ranges
  • automatically update dependencies for releases

Improve Swagger UI Config Support

#14 (comment)

A general solution to configuring Swagger UI JavaScript options will eliminate the need for adding in one off Flask config options whenevr a user requests them.

TODO: Further details and document current ideas. This is mainly here as a placeholder so the issue doesn't get lost.

before_request to namespace

It would be nice to be able to define a before_request per namespace instead of globally or per blueprint

use black to enforce codestyle

Any thoughts on using black in order to enforce a coding style?

This way we don't have to bother our contributors with a codestyle, they just have to run black prior submitting a PR.

List Response Model

I want to document my response model as a list of strings like:

[
    "value1", 
    "value2"
]

which is valid JSON.
So I want to be able to do something like:

response_model = ns.model('Response', [
    fields.String()
])

Is there a way of doing this currently without having to use a dict object in ns.model?

Open API V3 Support

flask_restplus currently outputs the swagger.json in the old "Swagger 2.0" format. Are there any plans to also support the OpenAPI 3.x specification?

OpenAPI 3.x has better support for different authentication methods, including JWT token-based authentication.

Push Releases from CI

To help avoid the problems with releasing experienced in the Flask-RESTPlus project. We should be able to make new releases to PyPi from CI - i.e. not relying on a core team member to release 'manually' from his/her own machine.

My initial proposal is that CI will push a release to PyPi when a release is tagged on GitHub. I haven't put a whole lot of thought into this as of yet so suggestions are more than welcome ๐Ÿ˜„

Werkzeug debugger is activate

Greetings to all,
I don't know why but since the version of werkzeug must be specified in the requirement, the werkzeug debugger sends me an html page for all my errors.
Let me explain, I have on a route applied an api.except with a model made of different fields, but instead of sending a flask-restx error I get an HTML page:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>

<head>
	<title>TypeError: 'in &lt;string&gt;' requires string as left operand, not dict // Werkzeug Debugger</title>
	<link rel="stylesheet" href="?__debugger__=yes&amp;cmd=resource&amp;f=style.css" type="text/css">
	<!-- We need to make sure this has a favicon so that the debugger does
         not by accident trigger a request to /favicon.ico which might
         change the application state. -->
	<link rel="shortcut icon" href="?__debugger__=yes&amp;cmd=resource&amp;f=console.png">
	<script src="?__debugger__=yes&amp;cmd=resource&amp;f=jquery.js"></script>
	<script src="?__debugger__=yes&amp;cmd=resource&amp;f=debugger.js"></script>
	<script type="text/javascript">
		var TRACEBACK = 140413129039264,
          CONSOLE_MODE = false,
          EVALEX = true,
          EVALEX_TRUSTED = false,
          SECRET = "sD6itptZZvKivP7pvVpH";
	</script>
</head>

<body style="background-color: #fff">
	<div class="debugger">
		<h1>TypeError</h1>
		<div class="detail">
			<p class="errormsg">TypeError: 'in &lt;string&gt;' requires string as left operand, not dict</p>
		</div>
		<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
		<div class="traceback">

			<ul>
				<li>
					<div class="frame" id="frame-140413103141048">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/model.py"</cite>,
							line <em class="line">104</em>,
							in <code class="function">validate</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">        </span>return model</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">    </span>def validate(self, data, resolver=None, format_checker=None):</pre>
							<pre
								class="line before"><span class="ws">        </span>validator = Draft4Validator(self.__schema__, resolver=resolver, format_checker=format_checker)</pre>
							<pre class="line before"><span class="ws">        </span>try:</pre>
							<pre class="line current"><span class="ws">            </span>validator.validate(data)</pre>
							<pre class="line after"><span class="ws">        </span>except ValidationError:</pre>
							<pre
								class="line after"><span class="ws">            </span>abort(HTTPStatus.BAD_REQUEST, message='Input payload validation failed',</pre>
							<pre
								class="line after"><span class="ws">                  </span>errors=dict(self.format_error(e) for e in validator.iter_errors(data)))</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def format_error(self, error):</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141720">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py"</cite>,
							line <em class="line">353</em>,
							in <code class="function">validate</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">                    </span>error.schema_path.appendleft(schema_path)</pre>
							<pre class="line before"><span class="ws">                </span>yield error</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">        </span>def validate(self, *args, **kwargs):</pre>
							<pre
								class="line before"><span class="ws">            </span>for error in self.iter_errors(*args, **kwargs):</pre>
							<pre class="line current"><span class="ws">                </span>raise error</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre
								class="line after"><span class="ws">        </span>def is_type(self, instance, type):</pre>
							<pre class="line after"><span class="ws">            </span>try:</pre>
							<pre
								class="line after"><span class="ws">                </span>return self.TYPE_CHECKER.is_type(instance, type)</pre>
							<pre
								class="line after"><span class="ws">            </span>except exceptions.UndefinedTypeCheck:</pre>
						</div>
					</div>

				<li>
					<div class="exc-divider">During handling of the above exception, another exception occurred:</div>
				<li>
					<div class="frame" id="frame-140413129039096">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">2463</em>,
							in <code class="function">__call__</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">    </span>def __call__(self, environ, start_response):</pre>
							<pre
								class="line before"><span class="ws">        </span>&quot;&quot;&quot;The WSGI server calls the Flask application object as the</pre>
							<pre
								class="line before"><span class="ws">        </span>WSGI application. This calls :meth:`wsgi_app` which can be</pre>
							<pre
								class="line before"><span class="ws">        </span>wrapped to applying middleware.&quot;&quot;&quot;</pre>
							<pre
								class="line current"><span class="ws">        </span>return self.wsgi_app(environ, start_response)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def __repr__(self):</pre>
							<pre
								class="line after"><span class="ws">        </span>return &quot;&lt;%s %r&gt;&quot; % (self.__class__.__name__, self.name)</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129038928">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">2449</em>,
							in <code class="function">wsgi_app</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">            </span>try:</pre>
							<pre class="line before"><span class="ws">                </span>ctx.push()</pre>
							<pre
								class="line before"><span class="ws">                </span>response = self.full_dispatch_request()</pre>
							<pre class="line before"><span class="ws">            </span>except Exception as e:</pre>
							<pre class="line before"><span class="ws">                </span>error = e</pre>
							<pre
								class="line current"><span class="ws">                </span>response = self.handle_exception(e)</pre>
							<pre class="line after"><span class="ws">            </span>except:  # noqa: B001</pre>
							<pre
								class="line after"><span class="ws">                </span>error = sys.exc_info()[1]</pre>
							<pre class="line after"><span class="ws">                </span>raise</pre>
							<pre
								class="line after"><span class="ws">            </span>return response(environ, start_response)</pre>
							<pre class="line after"><span class="ws">        </span>finally:</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039320">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/api.py"</cite>,
							line <em class="line">599</em>,
							in <code class="function">error_router</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">        </span>'''</pre>
							<pre class="line before"><span class="ws">        </span>if self._has_fr_route():</pre>
							<pre class="line before"><span class="ws">            </span>try:</pre>
							<pre
								class="line before"><span class="ws">                </span>return self.handle_error(e)</pre>
							<pre class="line before"><span class="ws">            </span>except Exception as f:</pre>
							<pre
								class="line current"><span class="ws">                </span>return original_handler(f)</pre>
							<pre class="line after"><span class="ws">        </span>return original_handler(e)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def handle_error(self, e):</pre>
							<pre class="line after"><span class="ws">        </span>'''</pre>
							<pre
								class="line after"><span class="ws">        </span>Error handler for the API transforms a raised exception into a Flask response,</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129038816">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">1866</em>,
							in <code class="function">handle_exception</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">            </span># if we want to repropagate the exception, we can attempt to</pre>
							<pre
								class="line before"><span class="ws">            </span># raise it with the whole traceback in case we can do that</pre>
							<pre
								class="line before"><span class="ws">            </span># (the function was actually called from the except part)</pre>
							<pre
								class="line before"><span class="ws">            </span># otherwise, we just raise the error again</pre>
							<pre class="line before"><span class="ws">            </span>if exc_value is e:</pre>
							<pre
								class="line current"><span class="ws">                </span>reraise(exc_type, exc_value, tb)</pre>
							<pre class="line after"><span class="ws">            </span>else:</pre>
							<pre class="line after"><span class="ws">                </span>raise e</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre
								class="line after"><span class="ws">        </span>self.log_exception((exc_type, exc_value, tb))</pre>
							<pre
								class="line after"><span class="ws">        </span>server_error = InternalServerError()</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039432">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/_compat.py"</cite>,
							line <em class="line">39</em>,
							in <code class="function">reraise</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">    </span>import collections.abc as collections_abc</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre class="line before"><span class="ws">    </span>def reraise(tp, value, tb=None):</pre>
							<pre
								class="line before"><span class="ws">        </span>if value.__traceback__ is not tb:</pre>
							<pre
								class="line before"><span class="ws">            </span>raise value.with_traceback(tb)</pre>
							<pre class="line current"><span class="ws">        </span>raise value</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>implements_to_string = _identity</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws"></span>else:</pre>
							<pre class="line after"><span class="ws">    </span>iterkeys = lambda d: d.iterkeys()</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039208">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/api.py"</cite>,
							line <em class="line">597</em>,
							in <code class="function">error_router</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">        </span>:param function original_handler: the original Flask error handler for the app</pre>
							<pre
								class="line before"><span class="ws">        </span>:param Exception e: the exception raised while handling the request</pre>
							<pre class="line before"><span class="ws">        </span>'''</pre>
							<pre class="line before"><span class="ws">        </span>if self._has_fr_route():</pre>
							<pre class="line before"><span class="ws">            </span>try:</pre>
							<pre
								class="line current"><span class="ws">                </span>return self.handle_error(e)</pre>
							<pre class="line after"><span class="ws">            </span>except Exception as f:</pre>
							<pre
								class="line after"><span class="ws">                </span>return original_handler(f)</pre>
							<pre class="line after"><span class="ws">        </span>return original_handler(e)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def handle_error(self, e):</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039488">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">2446</em>,
							in <code class="function">wsgi_app</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">        </span>ctx = self.request_context(environ)</pre>
							<pre class="line before"><span class="ws">        </span>error = None</pre>
							<pre class="line before"><span class="ws">        </span>try:</pre>
							<pre class="line before"><span class="ws">            </span>try:</pre>
							<pre class="line before"><span class="ws">                </span>ctx.push()</pre>
							<pre
								class="line current"><span class="ws">                </span>response = self.full_dispatch_request()</pre>
							<pre class="line after"><span class="ws">            </span>except Exception as e:</pre>
							<pre class="line after"><span class="ws">                </span>error = e</pre>
							<pre
								class="line after"><span class="ws">                </span>response = self.handle_exception(e)</pre>
							<pre class="line after"><span class="ws">            </span>except:  # noqa: B001</pre>
							<pre
								class="line after"><span class="ws">                </span>error = sys.exc_info()[1]</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039600">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">1951</em>,
							in <code class="function">full_dispatch_request</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">            </span>request_started.send(self)</pre>
							<pre
								class="line before"><span class="ws">            </span>rv = self.preprocess_request()</pre>
							<pre class="line before"><span class="ws">            </span>if rv is None:</pre>
							<pre
								class="line before"><span class="ws">                </span>rv = self.dispatch_request()</pre>
							<pre class="line before"><span class="ws">        </span>except Exception as e:</pre>
							<pre
								class="line current"><span class="ws">            </span>rv = self.handle_user_exception(e)</pre>
							<pre
								class="line after"><span class="ws">        </span>return self.finalize_request(rv)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre
								class="line after"><span class="ws">    </span>def finalize_request(self, rv, from_error_handler=False):</pre>
							<pre
								class="line after"><span class="ws">        </span>&quot;&quot;&quot;Given the return value from a view function this finalizes</pre>
							<pre
								class="line after"><span class="ws">        </span>the request by converting it into a response and invoking the</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039656">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/api.py"</cite>,
							line <em class="line">599</em>,
							in <code class="function">error_router</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">        </span>'''</pre>
							<pre class="line before"><span class="ws">        </span>if self._has_fr_route():</pre>
							<pre class="line before"><span class="ws">            </span>try:</pre>
							<pre
								class="line before"><span class="ws">                </span>return self.handle_error(e)</pre>
							<pre class="line before"><span class="ws">            </span>except Exception as f:</pre>
							<pre
								class="line current"><span class="ws">                </span>return original_handler(f)</pre>
							<pre class="line after"><span class="ws">        </span>return original_handler(e)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def handle_error(self, e):</pre>
							<pre class="line after"><span class="ws">        </span>'''</pre>
							<pre
								class="line after"><span class="ws">        </span>Error handler for the API transforms a raised exception into a Flask response,</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039544">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">1820</em>,
							in <code class="function">handle_user_exception</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">            </span>return self.handle_http_exception(e)</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">        </span>handler = self._find_error_handler(e)</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre class="line before"><span class="ws">        </span>if handler is None:</pre>
							<pre
								class="line current"><span class="ws">            </span>reraise(exc_type, exc_value, tb)</pre>
							<pre class="line after"><span class="ws">        </span>return handler(e)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def handle_exception(self, e):</pre>
							<pre
								class="line after"><span class="ws">        </span>&quot;&quot;&quot;Handle an exception that did not have an error handler</pre>
							<pre
								class="line after"><span class="ws">        </span>associated with it, or that was raised from an error handler.</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039768">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/_compat.py"</cite>,
							line <em class="line">39</em>,
							in <code class="function">reraise</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">    </span>import collections.abc as collections_abc</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre class="line before"><span class="ws">    </span>def reraise(tp, value, tb=None):</pre>
							<pre
								class="line before"><span class="ws">        </span>if value.__traceback__ is not tb:</pre>
							<pre
								class="line before"><span class="ws">            </span>raise value.with_traceback(tb)</pre>
							<pre class="line current"><span class="ws">        </span>raise value</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>implements_to_string = _identity</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws"></span>else:</pre>
							<pre class="line after"><span class="ws">    </span>iterkeys = lambda d: d.iterkeys()</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129038872">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/api.py"</cite>,
							line <em class="line">597</em>,
							in <code class="function">error_router</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">        </span>:param function original_handler: the original Flask error handler for the app</pre>
							<pre
								class="line before"><span class="ws">        </span>:param Exception e: the exception raised while handling the request</pre>
							<pre class="line before"><span class="ws">        </span>'''</pre>
							<pre class="line before"><span class="ws">        </span>if self._has_fr_route():</pre>
							<pre class="line before"><span class="ws">            </span>try:</pre>
							<pre
								class="line current"><span class="ws">                </span>return self.handle_error(e)</pre>
							<pre class="line after"><span class="ws">            </span>except Exception as f:</pre>
							<pre
								class="line after"><span class="ws">                </span>return original_handler(f)</pre>
							<pre class="line after"><span class="ws">        </span>return original_handler(e)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def handle_error(self, e):</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039824">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">1949</em>,
							in <code class="function">full_dispatch_request</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">        </span>self.try_trigger_before_first_request_functions()</pre>
							<pre class="line before"><span class="ws">        </span>try:</pre>
							<pre
								class="line before"><span class="ws">            </span>request_started.send(self)</pre>
							<pre
								class="line before"><span class="ws">            </span>rv = self.preprocess_request()</pre>
							<pre class="line before"><span class="ws">            </span>if rv is None:</pre>
							<pre
								class="line current"><span class="ws">                </span>rv = self.dispatch_request()</pre>
							<pre class="line after"><span class="ws">        </span>except Exception as e:</pre>
							<pre
								class="line after"><span class="ws">            </span>rv = self.handle_user_exception(e)</pre>
							<pre
								class="line after"><span class="ws">        </span>return self.finalize_request(rv)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre
								class="line after"><span class="ws">    </span>def finalize_request(self, rv, from_error_handler=False):</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039376">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/app.py"</cite>,
							line <em class="line">1935</em>,
							in <code class="function">dispatch_request</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">            </span>getattr(rule, &quot;provide_automatic_options&quot;, False)</pre>
							<pre
								class="line before"><span class="ws">            </span>and req.method == &quot;OPTIONS&quot;</pre>
							<pre class="line before"><span class="ws">        </span>):</pre>
							<pre
								class="line before"><span class="ws">            </span>return self.make_default_options_response()</pre>
							<pre
								class="line before"><span class="ws">        </span># otherwise dispatch to the handler for that endpoint</pre>
							<pre
								class="line current"><span class="ws">        </span>return self.view_functions[rule.endpoint](**req.view_args)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def full_dispatch_request(self):</pre>
							<pre
								class="line after"><span class="ws">        </span>&quot;&quot;&quot;Dispatches the request and on top of that performs request</pre>
							<pre
								class="line after"><span class="ws">        </span>pre and postprocessing as well as HTTP exception catching and</pre>
							<pre class="line after"><span class="ws">        </span>error handling.</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129039712">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/api.py"</cite>,
							line <em class="line">339</em>,
							in <code class="function">wrapper</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">        </span>:param resource: The resource as a flask view function</pre>
							<pre class="line before"><span class="ws">        </span>'''</pre>
							<pre class="line before"><span class="ws">        </span>@wraps(resource)</pre>
							<pre class="line before"><span class="ws">        </span>def wrapper(*args, **kwargs):</pre>
							<pre
								class="line current"><span class="ws">            </span>resp = resource(*args, **kwargs)</pre>
							<pre
								class="line after"><span class="ws">            </span>if isinstance(resp, BaseResponse):</pre>
							<pre class="line after"><span class="ws">                </span>return resp</pre>
							<pre
								class="line after"><span class="ws">            </span>data, code, headers = unpack(resp)</pre>
							<pre
								class="line after"><span class="ws">            </span>return self.make_response(data, code, headers=headers)</pre>
							<pre class="line after"><span class="ws">        </span>return wrapper</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413129038760">
						<h4>File <cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask/views.py"</cite>,
							line <em class="line">89</em>,
							in <code class="function">view</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">        </span>constructor of the class.</pre>
							<pre class="line before"><span class="ws">        </span>&quot;&quot;&quot;</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre class="line before"><span class="ws">        </span>def view(*args, **kwargs):</pre>
							<pre
								class="line before"><span class="ws">            </span>self = view.view_class(*class_args, **class_kwargs)</pre>
							<pre
								class="line current"><span class="ws">            </span>return self.dispatch_request(*args, **kwargs)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">        </span>if cls.decorators:</pre>
							<pre class="line after"><span class="ws">            </span>view.__name__ = name</pre>
							<pre
								class="line after"><span class="ws">            </span>view.__module__ = cls.__module__</pre>
							<pre
								class="line after"><span class="ws">            </span>for decorator in cls.decorators:</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103140992">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py"</cite>,
							line <em class="line">42</em>,
							in <code class="function">dispatch_request</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">        </span>assert meth is not None, 'Unimplemented method %r' % request.method</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">        </span>for decorator in self.method_decorators:</pre>
							<pre class="line before"><span class="ws">            </span>meth = decorator(meth)</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre class="line current"><span class="ws">        </span>self.validate_payload(meth)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">        </span>resp = meth(*args, **kwargs)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre
								class="line after"><span class="ws">        </span>if isinstance(resp, BaseResponse):</pre>
							<pre class="line after"><span class="ws">            </span>return resp</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141104">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py"</cite>,
							line <em class="line">88</em>,
							in <code class="function">validate_payload</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">                    </span># TODO: handle third party handlers</pre>
							<pre
								class="line before"><span class="ws">                    </span>if isinstance(expect, list) and len(expect) == 1:</pre>
							<pre
								class="line before"><span class="ws">                        </span>if isinstance(expect[0], ModelBase):</pre>
							<pre
								class="line before"><span class="ws">                            </span>self.__validate_payload(expect[0], collection=True)</pre>
							<pre
								class="line before"><span class="ws">                    </span>if isinstance(expect, ModelBase):</pre>
							<pre
								class="line current"><span class="ws">                        </span>self.__validate_payload(expect, collection=False)</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141160">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py"</cite>,
							line <em class="line">73</em>,
							in <code class="function">__validate_payload</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">        </span>if collection:</pre>
							<pre
								class="line before"><span class="ws">            </span>data = data if isinstance(data, list) else [data]</pre>
							<pre class="line before"><span class="ws">            </span>for obj in data:</pre>
							<pre
								class="line before"><span class="ws">                </span>expect.validate(obj, self.api.refresolver, self.api.format_checker)</pre>
							<pre class="line before"><span class="ws">        </span>else:</pre>
							<pre
								class="line current"><span class="ws">            </span>expect.validate(data, self.api.refresolver, self.api.format_checker)</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def validate_payload(self, func):</pre>
							<pre
								class="line after"><span class="ws">        </span>'''Perform a payload validation on expected model if necessary'''</pre>
							<pre
								class="line after"><span class="ws">        </span>if getattr(func, '__apidoc__', False) is not False:</pre>
							<pre class="line after"><span class="ws">            </span>doc = func.__apidoc__</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141216">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/model.py"</cite>,
							line <em class="line">107</em>,
							in <code class="function">validate</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">        </span>validator = Draft4Validator(self.__schema__, resolver=resolver, format_checker=format_checker)</pre>
							<pre class="line before"><span class="ws">        </span>try:</pre>
							<pre class="line before"><span class="ws">            </span>validator.validate(data)</pre>
							<pre class="line before"><span class="ws">        </span>except ValidationError:</pre>
							<pre
								class="line before"><span class="ws">            </span>abort(HTTPStatus.BAD_REQUEST, message='Input payload validation failed',</pre>
							<pre
								class="line current"><span class="ws">                  </span>errors=dict(self.format_error(e) for e in validator.iter_errors(data)))</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def format_error(self, error):</pre>
							<pre class="line after"><span class="ws">        </span>path = list(error.path)</pre>
							<pre
								class="line after"><span class="ws">        </span>if error.validator == 'required':</pre>
							<pre
								class="line after"><span class="ws">            </span>name = RE_REQUIRED.match(error.message).group('name')</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141272">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/flask_restx/model.py"</cite>,
							line <em class="line">107</em>,
							in <code class="function">&lt;genexpr&gt;</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">        </span>validator = Draft4Validator(self.__schema__, resolver=resolver, format_checker=format_checker)</pre>
							<pre class="line before"><span class="ws">        </span>try:</pre>
							<pre class="line before"><span class="ws">            </span>validator.validate(data)</pre>
							<pre class="line before"><span class="ws">        </span>except ValidationError:</pre>
							<pre
								class="line before"><span class="ws">            </span>abort(HTTPStatus.BAD_REQUEST, message='Input payload validation failed',</pre>
							<pre
								class="line current"><span class="ws">                  </span>errors=dict(self.format_error(e) for e in validator.iter_errors(data)))</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws">    </span>def format_error(self, error):</pre>
							<pre class="line after"><span class="ws">        </span>path = list(error.path)</pre>
							<pre
								class="line after"><span class="ws">        </span>if error.validator == 'required':</pre>
							<pre
								class="line after"><span class="ws">            </span>name = RE_REQUIRED.match(error.message).group('name')</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141328">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py"</cite>,
							line <em class="line">328</em>,
							in <code class="function">iter_errors</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">                    </span>validator = self.VALIDATORS.get(k)</pre>
							<pre
								class="line before"><span class="ws">                    </span>if validator is None:</pre>
							<pre class="line before"><span class="ws">                        </span>continue</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">                    </span>errors = validator(self, v, instance, _schema) or ()</pre>
							<pre
								class="line current"><span class="ws">                    </span>for error in errors:</pre>
							<pre
								class="line after"><span class="ws">                        </span># set details if not already set by the called fn</pre>
							<pre class="line after"><span class="ws">                        </span>error._set(</pre>
							<pre
								class="line after"><span class="ws">                            </span>validator=k,</pre>
							<pre
								class="line after"><span class="ws">                            </span>validator_value=v,</pre>
							<pre
								class="line after"><span class="ws">                            </span>instance=instance,</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141384">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/jsonschema/_validators.py"</cite>,
							line <em class="line">286</em>,
							in <code class="function">properties</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">        </span>if property in instance:</pre>
							<pre
								class="line before"><span class="ws">            </span>for error in validator.descend(</pre>
							<pre class="line before"><span class="ws">                </span>instance[property],</pre>
							<pre class="line before"><span class="ws">                </span>subschema,</pre>
							<pre class="line before"><span class="ws">                </span>path=property,</pre>
							<pre
								class="line current"><span class="ws">                </span>schema_path=property,</pre>
							<pre class="line after"><span class="ws">            </span>):</pre>
							<pre class="line after"><span class="ws">                </span>yield error</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre
								class="line after"><span class="ws"></span>def required(validator, required, instance, schema):</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141440">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py"</cite>,
							line <em class="line">344</em>,
							in <code class="function">descend</code></h4>
						<div class="source ">
							<pre class="line before"><span class="ws">            </span>finally:</pre>
							<pre class="line before"><span class="ws">                </span>if scope:</pre>
							<pre
								class="line before"><span class="ws">                    </span>self.resolver.pop_scope()</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">        </span>def descend(self, instance, schema, path=None, schema_path=None):</pre>
							<pre
								class="line current"><span class="ws">            </span>for error in self.iter_errors(instance, schema):</pre>
							<pre class="line after"><span class="ws">                </span>if path is not None:</pre>
							<pre
								class="line after"><span class="ws">                    </span>error.path.appendleft(path)</pre>
							<pre
								class="line after"><span class="ws">                </span>if schema_path is not None:</pre>
							<pre
								class="line after"><span class="ws">                    </span>error.schema_path.appendleft(schema_path)</pre>
							<pre class="line after"><span class="ws">                </span>yield error</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141496">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py"</cite>,
							line <em class="line">328</em>,
							in <code class="function">iter_errors</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws">                    </span>validator = self.VALIDATORS.get(k)</pre>
							<pre
								class="line before"><span class="ws">                    </span>if validator is None:</pre>
							<pre class="line before"><span class="ws">                        </span>continue</pre>
							<pre class="line before"><span class="ws"></span> </pre>
							<pre
								class="line before"><span class="ws">                    </span>errors = validator(self, v, instance, _schema) or ()</pre>
							<pre
								class="line current"><span class="ws">                    </span>for error in errors:</pre>
							<pre
								class="line after"><span class="ws">                        </span># set details if not already set by the called fn</pre>
							<pre class="line after"><span class="ws">                        </span>error._set(</pre>
							<pre
								class="line after"><span class="ws">                            </span>validator=k,</pre>
							<pre
								class="line after"><span class="ws">                            </span>validator_value=v,</pre>
							<pre
								class="line after"><span class="ws">                            </span>instance=instance,</pre>
						</div>
					</div>

				<li>
					<div class="frame" id="frame-140413103141552">
						<h4>File
							<cite class="filename">"/usr/local/lib/python3.5/dist-packages/jsonschema/_validators.py"</cite>,
							line <em class="line">248</em>,
							in <code class="function">enum</code></h4>
						<div class="source ">
							<pre
								class="line before"><span class="ws"></span>def enum(validator, enums, instance, schema):</pre>
							<pre
								class="line before"><span class="ws">    </span>if instance == 0 or instance == 1:</pre>
							<pre class="line before"><span class="ws">        </span>unbooled = unbool(instance)</pre>
							<pre
								class="line before"><span class="ws">        </span>if all(unbooled != unbool(each) for each in enums):</pre>
							<pre
								class="line before"><span class="ws">            </span>yield ValidationError(&quot;%r is not one of %r&quot; % (instance, enums))</pre>
							<pre class="line current"><span class="ws">    </span>elif instance not in enums:</pre>
							<pre
								class="line after"><span class="ws">        </span>yield ValidationError(&quot;%r is not one of %r&quot; % (instance, enums))</pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre class="line after"><span class="ws"></span> </pre>
							<pre
								class="line after"><span class="ws"></span>def ref(validator, ref, instance, schema):</pre>
							<pre
								class="line after"><span class="ws">    </span>resolve = getattr(validator.resolver, &quot;resolve&quot;, None)</pre>
						</div>
					</div>
			</ul>
			<blockquote>TypeError: 'in &lt;string&gt;' requires string as left operand, not dict</blockquote>
		</div>

		<div class="plain">
			<form action="/?__debugger__=yes&amp;cmd=paste" method="post">
				<p>
					<input type="hidden" name="language" value="pytb">
      This is the Copy/Paste friendly version of the traceback. <span
      class="pastemessage">You can also paste this traceback into
      a <a href="https://gist.github.com/">gist</a>:
      <input type="submit" value="create paste"></span>
				</p>
				<textarea cols="50" rows="10" name="code" readonly>Traceback (most recent call last):
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/model.py&quot;, line 104, in validate
    validator.validate(data)
  File &quot;/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py&quot;, line 353, in validate
    raise error
jsonschema.exceptions.ValidationError: {'interface': 'port1', 'gateway': '212.234.67.126', 'ip': '212.234.67.77'} is not of type 'string'

Failed validating 'type' in schema['properties']['admin_gw']:
    {'enum': 'MARIUS2', 'example': 'M', 'type': 'string'}

On instance['admin_gw']:
    {'gateway': '212.234.67.126',
     'interface': 'port1',
     'ip': '212.234.67.77'}

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 2449, in wsgi_app
    response = self.handle_exception(e)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/api.py&quot;, line 599, in error_router
    return original_handler(f)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/_compat.py&quot;, line 39, in reraise
    raise value
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/api.py&quot;, line 597, in error_router
    return self.handle_error(e)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/api.py&quot;, line 599, in error_router
    return original_handler(f)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/_compat.py&quot;, line 39, in reraise
    raise value
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/api.py&quot;, line 597, in error_router
    return self.handle_error(e)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/app.py&quot;, line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/api.py&quot;, line 339, in wrapper
    resp = resource(*args, **kwargs)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask/views.py&quot;, line 89, in view
    return self.dispatch_request(*args, **kwargs)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py&quot;, line 42, in dispatch_request
    self.validate_payload(meth)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py&quot;, line 88, in validate_payload
    self.__validate_payload(expect, collection=False)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py&quot;, line 73, in __validate_payload
    expect.validate(data, self.api.refresolver, self.api.format_checker)
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/model.py&quot;, line 107, in validate
    errors=dict(self.format_error(e) for e in validator.iter_errors(data)))
  File &quot;/usr/local/lib/python3.5/dist-packages/flask_restx/model.py&quot;, line 107, in &lt;genexpr&gt;
    errors=dict(self.format_error(e) for e in validator.iter_errors(data)))
  File &quot;/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py&quot;, line 328, in iter_errors
    for error in errors:
  File &quot;/usr/local/lib/python3.5/dist-packages/jsonschema/_validators.py&quot;, line 286, in properties
    schema_path=property,
  File &quot;/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py&quot;, line 344, in descend
    for error in self.iter_errors(instance, schema):
  File &quot;/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py&quot;, line 328, in iter_errors
    for error in errors:
  File &quot;/usr/local/lib/python3.5/dist-packages/jsonschema/_validators.py&quot;, line 248, in enum
    elif instance not in enums:
TypeError: 'in &lt;string&gt;' requires string as left operand, not dict</textarea>
			</form>
		</div>
		<div class="explanation">
			The debugger caught an exception in your WSGI application. You can now
			look at the traceback which led to the error. <span class="nojavascript">
  If you enable JavaScript you can also use additional features such as code
  execution (if the evalex feature is enabled), automatic pasting of the
  exceptions and much more.</span>
		</div>
		<div class="footer">
			Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
			friendly Werkzeug powered traceback interpreter.
		</div>
	</div>

	<div class="pin-prompt">
		<div class="inner">
			<h3>Console Locked</h3>
			<p>
				The console is locked and needs to be unlocked by entering the PIN.
				You can find the PIN printed out on the standard output of your
				shell that runs the server.
				<form>
					<p>PIN:
						<input type=text name=pin size=14>
						<input type=submit name=btn value="Confirm Pin">
        </form>
		</div>
	</div>
</body>

</html>

<!--

Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/model.py", line 104, in validate
    validator.validate(data)
  File "/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py", line 353, in validate
    raise error
jsonschema.exceptions.ValidationError: {'interface': 'port1', 'gateway': '212.234.67.126', 'ip': '212.234.67.77'} is not of type 'string'

Failed validating 'type' in schema['properties']['admin_gw']:
    {'enum': 'MARIUS2', 'example': 'M', 'type': 'string'}

On instance['admin_gw']:
    {'gateway': '212.234.67.126',
     'interface': 'port1',
     'ip': '212.234.67.77'}

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 2449, in wsgi_app
    response = self.handle_exception(e)
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/api.py", line 599, in error_router
    return original_handler(f)
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.5/dist-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/api.py", line 597, in error_router
    return self.handle_error(e)
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1951, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/api.py", line 599, in error_router
    return original_handler(f)
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1820, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.5/dist-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/api.py", line 597, in error_router
    return self.handle_error(e)
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1949, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.5/dist-packages/flask/app.py", line 1935, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/api.py", line 339, in wrapper
    resp = resource(*args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/flask/views.py", line 89, in view
    return self.dispatch_request(*args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py", line 42, in dispatch_request
    self.validate_payload(meth)
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py", line 88, in validate_payload
    self.__validate_payload(expect, collection=False)
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/resource.py", line 73, in __validate_payload
    expect.validate(data, self.api.refresolver, self.api.format_checker)
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/model.py", line 107, in validate
    errors=dict(self.format_error(e) for e in validator.iter_errors(data)))
  File "/usr/local/lib/python3.5/dist-packages/flask_restx/model.py", line 107, in <genexpr>
    errors=dict(self.format_error(e) for e in validator.iter_errors(data)))
  File "/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py", line 328, in iter_errors
    for error in errors:
  File "/usr/local/lib/python3.5/dist-packages/jsonschema/_validators.py", line 286, in properties
    schema_path=property,
  File "/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py", line 344, in descend
    for error in self.iter_errors(instance, schema):
  File "/usr/local/lib/python3.5/dist-packages/jsonschema/validators.py", line 328, in iter_errors
    for error in errors:
  File "/usr/local/lib/python3.5/dist-packages/jsonschema/_validators.py", line 248, in enum
    elif instance not in enums:
TypeError: 'in <string>' requires string as left operand, not dict

-->

PIP FREEZE:
aniso8601==8.0.0
ansible==2.9.5
ansible-runner==1.4.4
attrs==19.3.0
bcrypt==3.1.7
certifi==2019.11.28
cffi==1.14.0
chardet==3.0.4
Click==7.0
CouchDB==1.2
cryptography==1.7.1
docker==4.2.0
docutils==0.16
Flask==1.1.1
flask-restx==0.1.1
idna==2.9
importlib-metadata==1.5.0
itsdangerous==1.1.0
Jinja2==2.11.1
jsonschema==3.2.0
keyring==10.1
keyrings.alt==1.3
lockfile==0.12.2
lxml==4.5.0
MarkupSafe==1.1.1
paramiko==2.7.1
pexpect==4.8.0
psutil==5.7.0
ptyprocess==0.6.0
pyasn1==0.1.9
pyasn1-modules==0.2.8
pycparser==2.20
pycrypto==2.6.1
pygobject==3.22.0
PyJWT==1.7.1
PyNaCl==1.3.0
pyrsistent==0.15.7
python-daemon==2.2.4
python-ldap==3.2.0
pytz==2019.3
pyxdg==0.25
PyYAML==5.3
requests==2.23.0
SecretStorage==2.3.1
six==1.10.0
urllib3==1.25.8
websocket-client==0.57.0
Werkzeug==0.16.1
zipp==1.2.0

Do you have any idea about how to get flask restx errors instead of werkzeug debuger?

Possible to rewrite BadRequest response contents?

Ask a question
Hi!

This might very well be by design or my incorrect usage of the framework, but I expected to be able to use an error handler to rewrite the response generated when a request fails input validation as defined by my @api.expect()-model.

I'm replacing a system, and I'm trying to keep the responses as close to the old systems format as possible.

I can see while debugging that my custom error handler is triggered, but it seems as there is a "data"-field on the BadRequest exception, that my response in the end is disregarded and the original message is returned to the client? (https://github.com/python-restx/flask-restx/blob/master/flask_restx/api.py#L700)

Additional context
Code to reproduce the issue:

from flask import Flask, request
from flask_restx import Resource, Api, fields


app = Flask(__name__)
app.config['PROPAGATE_EXCEPTIONS'] = False
api = Api(app)


# Actual validation error: werkzeug.exceptions.BadRequest
@api.errorhandler(Exception)
def handle_validation_error(error):
    return {'my custom': 'error message'}, 400


resource_fields = api.model('Resource', {
    'name': fields.String,
})


@api.route('/hello')
class HelloResource(Resource):
    @api.expect(resource_fields, validate=True)
    def post(self):
        json_data = request.get_json()
        return f'Hello {json_data["name"]}', 200


if __name__ == '__main__':
    app.run(debug=True)
  • Invalid request that I hoped should return my custom error message as json:
curl --data '{"name": 42}' -H 'Content-Type: application/json' http://127.0.0.1:5000/hello

Actual response:

{
    "errors": {
        "name": "42 is not of type 'string'"
    },
    "message": "Input payload validation failed"
}

Expected response:

{"my custom": "error message"}
  • Valid request (for reference):
curl --data '{"name": "Anders"}' -H 'Content-Type: application/json' http://127.0.0.1:5000/hello

pip install flask-restx erratic behaviour

On a previous project i was developing less than a week ago flask-restx was available through pip, today it seems that the package is no longer on pypi.

Code

pip install flask-restx

Expected Behavior

flask-restx package should be installed in my python directory or venv

Actual Behavior

flask-restx package installation fails

Error Messages/Stack Trace

ERROR: Could not find a version that satisfies the requirement flask-restx (from versions: none)
ERROR: No matching distribution found for flask-restx

Environment

  • Python 3.0.1
  • Flask 1.1.1
  • Flask-RESTX latest
  • flask-pymongo
  • Win 10 Pro build 1909

Swagger set host field?

Ask a question
I'm using flask-restx to set up an API, and then I'm trying to generate a python client using swagger codegen. Here's the swagger that gets generated.

  "swagger": "2.0",
  "basePath": "/",
  "paths": {
  ...
  },
  "info": {
  ...
  },
  "produces": [
    "application/json"
  ],
  "consumes": [
    "application/json"
  ],
  "tags": [
  ...
  ],
  "definitions": {
  ...
  },
  "responses": {
   ...
  }
}

However, I'd like to add the host field (which seems to be allowed by https://swagger.io/docs/specification/2-0/api-host-and-base-path/). I can't find anywhere in the flask-restx where I can set it. Does anyone know?

Describing response bodies with api.doc()

In the documentation, when using api.doc(responses=.... it appears you can only specify the set of response codes and a corresponding description but, no schema.

@api.route('/my-resource/')
class MyResource(Resource):
    @api.doc(responses={
        200: 'Success',
        400: 'Validation Error'
    })
    def get(self):
        pass

I saw you can specify the schema of a particular response when using the @response decorator ...

@api.route('/my-resource/')
class MyResource(Resource):
    @api.response(200, 'Success', model)
    def get(self):
        pass

Is there a way to specify the model for a response code when using the @api.doc(responses=.... ? It'd be convenient to have the ability to define a dict of response types with their corresponding descriptions and models (maybe headers too?) to use in the @api.doc decorator instead of duplicating the same @response annotation for every endpoint and every common response code.

I am willing to do the work, just wanted to check that i didn't miss something in the docs and that this will be something of value.

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.