jmcarp / jmcarp/flask-apispec

Using view args and use_kwargs results in duplicate args

Open
#144 4 comments 3 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.