Skip to content

Commit

Permalink
Merge pull request #68 from zalando/issue66-body-list
Browse files Browse the repository at this point in the history
Handle non object body parameters
  • Loading branch information
jmcs committed Sep 28, 2015
2 parents 06ef604 + 33dc2b8 commit 5fa7642
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 21 deletions.
24 changes: 10 additions & 14 deletions connexion/decorators/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,16 @@ def get_function_arguments(function): # pragma: no cover
return inspect.getargspec(function).args


def parameter_to_arg(body_schema, parameters, function):
def parameter_to_arg(parameters, function):
"""
Pass query and body parameters as keyword arguments to handler function.
See (https://github.com/zalando/connexion/issues/59)
:type body_schema: dict|None
:type parameters: dict|None
"""
body_schema = body_schema or {} # type: dict
body_properties = body_schema.get('properties', {})
body_types = {name: properties['type'] for name, properties in body_properties.items()} # type: dict[str, str]
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']
for parameter in parameters if parameter['in'] == 'query'} # type: dict[str, str]
arguments = get_function_arguments(function)
Expand All @@ -49,20 +48,17 @@ def wrapper(*args, **kwargs):
logger.debug('Function Arguments: %s', arguments)

try:
body_parameters = flask.request.json or {} # type: dict
request_body = flask.request.json
except exceptions.BadRequest:
body_parameters = {}
request_body = None

# Add body parameters
for key, value in body_parameters.items():
if key not in arguments:
logger.debug("Body parameter '%s' not in function arguments", key)
if request_body is not None:
if body_name not in arguments:
logger.debug("Body parameter '%s' not in function arguments", body_name)
else:
logger.debug("Body parameter '%s' in function arguments", key)
key_type = body_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)
logger.debug("Body parameter '%s' in function arguments", body_name)
kwargs[body_name] = request_body

# Add query parameters
for key, value in flask.request.args.items():
Expand Down
1 change: 0 additions & 1 deletion connexion/decorators/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ def validate_path_parameter(self, args, param):

def validate_header_parameter(self, param):
val = flask.request.headers.get(param['name'])
print(val, param)
return self.validate_parameter('header', val, param)

def __call__(self, function):
Expand Down
11 changes: 9 additions & 2 deletions connexion/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import functools
import logging
import os

from .decorators.parameter import parameter_to_arg
from .decorators.produces import BaseSerializer, Produces, Jsonifier
from .decorators.security import security_passthrough, verify_oauth
Expand Down Expand Up @@ -143,7 +142,15 @@ def function(self):
:rtype: types.FunctionType
"""

function = parameter_to_arg(self.body_schema, self.parameters, self.__undecorated_function)
parameters = []
for param in self.parameters: # resolve references
param = param.copy()
schema = param.get('schema')
if schema:
schema = self.resolve_reference(schema)
param['schema'] = schema
parameters.append(param)
function = parameter_to_arg(parameters, self.__undecorated_function)

produces_decorator = self.__content_type_decorator
logger.debug('... Adding produces decorator (%r)', produces_decorator, extra=vars(self))
Expand Down
30 changes: 30 additions & 0 deletions tests/fakeapi/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,36 @@ paths:
in: query
type: number
required: true
/schema_array:
get:
tags:
description: test schema array
operationId: fakeapi.hello.test_schema_array
parameters:
- name: test_array
in: body
required: true
schema:
type: array
items:
type: string
responses:
200:
description: OK
/schema_int:
get:
tags:
description: test schema int
operationId: fakeapi.hello.test_schema_int
parameters:
- name: test_int
in: body
required: true
schema:
type: integer
responses:
200:
description: OK
definitions:
new_stack:
type: object
Expand Down
12 changes: 10 additions & 2 deletions tests/fakeapi/hello.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ def empty():
return None, 204


def schema(image_version):
return {'image_version': image_version}
def schema(new_stack):
return new_stack


def schema_query(image_version=None):
Expand All @@ -78,3 +78,11 @@ def test_parameter_validation():

def test_required_query_param():
return ''


def test_schema_array(test_array):
return test_array


def test_schema_int(test_int):
return test_int
25 changes: 24 additions & 1 deletion tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import requests
import logging
import _pytest.monkeypatch

from connexion.app import App

logging.basicConfig(level=logging.DEBUG)
Expand Down Expand Up @@ -385,3 +384,27 @@ def test_required_query_param(app):

response = app_client.get(url, query_string={'n': '1.23'})
assert response.status_code == 200


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

array_request = app_client.get('/v1.0/schema_array', headers=headers,
data=json.dumps(['list', 'hello'])) # type: flask.Response
assert array_request.status_code == 200
assert array_request.content_type == 'application/json'
array_response = json.loads(array_request.data.decode()) # type: list
assert array_response == ['list', 'hello']


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

array_request = app_client.get('/v1.0/schema_int', headers=headers,
data=json.dumps(42)) # type: flask.Response
assert array_request.status_code == 200
assert array_request.content_type == 'application/json'
array_response = json.loads(array_request.data.decode()) # type: list
assert array_response == 42
1 change: 0 additions & 1 deletion tests/test_operation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pathlib
import pytest
import types

from connexion.exceptions import InvalidSpecification
from connexion.operation import Operation
from connexion.decorators.security import security_passthrough, verify_oauth
Expand Down

0 comments on commit 5fa7642

Please sign in to comment.