From 005d046a8de94bedbef623abfc49c55528182cd8 Mon Sep 17 00:00:00 2001 From: "Miguel G. Flores" Date: Fri, 16 Jul 2021 08:50:00 +0200 Subject: [PATCH] Allow "Required: False" on Response headers - Fix #1261 (#1293) * Add failing test for optional header * Required: False headers don't give error if missing * Use dict.get * Required keys are when they are not present, or present with "True" * Update connexion/decorators/response.py Co-authored-by: Ruwann * Update tests for required and optional headers Swagger2 response headers are optional, and have no 'required' attribute. OpenAPI3 response headers are optional by default, but can be specified to be required by setting 'required: true'. * Make headers required for tests that rely on it Co-authored-by: Ruwann --- connexion/decorators/response.py | 7 ++++--- tests/api/test_headers.py | 11 +++++++++-- tests/fakeapi/hello/__init__.py | 4 ++++ tests/fixtures/simple/openapi.yaml | 19 +++++++++++++++++++ tests/fixtures/simple/swagger.yaml | 15 --------------- 5 files changed, 36 insertions(+), 20 deletions(-) diff --git a/connexion/decorators/response.py b/connexion/decorators/response.py index c278b6715..f1a9bcefc 100644 --- a/connexion/decorators/response.py +++ b/connexion/decorators/response.py @@ -55,9 +55,10 @@ def validate_response(self, data, status_code, headers, url): raise NonConformingResponseBody(message=str(e)) if response_definition and response_definition.get("headers"): - response_definition_header_keys = response_definition.get("headers").keys() - header_keys = headers.keys() - missing_keys = response_definition_header_keys - header_keys + required_header_keys = {k for (k, v) in response_definition.get("headers").items() + if v.get("required", False)} + header_keys = set(headers.keys()) + missing_keys = required_header_keys - header_keys if missing_keys: pretty_list = ', '.join(missing_keys) msg = ("Keys in header don't match response specification. " diff --git a/tests/api/test_headers.py b/tests/api/test_headers.py index 0b6c88c75..0eb64ed63 100644 --- a/tests/api/test_headers.py +++ b/tests/api/test_headers.py @@ -17,8 +17,8 @@ def test_headers_produces(simple_app): assert response.headers["Location"] == "http://localhost/my/uri" -def test_header_not_returned(simple_app): - app_client = simple_app.app.test_client() +def test_header_not_returned(simple_openapi_app): + app_client = simple_openapi_app.app.test_client() response = app_client.post('/v1.0/goodday/noheader', data={}) # type: flask.Response assert response.status_code == 500 # view_func has not returned what was promised in spec @@ -42,3 +42,10 @@ def test_no_content_object_and_have_headers(simple_app): resp = app_client.get('/v1.0/test-204-with-headers-nocontent-obj') assert resp.status_code == 204 assert 'X-Something' in resp.headers + + +def test_optional_header(simple_openapi_app): + app_client = simple_openapi_app.app.test_client() + resp = app_client.get('/v1.0/test-optional-headers') + assert resp.status_code == 200 + assert 'X-Optional-Header' not in resp.headers diff --git a/tests/fakeapi/hello/__init__.py b/tests/fakeapi/hello/__init__.py index 26ef42e74..997c2ac0c 100644 --- a/tests/fakeapi/hello/__init__.py +++ b/tests/fakeapi/hello/__init__.py @@ -601,3 +601,7 @@ def get_date(): def get_uuid(): return {'value': uuid.UUID(hex='e7ff66d0-3ec2-4c4e-bed0-6e4723c24c51')} + + +def test_optional_headers(): + return {}, 200 diff --git a/tests/fixtures/simple/openapi.yaml b/tests/fixtures/simple/openapi.yaml index 80a3bf4fc..a448830c6 100644 --- a/tests/fixtures/simple/openapi.yaml +++ b/tests/fixtures/simple/openapi.yaml @@ -690,6 +690,7 @@ paths: description: The URI of the created resource schema: type: string + required: true content: 'application/json': schema: @@ -714,6 +715,7 @@ paths: description: The URI of the created resource schema: type: string + required: true content: 'application/json': schema: @@ -731,6 +733,7 @@ paths: description: The URI of the created resource schema: type: string + required: true content: text/plain: schema: @@ -1140,6 +1143,22 @@ paths: application/json: schema: type: object + /test-optional-headers: + get: + operationId: fakeapi.hello.test_optional_headers + responses: + '200': + description: Some object response + content: + application/json: + schema: + type: object + headers: + X-Optional-Header: + description: Optional header + schema: + type: string + required: false servers: - url: http://localhost:{port}/{basePath} diff --git a/tests/fixtures/simple/swagger.yaml b/tests/fixtures/simple/swagger.yaml index 32d680e60..de3db771e 100644 --- a/tests/fixtures/simple/swagger.yaml +++ b/tests/fixtures/simple/swagger.yaml @@ -556,21 +556,6 @@ paths: required: true type: string - /goodday/noheader: - post: - summary: Generate good day greeting - description: Generates a good day message. - operationId: fakeapi.hello.post_goodday_no_header - responses: - 201: - description: gooday response - headers: - Location: - type: string - description: The URI of the created resource - schema: - type: object - /goodevening/{name}: post: summary: Generate good evening