From 5a91425c68040aa5d4f2e253c5a94f9c460abb5c Mon Sep 17 00:00:00 2001 From: Elisha Yadgaran Date: Tue, 17 Nov 2020 00:12:56 -0800 Subject: [PATCH] use prepared request to format payload before converting --- openapi_core/contrib/requests/requests.py | 50 +++++++++++++++++++---- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/openapi_core/contrib/requests/requests.py b/openapi_core/contrib/requests/requests.py index 12921d9..c25ad67 100644 --- a/openapi_core/contrib/requests/requests.py +++ b/openapi_core/contrib/requests/requests.py @@ -1,5 +1,8 @@ """OpenAPI core contrib requests requests module""" +from typing import Union from werkzeug.datastructures import ImmutableMultiDict +from requests import Request, PreparedRequest +from urllib.parse import urlparse, parse_qs from openapi_core.validation.request.datatypes import ( RequestParameters, OpenAPIRequest, @@ -9,26 +12,57 @@ from openapi_core.validation.request.datatypes import ( class RequestsOpenAPIRequestFactory(object): @classmethod - def create(cls, request): + def create(cls, request: Union[Request, PreparedRequest]) -> OpenAPIRequest: + """ + Converts a requests request to an OpenAPI one + + Internally converts to a `PreparedRequest` first to parse the exact + payload being sent + """ + if isinstance(request, Request): + request = request.prepare() + + # Method method = request.method.lower() - cookie = request.cookies or {} + # Cookies + if request._cookies is not None: + # cookies are stored in a cookiejar object + cookie = request._cookies.get_dict() + else: + cookie = {} + + # Preparing a request formats the URL with params, strip them out again + o = urlparse(request.url) + params = parse_qs(o.query) + # extract the URL without query parameters + url = o._replace(query=None).geturl() # gets deduced by path finder against spec path = {} - mimetype = request.headers.get('Accept') or \ - request.headers.get('Content-Type') + # Order matters because all python requests issued from a session include + # Accept */* which does not necessarily match the content type + mimetype = request.headers.get('Content-Type') or \ + request.headers.get('Accept') + + # Headers - request.headers is not an instance of dict, which is expected + header = dict(request.headers) + + # Body + # TODO: figure out if request._body_position is relevant + body = request.body + parameters = RequestParameters( - query=ImmutableMultiDict(request.params), - header=request.headers, + query=ImmutableMultiDict(params), + header=header, cookie=cookie, path=path, ) return OpenAPIRequest( - full_url_pattern=request.url, + full_url_pattern=url, method=method, parameters=parameters, - body=request.data, + body=body, mimetype=mimetype, )