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.16k stars 335 forks source link

automatic swagger documentation fails when multiple locations provided to regparser #476

Open db0 opened 2 years ago

db0 commented 2 years ago

Code

class AsyncGenerate(Resource):
    parser = reqparse.RequestParser()
    parser.add_argument("prompt", type=str, required=True, help="The prompt to generate from")
    parser.add_argument("api_key", type=str, required=True, help="The API Key corresponding to a registered user", location=['headers', 'json'])
    parser.add_argument("params", type=dict, required=False, default={}, help="Extra generate params to send to the SD server")
    parser.add_argument("servers", type=str, action='append', required=False, default=[], help="If specified, only the server with this ID will be able to generate this prompt")
    @api.expect(parser)
    def post(self, api_version = None):

Expected Behavior

The swagger API should renger my regparser requirements correctly

Actual Behavior

If I set the location parameter of add_argument to a list of locations, the api.expect(parser) errors

Error Messages/Stack Trace

2022-09-22 16:44:58,743 - ERROR - api:576 - Unable to render schema
Traceback (most recent call last):
  File "C:\Users\Db0\AppData\Roaming\Python\Python310\site-packages\flask_restx\api.py", line 571, in __schema__
    self._schema = Swagger(self).as_dict()
  File "C:\Users\Db0\AppData\Roaming\Python\Python310\site-packages\flask_restx\swagger.py", line 239, in as_dict
    serialized = self.serialize_resource(
  File "C:\Users\Db0\AppData\Roaming\Python\Python310\site-packages\flask_restx\swagger.py", line 438, in serialize_resource
    doc = self.extract_resource_doc(resource, url, route_doc=route_doc)
  File "C:\Users\Db0\AppData\Roaming\Python\Python310\site-packages\flask_restx\swagger.py", line 343, in extract_resource_doc
    method_params = self.expected_params(method_doc)
  File "C:\Users\Db0\AppData\Roaming\Python\Python310\site-packages\flask_restx\swagger.py", line 382, in expected_params
    (p["name"], p) for p in expect.__schema__ if p["in"] != "body"
  File "C:\Users\Db0\AppData\Roaming\Python\Python310\site-packages\flask_restx\reqparse.py", line 435, in __schema__
    param = arg.__schema__
  File "C:\Users\Db0\AppData\Roaming\Python\Python310\site-packages\flask_restx\reqparse.py", line 290, in __schema__
    param = {"name": self.name, "in": LOCATIONS.get(self.location, "query")}
TypeError: unhashable type: 'list'

Environment

peter-doggart commented 2 years ago

I think this is actually a shortfall of Swagger 2.0 rather than flask-restx. We currently use OpenAPI 2.0 to generate the swagger.json file. The Parameter Object specification requires you to set the in: location, which only accepts a single string value:

Field Name: in
Type: string
Description: Required. The location of the parameter. Possible values are "query", "header", "path", "formData" or "body".

If this is just for authorizations, you might want to add them to swagger differently, such as using the build in documenting authorizations fields?

db0 commented 2 years ago

Oh cool. I'll look into that.

nevertheless, according to the documentation, my approach should work as well, no?

peter-doggart commented 2 years ago

Yes, I agree the docs are unclear on this. The actual request parser will work correctly with your code (i.e. it will check both locations for the value), it's just the swagger docs that fail to be generated and that isn't mentioned anywhere currently, and it's all a bit messy!

A workaround I've used in the past is to define a separate API model / reqparser (which you can do with copy() and replace_argument()) for the endpoint with only a single location, but then use the multi-location one in code to actually do the parsing, but obviously that is not ideal either.

The replacement of request parsing / models with something more flexible has been under discussion for a long time on this project too!