mirror of
https://github.com/correl/openapi-core.git
synced 2024-12-28 19:19:23 +00:00
Merge pull request #271 from eyadgaran/master
use prepared request to format payload before converting
This commit is contained in:
commit
695f4e64c7
4 changed files with 98 additions and 18 deletions
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue