diff --git a/openapi_core/contrib/django/compat.py b/openapi_core/contrib/django/compat.py index 7459c9b0..fa521179 100644 --- a/openapi_core/contrib/django/compat.py +++ b/openapi_core/contrib/django/compat.py @@ -4,12 +4,18 @@ ) -def get_headers(req): +def get_request_headers(req): # in Django 1 headers is not defined return req.headers if hasattr(req, 'headers') else \ HttpHeaders(req.META) +def get_response_headers(resp): + # in Django 2 headers is not defined + return resp.headers if hasattr(resp, 'headers') else \ + dict(resp._headers.values()) + + def get_current_scheme_host(req): # in Django 1 _current_scheme_host is not defined return req._current_scheme_host if hasattr(req, '_current_scheme_host') \ diff --git a/openapi_core/contrib/django/requests.py b/openapi_core/contrib/django/requests.py index d674657c..ace403ea 100644 --- a/openapi_core/contrib/django/requests.py +++ b/openapi_core/contrib/django/requests.py @@ -4,7 +4,7 @@ from six.moves.urllib.parse import urljoin from openapi_core.contrib.django.compat import ( - get_headers, get_current_scheme_host, + get_request_headers, get_current_scheme_host, ) from openapi_core.validation.request.datatypes import ( RequestParameters, OpenAPIRequest, @@ -39,7 +39,7 @@ def create(cls, request): path_pattern = '/' + route path = request.resolver_match and request.resolver_match.kwargs or {} - headers = get_headers(request) + headers = get_request_headers(request) parameters = RequestParameters( path=path, query=request.GET, diff --git a/openapi_core/contrib/django/responses.py b/openapi_core/contrib/django/responses.py index efbe69d3..8c436897 100644 --- a/openapi_core/contrib/django/responses.py +++ b/openapi_core/contrib/django/responses.py @@ -1,4 +1,5 @@ """OpenAPI core contrib django responses module""" +from openapi_core.contrib.django.compat import get_response_headers from openapi_core.validation.response.datatypes import OpenAPIResponse @@ -7,8 +8,10 @@ class DjangoOpenAPIResponseFactory(object): @classmethod def create(cls, response): mimetype = response["Content-Type"] + headers = get_response_headers(response) return OpenAPIResponse( data=response.content, status_code=response.status_code, + headers=headers.items(), mimetype=mimetype, ) diff --git a/openapi_core/contrib/falcon/responses.py b/openapi_core/contrib/falcon/responses.py index cc996920..8b28f09a 100644 --- a/openapi_core/contrib/falcon/responses.py +++ b/openapi_core/contrib/falcon/responses.py @@ -19,5 +19,6 @@ def create(cls, response): return OpenAPIResponse( data=data, status_code=status_code, + headers=response.headers, mimetype=mimetype, ) diff --git a/openapi_core/contrib/flask/responses.py b/openapi_core/contrib/flask/responses.py index 73e7605b..c6a1cffb 100644 --- a/openapi_core/contrib/flask/responses.py +++ b/openapi_core/contrib/flask/responses.py @@ -9,5 +9,6 @@ def create(cls, response): return OpenAPIResponse( data=response.data, status_code=response._status_code, + headers=response.headers, mimetype=response.mimetype, ) diff --git a/openapi_core/contrib/requests/responses.py b/openapi_core/contrib/requests/responses.py index 502d6b9b..6f3c324a 100644 --- a/openapi_core/contrib/requests/responses.py +++ b/openapi_core/contrib/requests/responses.py @@ -7,8 +7,10 @@ class RequestsOpenAPIResponseFactory(object): @classmethod def create(cls, response): mimetype = response.headers.get('Content-Type') + headers = dict(response.headers) return OpenAPIResponse( data=response.content, status_code=response.status_code, + headers=headers, mimetype=mimetype, ) diff --git a/tests/integration/contrib/django/data/djangoproject/testapp/views.py b/tests/integration/contrib/django/data/djangoproject/testapp/views.py index fa8448a6..e28a80af 100644 --- a/tests/integration/contrib/django/data/djangoproject/testapp/views.py +++ b/tests/integration/contrib/django/data/djangoproject/testapp/views.py @@ -30,6 +30,7 @@ def get(self, request, pk): "test": "test_val", } django_response = JsonResponse(response_dict) + django_response['X-Rate-Limit'] = '12' openapi_response = DjangoOpenAPIResponse(django_response) validator = ResponseValidator(spec) diff --git a/tests/integration/contrib/django/data/openapi.yaml b/tests/integration/contrib/django/data/openapi.yaml index 37ba7be6..58c8ec57 100644 --- a/tests/integration/contrib/django/data/openapi.yaml +++ b/tests/integration/contrib/django/data/openapi.yaml @@ -17,6 +17,12 @@ paths: type: string required: - test + headers: + X-Rate-Limit: + description: Rate limit + schema: + type: integer + required: true parameters: - required: true in: path diff --git a/tests/integration/contrib/falcon/conftest.py b/tests/integration/contrib/falcon/conftest.py index 5ad05030..66d209de 100644 --- a/tests/integration/contrib/falcon/conftest.py +++ b/tests/integration/contrib/falcon/conftest.py @@ -40,11 +40,13 @@ def create_request( @pytest.fixture def response_factory(environ_factory): def create_response( - data, status_code=200, content_type='application/json'): + data, status_code=200, headers=None, + content_type='application/json'): options = ResponseOptions() resp = Response(options) resp.body = data resp.content_type = content_type resp.status = HTTP_200 + resp.set_headers(headers or {}) return resp return create_response diff --git a/tests/integration/contrib/falcon/data/v3.0/falcon_factory.yaml b/tests/integration/contrib/falcon/data/v3.0/falcon_factory.yaml index d6b5e4be..295f3670 100644 --- a/tests/integration/contrib/falcon/data/v3.0/falcon_factory.yaml +++ b/tests/integration/contrib/falcon/data/v3.0/falcon_factory.yaml @@ -32,6 +32,12 @@ paths: properties: data: type: string + headers: + X-Rate-Limit: + description: Rate limit + schema: + type: integer + required: true default: description: Return errors. content: diff --git a/tests/integration/contrib/falcon/test_falcon_middlewares.py b/tests/integration/contrib/falcon/test_falcon_middlewares.py index a2348357..cbfce002 100644 --- a/tests/integration/contrib/falcon/test_falcon_middlewares.py +++ b/tests/integration/contrib/falcon/test_falcon_middlewares.py @@ -68,6 +68,7 @@ def view_response_callable(request, response, id): response.content_type = MEDIA_HTML response.status = HTTP_200 response.body = 'success' + response.set_header('X-Rate-Limit', '12') self.view_response_callable = view_response_callable headers = {'Content-Type': 'application/json'} result = client.simulate_get( @@ -193,6 +194,7 @@ def view_response_callable(request, response, id): response.body = dumps({ 'data': 'data', }) + response.set_header('X-Rate-Limit', '12') self.view_response_callable = view_response_callable headers = {'Content-Type': 'application/json'} diff --git a/tests/integration/contrib/falcon/test_falcon_validation.py b/tests/integration/contrib/falcon/test_falcon_validation.py index 9e5466cf..c54f448c 100644 --- a/tests/integration/contrib/falcon/test_falcon_validation.py +++ b/tests/integration/contrib/falcon/test_falcon_validation.py @@ -21,7 +21,10 @@ def test_response_validator_path_pattern(self, validator = ResponseValidator(spec) request = request_factory('GET', '/browse/12', subdomain='kb') openapi_request = FalconOpenAPIRequestFactory.create(request) - response = response_factory('{"data": "data"}', status_code=200) + response = response_factory( + '{"data": "data"}', + status_code=200, headers={'X-Rate-Limit': '12'}, + ) openapi_response = FalconOpenAPIResponseFactory.create(response) result = validator.validate(openapi_request, openapi_response) assert not result.errors diff --git a/tests/integration/contrib/flask/conftest.py b/tests/integration/contrib/flask/conftest.py index 4e86bcdc..c737f009 100644 --- a/tests/integration/contrib/flask/conftest.py +++ b/tests/integration/contrib/flask/conftest.py @@ -44,6 +44,9 @@ def create_request(method, path, subdomain=None, query_string=None): @pytest.fixture def response_factory(): def create_response( - data, status_code=200, content_type='application/json'): - return Response(data, status=status_code, content_type=content_type) + data, status_code=200, headers=None, + content_type='application/json'): + return Response( + data, status=status_code, headers=headers, + content_type=content_type) return create_response diff --git a/tests/integration/contrib/flask/data/v3.0/flask_factory.yaml b/tests/integration/contrib/flask/data/v3.0/flask_factory.yaml index 6ed6d563..3b674c7c 100644 --- a/tests/integration/contrib/flask/data/v3.0/flask_factory.yaml +++ b/tests/integration/contrib/flask/data/v3.0/flask_factory.yaml @@ -26,6 +26,12 @@ paths: properties: data: type: string + headers: + X-Rate-Limit: + description: Rate limit + schema: + type: integer + required: true default: description: Return errors. content: diff --git a/tests/integration/contrib/flask/test_flask_decorator.py b/tests/integration/contrib/flask/test_flask_decorator.py index 31c30c6f..6e6c899c 100644 --- a/tests/integration/contrib/flask/test_flask_decorator.py +++ b/tests/integration/contrib/flask/test_flask_decorator.py @@ -62,7 +62,9 @@ def view_response_callable(*args, **kwargs): assert request.openapi.parameters == RequestParameters(path={ 'id': 12, }) - return make_response('success', 200) + resp = make_response('success', 200) + resp.headers['X-Rate-Limit'] = '12' + return resp self.view_response_callable = view_response_callable result = client.get('/browse/12/') @@ -172,7 +174,9 @@ def view_response_callable(*args, **kwargs): assert request.openapi.parameters == RequestParameters(path={ 'id': 12, }) - return jsonify(data='data') + resp = jsonify(data='data') + resp.headers['X-Rate-Limit'] = '12' + return resp self.view_response_callable = view_response_callable result = client.get('/browse/12/') diff --git a/tests/integration/contrib/flask/test_flask_validation.py b/tests/integration/contrib/flask/test_flask_validation.py index 672df583..95170f37 100644 --- a/tests/integration/contrib/flask/test_flask_validation.py +++ b/tests/integration/contrib/flask/test_flask_validation.py @@ -22,7 +22,10 @@ def test_response_validator_path_pattern(self, validator = ResponseValidator(flask_spec) request = request_factory('GET', '/browse/12/', subdomain='kb') openapi_request = FlaskOpenAPIRequest(request) - response = response_factory('{"data": "data"}', status_code=200) + response = response_factory( + '{"data": "data"}', + status_code=200, headers={'X-Rate-Limit': '12'}, + ) openapi_response = FlaskOpenAPIResponse(response) result = validator.validate(openapi_request, openapi_response) assert not result.errors diff --git a/tests/integration/contrib/flask/test_flask_views.py b/tests/integration/contrib/flask/test_flask_views.py index 4134a0f7..2933e505 100644 --- a/tests/integration/contrib/flask/test_flask_views.py +++ b/tests/integration/contrib/flask/test_flask_views.py @@ -55,6 +55,7 @@ def view(self, app, details_view_func, list_view_func): def test_invalid_content_type(self, client): self.view_response = make_response('success', 200) + self.view_response.headers['X-Rate-Limit'] = '12' result = client.get('/browse/12/') @@ -158,8 +159,31 @@ def test_endpoint_error(self, client): assert result.status_code == 400 assert result.json == expected_data + def test_missing_required_header(self, client): + self.view_response = jsonify(data='data') + + result = client.get('/browse/12/') + + expected_data = { + 'errors': [ + { + 'class': ( + "" + ), + 'status': 400, + 'title': ( + "Missing required header: X-Rate-Limit" + ) + } + ] + } + assert result.status_code == 400 + assert result.json == expected_data + def test_valid(self, client): self.view_response = jsonify(data='data') + self.view_response.headers['X-Rate-Limit'] = '12' result = client.get('/browse/12/') diff --git a/tests/integration/contrib/requests/data/v3.0/requests_factory.yaml b/tests/integration/contrib/requests/data/v3.0/requests_factory.yaml index c3f73cd2..c7ea6c3a 100644 --- a/tests/integration/contrib/requests/data/v3.0/requests_factory.yaml +++ b/tests/integration/contrib/requests/data/v3.0/requests_factory.yaml @@ -44,6 +44,12 @@ paths: properties: data: type: string + headers: + X-Rate-Limit: + description: Rate limit + schema: + type: integer + required: true default: description: Return errors. content: diff --git a/tests/integration/contrib/requests/test_requests_validation.py b/tests/integration/contrib/requests/test_requests_validation.py index 997a1a43..c15bf361 100644 --- a/tests/integration/contrib/requests/test_requests_validation.py +++ b/tests/integration/contrib/requests/test_requests_validation.py @@ -22,6 +22,7 @@ def test_response_validator_path_pattern(self, spec): responses.add( responses.POST, 'http://localhost/browse/12/?q=string', json={"data": "data"}, status=200, match_querystring=True, + headers={'X-Rate-Limit': '12'}, ) validator = ResponseValidator(spec) request = requests.Request( pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy