flask-restx icon indicating copy to clipboard operation
flask-restx copied to clipboard

RequestParser json list shown as type string in swagger json

Open mauvilsa opened this issue 5 years ago • 5 comments

If I create a request parser with an argument whose location is json and type list, in the api swagger json I see this argument as type string. I would have expected this to be shown as an array with items of type string. Am I right to expect this? Or is there some reason why it is shown as type string?

Steps to reproduce

  1. Create a python script issue.py with the following:
from flask import Flask
from flask_restx import Api, Resource, reqparse

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

request_parser = reqparse.RequestParser(bundle_errors=True)
request_parser.add_argument('list', location='json', type=list)

@api.route('/test_list')
class TestList(Resource):
    @api.expect(request_parser)
    def get(self):
        return {}

app.run()
  1. Run the script
python3 issue.py
  1. Get the swagger json
curl http://127.0.0.1:5000/swagger.json | python3 -mjson.tool

In the printed json you would observe the type of list as string

...
    "schema": {
        "type": "object",
        "properties": {
            "list": {
                "type": "string"
            }
        }
    }
...

mauvilsa avatar Jul 10 '20 12:07 mauvilsa

After creating this issue I saw https://github.com/python-restx/flask-restx/issues/59 (did not read it all) which makes me think that any issues related to RequestParser will not be fixed. I have noted that I can get the behavior that I want by creating a model from a json schema and expecting the model. Is this a good approach to achieve this which hopefully will not be affected by future changes related to issue https://github.com/python-restx/flask-restx/issues/59? What worked for me is the following:

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

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

request_schema = {
    'type': 'object',
    'properties': {
        'list': {
            'type': 'array',
            'items': {'type': 'string'},
            'minItems': 1,
        },
    },
    'additionalProperties': False,
    'required': ['list'],
}
request_model = api.schema_model('test_list_request', request_schema)

@api.route('/test_list')
class TestList(Resource):
    @api.expect(request_model, validate=True)
    def get(self):
        print(request.json)
        return request.json

app.run()

mauvilsa avatar Jul 10 '20 13:07 mauvilsa

I have a similar issue with file uploads:

Swagger.json shows a string type for the parser argument image. Hence, no File Upload Button

uploadParser = reqparse.RequestParser()
uploadParser.add_argument('image', type=werkzeug.datastructures.FileStorage, required=True)
class UploadImage(Resource):  
    @api.expect(uploadParser)
    def post(self):
        args = uploadParser.parse_args()
        uploaded_file = args['image']  # This is FileStorage instance
        filename = secure_filename(uploaded_file.filename)
        return {'url': filename}, 201

BenTStark avatar Dec 18 '20 21:12 BenTStark

After creating this issue I saw #59 (did not read it all) which makes me think that any issues related to RequestParser will not be fixed. I have noted that I can get the behavior that I want by creating a model from a json schema and expecting the model. Is this a good approach to achieve this which hopefully will not be affected by future changes related to issue #59? What worked for me is the following:

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

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

request_schema = {
    'type': 'object',
    'properties': {
        'list': {
            'type': 'array',
            'items': {'type': 'string'},
            'minItems': 1,
        },
    },
    'additionalProperties': False,
    'required': ['list'],
}
request_model = api.schema_model('test_list_request', request_schema)

@api.route('/test_list')
class TestList(Resource):
    @api.expect(request_model, validate=True)
    def get(self):
        print(request.json)
        return request.json

app.run()

I also faced this problem.

After reading the documentation, I have to add this lines of code to render type list parameter. But i still use the request parser to validate the request. It's just a temporary solution, because we have to write the parameters twice, both for documentation and for receiving the parameters.

add_photo_parser = reqparse.RequestParser()
add_photo_parser.add_argument('photo_url', type=list, location='json', help='Photo URL', required=True)

add_photo_fields = api.model('Add Photo', {
    'photo_url': fields.List(fields.String)
 })

@api.doc('projects_add_photo', description='Add photo to project', body=add_photo_fields)
@api.expect(add_photo_fields, validate=True)
@api.marshal_with(project_photos_serializer)
def post(self, project_id):
    args = self.add_photo_parser.parse_args()

linxlunx avatar Mar 17 '21 03:03 linxlunx

I have a similar issue with file uploads:

Swagger.json shows a string type for the parser argument image. Hence, no File Upload Button

uploadParser = reqparse.RequestParser()
uploadParser.add_argument('image', type=werkzeug.datastructures.FileStorage, required=True)
class UploadImage(Resource):  
    @api.expect(uploadParser)
    def post(self):
        args = uploadParser.parse_args()
        uploaded_file = args['image']  # This is FileStorage instance
        filename = secure_filename(uploaded_file.filename)
        return {'url': filename}, 201

Add location='files' uploadParser.add_argument('image', location='files', type=werkzeug.datastructures.FileStorage, required=True)

Abhijit-Dixit avatar Jun 29 '21 11:06 Abhijit-Dixit

Still not fix yet?

chaiyaboons avatar Mar 06 '24 08:03 chaiyaboons