#!/usr/bin/env python3
"""Extract examples from OpenAPI spec and generate a Python module.
This script parses an OpenAPI JSON specification and extracts example values
from schema definitions. It generates a Python module with constants that can
be imported in tests to use realistic, spec-compliant test data.
Usage:
# Generate to stdout (preview)
uv run python scripts/extract_openapi_examples.py /path/to/openapi.json
# Generate and save to file
uv run python scripts/extract_openapi_examples.py /path/to/openapi.json > tests/examples.py
# Use default OpenAPI path (SDK location)
uv run python scripts/extract_openapi_examples.py
Output:
- Writes generated Python code to stdout
- Writes warnings to stderr (won't interfere with file redirection)
Generated module format:
- Constants named after schemas in UPPER_CASE (e.g., AGENTME, CHATROOM)
- Each constant is a dict with field names and example values
- Enum schemas generate _VALUES (list) and _EXAMPLE (single value) constants
- EXAMPLES dict for convenient access to all extracted examples
Warnings (logged to stderr):
- Schema not found in OpenAPI spec
- Schema has no 'properties' defined
- Properties missing examples (lists specific field names)
- Enum schema has no example value
- No examples could be extracted from schema
Configuration:
Edit SCHEMAS_TO_EXTRACT list to add/remove schemas to process.
Example workflow:
1. Update the OpenAPI spec (e.g., after API changes)
2. Run: uv run python scripts/extract_openapi_examples.py /path/to/openapi.json > tests/examples.py
3. Review any warnings in stderr for missing examples
4. Run tests to verify fixtures still work: uv run pytest tests/
"""
import json
import logging
import sys
from pathlib import Path
from typing import Any
# Configure logging to stderr (so it doesn't interfere with stdout output)
logging.basicConfig(
level=logging.WARNING,
format="%(levelname)s: %(message)s",
stream=sys.stderr,
)
logger = logging.getLogger(__name__)
# Default OpenAPI spec location (relative to this script)
DEFAULT_OPENAPI_PATH = Path(__file__).parents[4] / "dist_rearch/fern/fern/openapi.json"
# Schemas we need examples for (used in MCP tools)
SCHEMAS_TO_EXTRACT = [
"AgentMe",
"Peer",
"ChatRoom",
"ChatParticipant",
"ChatMessage",
"ChatEvent",
"ChatEventMessageType",
"ParticipantRole",
]
def extract_schema_example(
schema: dict[str, Any], schema_name: str, warn: bool = True
) -> dict[str, Any]:
"""Extract example values from a schema's properties.
Args:
schema: The OpenAPI schema definition.
schema_name: Name of the schema (for logging).
warn: Whether to log warnings for missing examples (default True).
"""
if "properties" not in schema:
if warn:
logger.warning(f"{schema_name}: Schema has no 'properties' defined")
return {}
example = {}
missing_examples = []
for field_name, field_def in schema["properties"].items():
if "example" in field_def:
example[field_name] = field_def["example"]
elif "$ref" in field_def:
# Reference to another schema - skip for now, will be resolved separately
pass
else:
missing_examples.append(field_name)
if warn and missing_examples:
logger.warning(
f"{schema_name}: Properties missing examples: {', '.join(missing_examples)}"
)
return example
def extract_enum_values(schema: dict[str, Any]) -> list[str] | None:
"""Extract enum values if this is an enum schema."""
if "enum" in schema:
return schema["enum"]
return None
def generate_examples_module(openapi_path: Path) -> str:
"""Generate the examples.py module content."""
with open(openapi_path) as f:
spec = json.load(f)
schemas = spec.get("components", {}).get("schemas", {})
lines = [
'"""OpenAPI examples extracted from the spec.',
"",
"Auto-generated by scripts/extract_openapi_examples.py",
"Do not edit manually - regenerate when OpenAPI spec changes:",
"",
" uv run python scripts/extract_openapi_examples.py",
'"""',
"",
"# fmt: off",
"# ruff: noqa: E501",
"",
]
# Extract examples for each schema
for schema_name in SCHEMAS_TO_EXTRACT:
if schema_name not in schemas:
logger.warning(f"{schema_name}: Schema not found in OpenAPI spec")
lines.append(f"# WARNING: Schema '{schema_name}' not found in OpenAPI spec")
lines.append("")
continue
schema = schemas[schema_name]
# Check if it's an enum
enum_values = extract_enum_values(schema)
if enum_values:
lines.append(f"# {schema_name} enum values")
lines.append(f"{schema_name.upper()}_VALUES = {enum_values!r}")
if "example" in schema:
lines.append(f"{schema_name.upper()}_EXAMPLE = {schema['example']!r}")
else:
logger.warning(f"{schema_name}: Enum schema has no example value")
lines.append("")
continue
# Regular object schema
example = extract_schema_example(schema, schema_name)
if example:
lines.append(
f"# {schema_name} - {schema.get('description', 'No description')}"
)
lines.append(f"{schema_name.upper()} = {{")
for key, value in example.items():
lines.append(f" {key!r}: {value!r},")
lines.append("}")
lines.append("")
else:
logger.warning(f"{schema_name}: No examples could be extracted")
# Add a combined EXAMPLES dict for convenience (only schemas that have examples)
lines.append("# Combined examples dict for easy access")
lines.append("EXAMPLES = {")
for schema_name in SCHEMAS_TO_EXTRACT:
if schema_name in schemas:
schema = schemas[schema_name]
if "enum" not in schema and "properties" in schema:
# Only include if we actually extracted examples (warn=False to avoid duplicates)
example = extract_schema_example(schema, schema_name, warn=False)
if example:
lines.append(f" {schema_name!r}: {schema_name.upper()},")
lines.append("}")
lines.append("")
lines.append("# fmt: on")
return "\n".join(lines)
def main():
# Handle --help flag
if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"):
sys.stdout.write(__doc__)
sys.exit(0)
# Determine OpenAPI path
if len(sys.argv) > 1:
openapi_path = Path(sys.argv[1])
else:
openapi_path = DEFAULT_OPENAPI_PATH
if not openapi_path.exists():
logger.error("OpenAPI spec not found at %s", openapi_path)
logger.error(
"Usage: python scripts/extract_openapi_examples.py [path/to/openapi.json]"
)
logger.error(" python scripts/extract_openapi_examples.py --help")
sys.exit(1)
# Generate and output
content = generate_examples_module(openapi_path)
sys.stdout.write(content)
if __name__ == "__main__":
main()