diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f155cb..e308403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/openapidocs/mk/v3/examples.py b/openapidocs/mk/v3/examples.py index d3e0123..97463aa 100644 --- a/openapidocs/mk/v3/examples.py +++ b/openapidocs/mk/v3/examples.py @@ -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: @@ -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" @@ -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) diff --git a/tests/test_oas31.py b/tests/test_oas31.py index 965353d..09268ca 100644 --- a/tests/test_oas31.py +++ b/tests/test_oas31.py @@ -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: