diff --git a/CHANGELOG.md b/CHANGELOG.md index b024216..6f155cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 fragment), reported by @mbklein. - Fix [#55](https://github.com/Neoteroi/essentials-openapi/issues/55): `jsonSchemaDialect` is not required and should not have a default value. +- Fix [#60](https://github.com/Neoteroi/essentials-openapi/issues/60): resolve `$ref` + values in response headers pointing to `#/components/headers/...` to avoid + `UndefinedError` when rendering response tables, reported by @copiousfreetime. ## [1.3.0] - 2025-11-19 diff --git a/openapidocs/mk/v3/__init__.py b/openapidocs/mk/v3/__init__.py index 04419b2..322a2b0 100644 --- a/openapidocs/mk/v3/__init__.py +++ b/openapidocs/mk/v3/__init__.py @@ -607,6 +607,19 @@ def get_parameters(self, operation) -> List[dict]: return results + def get_response_headers(self, response_definition: dict) -> dict: + """ + Returns the headers of a response definition, resolving any $ref values + so that the template can access fields like schema and description directly. + """ + headers = response_definition.get("headers") + if not headers: + return {} + return { + name: self._resolve_opt_ref(header_def) + for name, header_def in headers.items() + } + def write(self) -> str: return self._writer.write( self.doc, diff --git a/openapidocs/mk/v3/views_markdown/partial/request-responses.html b/openapidocs/mk/v3/views_markdown/partial/request-responses.html index 8e60bbf..09035a7 100644 --- a/openapidocs/mk/v3/views_markdown/partial/request-responses.html +++ b/openapidocs/mk/v3/views_markdown/partial/request-responses.html @@ -25,7 +25,7 @@ {%- if definition.headers %} {% with rows = [[texts.name, texts.description, texts.schema]] %} -{%- for header_name, header_definition in definition.headers.items() -%} +{%- for header_name, header_definition in handler.get_response_headers(definition).items() -%} {%- set _ = rows.append([header_name, header_definition.description, header_definition.schema.type]) -%} {%- endfor -%} {{ rows | table }} diff --git a/openapidocs/mk/v3/views_mkdocs/partial/request-responses.html b/openapidocs/mk/v3/views_mkdocs/partial/request-responses.html index e3a1503..b239511 100644 --- a/openapidocs/mk/v3/views_mkdocs/partial/request-responses.html +++ b/openapidocs/mk/v3/views_mkdocs/partial/request-responses.html @@ -32,7 +32,7 @@ - {%- for header_name, header_definition in definition.headers.items() %} + {%- for header_name, header_definition in handler.get_response_headers(definition).items() %} {{header_name}} {{header_definition.description}} diff --git a/tests/res/example9-openapi.yaml b/tests/res/example9-openapi.yaml new file mode 100644 index 0000000..fb000f0 --- /dev/null +++ b/tests/res/example9-openapi.yaml @@ -0,0 +1,42 @@ +--- +openapi: 3.0.0 +info: + title: Header Ref Example + version: v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - Pets + responses: + "200": + description: A paged array of pets + headers: + Current-Page: + $ref: '#/components/headers/current-page' + X-Rate-Limit: + description: Rate limit per hour + schema: + type: integer + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: integer + name: + type: string +components: + headers: + current-page: + description: The current page of total pages this response represents. + example: '1' + style: simple + schema: + type: string + required: true diff --git a/tests/test_mk_v3.py b/tests/test_mk_v3.py index c77d9d9..59aae3e 100644 --- a/tests/test_mk_v3.py +++ b/tests/test_mk_v3.py @@ -84,6 +84,22 @@ def test_swagger2_raises_not_supported(): OpenAPIV3DocumentationHandler({"swagger": "2.0", "info": {}, "paths": {}}) +def test_v3_response_header_ref(): + """ + Regression test for https://github.com/Neoteroi/essentials-openapi/issues/60 + Response headers that use $ref to #/components/headers/... should be resolved + without raising an UndefinedError. + """ + data = get_file_yaml("example9-openapi.yaml") + handler = OpenAPIV3DocumentationHandler(data) + output = handler.write() + + assert "Current-Page" in output + assert "The current page of total pages this response represents." in output + assert "X-Rate-Limit" in output + assert "Rate limit per hour" in output + + @pytest.mark.parametrize( "input,expected_result", [