From 8fe35a39866e32a9915f3b371ae010149bff26a2 Mon Sep 17 00:00:00 2001 From: Pavol Vargovcik Date: Wed, 4 Mar 2020 13:59:50 +0100 Subject: [PATCH] openapi: remove body preprocessing Body is already validated using jsonschema. There was also some type casting but it was wrong: e.g. not recurring deeply into dicts and lists, relying on existence of "type" in schema (which is not there e.g. if oneOf is used). Anyway, the only reason why types should be casted is converting integer values to float if the type is number. But this is in most cases irrelevant. Added an example, which did not work before this commit (echoed `{}`) e.g. for ``` curl localhost:8080/api/foo -H 'content-type: application/json' -d '{"foo": 1}' ``` but now the example works (echoes `{"foo": 1}`). --- connexion/operations/openapi.py | 26 ++++++++++++++--------- examples/openapi3/oneof/oneof.py | 20 ++++++++++++++++++ examples/openapi3/oneof/schema.yaml | 32 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 examples/openapi3/oneof/oneof.py create mode 100644 examples/openapi3/oneof/schema.yaml diff --git a/connexion/operations/openapi.py b/connexion/operations/openapi.py index 7f5d0de3a..c6ee678a1 100644 --- a/connexion/operations/openapi.py +++ b/connexion/operations/openapi.py @@ -10,6 +10,7 @@ from ..decorators.uri_parsing import OpenAPIURIParser from ..utils import deep_get, deep_merge, is_null, is_nullable, make_type +from ..http_facts import FORM_CONTENT_TYPES logger = logging.getLogger("connexion.operations.openapi3") @@ -286,6 +287,12 @@ def _get_body_argument(self, body, arguments, has_kwargs, sanitize): 'the requestBody instead.', DeprecationWarning) x_body_name = sanitize(self.body_schema.get('x-body-name', 'body')) + if self.consumes[0] in FORM_CONTENT_TYPES: + return self._get_body_argument_form(x_body_name, body, arguments, has_kwargs, sanitize) + else: + return self._get_body_argument_json(x_body_name, body, arguments, has_kwargs, sanitize) + + def _get_body_argument_json(self, x_body_name, body, arguments, has_kwargs, sanitize): # if the body came in null, and the schema says it can be null, we decide # to include no value for the body argument, rather than the default body if is_nullable(self.body_schema) and is_null(body): @@ -293,6 +300,15 @@ def _get_body_argument(self, body, arguments, has_kwargs, sanitize): return {x_body_name: None} return {} + if body is None: + default_body = self.body_schema.get('default', {}) + body = deepcopy(default_body) + + if x_body_name in arguments or has_kwargs: + return {x_body_name: body} + return {} + + def _get_body_argument_form(self, x_body_name, body, arguments, has_kwargs, sanitize): # now determine the actual value for the body (whether it came in or is default) default_body = self.body_schema.get('default', {}) body_props = {k: {"schema": v} for k, v @@ -302,16 +318,6 @@ def _get_body_argument(self, body, arguments, has_kwargs, sanitize): # see: https://github.com/OAI/OpenAPI-Specification/blame/3.0.2/versions/3.0.2.md#L2305 additional_props = self.body_schema.get("additionalProperties", True) - if body is None: - body = deepcopy(default_body) - - # if the body isn't even an object, then none of the concerns below matter - if self.body_schema.get("type") != "object": - if x_body_name in arguments or has_kwargs: - return {x_body_name: body} - return {} - - # supply the initial defaults and convert all values to the proper types by schema body_arg = deepcopy(default_body) body_arg.update(body or {}) diff --git a/examples/openapi3/oneof/oneof.py b/examples/openapi3/oneof/oneof.py new file mode 100644 index 000000000..03dd4e0e3 --- /dev/null +++ b/examples/openapi3/oneof/oneof.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import connexion +import jsonschema +import six +from connexion.decorators.validation import RequestBodyValidator +from connexion.json_schema import Draft4RequestValidator + +async def echo(body): + return body + +if __name__ == '__main__': + app = connexion.AioHttpApp( + __name__, + port=8080, + specification_dir='.', + options={'swagger_ui': True} + ) + app.add_api('schema.yaml') + app.run() diff --git a/examples/openapi3/oneof/schema.yaml b/examples/openapi3/oneof/schema.yaml new file mode 100644 index 000000000..9ab9937ea --- /dev/null +++ b/examples/openapi3/oneof/schema.yaml @@ -0,0 +1,32 @@ +openapi: '3.0.0' +info: + version: '1' + title: Custom Validator Example +servers: + - url: http://localhost:8080/{basePath} + variables: + basePath: + default: api +paths: + /echo: + post: + operationId: oneof.echo + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + required: + - foo + properties: + foo: + oneOf: + - {type: boolean} + - {type: number} + default: + foo: false + responses: + '200': + description: Echo the validated request.