Merge pull request #271 from eyadgaran/master

use prepared request to format payload before converting
This commit is contained in:
A 2021-03-24 13:43:27 +00:00 committed by GitHub
commit 695f4e64c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 18 deletions

View file

@ -1,5 +1,8 @@
"""OpenAPI core contrib requests requests module"""
from __future__ import absolute_import
from werkzeug.datastructures import ImmutableMultiDict
from requests import Request
from six.moves.urllib.parse import urlparse, parse_qs
from openapi_core.validation.request.datatypes import (
RequestParameters, OpenAPIRequest,
@ -10,25 +13,56 @@ class RequestsOpenAPIRequestFactory(object):
@classmethod
def create(cls, request):
"""
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
cookie = {}
if request._cookies is not None:
# cookies are stored in a cookiejar object
cookie = request._cookies.get_dict()
# 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,
)

View file

@ -13,7 +13,25 @@ paths:
description: the ID of the resource to retrieve
schema:
type: integer
get:
- name: q
in: query
required: true
description: query key
schema:
type: string
post:
requestBody:
description: request data
required: True
content:
application/json:
schema:
type: object
required:
- param1
properties:
param1:
type: integer
responses:
200:
description: Return the resource.

View file

@ -15,6 +15,7 @@ class TestRequestsOpenAPIRequest(object):
query = ImmutableMultiDict([])
headers = request.headers
cookies = {}
prepared = request.prepare()
assert openapi_request.parameters == RequestParameters(
path=path,
query=query,
@ -23,7 +24,7 @@ class TestRequestsOpenAPIRequest(object):
)
assert openapi_request.method == request.method.lower()
assert openapi_request.full_url_pattern == 'http://localhost/'
assert openapi_request.body == request.data
assert openapi_request.body == prepared.body
assert openapi_request.mimetype == 'application/json'
def test_multiple_values(self, request_factory, request):
@ -44,9 +45,10 @@ class TestRequestsOpenAPIRequest(object):
header=headers,
cookie=cookies,
)
prepared = request.prepare()
assert openapi_request.method == request.method.lower()
assert openapi_request.full_url_pattern == 'http://localhost/'
assert openapi_request.body == request.data
assert openapi_request.body == prepared.body
assert openapi_request.mimetype == 'application/json'
def test_url_rule(self, request_factory, request):
@ -57,7 +59,9 @@ class TestRequestsOpenAPIRequest(object):
# empty when not bound to spec
path = {}
query = ImmutableMultiDict([])
headers = request.headers
headers = (
('Content-Type', 'application/json'),
)
cookies = {}
assert openapi_request.parameters == RequestParameters(
path=path,
@ -65,8 +69,9 @@ class TestRequestsOpenAPIRequest(object):
header=headers,
cookie=cookies,
)
prepared = request.prepare()
assert openapi_request.method == request.method.lower()
assert openapi_request.full_url_pattern == \
'http://localhost/browse/12/'
assert openapi_request.body == request.data
assert openapi_request.body == prepared.body
assert openapi_request.mimetype == 'application/json'

View file

@ -10,7 +10,7 @@ from openapi_core.validation.request.validators import RequestValidator
from openapi_core.validation.response.validators import ResponseValidator
class TestFlaskOpenAPIValidation(object):
class TestRequestsOpenAPIValidation(object):
@pytest.fixture
def spec(self, factory):
@ -20,10 +20,16 @@ class TestFlaskOpenAPIValidation(object):
@responses.activate
def test_response_validator_path_pattern(self, spec):
responses.add(
responses.GET, 'http://localhost/browse/12/',
json={"data": "data"}, status=200)
responses.POST, 'http://localhost/browse/12/?q=string',
json={"data": "data"}, status=200, match_querystring=True,
)
validator = ResponseValidator(spec)
request = requests.Request('GET', 'http://localhost/browse/12/')
request = requests.Request(
'POST', 'http://localhost/browse/12/',
params={'q': 'string'},
headers={'content-type': 'application/json'},
json={'param1': 1},
)
request_prepared = request.prepare()
session = requests.Session()
response = session.send(request_prepared)
@ -32,10 +38,27 @@ class TestFlaskOpenAPIValidation(object):
result = validator.validate(openapi_request, openapi_response)
assert not result.errors
@responses.activate
def test_request_validator_path_pattern(self, spec):
validator = RequestValidator(spec)
request = requests.Request('GET', 'http://localhost/browse/12/')
request = requests.Request(
'POST', 'http://localhost/browse/12/',
params={'q': 'string'},
headers={'content-type': 'application/json'},
json={'param1': 1},
)
openapi_request = RequestsOpenAPIRequest(request)
result = validator.validate(openapi_request)
assert not result.errors
def test_request_validator_prepared_request(self, spec):
validator = RequestValidator(spec)
request = requests.Request(
'POST', 'http://localhost/browse/12/',
params={'q': 'string'},
headers={'content-type': 'application/json'},
json={'param1': 1},
)
request_prepared = request.prepare()
openapi_request = RequestsOpenAPIRequest(request_prepared)
result = validator.validate(openapi_request)
assert not result.errors