Skip to content

Commit fee4cee

Browse files
tnweissSteadBytes
authored andcommitted
Correctly specify a JSON request body payload as a single schema
Currently, a JSON object payload is treated as a collection of separate parameters and specified as a list of schema in the generated Swagger spec. This commit changes this behaviour to comply with https://swagger.io/docs/specification/2-0/describing-request-body/ where object payloads are specified using a single schema encapsulation all the properties.
1 parent 3582c84 commit fee4cee

File tree

2 files changed

+72
-3
lines changed

2 files changed

+72
-3
lines changed

flask_restx/swagger.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,48 @@ def is_hidden(resource, route_doc=None):
148148
return hasattr(resource, "__apidoc__") and resource.__apidoc__ is False
149149

150150

151+
def build_request_body_parameters_schema(body_params):
152+
"""
153+
:param body_params: List of JSON schema of body parameters.
154+
:type body_params: list of dict, generated from the json body parameters of a request parser
155+
:return dict: The Swagger schema representation of the request body
156+
157+
:Example:
158+
{
159+
'name': 'payload',
160+
'required': True,
161+
'in': 'body',
162+
'schema': {
163+
'type': 'object',
164+
'properties': [
165+
'parameter1': {
166+
'type': 'integer'
167+
},
168+
'parameter2': {
169+
'type': 'string'
170+
}
171+
]
172+
}
173+
}
174+
"""
175+
176+
properties = {}
177+
for param in body_params:
178+
properties[param['name']] = {
179+
'type': param.get('type', 'string')
180+
}
181+
182+
return {
183+
'name': 'payload',
184+
'required': True,
185+
'in': 'body',
186+
'schema': {
187+
'type': 'object',
188+
'properties': properties
189+
}
190+
}
191+
192+
151193
class Swagger(object):
152194
'''
153195
A Swagger documentation wrapper for an API instance.
@@ -334,8 +376,12 @@ def expected_params(self, doc):
334376

335377
for expect in doc.get('expect', []):
336378
if isinstance(expect, RequestParser):
337-
parser_params = OrderedDict((p['name'], p) for p in expect.__schema__)
379+
parser_params = OrderedDict((p['name'], p) for p in expect.__schema__ if p['in'] != 'body')
338380
params.update(parser_params)
381+
382+
body_params = [p for p in expect.__schema__ if p['in'] == 'body']
383+
if body_params:
384+
params['payload'] = build_request_body_parameters_schema(body_params)
339385
elif isinstance(expect, ModelBase):
340386
params['payload'] = not_none({
341387
'name': 'payload',

tests/test_swagger.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ def get(self, age):
623623
def test_expect_parser(self, api, client):
624624
parser = api.parser()
625625
parser.add_argument('param', type=int, help='Some param')
626+
parser.add_argument('jsonparam', type=str, location='json', help='Some param')
626627

627628
@api.route('/with-parser/', endpoint='with-parser')
628629
class WithParserResource(restx.Resource):
@@ -634,14 +635,20 @@ def get(self):
634635
assert '/with-parser/' in data['paths']
635636

636637
op = data['paths']['/with-parser/']['get']
637-
assert len(op['parameters']) == 1
638+
assert len(op['parameters']) == 2
638639

639-
parameter = op['parameters'][0]
640+
parameter = [o for o in op['parameters'] if o['in'] == 'query'][0]
640641
assert parameter['name'] == 'param'
641642
assert parameter['type'] == 'integer'
642643
assert parameter['in'] == 'query'
643644
assert parameter['description'] == 'Some param'
644645

646+
parameter = [o for o in op['parameters'] if o['in'] == 'body'][0]
647+
assert parameter['name'] == 'payload'
648+
assert parameter['required']
649+
assert parameter['in'] == 'body'
650+
assert parameter['schema']['properties']['jsonparam']['type'] == 'string'
651+
645652
def test_expect_parser_on_class(self, api, client):
646653
parser = api.parser()
647654
parser.add_argument('param', type=int, help='Some param')
@@ -3377,3 +3384,19 @@ def post(self):
33773384

33783385
assert 'body' not in ModelAsDict.post.__apidoc__
33793386
assert ModelAsDict.post.__apidoc__['expect'] == [(fields, 'Body description')]
3387+
3388+
def test_build_request_body_parameters_schema(self):
3389+
parser = restx.reqparse.RequestParser()
3390+
parser.add_argument('test', type=int, location='headers')
3391+
parser.add_argument('test1', type=int, location='json')
3392+
parser.add_argument('test2', location='json')
3393+
3394+
body_params = [p for p in parser.__schema__ if p['in'] == 'body']
3395+
result = restx.swagger.build_request_body_parameters_schema(body_params)
3396+
3397+
assert result['name'] == 'payload'
3398+
assert result['required']
3399+
assert result['in'] == 'body'
3400+
assert result['schema']['type'] == 'object'
3401+
assert result['schema']['properties']['test1']['type'] == 'integer'
3402+
assert result['schema']['properties']['test2']['type'] == 'string'

0 commit comments

Comments
 (0)