The networks core of the Minos Framework providing networking components for reactive microservices
—
Built-in services for generating OpenAPI and AsyncAPI specifications from decorated handlers. Provides automated API documentation and specification generation.
Service for generating OpenAPI 3.0 specifications from REST endpoints.
class OpenAPIService:
def __init__(self, config: Config): ...
config: Config
spec: dict
endpoints: list[dict]
@enroute.rest.command("/spec/openapi", "GET")
async def generate_specification(self, request: Request) -> Response: ...Usage Examples:
from minos.networks import OpenAPIService, enroute
from minos.common import Config
# Service with REST endpoints
class UserAPI:
@enroute.rest.query("/users", method="GET")
async def list_users(self, request) -> Response:
return Response({"users": []})
@enroute.rest.command("/users", method="POST")
async def create_user(self, request) -> Response:
return Response({"id": "123"}, status=201)
# Setup OpenAPI service
config = Config("config.yml")
openapi_service = OpenAPIService(config)
# The service automatically exposes /spec/openapi endpoint
# Access at: GET http://localhost:8080/spec/openapiService for generating AsyncAPI 2.3.0 specifications from broker events.
class AsyncAPIService:
def __init__(self, config: Config): ...
config: Config
spec: dict
@enroute.rest.command("/spec/asyncapi", "GET")
async def generate_specification(self, request: Request) -> Response: ...
def get_events(self) -> list[dict]: ...Usage Examples:
from minos.networks import AsyncAPIService, enroute
from minos.common import Config
# Service with broker events
class UserEvents:
@enroute.broker.event("user.created")
async def handle_user_created(self, request) -> Response:
return Response({"processed": True})
@enroute.broker.event("user.updated")
async def handle_user_updated(self, request) -> Response:
return Response({"processed": True})
# Setup AsyncAPI service
config = Config("config.yml")
asyncapi_service = AsyncAPIService(config)
# The service automatically exposes /spec/asyncapi endpoint
# Access at: GET http://localhost:8080/spec/asyncapifrom minos.networks import (
OpenAPIService, AsyncAPIService, HttpPort,
enroute, Request, Response
)
from minos.common import Config
class DocumentedAPI:
# REST endpoints for OpenAPI
@enroute.rest.query("/users", method="GET")
async def list_users(self, request: Request) -> Response:
"""List all users"""
return Response({"users": []})
@enroute.rest.command("/users", method="POST")
async def create_user(self, request: Request) -> Response:
"""Create a new user"""
user_data = await request.content()
return Response({"id": "123", **user_data}, status=201)
@enroute.rest.query("/users/{user_id}", method="GET")
async def get_user(self, request: Request) -> Response:
"""Get user by ID"""
params = await request.params()
return Response({"id": params["user_id"], "name": "John"})
# Broker events for AsyncAPI
@enroute.broker.event("user.created")
async def on_user_created(self, request: Request) -> Response:
"""Handle user creation events"""
return Response({"processed": True})
@enroute.broker.event("user.updated")
async def on_user_updated(self, request: Request) -> Response:
"""Handle user update events"""
return Response({"processed": True})
@enroute.broker.command("user.validate")
async def validate_user(self, request: Request) -> Response:
"""Validate user data"""
return Response({"valid": True})
# Configuration
config = Config({
"service": {
"name": "user-service",
"version": "1.0.0",
"description": "User management microservice"
}
})
# Setup specification services
openapi_service = OpenAPIService(config)
asyncapi_service = AsyncAPIService(config)
# Setup HTTP port
http_port = HttpPort._from_config(config)
# Start services
await http_port.start()
print("API running with auto-generated documentation:")
print("- OpenAPI spec: http://localhost:8080/spec/openapi")
print("- AsyncAPI spec: http://localhost:8080/spec/asyncapi")class EnhancedOpenAPIService(OpenAPIService):
def __init__(self, config: Config):
super().__init__(config)
# Enhance base specification
self.spec.update({
"info": {
"title": config.service.name,
"version": config.service.version,
"description": config.service.description,
"contact": {
"name": "API Support",
"email": "support@example.com"
}
},
"servers": [
{
"url": f"http://localhost:{config.service.port}",
"description": "Development server"
},
{
"url": f"https://api.example.com",
"description": "Production server"
}
]
})
@enroute.rest.command("/spec/openapi", "GET")
async def generate_specification(self, request: Request) -> Response:
"""Generate enhanced OpenAPI specification"""
# Get base specification
base_response = await super().generate_specification(request)
base_spec = await base_response.content()
# Add custom extensions
base_spec["x-custom-version"] = "1.0"
base_spec["x-generator"] = "minos-networks"
# Add security schemes
base_spec["components"] = {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
},
"apiKey": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
}
}
}
return Response(base_spec)
# Usage
enhanced_openapi = EnhancedOpenAPIService(config)import json
import yaml
from pathlib import Path
class SpecificationManager:
def __init__(self, config: Config):
self.config = config
self.openapi_service = OpenAPIService(config)
self.asyncapi_service = AsyncAPIService(config)
async def export_specifications(self, output_dir: str = "./specs"):
"""Export specifications to files"""
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
# Generate OpenAPI spec
openapi_response = await self.openapi_service.generate_specification(None)
openapi_spec = await openapi_response.content()
# Generate AsyncAPI spec
asyncapi_response = await self.asyncapi_service.generate_specification(None)
asyncapi_spec = await asyncapi_response.content()
# Export as JSON
with open(output_path / "openapi.json", "w") as f:
json.dump(openapi_spec, f, indent=2)
with open(output_path / "asyncapi.json", "w") as f:
json.dump(asyncapi_spec, f, indent=2)
# Export as YAML
with open(output_path / "openapi.yaml", "w") as f:
yaml.dump(openapi_spec, f, default_flow_style=False)
with open(output_path / "asyncapi.yaml", "w") as f:
yaml.dump(asyncapi_spec, f, default_flow_style=False)
print(f"Specifications exported to {output_path}")
def validate_openapi_spec(self, spec: dict) -> bool:
"""Validate OpenAPI specification"""
required_fields = ["openapi", "info", "paths"]
return all(field in spec for field in required_fields)
def validate_asyncapi_spec(self, spec: dict) -> bool:
"""Validate AsyncAPI specification"""
required_fields = ["asyncapi", "info", "channels"]
return all(field in spec for field in required_fields)
# Usage
spec_manager = SpecificationManager(config)
# Export specifications
await spec_manager.export_specifications("./api-docs")
# Validate specifications
openapi_response = await spec_manager.openapi_service.generate_specification(None)
openapi_spec = await openapi_response.content()
is_valid = spec_manager.validate_openapi_spec(openapi_spec)
print(f"OpenAPI spec valid: {is_valid}")class DocumentationIntegration:
def __init__(self, config: Config):
self.config = config
self.openapi_service = OpenAPIService(config)
self.asyncapi_service = AsyncAPIService(config)
@enroute.rest.query("/docs", method="GET")
async def swagger_ui(self, request: Request) -> Response:
"""Serve Swagger UI for OpenAPI documentation"""
swagger_html = f"""
<!DOCTYPE html>
<html>
<head>
<title>API Documentation</title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3.52.5/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@3.52.5/swagger-ui-bundle.js"></script>
<script>
SwaggerUIBundle({{
url: '/spec/openapi',
dom_id: '#swagger-ui',
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.presets.standalone]
}});
</script>
</body>
</html>
"""
return Response(swagger_html, content_type="text/html")
@enroute.rest.query("/async-docs", method="GET")
async def asyncapi_docs(self, request: Request) -> Response:
"""Serve AsyncAPI documentation"""
asyncapi_html = f"""
<!DOCTYPE html>
<html>
<head>
<title>AsyncAPI Documentation</title>
</head>
<body>
<div id="asyncapi"></div>
<script src="https://unpkg.com/@asyncapi/web-component@1.0.0-next.39/lib/asyncapi-web-component.js" defer></script>
<asyncapi-component
schemaUrl="/spec/asyncapi"
config='{{"show": {{"sidebar": true}}}}'
></asyncapi-component>
</body>
</html>
"""
return Response(asyncapi_html, content_type="text/html")
@enroute.rest.query("/", method="GET")
async def api_index(self, request: Request) -> Response:
"""API documentation index page"""
index_html = """
<!DOCTYPE html>
<html>
<head>
<title>API Documentation</title>
</head>
<body>
<h1>API Documentation</h1>
<ul>
<li><a href="/docs">REST API (Swagger UI)</a></li>
<li><a href="/async-docs">Async API Documentation</a></li>
<li><a href="/spec/openapi">OpenAPI Specification (JSON)</a></li>
<li><a href="/spec/asyncapi">AsyncAPI Specification (JSON)</a></li>
</ul>
</body>
</html>
"""
return Response(index_html, content_type="text/html")
# Usage - automatically provides documentation endpoints
doc_integration = DocumentationIntegration(config)
# Access documentation at:
# http://localhost:8080/ - Documentation index
# http://localhost:8080/docs - Swagger UI
# http://localhost:8080/async-docs - AsyncAPI docsInstall with Tessl CLI
npx tessl i tessl/pypi-minos-microservice-networks