Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 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.
- Fix [#64](https://github.com/Neoteroi/essentials-openapi/issues/64): use `examples`
array (JSON Schema draft 6+) as a fallback for auto-generated response examples;
also use `enum` values as examples for all scalar types (integer, number, boolean),
reported by @jan-ldwg.

## [1.3.0] - 2025-11-19

Expand Down
14 changes: 8 additions & 6 deletions openapidocs/mk/v3/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class ScalarExampleHandler(SchemaExampleHandler):
formats: Dict[str, Callable[[], Any]]

def get_example(self, schema) -> str:
enum = schema.get("enum")
if isinstance(enum, list) and enum:
return enum[0]

format = schema.get("format")

if format and format in self.formats:
Expand All @@ -56,12 +60,6 @@ class StringExampleHandler(ScalarExampleHandler):
"binary": lambda: "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=",
}

def get_example(self, schema) -> str:
enum = schema.get("enum")
if isinstance(enum, list):
return enum[0]
return super().get_example(schema)


class IntegerExampleHandler(ScalarExampleHandler):
type_name = "integer"
Expand Down Expand Up @@ -138,6 +136,10 @@ def get_example_from_schema(schema) -> Any:
if "example" in schema:
return schema["example"]

examples = schema.get("examples")
if isinstance(examples, list) and examples:
return examples[0]

# does it have a type?
handlers_types: List[Type[SchemaExampleHandler]] = list(
get_subclasses(SchemaExampleHandler)
Expand Down
31 changes: 28 additions & 3 deletions tests/test_oas31.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,34 @@ def test_null_only_type(self):
assert get_example_from_schema({"type": ["null"]}) is None


# ---------------------------------------------------------------------------
# OpenAPIV3DocumentationHandler — OAS 3.1 rendering
# ---------------------------------------------------------------------------
class TestGetExampleFromSchemaAnnotations:
"""Tests for JSON Schema draft 6+ examples array and enum handling."""

@pytest.mark.parametrize(
"schema, expected",
[
# examples array (JSON Schema draft 6+) - first value should be used
({"type": "string", "examples": ["A-DSP", "MON 1"]}, "A-DSP"),
({"type": "integer", "examples": [42, 99]}, 42),
({"type": "number", "examples": [3.14, 2.71]}, 3.14),
({"type": "boolean", "examples": [False, True]}, False),
# example (singular) takes precedence over examples (plural)
(
{"type": "string", "example": "override", "examples": ["A-DSP"]},
"override",
),
# empty examples list falls through to type handler
({"type": "string", "examples": []}, "string"),
# enum on non-string types
({"type": "integer", "enum": [1, 2, 3]}, 1),
({"type": "number", "enum": [1.5, 2.5]}, 1.5),
({"type": "boolean", "enum": [False]}, False),
# enum on string (pre-existing behaviour still works)
({"type": "string", "enum": ["active", "inactive"]}, "active"),
],
)
def test_examples_and_enum(self, schema, expected):
assert get_example_from_schema(schema) == expected


class TestOas31DocumentationHandler:
Expand Down