Using view args and use_kwargs results in duplicate args
- Dominant language
- Python
- Stars
- 652
- Forks
- 151
- PR merge metrics
- No merged PRs in 30d
Description
When using `use_kwargs` in combination with a view argument (eg `pet_guid`), the parameter is registered twice, and the flask argument is not removed. Example:
```python
import flask.views
from flask_apispec import FlaskApiSpec, doc, use_kwargs, MethodResource, marshal_with
from marshmallow.fields import UUID
import marshmallow as ma
class Pet:
def __init__(self, name, type):
self.name = name
self.type = type
class PetSchema(ma.Schema):
name = ma.fields.Str()
type = ma.fields.Str()
app = flask.Flask(__name__)
docs = FlaskApiSpec(app)
@doc(tags=['pets'])
@use_kwargs({
'pet_guid': UUID(
required=True,
description='the pet name'
)
}, locations=['path'])
class CatResource(MethodResource):
@marshal_with(PetSchema)
def get(self, pet_guid):
return Pet('calici', 'cat')
app.add_url_rule('/cat/', view_func=CatResource.as_view('CatResource'))
docs.register(CatResource, endpoint='CatResource')
if __name__ == '__main__':
app.run(debug=True, port=5001)
```
Going to the swagger URL results in the following output:
```JSON
{
"definitions": {
"Pet": {
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
}
},
"type": "object"
}
},
"info": {
"title": "flask-apispec",
"version": "v1"
},
"paths": {
"/cat/{pet_guid}": {
"get": {
"parameters": [
{
"description": "the pet name",
"format": "uuid",
"in": "path",
"name": "pet_guid",
"required": true,
"type": "string"
},
{
"in": "path",
"name": "pet_guid",
"required": true,
"type": "string"
}
],
"responses": {
"default": {
"description": "",
"schema": {
"$ref": "#/definitions/Pet"
}
}
},
"tags": [
"pets"
]
}
}
},
"swagger": "2.0"
}
```
The following parameter should not be there as it's already defined above.
```json
{
"in": "path",
"name": "pet_guid",
"required": true,
"type": "string"
}
```
It's possible to bypass this by putting the registration inside of the `@doc(params={...})`, however this will not lead to any validation from webargs, which leads to invalid input reaching the flask code.
I've found that this is caused by `get_parameters` in [apidoc.py](https://github.com/jmcarp/flask-apispec/blob/master/flask_apispec/apidoc.py#L89), when the parameter is returned, no check is made if duplicate `name` and `in` keys exist. arguments exist in the `rule_params` and `extra_params`:
```python
rule_params = rule_to_params(rule, docs.get('params')) or []
extra_params = converter(schema, **options) if args else []
return extra_params + rule_params
```
The following fix would ensure that kwargs passed by `use_kwargs` have priority over the default flask rule params:
```python
from itertools import groupby
...
rule_params = rule_to_params(rule, docs.get('params')) or []
extra_params = converter(schema, **options) if args else []
params = {}
for key, val in groupby(result, lambda i: (i['name'], i['in'])):
if key not in params:
params[key] = next(val)
return list(params.values())
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Read get_parameters in flask_apispec/apidoc.py, especially rule_params and extra_params, and reproduce the provided Flask/Swagger example first. Done means a path parameter supplied through use_kwargs is emitted once, with its validation and description retained, while the generated spec still represents the Flask route correctly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- flask, python
- Domain
- api, documentation
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100