From 18a2d6ce7644d5d6191c65d27def536ba4ae984c Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Tue, 24 Apr 2018 10:43:35 -0400 Subject: [PATCH 1/9] Adding Support For Requests Library --- openapi_core/wrappers.py | 157 ++++++++++++ requirements_dev.txt | 2 + tests/integration/conftest.py | 1 - .../data/v3.0/server_path_variations.yaml | 21 ++ tests/integration/test_requests_factory.py | 223 ++++++++++++++++++ tests/integration/test_wrappers.py | 7 +- 6 files changed, 408 insertions(+), 3 deletions(-) create mode 100644 tests/integration/data/v3.0/server_path_variations.yaml create mode 100644 tests/integration/test_requests_factory.py diff --git a/openapi_core/wrappers.py b/openapi_core/wrappers.py index 019e242d..94a8d18b 100644 --- a/openapi_core/wrappers.py +++ b/openapi_core/wrappers.py @@ -1,6 +1,9 @@ """OpenAPI core wrappers module""" +import re +from string import Formatter import warnings +from urllib.parse import urlparse from six.moves.urllib.parse import urljoin from werkzeug.datastructures import ImmutableMultiDict @@ -107,6 +110,70 @@ def mimetype(self): return self.request.mimetype +class RequestsOpenAPIRequest(BaseOpenAPIRequest): + + def __init__(self, request, path_pattern, regex_pattern, server_pattern): + """ + + :param request: The Requests Request Object + :type request: requests.models.PreparedRequest + :param path_pattern: The path pattern determined by the factory + :type path_pattern: str + :param regex_pattern: The regex pattern that matched the path. Used to extract path params. + :type regex_pattern: str + """ + self.request = request + self.url = urlparse(self.request.url) + self._path_pattern = path_pattern + self._regex_pattern = regex_pattern + self._server_pattern = server_pattern + self._parameters = { + 'path': self._extract_path_params(), + 'query': self.request.qs, + 'headers': self.request.headers, + 'cookies': self.request._cookies, + } + + @property + def full_url_pattern(self): + return self.host_url + self.path_pattern + + @property + def host_url(self): + return re.match(self._server_pattern, self.request.url)[0] + + @property + def path(self): + return re.sub(self._server_pattern, '', urljoin(self.host_url, self.url.path)) + + @property + def method(self): + return self.request.method.lower() + + @property + def path_pattern(self): + return self._path_pattern + + @property + def parameters(self): + return self._parameters + + def _extract_path_params(self): + # Get the values of the path parameters + groups = re.match(self._regex_pattern, self.path).groups() + # Get the names of path parameters + names = [fname[1] for fname in Formatter().parse(self.path_pattern) if fname] + return {name: group for name, group in zip(names, groups)} + + @property + def body(self): + return self.request.text + + @property + def mimetype(self): + return self.request.headers.get('Accept') + + class BaseOpenAPIResponse(object): body = NotImplemented @@ -140,3 +207,93 @@ def status_code(self): @property def mimetype(self): return self.response.mimetype + + +class RequestsOpenAPIResponse(BaseOpenAPIResponse): + + def __init__(self, response): + self.response = response + + @property + def data(self): + return self.response.text + + @property + def status_code(self): + return self.response.status_code + + @property + def mimetype(self): + return self.response.headers.get('Content-Type') + + +class RequestsFactory(object): + path_regex = re.compile('{(.*?)}') + + def __init__(self, spec): + """ + Creates the request factory. A spec is required for path_pattern parsing. + :param spec: The openapi spec to use for decoding the request + :type spec: openapi_core.specs.Spec + """ + self.paths_regex = self._create_paths_regex(spec) + self.server_regex = self._create_server_regex(spec) + + def _create_server_regex(self, spec): + server_regex = [] + for server in spec.servers: + var_map = {} + for var in server.variables: + if server.variables[var].enum: + var_map[var] = "(" + "|".join(server.variables[var].enum) + ")" + else: + var_map[var] = "(.*)" + server_regex.append(server.url.format_map(var_map)) + return server_regex + + def _create_paths_regex(self, spec): + paths_regex = {} + for path in spec.paths: + pattern = self.path_regex.sub('(.*)', path) + paths_regex[pattern] = path + return paths_regex + + def _match_operation(self, path_pattern): + for expr in self.paths_regex: + if re.fullmatch(expr, path_pattern): + return expr + return None + + def _match_server(self, server_pattern): + for expr in self.server_regex: + if re.match(expr, server_pattern): + return expr + return None + + def create_request(self, request): + """ + Creates an OpenApi compatible request out of the raw requests request + + :param request: requests.models.Request + :return: RequestsOpenApiRequest + """ + url = request.url + server_pattern = self._match_server(request.url) + path = re.sub(server_pattern, '', request.url) + pattern = self._match_operation(path) + request = RequestsOpenAPIRequest(request, self.paths_regex[pattern], pattern, server_pattern) + return request + + def create_response(self, response): + """ + + :param request: + :type request: requests.models.Request + :return: RequestsOpenAPIResponse + """ + return RequestsOpenAPIResponse(response) + + def create_validation_pair(self, request): + return None, None + + diff --git a/requirements_dev.txt b/requirements_dev.txt index 2326086e..656468b3 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -3,4 +3,6 @@ pytest pytest-pep8 pytest-flakes pytest-cov +requests +requests-mock flask \ No newline at end of file diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 3f537be6..880ebe43 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,5 +1,4 @@ from os import path - import pytest from six.moves.urllib import request from yaml import safe_load diff --git a/tests/integration/data/v3.0/server_path_variations.yaml b/tests/integration/data/v3.0/server_path_variations.yaml new file mode 100644 index 00000000..6b9cd14c --- /dev/null +++ b/tests/integration/data/v3.0/server_path_variations.yaml @@ -0,0 +1,21 @@ +openapi: "3.0.0" +info: + title: Minimal valid OpenAPI specification with explicit 'servers' array + version: "0.1" +servers: + - url: https://{customerId}.saas-app.com:{port}/v2 + variables: + customerId: + default: demo + description: Customer ID assigned by the service provider + port: + enum: + - '443' + - '8443' + default: '443' +paths: + /status: + get: + responses: + default: + description: Return the API status. diff --git a/tests/integration/test_requests_factory.py b/tests/integration/test_requests_factory.py new file mode 100644 index 00000000..01b0c8a4 --- /dev/null +++ b/tests/integration/test_requests_factory.py @@ -0,0 +1,223 @@ +import json + +import pytest +import requests +import requests_mock + +from openapi_core.wrappers import RequestsFactory +from openapi_core.shortcuts import create_spec +from openapi_core.validators import ResponseValidator, RequestValidator + + +@pytest.fixture(scope='session', autouse=True) +def create_mock_session(): + session = requests.Session() + adapter = requests_mock.Adapter() + session.mount('mock', adapter) + + +@pytest.fixture(scope='session') +def server_spec(factory): + server_spec_dict = factory.spec_from_file("data/v3.0/server_path_variations.yaml") + return create_spec(server_spec_dict) + + +@pytest.fixture(scope='session') +def spec_dict(factory): + return factory.spec_from_file("data/v3.0/petstore.yaml") + + +@pytest.fixture(scope='session') +def spec(spec_dict): + return create_spec(spec_dict) + + +@pytest.fixture(scope='session') +def request_validator(spec): + return RequestValidator(spec) + + +@pytest.fixture(scope='session') +def response_validator(spec): + return ResponseValidator(spec) + + +@pytest.fixture(scope='session') +def server_request_validator(server_spec): + return RequestValidator(server_spec) + + +@pytest.fixture +def requests_factory(spec): + return RequestsFactory(spec) + + +@pytest.fixture +def server_requests_factory(server_spec): + return RequestsFactory(server_spec) + + +def test_mock_get_request_converts_correctly(requests_factory): + """ + Verifies that a GET request is correctly converted. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + """ + with requests_mock.mock() as m: + m.get('http://petstore.swagger.io/v1/pets/12345?format=json', + text='{"name": "Sparky"}', + headers={'Authentication': 'Bearer 123456', + 'Content-Type': 'application/json'}, + cookies={} + ) + response = requests.get(url='http://petstore.swagger.io/v1/pets/12345?format=json', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}) + request = requests_factory.create_request(response.request) + + assert request.host_url == "http://petstore.swagger.io/v1" + assert request.path == "/pets/12345" + assert request.method == 'get' + assert request.path_pattern == "/pets/{petId}" + assert request.parameters['path']['petId'] == '12345' + assert request.parameters['query']['format'] == ['json'] + assert request.parameters['headers']['Authorization'] == 'Bearer 123456' + assert not request.parameters['cookies'] + assert not request.body + assert request.mimetype == 'application/json' + + response = requests_factory.create_response(response) + assert response.data == '{"name": "Sparky"}' + assert response.status_code == 200 + assert response.mimetype == 'application/json' + + +def test_mock_post_request_converts_correctly(requests_factory): + """ + Verifies that a POST request is correctly converted. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + """ + with requests_mock.mock() as m: + m.post('http://petstore.swagger.io/v1/pets/12345?format=json', + text='{"name": "Sparky"}', + headers={'Authentication': 'Bearer 123456', + 'Content-Type': 'application/json'}, + cookies={} + ) + response = requests.post(url='http://petstore.swagger.io/v1/pets/12345?format=json', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}, + data='{"name": "Sparky"}' + ) + request = requests_factory.create_request(response.request) + + assert request.host_url == "http://petstore.swagger.io/v1" + assert request.path == "/pets/12345" + assert request.method == 'post' + assert request.path_pattern == "/pets/{petId}" + assert request.parameters['path']['petId'] == '12345' + assert request.parameters['query']['format'] == ['json'] + assert request.parameters['headers']['Authorization'] == 'Bearer 123456' + assert not request.parameters['cookies'] + assert request.body == '{"name": "Sparky"}' + assert request.mimetype == 'application/json' + + response = requests_factory.create_response(response) + assert response.data == '{"name": "Sparky"}' + assert response.status_code == 200 + assert response.mimetype == 'application/json' + + +def test_server_regex_for_server_wildcards(server_requests_factory, server_request_validator): + with requests_mock.mock() as m: + m.get('https://123456.saas-app.com:443/v2/status') + response = requests.get(url='https://123456.saas-app.com:443/v2/status') + request = server_requests_factory.create_request(response.request) + assert request._server_pattern == 'https://(.*).saas-app.com:(443|8443)/v2' + server_request_validator.validate(request) + + +def test_get_validation(requests_factory, request_validator, response_validator): + """ + Verifies that a GET request is correctly converted. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + :param request_validator: + :type request_validator: openapi_core.validators.RequestValidator + """ + + mock_resp = { + "data": { + "id": 12345, + "name": "Sparky", + "tag": "dogs", + "address": "1234 Someplace", + "position": 1 + + } + } + with requests_mock.mock() as m: + m.get('http://petstore.swagger.io/v1/pets/12345', + text=json.dumps(mock_resp), + headers={'Authentication': 'Bearer 123456', + 'Content-Type': 'application/json'}, + cookies={} + ) + response = requests.get(url='http://petstore.swagger.io/v1/pets/12345', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}) + request = requests_factory.create_request(response.request) + request_validator.validate(request) + + response = requests_factory.create_response(response) + response_validator.validate(request, response) + + +def test_post_validation(requests_factory, request_validator, response_validator): + """ + Verifies that a POST request is correctly validated. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + :param request_validator: + :type request_validator: openapi_core.validators.RequestValidator + """ + mock_resp = { + "data": { + "id": 12345, + "name": "Sparky", + "tag": "dogs", + "address": "1234 Someplace", + "position": 1, + "healthy": True + + } + } + mock_body = { + "tag": "dogs", + "address": "1234 Someplace", + "position": 1, + "healthy": True + } + with requests_mock.mock() as m: + m.post('http://petstore.swagger.io/v1/pets', + text=json.dumps(mock_resp), + headers={'Authentication': 'Bearer 123456', + 'Content-Type': 'application/json'}, + cookies={} + ) + response = requests.post(url='http://petstore.swagger.io/v1/pets', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}, + data=json.dumps(mock_body)) + request = requests_factory.create_request(response.request) + errors = request_validator.validate(request) + assert not errors + + response = requests_factory.create_response(response) + errors = response_validator.validate(request, response) + assert not errors diff --git a/tests/integration/test_wrappers.py b/tests/integration/test_wrappers.py index b33505e9..1380c5bc 100644 --- a/tests/integration/test_wrappers.py +++ b/tests/integration/test_wrappers.py @@ -1,11 +1,14 @@ import pytest - +from requests.models import Request, Response from flask.wrappers import Request, Response from werkzeug.datastructures import EnvironHeaders, ImmutableMultiDict from werkzeug.routing import Map, Rule, Subdomain from werkzeug.test import create_environ -from openapi_core.wrappers import FlaskOpenAPIRequest, FlaskOpenAPIResponse +from openapi_core.wrappers import FlaskOpenAPIRequest, \ + FlaskOpenAPIResponse, \ + RequestsOpenAPIRequest, \ + RequestsOpenAPIResponse class TestFlaskOpenAPIRequest(object): From 63b04210900ecba18b5e564522c3cc434d3944c2 Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Tue, 24 Apr 2018 13:14:58 -0400 Subject: [PATCH 2/9] Cleaning Up New Tests --- openapi_core/wrappers.py | 45 ++-- tests/integration/data/v3.0/petstore.yaml | 8 + tests/integration/test_requests_factory.py | 266 ++++++++++++--------- 3 files changed, 182 insertions(+), 137 deletions(-) diff --git a/openapi_core/wrappers.py b/openapi_core/wrappers.py index 94a8d18b..a1965e4f 100644 --- a/openapi_core/wrappers.py +++ b/openapi_core/wrappers.py @@ -112,26 +112,26 @@ def mimetype(self): class RequestsOpenAPIRequest(BaseOpenAPIRequest): - def __init__(self, request, path_pattern, regex_pattern, server_pattern): + def __init__(self, response, path_pattern, regex_pattern, server_pattern): """ - :param request: The Requests Request Object - :type request: requests.models.PreparedRequest + :param response: The Requests Responce Object + :type response: requests.models.Response :param path_pattern: The path pattern determined by the factory :type path_pattern: str :param regex_pattern: The regex pattern that matched the path. Used to extract path params. :type regex_pattern: str """ - self.request = request - self.url = urlparse(self.request.url) + self.response = response + self.url = urlparse(self.response.url) self._path_pattern = path_pattern self._regex_pattern = regex_pattern self._server_pattern = server_pattern self._parameters = { 'path': self._extract_path_params(), - 'query': self.request.qs, - 'headers': self.request.headers, - 'cookies': self.request._cookies, + 'query': self.response.request.qs, + 'headers': self.response.request.headers, + 'cookies': self.response.cookies, } @property @@ -140,7 +140,7 @@ def full_url_pattern(self): @property def host_url(self): - return re.match(self._server_pattern, self.request.url)[0] + return re.match(self._server_pattern, self.response.url)[0] @property def path(self): @@ -148,7 +148,7 @@ def path(self): @property def method(self): - return self.request.method.lower() + return self.response.request.method.lower() @property def path_pattern(self): @@ -167,11 +167,11 @@ def _extract_path_params(self): @property def body(self): - return self.request.text + return self.response.request.text @property def mimetype(self): - return self.request.headers.get('Accept') + return self.response.request.headers.get('Accept') or self.response.headers.get('Content-Type') class BaseOpenAPIResponse(object): @@ -202,7 +202,7 @@ def data(self): @property def status_code(self): - return self.response._status_code + return self.response.status_code @property def mimetype(self): @@ -270,30 +270,25 @@ def _match_server(self, server_pattern): return expr return None - def create_request(self, request): + def create_request(self, response): """ Creates an OpenApi compatible request out of the raw requests request :param request: requests.models.Request :return: RequestsOpenApiRequest """ - url = request.url - server_pattern = self._match_server(request.url) - path = re.sub(server_pattern, '', request.url) + url = response.url + server_pattern = self._match_server(response.url) + path = re.sub(server_pattern, '', response.url) pattern = self._match_operation(path) - request = RequestsOpenAPIRequest(request, self.paths_regex[pattern], pattern, server_pattern) - return request + response = RequestsOpenAPIRequest(response, self.paths_regex[pattern], pattern, server_pattern) + return response def create_response(self, response): """ :param request: - :type request: requests.models.Request + :type request: requests.models.PreparedRequest :return: RequestsOpenAPIResponse """ return RequestsOpenAPIResponse(response) - - def create_validation_pair(self, request): - return None, None - - diff --git a/tests/integration/data/v3.0/petstore.yaml b/tests/integration/data/v3.0/petstore.yaml index 81fe7b1f..df7eb54e 100644 --- a/tests/integration/data/v3.0/petstore.yaml +++ b/tests/integration/data/v3.0/petstore.yaml @@ -81,6 +81,12 @@ paths: schema: $ref: '#/components/schemas/PetCreate' responses: + '200': + description: Pet Created Response + content: + application/json: + schema: + $ref: "#/components/schemas/PetData" '201': description: Null response default: @@ -213,3 +219,5 @@ components: application/json: schema: $ref: "#/components/schemas/ExtendedError" + + diff --git a/tests/integration/test_requests_factory.py b/tests/integration/test_requests_factory.py index 01b0c8a4..15f7b7b5 100644 --- a/tests/integration/test_requests_factory.py +++ b/tests/integration/test_requests_factory.py @@ -57,105 +57,42 @@ def server_requests_factory(server_spec): return RequestsFactory(server_spec) -def test_mock_get_request_converts_correctly(requests_factory): - """ - Verifies that a GET request is correctly converted. +@pytest.fixture(scope='session') +def pets_path_mock_get(): + mock_resp = { + "data": { + "id": 12345, + "name": "Sparky", + "tag": "dogs", + "address": {"street": "1234 Someplace", + "city": "Atlanta"}, + "position": 1 - :param requests_factory: - :type requests_factory: openapi_core.wrappers.RequestsFactory - """ + } + } with requests_mock.mock() as m: - m.get('http://petstore.swagger.io/v1/pets/12345?format=json', - text='{"name": "Sparky"}', - headers={'Authentication': 'Bearer 123456', + url = 'http://petstore.swagger.io/v1/pets/12345?page=3' + m.get(url, + text=json.dumps(mock_resp), + headers={'Authorization': 'Bearer 123456', 'Content-Type': 'application/json'}, cookies={} ) - response = requests.get(url='http://petstore.swagger.io/v1/pets/12345?format=json', + response = requests.get(url=url, headers={'Authorization': 'Bearer 123456', 'Accept': 'application/json'}) - request = requests_factory.create_request(response.request) - - assert request.host_url == "http://petstore.swagger.io/v1" - assert request.path == "/pets/12345" - assert request.method == 'get' - assert request.path_pattern == "/pets/{petId}" - assert request.parameters['path']['petId'] == '12345' - assert request.parameters['query']['format'] == ['json'] - assert request.parameters['headers']['Authorization'] == 'Bearer 123456' - assert not request.parameters['cookies'] - assert not request.body - assert request.mimetype == 'application/json' - - response = requests_factory.create_response(response) - assert response.data == '{"name": "Sparky"}' - assert response.status_code == 200 - assert response.mimetype == 'application/json' - - -def test_mock_post_request_converts_correctly(requests_factory): - """ - Verifies that a POST request is correctly converted. + return response - :param requests_factory: - :type requests_factory: openapi_core.wrappers.RequestsFactory - """ - with requests_mock.mock() as m: - m.post('http://petstore.swagger.io/v1/pets/12345?format=json', - text='{"name": "Sparky"}', - headers={'Authentication': 'Bearer 123456', - 'Content-Type': 'application/json'}, - cookies={} - ) - response = requests.post(url='http://petstore.swagger.io/v1/pets/12345?format=json', - headers={'Authorization': 'Bearer 123456', - 'Accept': 'application/json'}, - data='{"name": "Sparky"}' - ) - request = requests_factory.create_request(response.request) - - assert request.host_url == "http://petstore.swagger.io/v1" - assert request.path == "/pets/12345" - assert request.method == 'post' - assert request.path_pattern == "/pets/{petId}" - assert request.parameters['path']['petId'] == '12345' - assert request.parameters['query']['format'] == ['json'] - assert request.parameters['headers']['Authorization'] == 'Bearer 123456' - assert not request.parameters['cookies'] - assert request.body == '{"name": "Sparky"}' - assert request.mimetype == 'application/json' - - response = requests_factory.create_response(response) - assert response.data == '{"name": "Sparky"}' - assert response.status_code == 200 - assert response.mimetype == 'application/json' - - -def test_server_regex_for_server_wildcards(server_requests_factory, server_request_validator): - with requests_mock.mock() as m: - m.get('https://123456.saas-app.com:443/v2/status') - response = requests.get(url='https://123456.saas-app.com:443/v2/status') - request = server_requests_factory.create_request(response.request) - assert request._server_pattern == 'https://(.*).saas-app.com:(443|8443)/v2' - server_request_validator.validate(request) - - -def test_get_validation(requests_factory, request_validator, response_validator): - """ - Verifies that a GET request is correctly converted. - - :param requests_factory: - :type requests_factory: openapi_core.wrappers.RequestsFactory - :param request_validator: - :type request_validator: openapi_core.validators.RequestValidator - """ +@pytest.fixture(scope='session') +def pets_mock_get(): mock_resp = { "data": { "id": 12345, "name": "Sparky", "tag": "dogs", - "address": "1234 Someplace", + "address": {"street": "1234 Someplace", + "city": "Atlanta"}, "position": 1 } @@ -163,61 +100,166 @@ def test_get_validation(requests_factory, request_validator, response_validator) with requests_mock.mock() as m: m.get('http://petstore.swagger.io/v1/pets/12345', text=json.dumps(mock_resp), - headers={'Authentication': 'Bearer 123456', + headers={'Authorization': 'Bearer 123456', 'Content-Type': 'application/json'}, cookies={} ) response = requests.get(url='http://petstore.swagger.io/v1/pets/12345', headers={'Authorization': 'Bearer 123456', 'Accept': 'application/json'}) - request = requests_factory.create_request(response.request) - request_validator.validate(request) + return response - response = requests_factory.create_response(response) - response_validator.validate(request, response) - -def test_post_validation(requests_factory, request_validator, response_validator): - """ - Verifies that a POST request is correctly validated. - - :param requests_factory: - :type requests_factory: openapi_core.wrappers.RequestsFactory - :param request_validator: - :type request_validator: openapi_core.validators.RequestValidator - """ +@pytest.fixture(scope='session') +def pets_mock_post(): mock_resp = { "data": { "id": 12345, "name": "Sparky", "tag": "dogs", - "address": "1234 Someplace", + "address": {"street": "1234 Someplace", + "city": "Atlanta"}, "position": 1, "healthy": True - } } mock_body = { "tag": "dogs", - "address": "1234 Someplace", + "name": "Sparky", + "address": {"street": "1234 Someplace", + "city": "Atlanta"}, "position": 1, "healthy": True } + url = 'http://petstore.swagger.io/v1/pets' with requests_mock.mock() as m: - m.post('http://petstore.swagger.io/v1/pets', + m.post(url, text=json.dumps(mock_resp), - headers={'Authentication': 'Bearer 123456', + headers={'Authorization': 'Bearer 123456', 'Content-Type': 'application/json'}, cookies={} ) - response = requests.post(url='http://petstore.swagger.io/v1/pets', + response = requests.post(url=url, headers={'Authorization': 'Bearer 123456', 'Accept': 'application/json'}, data=json.dumps(mock_body)) - request = requests_factory.create_request(response.request) - errors = request_validator.validate(request) - assert not errors + return response + + +def test_mock_get_request_converts_correctly(requests_factory, pets_path_mock_get): + """ + Verifies that a GET request is correctly converted. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + :param pets_path_mock_get: + :type pets_path_mock_get: requests.models.Request + """ + request = requests_factory.create_request(pets_path_mock_get) + + assert request.host_url == "http://petstore.swagger.io/v1" + assert request.path == "/pets/12345" + assert request.method == 'get' + assert request.path_pattern == "/pets/{petId}" + assert request.parameters['path']['petId'] == '12345' + assert request.parameters['query']['page'] == ['3'] + assert request.parameters['headers']['Authorization'] == 'Bearer 123456' + assert not request.parameters['cookies'] + assert not request.body + assert request.mimetype == 'application/json' + + response = requests_factory.create_response(pets_path_mock_get) + assert response.data == '{"data": {"id": 12345, ' \ + '"name": "Sparky", ' \ + '"tag": "dogs", ' \ + '"address": {"street": "1234 Someplace", "city": "Atlanta"}, ' \ + '"position": 1}}' + assert response.status_code == 200 + assert response.mimetype == 'application/json' + + +def test_mock_post_request_converts_correctly(requests_factory, pets_mock_post): + """ + Verifies that a POST request is correctly converted. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + :param pets_mock_post: + :type pets_mock_post: requests.models.Request + """ + + request = requests_factory.create_request(pets_mock_post) + assert request.host_url == "http://petstore.swagger.io/v1" + assert request.path == "/pets" + assert request.method == 'post' + assert request.path_pattern == "/pets" + assert request.parameters['headers']['Authorization'] == 'Bearer 123456' + assert not request.parameters['cookies'] + assert request.body == '{"tag": "dogs", ' \ + '"name": "Sparky", ' \ + '"address": {"street": "1234 Someplace", "city": "Atlanta"}, ' \ + '"position": 1, ' \ + '"healthy": true}' + assert request.mimetype == 'application/json' + + response = requests_factory.create_response(pets_mock_post) + assert response.data == '{"data": {' \ + '"id": 12345, ' \ + '"name": "Sparky", ' \ + '"tag": "dogs", ' \ + '"address": {"street": "1234 Someplace", "city": "Atlanta"}, ' \ + '"position": 1, ' \ + '"healthy": true}}' + assert response.status_code == 200 + assert response.mimetype == 'application/json' + + +def test_server_regex_for_server_wildcards(server_requests_factory, server_request_validator): + with requests_mock.mock() as m: + m.get('https://123456.saas-app.com:443/v2/status') + response = requests.get(url='https://123456.saas-app.com:443/v2/status') + request = server_requests_factory.create_request(response) + assert request._server_pattern == 'https://(.*).saas-app.com:(443|8443)/v2' + server_request_validator.validate(request) + + +def test_get_validation(requests_factory, request_validator, response_validator, pets_mock_get): + """ + Verifies that a GET request is correctly validated. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + :param request_validator: + :type request_validator: openapi_core.validators.RequestValidator + :param pets_mock_get: + :type pets_mock_get: requests.models.Request + """ + + request = requests_factory.create_request(pets_mock_get) + results = request_validator.validate(request) + assert not results.errors + + response = requests_factory.create_response(pets_mock_get) + results = response_validator.validate(request, response) + assert not results.errors + + +def test_post_validation(requests_factory, request_validator, response_validator, pets_mock_post): + """ + Verifies that a POST request is correctly validated. + + :param requests_factory: + :type requests_factory: openapi_core.wrappers.RequestsFactory + :param request_validator: + :type request_validator: openapi_core.validators.RequestValidator + :param pets_mock_post: + :type pets_mock_post: requests.models.Request + """ + + request = requests_factory.create_request(pets_mock_post) + results = request_validator.validate(request) + assert not results.errors - response = requests_factory.create_response(response) - errors = response_validator.validate(request, response) - assert not errors + response = requests_factory.create_response(pets_mock_post) + results = response_validator.validate(request, response) + assert not results.errors From 650a57b6e916c254ae402ae361ed135c708649a4 Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Tue, 24 Apr 2018 14:54:52 -0400 Subject: [PATCH 3/9] Fixing Pep8 Errors --- openapi_core/wrappers.py | 21 +++++++----- tests/integration/test_requests_factory.py | 37 ++++++++++++++++------ 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/openapi_core/wrappers.py b/openapi_core/wrappers.py index a1965e4f..7fde9377 100644 --- a/openapi_core/wrappers.py +++ b/openapi_core/wrappers.py @@ -119,7 +119,7 @@ def __init__(self, response, path_pattern, regex_pattern, server_pattern): :type response: requests.models.Response :param path_pattern: The path pattern determined by the factory :type path_pattern: str - :param regex_pattern: The regex pattern that matched the path. Used to extract path params. + :param regex_pattern: Used to extract path params. :type regex_pattern: str """ self.response = response @@ -144,7 +144,8 @@ def host_url(self): @property def path(self): - return re.sub(self._server_pattern, '', urljoin(self.host_url, self.url.path)) + return re.sub(self._server_pattern, '', urljoin(self.host_url, + self.url.path)) @property def method(self): @@ -162,7 +163,8 @@ def _extract_path_params(self): # Get the values of the path parameters groups = re.match(self._regex_pattern, self.path).groups() # Get the names of path parameters - names = [fname[1] for fname in Formatter().parse(self.path_pattern) if fname] + names = [fname[1] for fname in Formatter() + .parse(self.path_pattern) if fname] return {name: group for name, group in zip(names, groups)} @property @@ -171,7 +173,8 @@ def body(self): @property def mimetype(self): - return self.response.request.headers.get('Accept') or self.response.headers.get('Content-Type') + return self.response.request.headers.get('Accept') or\ + self.response.headers.get('Content-Type') class BaseOpenAPIResponse(object): @@ -232,7 +235,8 @@ class RequestsFactory(object): def __init__(self, spec): """ - Creates the request factory. A spec is required for path_pattern parsing. + Creates the request factory. A spec is required. + :param spec: The openapi spec to use for decoding the request :type spec: openapi_core.specs.Spec """ @@ -245,7 +249,8 @@ def _create_server_regex(self, spec): var_map = {} for var in server.variables: if server.variables[var].enum: - var_map[var] = "(" + "|".join(server.variables[var].enum) + ")" + var_map[var] = "(" +\ + "|".join(server.variables[var].enum) + ")" else: var_map[var] = "(.*)" server_regex.append(server.url.format_map(var_map)) @@ -277,11 +282,11 @@ def create_request(self, response): :param request: requests.models.Request :return: RequestsOpenApiRequest """ - url = response.url server_pattern = self._match_server(response.url) path = re.sub(server_pattern, '', response.url) pattern = self._match_operation(path) - response = RequestsOpenAPIRequest(response, self.paths_regex[pattern], pattern, server_pattern) + response = RequestsOpenAPIRequest( + response, self.paths_regex[pattern], pattern, server_pattern) return response def create_response(self, response): diff --git a/tests/integration/test_requests_factory.py b/tests/integration/test_requests_factory.py index 15f7b7b5..acd0fe33 100644 --- a/tests/integration/test_requests_factory.py +++ b/tests/integration/test_requests_factory.py @@ -18,7 +18,9 @@ def create_mock_session(): @pytest.fixture(scope='session') def server_spec(factory): - server_spec_dict = factory.spec_from_file("data/v3.0/server_path_variations.yaml") + server_spec_dict = factory.spec_from_file( + "data/v3.0/server_path_variations.yaml" + ) return create_spec(server_spec_dict) @@ -172,7 +174,9 @@ def test_mock_get_request_converts_correctly(requests_factory, pets_path_mock_ge assert response.data == '{"data": {"id": 12345, ' \ '"name": "Sparky", ' \ '"tag": "dogs", ' \ - '"address": {"street": "1234 Someplace", "city": "Atlanta"}, ' \ + '"address": {' \ + '"street": "1234 Someplace", ' \ + '"city": "Atlanta"}, ' \ '"position": 1}}' assert response.status_code == 200 assert response.mimetype == 'application/json' @@ -197,7 +201,9 @@ def test_mock_post_request_converts_correctly(requests_factory, pets_mock_post): assert not request.parameters['cookies'] assert request.body == '{"tag": "dogs", ' \ '"name": "Sparky", ' \ - '"address": {"street": "1234 Someplace", "city": "Atlanta"}, ' \ + '"address": {' \ + '"street": "1234 Someplace", ' \ + '"city": "Atlanta"}, ' \ '"position": 1, ' \ '"healthy": true}' assert request.mimetype == 'application/json' @@ -207,23 +213,33 @@ def test_mock_post_request_converts_correctly(requests_factory, pets_mock_post): '"id": 12345, ' \ '"name": "Sparky", ' \ '"tag": "dogs", ' \ - '"address": {"street": "1234 Someplace", "city": "Atlanta"}, ' \ + '"address": {' \ + '"street": "1234 Someplace", ' \ + '"city": "Atlanta"}, ' \ '"position": 1, ' \ '"healthy": true}}' assert response.status_code == 200 assert response.mimetype == 'application/json' -def test_server_regex_for_server_wildcards(server_requests_factory, server_request_validator): +def test_server_regex_for_server_wildcards( + server_requests_factory, + server_request_validator): with requests_mock.mock() as m: m.get('https://123456.saas-app.com:443/v2/status') - response = requests.get(url='https://123456.saas-app.com:443/v2/status') + response = requests.get( + url='https://123456.saas-app.com:443/v2/status') request = server_requests_factory.create_request(response) - assert request._server_pattern == 'https://(.*).saas-app.com:(443|8443)/v2' + expected_server_pattern = 'https://(.*).saas-app.com:(443|8443)/v2' + assert request._server_pattern == expected_server_pattern server_request_validator.validate(request) -def test_get_validation(requests_factory, request_validator, response_validator, pets_mock_get): +def test_get_validation( + requests_factory, + request_validator, + response_validator, + pets_mock_get): """ Verifies that a GET request is correctly validated. @@ -244,7 +260,10 @@ def test_get_validation(requests_factory, request_validator, response_validator, assert not results.errors -def test_post_validation(requests_factory, request_validator, response_validator, pets_mock_post): +def test_post_validation(requests_factory, + request_validator, + response_validator, + pets_mock_post): """ Verifies that a POST request is correctly validated. From 6a4464edb36b34b156997d9a1e9e99f1fc6abd20 Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Tue, 24 Apr 2018 15:05:23 -0400 Subject: [PATCH 4/9] Using Python 3.4,3.5 Compatible Syntax --- openapi_core/wrappers.py | 4 ++-- tests/integration/test_requests_factory.py | 8 ++++++-- tests/integration/test_wrappers.py | 6 +----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/openapi_core/wrappers.py b/openapi_core/wrappers.py index 7fde9377..91955fe0 100644 --- a/openapi_core/wrappers.py +++ b/openapi_core/wrappers.py @@ -140,7 +140,7 @@ def full_url_pattern(self): @property def host_url(self): - return re.match(self._server_pattern, self.response.url)[0] + return re.match(self._server_pattern, self.response.url).group(0) @property def path(self): @@ -164,7 +164,7 @@ def _extract_path_params(self): groups = re.match(self._regex_pattern, self.path).groups() # Get the names of path parameters names = [fname[1] for fname in Formatter() - .parse(self.path_pattern) if fname] + .parse(self.path_pattern) if fname] return {name: group for name, group in zip(names, groups)} @property diff --git a/tests/integration/test_requests_factory.py b/tests/integration/test_requests_factory.py index acd0fe33..ee4a62b2 100644 --- a/tests/integration/test_requests_factory.py +++ b/tests/integration/test_requests_factory.py @@ -148,7 +148,9 @@ def pets_mock_post(): return response -def test_mock_get_request_converts_correctly(requests_factory, pets_path_mock_get): +def test_mock_get_request_converts_correctly( + requests_factory, + pets_path_mock_get): """ Verifies that a GET request is correctly converted. @@ -182,7 +184,9 @@ def test_mock_get_request_converts_correctly(requests_factory, pets_path_mock_ge assert response.mimetype == 'application/json' -def test_mock_post_request_converts_correctly(requests_factory, pets_mock_post): +def test_mock_post_request_converts_correctly( + requests_factory, + pets_mock_post): """ Verifies that a POST request is correctly converted. diff --git a/tests/integration/test_wrappers.py b/tests/integration/test_wrappers.py index 1380c5bc..660a017e 100644 --- a/tests/integration/test_wrappers.py +++ b/tests/integration/test_wrappers.py @@ -1,14 +1,10 @@ import pytest -from requests.models import Request, Response from flask.wrappers import Request, Response from werkzeug.datastructures import EnvironHeaders, ImmutableMultiDict from werkzeug.routing import Map, Rule, Subdomain from werkzeug.test import create_environ -from openapi_core.wrappers import FlaskOpenAPIRequest, \ - FlaskOpenAPIResponse, \ - RequestsOpenAPIRequest, \ - RequestsOpenAPIResponse +from openapi_core.wrappers import FlaskOpenAPIRequest, FlaskOpenAPIResponse class TestFlaskOpenAPIRequest(object): From 1e5b1419407990a4a9f0bcaa19b06fc1c41811f6 Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Tue, 24 Apr 2018 15:28:14 -0400 Subject: [PATCH 5/9] Streamlining Test Response Comparisons --- tests/integration/test_requests_factory.py | 101 +++++++-------------- 1 file changed, 35 insertions(+), 66 deletions(-) diff --git a/tests/integration/test_requests_factory.py b/tests/integration/test_requests_factory.py index ee4a62b2..f50699b0 100644 --- a/tests/integration/test_requests_factory.py +++ b/tests/integration/test_requests_factory.py @@ -59,23 +59,39 @@ def server_requests_factory(server_spec): return RequestsFactory(server_spec) -@pytest.fixture(scope='session') -def pets_path_mock_get(): - mock_resp = { +@pytest.fixture('session') +def pets_get_response_body(): + return { "data": { "id": 12345, "name": "Sparky", "tag": "dogs", "address": {"street": "1234 Someplace", "city": "Atlanta"}, - "position": 1 - + "position": 1, + "healthy": True } } + + +@pytest.fixture('session') +def pets_post_body(): + return { + "tag": "dogs", + "name": "Sparky", + "address": {"street": "1234 Someplace", + "city": "Atlanta"}, + "position": 1, + "healthy": True + } + + +@pytest.fixture(scope='session') +def pets_path_mock_get(pets_get_response_body): with requests_mock.mock() as m: url = 'http://petstore.swagger.io/v1/pets/12345?page=3' m.get(url, - text=json.dumps(mock_resp), + text=json.dumps(pets_get_response_body), headers={'Authorization': 'Bearer 123456', 'Content-Type': 'application/json'}, cookies={} @@ -87,21 +103,10 @@ def pets_path_mock_get(): @pytest.fixture(scope='session') -def pets_mock_get(): - mock_resp = { - "data": { - "id": 12345, - "name": "Sparky", - "tag": "dogs", - "address": {"street": "1234 Someplace", - "city": "Atlanta"}, - "position": 1 - - } - } +def pets_mock_get(pets_get_response_body): with requests_mock.mock() as m: m.get('http://petstore.swagger.io/v1/pets/12345', - text=json.dumps(mock_resp), + text=json.dumps(pets_get_response_body), headers={'Authorization': 'Bearer 123456', 'Content-Type': 'application/json'}, cookies={} @@ -113,30 +118,11 @@ def pets_mock_get(): @pytest.fixture(scope='session') -def pets_mock_post(): - mock_resp = { - "data": { - "id": 12345, - "name": "Sparky", - "tag": "dogs", - "address": {"street": "1234 Someplace", - "city": "Atlanta"}, - "position": 1, - "healthy": True - } - } - mock_body = { - "tag": "dogs", - "name": "Sparky", - "address": {"street": "1234 Someplace", - "city": "Atlanta"}, - "position": 1, - "healthy": True - } +def pets_mock_post(pets_get_response_body, pets_post_body): url = 'http://petstore.swagger.io/v1/pets' with requests_mock.mock() as m: m.post(url, - text=json.dumps(mock_resp), + text=json.dumps(pets_get_response_body), headers={'Authorization': 'Bearer 123456', 'Content-Type': 'application/json'}, cookies={} @@ -144,13 +130,14 @@ def pets_mock_post(): response = requests.post(url=url, headers={'Authorization': 'Bearer 123456', 'Accept': 'application/json'}, - data=json.dumps(mock_body)) + data=json.dumps(pets_post_body)) return response def test_mock_get_request_converts_correctly( requests_factory, - pets_path_mock_get): + pets_path_mock_get, + pets_get_response_body): """ Verifies that a GET request is correctly converted. @@ -173,20 +160,16 @@ def test_mock_get_request_converts_correctly( assert request.mimetype == 'application/json' response = requests_factory.create_response(pets_path_mock_get) - assert response.data == '{"data": {"id": 12345, ' \ - '"name": "Sparky", ' \ - '"tag": "dogs", ' \ - '"address": {' \ - '"street": "1234 Someplace", ' \ - '"city": "Atlanta"}, ' \ - '"position": 1}}' + assert json.loads(response.data) == pets_get_response_body assert response.status_code == 200 assert response.mimetype == 'application/json' def test_mock_post_request_converts_correctly( requests_factory, - pets_mock_post): + pets_mock_post, + pets_post_body, + pets_get_response_body): """ Verifies that a POST request is correctly converted. @@ -203,25 +186,11 @@ def test_mock_post_request_converts_correctly( assert request.path_pattern == "/pets" assert request.parameters['headers']['Authorization'] == 'Bearer 123456' assert not request.parameters['cookies'] - assert request.body == '{"tag": "dogs", ' \ - '"name": "Sparky", ' \ - '"address": {' \ - '"street": "1234 Someplace", ' \ - '"city": "Atlanta"}, ' \ - '"position": 1, ' \ - '"healthy": true}' + assert json.loads(request.body) == pets_post_body assert request.mimetype == 'application/json' response = requests_factory.create_response(pets_mock_post) - assert response.data == '{"data": {' \ - '"id": 12345, ' \ - '"name": "Sparky", ' \ - '"tag": "dogs", ' \ - '"address": {' \ - '"street": "1234 Someplace", ' \ - '"city": "Atlanta"}, ' \ - '"position": 1, ' \ - '"healthy": true}}' + assert json.loads(response.data) == pets_get_response_body assert response.status_code == 200 assert response.mimetype == 'application/json' From 7697d3042f90cce9d780b85d48688bec49fec9ac Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Wed, 25 Apr 2018 09:07:33 -0400 Subject: [PATCH 6/9] Adding Better Error Handling and Code Coverage --- openapi_core/wrappers.py | 6 ++++ tests/integration/test_requests_factory.py | 41 ++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/openapi_core/wrappers.py b/openapi_core/wrappers.py index 91955fe0..2e48f40e 100644 --- a/openapi_core/wrappers.py +++ b/openapi_core/wrappers.py @@ -7,6 +7,8 @@ from six.moves.urllib.parse import urljoin from werkzeug.datastructures import ImmutableMultiDict +from openapi_core.exceptions import InvalidServer, InvalidOperation + class BaseOpenAPIRequest(object): @@ -283,8 +285,12 @@ def create_request(self, response): :return: RequestsOpenApiRequest """ server_pattern = self._match_server(response.url) + if not server_pattern: + raise InvalidServer("Url server not in spec.") path = re.sub(server_pattern, '', response.url) pattern = self._match_operation(path) + if not pattern: + raise InvalidOperation("Operation not in spec.") response = RequestsOpenAPIRequest( response, self.paths_regex[pattern], pattern, server_pattern) return response diff --git a/tests/integration/test_requests_factory.py b/tests/integration/test_requests_factory.py index f50699b0..a4bbfd01 100644 --- a/tests/integration/test_requests_factory.py +++ b/tests/integration/test_requests_factory.py @@ -7,6 +7,7 @@ from openapi_core.wrappers import RequestsFactory from openapi_core.shortcuts import create_spec from openapi_core.validators import ResponseValidator, RequestValidator +from openapi_core.exceptions import InvalidServer, InvalidOperation @pytest.fixture(scope='session', autouse=True) @@ -255,3 +256,43 @@ def test_post_validation(requests_factory, response = requests_factory.create_response(pets_mock_post) results = response_validator.validate(request, response) assert not results.errors + + +def test_invalid_server(requests_factory, pets_get_response_body): + """ + Verifies that an invalid server will throw an error + + :param requests_factory: + """ + with pytest.raises(InvalidServer): + with requests_mock.mock() as m: + m.get('http://petstore.swagger.io.derp/v1/pets/12345', + text=json.dumps(pets_get_response_body), + headers={'Authorization': 'Bearer 123456', + 'Content-Type': 'application/json'}, + cookies={} + ) + response = requests.get(url='http://petstore.swagger.io.derp/v1/pets/12345', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}) + request = requests_factory.create_request(response) + + +def test_invalid_operation(requests_factory, pets_get_response_body): + """ + Verifies that an invalid server will throw an error + + :param requests_factory: + """ + with pytest.raises(InvalidOperation): + with requests_mock.mock() as m: + m.get('http://petstore.swagger.io/v1/petters/12345', + text=json.dumps(pets_get_response_body), + headers={'Authorization': 'Bearer 123456', + 'Content-Type': 'application/json'}, + cookies={} + ) + response = requests.get(url='http://petstore.swagger.io/v1/petters/12345', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}) + request = requests_factory.create_request(response) From 7b396ea2ff8ddc151652d3af5c376b2f225b6e40 Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Wed, 25 Apr 2018 09:12:15 -0400 Subject: [PATCH 7/9] Fixing Pep Errors --- tests/integration/test_requests_factory.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/integration/test_requests_factory.py b/tests/integration/test_requests_factory.py index a4bbfd01..44d7fc2c 100644 --- a/tests/integration/test_requests_factory.py +++ b/tests/integration/test_requests_factory.py @@ -272,10 +272,11 @@ def test_invalid_server(requests_factory, pets_get_response_body): 'Content-Type': 'application/json'}, cookies={} ) - response = requests.get(url='http://petstore.swagger.io.derp/v1/pets/12345', - headers={'Authorization': 'Bearer 123456', - 'Accept': 'application/json'}) - request = requests_factory.create_request(response) + response = requests.get( + url='http://petstore.swagger.io.derp/v1/pets/12345', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}) + requests_factory.create_request(response) def test_invalid_operation(requests_factory, pets_get_response_body): @@ -292,7 +293,8 @@ def test_invalid_operation(requests_factory, pets_get_response_body): 'Content-Type': 'application/json'}, cookies={} ) - response = requests.get(url='http://petstore.swagger.io/v1/petters/12345', - headers={'Authorization': 'Bearer 123456', - 'Accept': 'application/json'}) - request = requests_factory.create_request(response) + response = requests.get( + url='http://petstore.swagger.io/v1/petters/12345', + headers={'Authorization': 'Bearer 123456', + 'Accept': 'application/json'}) + requests_factory.create_request(response) From e28a29d7dbcf8d74b655ee0d3c4b3f7e1a71306c Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Thu, 3 May 2018 17:02:06 -0400 Subject: [PATCH 8/9] Fixing Bug Where Requests Request Doesn't Have a Property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a request doesn’t have a feature, like query params, then the requests library will omit it. Unfortunately this causes a property not found error in the previous code. --- openapi_core/wrappers.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/openapi_core/wrappers.py b/openapi_core/wrappers.py index 2e48f40e..a19db2b5 100644 --- a/openapi_core/wrappers.py +++ b/openapi_core/wrappers.py @@ -131,7 +131,7 @@ def __init__(self, response, path_pattern, regex_pattern, server_pattern): self._server_pattern = server_pattern self._parameters = { 'path': self._extract_path_params(), - 'query': self.response.request.qs, + 'query': self._extract_query_params(), 'headers': self.response.request.headers, 'cookies': self.response.cookies, } @@ -161,6 +161,16 @@ def path_pattern(self): def parameters(self): return self._parameters + def _extract_body(self): + if hasattr(self.response.request, 'text'): + return self.response.request.text + return '' + + def _extract_query_params(self): + if hasattr(self.response.request, 'qs'): + return self.response.request.qs + return {} + def _extract_path_params(self): # Get the values of the path parameters groups = re.match(self._regex_pattern, self.path).groups() @@ -171,7 +181,7 @@ def _extract_path_params(self): @property def body(self): - return self.response.request.text + return self._extract_body() @property def mimetype(self): @@ -221,7 +231,7 @@ def __init__(self, response): @property def data(self): - return self.response.text + return self._extract_data() @property def status_code(self): @@ -231,6 +241,11 @@ def status_code(self): def mimetype(self): return self.response.headers.get('Content-Type') + def _extract_data(self): + if hasattr(self.response, 'text'): + return self.response.text + return '' + class RequestsFactory(object): path_regex = re.compile('{(.*?)}') From 1cfb85778984bb002781a376ac5b3e6bd2feb617 Mon Sep 17 00:00:00 2001 From: amcmanigal Date: Thu, 3 May 2018 17:16:33 -0400 Subject: [PATCH 9/9] Fixing Yarl Requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 86471695..7dc7a102 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ openapi-spec-validator six +typing yarl