Skip to content

Commit

Permalink
Merge pull request #90 from danballance/query_param_arrays
Browse files Browse the repository at this point in the history
Fixed an issue with query parameters not properly handling arrays
  • Loading branch information
hjacobs committed Nov 12, 2015
2 parents 7bada6b + b2952e8 commit 233b953
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 5 deletions.
25 changes: 20 additions & 5 deletions connexion/decorators/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ def get_function_arguments(function): # pragma: no cover
return inspect.getargspec(function).args


def make_type(value, type):
type_func = TYPE_MAP[type] # convert value to right type
return type_func(value)


def get_val_from_param(value, query_param):
if query_param["type"] == "array": # then logic is more complex
if query_param.get("collectionFormat") and query_param.get("collectionFormat") == "pipes":

This comment has been minimized.

Copy link
@valgog

valgog Nov 13, 2015

Actually it would be nice to check that other allowed values for the collectionFormat are not used, and throw an exception on the unsupported once. Current code will make it very difficult to understand why tsv does not work.

parts = value.split("|")
else: # default: csv
parts = value.split(",")

This comment has been minimized.

Copy link
@valgog

valgog Nov 13, 2015

Does the specification define anything about escaping separator characters in the used array values?

return [make_type(part, query_param["items"]["type"]) for part in parts]
else:
return make_type(value, query_param["type"])


def parameter_to_arg(parameters, function):
"""
Pass query and body parameters as keyword arguments to handler function.
Expand All @@ -39,7 +55,7 @@ def parameter_to_arg(parameters, function):
"""
body_parameters = [parameter for parameter in parameters if parameter['in'] == 'body'] or [{}]
body_name = body_parameters[0].get('name')
query_types = {parameter['name']: parameter['type']
query_types = {parameter['name']: parameter
for parameter in parameters if parameter['in'] == 'query'} # type: dict[str, str]
arguments = get_function_arguments(function)

Expand All @@ -66,10 +82,9 @@ def wrapper(*args, **kwargs):
logger.debug("Query Parameter '%s' not in function arguments", key)
else:
logger.debug("Query Parameter '%s' in function arguments", key)
key_type = query_types[key]
logger.debug('%s is a %s', key, key_type)
type_func = TYPE_MAP[key_type] # convert value to right type
kwargs[key] = type_func(value)
query_param = query_types[key]
logger.debug('%s is a %s', key, query_param)
kwargs[key] = get_val_from_param(value, query_param)

return function(*args, **kwargs)

Expand Down
36 changes: 36 additions & 0 deletions tests/fakeapi/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,42 @@ paths:
in: query
type: number
required: true
/test_array_csv_query_param:
get:
operationId: fakeapi.hello.test_array_csv_query_param
parameters:
- name: items
in: query
description: An comma separated array of items
required: true
type: array
items:
type: string
collectionFormat: csv
/test_array_pipes_query_param:
get:
operationId: fakeapi.hello.test_array_pipes_query_param
parameters:
- name: items
in: query
description: An pipe separated array of items
required: true
type: array
items:
type: integer
collectionFormat: pipes
/test_array_unsupported_query_param:
get:
operationId: fakeapi.hello.test_array_unsupported_query_param
parameters:
- name: items
in: query
description: An pipe separated array of items
required: true
type: array
items:
type: string
collectionFormat: unsupported
/test_no_content_response:
get:
operationId: fakeapi.hello.test_no_content_response
Expand Down
12 changes: 12 additions & 0 deletions tests/fakeapi/hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@ def test_required_query_param():
return ''


def test_array_csv_query_param(items):
return items


def test_array_pipes_query_param(items):
return items


def test_array_unsupported_query_param(items):
return items


def test_no_content_response():
return NoContent, 204

Expand Down
17 changes: 17 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,23 @@ def test_required_query_param(app):
assert response.status_code == 200


def test_array_query_param(app):
app_client = app.app.test_client()
headers = {'Content-type': 'application/json'}
url = '/v1.0/test_array_csv_query_param?items=one,two,three'
response = app_client.get(url, headers=headers)
array_response = json.loads(response.data.decode()) # type: [str]
assert array_response == ['one', 'two', 'three']
url = '/v1.0/test_array_pipes_query_param?items=1|2|3'
response = app_client.get(url, headers=headers)
array_response = json.loads(response.data.decode()) # type: [int]
assert array_response == [1, 2, 3]
url = '/v1.0/test_array_unsupported_query_param?items=1;2;3'
response = app_client.get(url, headers=headers)
array_response = json.loads(response.data.decode()) # [str] unsupported collectionFormat
assert array_response == ["1;2;3"]


def test_test_schema_array(app):
app_client = app.app.test_client()
headers = {'Content-type': 'application/json'}
Expand Down

0 comments on commit 233b953

Please sign in to comment.