mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-09-13 07:21:16 +00:00
Add documentation. (#321)
Add documentation that covers: - Prediction - Measuremnt - REST API Add Python scripts that support automatic documentation generation using the Sphinx sphinxcontrib.eval extension. Add automatic update/ test for REST API documentation. Filter proxy endpoints from REST API documentation. Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
239
scripts/generate_openapi_md.py
Executable file
239
scripts/generate_openapi_md.py
Executable file
@@ -0,0 +1,239 @@
|
||||
#!.venv/bin/python
|
||||
"""Utility functions for OpenAPI specification conversion tasks."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
if __package__ is None or __package__ == "":
|
||||
# uses current directory visibility
|
||||
import generate_openapi
|
||||
else:
|
||||
# uses current package visibility
|
||||
from . import generate_openapi
|
||||
|
||||
|
||||
def extract_info(openapi_json: dict) -> dict:
|
||||
"""Extract basic information from OpenAPI JSON.
|
||||
|
||||
Args:
|
||||
openapi_json (dict): The OpenAPI specification as a Python dictionary.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the title, version, description, and base_url.
|
||||
"""
|
||||
info = openapi_json.get("info", {})
|
||||
servers = openapi_json.get("servers", [{}])
|
||||
|
||||
return {
|
||||
"title": info.get("title", "API Documentation"),
|
||||
"version": info.get("version", "1.0.0"),
|
||||
"description": info.get("description", "No description provided."),
|
||||
"base_url": servers[0].get("url", "No base URL provided."),
|
||||
}
|
||||
|
||||
|
||||
def format_authentication(security_schemes: dict) -> str:
|
||||
"""Format the authentication section for the Markdown.
|
||||
|
||||
Args:
|
||||
security_schemes (dict): The security schemes from the OpenAPI spec.
|
||||
|
||||
Returns:
|
||||
str: The formatted authentication section in Markdown.
|
||||
"""
|
||||
if not security_schemes:
|
||||
return ""
|
||||
|
||||
markdown = "## Authentication\n\n"
|
||||
for scheme, details in security_schemes.items():
|
||||
auth_type = details.get("type", "unknown")
|
||||
markdown += f"- **{scheme}**: {auth_type}\n\n"
|
||||
return markdown
|
||||
|
||||
|
||||
def format_parameters(parameters: list) -> str:
|
||||
"""Format the parameters section for the Markdown.
|
||||
|
||||
Args:
|
||||
parameters (list): The list of parameters from an endpoint.
|
||||
|
||||
Returns:
|
||||
str: The formatted parameters section in Markdown.
|
||||
"""
|
||||
if not parameters:
|
||||
return ""
|
||||
|
||||
markdown = "**Parameters**:\n\n"
|
||||
for param in parameters:
|
||||
name = param.get("name", "unknown")
|
||||
location = param.get("in", "unknown")
|
||||
required = param.get("required", False)
|
||||
description = param.get("description", "No description provided.")
|
||||
markdown += (
|
||||
f"- `{name}` ({location}, {'required' if required else 'optional'}): {description}\n\n"
|
||||
)
|
||||
return markdown
|
||||
|
||||
|
||||
def format_request_body(request_body: dict) -> str:
|
||||
"""Format the request body section for the Markdown.
|
||||
|
||||
Args:
|
||||
request_body (dict): The request body content from an endpoint.
|
||||
|
||||
Returns:
|
||||
str: The formatted request body section in Markdown.
|
||||
"""
|
||||
if not request_body:
|
||||
return ""
|
||||
|
||||
markdown = "**Request Body**:\n\n"
|
||||
for content_type, schema in request_body.items():
|
||||
markdown += f"- `{content_type}`: {json.dumps(schema.get('schema', {}), indent=2)}\n\n"
|
||||
return markdown
|
||||
|
||||
|
||||
def format_responses(responses: dict) -> str:
|
||||
"""Format the responses section for the Markdown.
|
||||
|
||||
Args:
|
||||
responses (dict): The responses from an endpoint.
|
||||
|
||||
Returns:
|
||||
str: The formatted responses section in Markdown.
|
||||
"""
|
||||
if not responses:
|
||||
return ""
|
||||
|
||||
markdown = "**Responses**:\n\n"
|
||||
for status, response in responses.items():
|
||||
desc = response.get("description", "No description provided.")
|
||||
markdown += f"- **{status}**: {desc}\n\n"
|
||||
return markdown
|
||||
|
||||
|
||||
def format_endpoint(path: str, method: str, details: dict) -> str:
|
||||
"""Format a single endpoint's details for the Markdown.
|
||||
|
||||
Args:
|
||||
path (str): The endpoint path.
|
||||
method (str): The HTTP method.
|
||||
details (dict): The details of the endpoint.
|
||||
|
||||
Returns:
|
||||
str: The formatted endpoint section in Markdown.
|
||||
"""
|
||||
link_summary = (
|
||||
details.get("summary", "<summary missing>")
|
||||
.lower()
|
||||
.strip()
|
||||
.replace(" ", "_")
|
||||
.replace("-", "_")
|
||||
)
|
||||
link_path = (
|
||||
path.lower().strip().replace("/", "_").replace(".", "_").replace("{", "_").replace("}", "_")
|
||||
)
|
||||
link_method = f"_{method.lower()})"
|
||||
# [local](http://localhost:8503/docs#/default/fastapi_config_get_v1_config_get)
|
||||
local_path = (
|
||||
"[local](http://localhost:8503/docs#/default/" + link_summary + link_path + link_method
|
||||
)
|
||||
# [swagger](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/fastapi_strompreis_strompreis_get)
|
||||
swagger_path = (
|
||||
"[swagger](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/openapi.json#/default/"
|
||||
+ link_summary
|
||||
+ link_path
|
||||
+ link_method
|
||||
)
|
||||
|
||||
markdown = f"## {method.upper()} {path}\n\n"
|
||||
|
||||
markdown += f"**Links**: {local_path}, {swagger_path}\n\n"
|
||||
|
||||
summary = details.get("summary", None)
|
||||
if summary:
|
||||
markdown += f"{summary}\n\n"
|
||||
|
||||
description = details.get("description", None)
|
||||
if description:
|
||||
markdown += "```\n"
|
||||
markdown += f"{description}"
|
||||
markdown += "\n```\n\n"
|
||||
|
||||
markdown += format_parameters(details.get("parameters", []))
|
||||
markdown += format_request_body(details.get("requestBody", {}).get("content", {}))
|
||||
markdown += format_responses(details.get("responses", {}))
|
||||
markdown += "---\n\n"
|
||||
|
||||
return markdown
|
||||
|
||||
|
||||
def openapi_to_markdown(openapi_json: dict) -> str:
|
||||
"""Convert OpenAPI JSON specification to a Markdown representation.
|
||||
|
||||
Args:
|
||||
openapi_json (dict): The OpenAPI specification as a Python dictionary.
|
||||
|
||||
Returns:
|
||||
str: The Markdown representation of the OpenAPI spec.
|
||||
"""
|
||||
info = extract_info(openapi_json)
|
||||
markdown = f"# {info['title']}\n\n"
|
||||
markdown += f"**Version**: `{info['version']}`\n\n"
|
||||
markdown += f"**Description**: {info['description']}\n\n"
|
||||
markdown += f"**Base URL**: `{info['base_url']}`\n\n"
|
||||
|
||||
security_schemes = openapi_json.get("components", {}).get("securitySchemes", {})
|
||||
markdown += format_authentication(security_schemes)
|
||||
|
||||
markdown += "**Endpoints**:\n\n"
|
||||
paths = openapi_json.get("paths", {})
|
||||
for path, methods in paths.items():
|
||||
for method, details in methods.items():
|
||||
markdown += format_endpoint(path, method, details)
|
||||
|
||||
# Assure the is no double \n at end of file
|
||||
markdown = markdown.rstrip("\n")
|
||||
markdown += "\n"
|
||||
|
||||
return markdown
|
||||
|
||||
|
||||
def generate_openapi_md() -> str:
|
||||
"""Generate OpenAPI specification in Markdown.
|
||||
|
||||
Returns:
|
||||
str: The Markdown representation of the OpenAPI spec.
|
||||
"""
|
||||
openapi_spec = generate_openapi.generate_openapi()
|
||||
openapi_md = openapi_to_markdown(openapi_spec)
|
||||
return openapi_md
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to run the generation of the OpenAPI specification as Markdown."""
|
||||
parser = argparse.ArgumentParser(description="Generate OpenAPI Specification as Markdown")
|
||||
parser.add_argument(
|
||||
"--output-file", type=str, default=None, help="File to write the OpenAPI Specification to"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
openapi_md = generate_openapi_md()
|
||||
if args.output_file:
|
||||
# Write to file
|
||||
with open(args.output_file, "w") as f:
|
||||
f.write(openapi_md)
|
||||
else:
|
||||
# Write to std output
|
||||
print(openapi_md)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during OpenAPI specification generation: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user