python-restx / flask-restx

Fork of Flask-RESTPlus: Fully featured framework for fast, easy and documented API development with Flask
https://flask-restx.readthedocs.io/en/latest/
Other
2.14k stars 333 forks source link

Value of different types (e.g., str, list) #564

Open thanasissdr opened 11 months ago

thanasissdr commented 11 months ago

It seems swagger cannot properly handle the JSON payload and I've been wondering if it's my implementation or there's something wrong. I have attached a MWE to demonstrate part of the issue I have been facing and it is mainly associated with the fact that a value of a key could be of different types, e.g. str or list (that's why in the following example I have used fields.Raw)

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

app = Flask(__name__)
api = Api(app, doc="/swagger")

@app.route("/hello_world", methods=["POST"])
def hello_world():
    payload = request.get_json()

    return jsonify(payload)

filter_component = api.model(
    "FilterComponent",
    {
        "variable": fields.String(required=True),
        "operator": fields.String(required=True),
        "value": fields.Raw(required=True),
    },
)

hello_world_payload = api.model(
    "HelloWorldPayload",
    {
        "filters": fields.List(
            fields.Nested(filter_component, required=True), required=True
        )
    },
)

@api.route("/hello_world")
class HelloWorldResource(Resource):
    @api.expect(hello_world_payload)
    def post(self):
        pass

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)

If I go to swagger and try out the endpoint with the following endpoint:


{
  "filters": [
    {
      "variable": "name",
      "operator": "eq",
      "value": ["Bob"]
    }
  ]
}

everything seems to work as expected and get the expected output:

functional

However, if I use the following payload:


{
  "filters": [
    {
      "variable": "name",
      "operator": "eq",
      "value": "Bob"
    }
  ]
}

when I hit the execute button, I don't seem to get anything back.

Expected output:


{
  "filters": [
    {
      "operator": "eq",
      "value": "Bob",
      "variable": "name"
    }
  ]
}

I'm not sure if this is a bug or no, so I have set this as a question. Please feel free to ask if anything else is needed.

python version: 3.11.5 flask version: 2.3.3 flask-restx version: 1.1.0

peter-doggart commented 11 months ago

Having a look at the swagger.json that gets generated by your example, the error is caused by the fact that when using fields.Raw the default data type is object and in example two, you are passing a string which is not an object as far as swagger is concerned.

I don't think there is any neat way to allow flask-restx / Swagger 2.0 to allow you to have multiple data types. My suggestion would be to always pass a list of strings, even if you only have one value, so you have a fixed data type, and then use fields.String instead.

thanasissdr commented 11 months ago

@peter-doggart Thanks for the insight! This is what I have seen, as well, however I am not sure If I can apply your suggestion as is, since there might be objects of different types, i.e., floats, integers, strings. I was hoping flask-restx could support multiple data types.