0
# API Specification Services
1
2
Built-in services for generating OpenAPI and AsyncAPI specifications from decorated handlers. Provides automated API documentation and specification generation.
3
4
## Capabilities
5
6
### OpenAPI Service
7
8
Service for generating OpenAPI 3.0 specifications from REST endpoints.
9
10
```python { .api }
11
class OpenAPIService:
12
def __init__(self, config: Config): ...
13
config: Config
14
spec: dict
15
endpoints: list[dict]
16
@enroute.rest.command("/spec/openapi", "GET")
17
async def generate_specification(self, request: Request) -> Response: ...
18
```
19
20
**Usage Examples:**
21
22
```python
23
from minos.networks import OpenAPIService, enroute
24
from minos.common import Config
25
26
# Service with REST endpoints
27
class UserAPI:
28
@enroute.rest.query("/users", method="GET")
29
async def list_users(self, request) -> Response:
30
return Response({"users": []})
31
32
@enroute.rest.command("/users", method="POST")
33
async def create_user(self, request) -> Response:
34
return Response({"id": "123"}, status=201)
35
36
# Setup OpenAPI service
37
config = Config("config.yml")
38
openapi_service = OpenAPIService(config)
39
40
# The service automatically exposes /spec/openapi endpoint
41
# Access at: GET http://localhost:8080/spec/openapi
42
```
43
44
### AsyncAPI Service
45
46
Service for generating AsyncAPI 2.3.0 specifications from broker events.
47
48
```python { .api }
49
class AsyncAPIService:
50
def __init__(self, config: Config): ...
51
config: Config
52
spec: dict
53
@enroute.rest.command("/spec/asyncapi", "GET")
54
async def generate_specification(self, request: Request) -> Response: ...
55
def get_events(self) -> list[dict]: ...
56
```
57
58
**Usage Examples:**
59
60
```python
61
from minos.networks import AsyncAPIService, enroute
62
from minos.common import Config
63
64
# Service with broker events
65
class UserEvents:
66
@enroute.broker.event("user.created")
67
async def handle_user_created(self, request) -> Response:
68
return Response({"processed": True})
69
70
@enroute.broker.event("user.updated")
71
async def handle_user_updated(self, request) -> Response:
72
return Response({"processed": True})
73
74
# Setup AsyncAPI service
75
config = Config("config.yml")
76
asyncapi_service = AsyncAPIService(config)
77
78
# The service automatically exposes /spec/asyncapi endpoint
79
# Access at: GET http://localhost:8080/spec/asyncapi
80
```
81
82
## Advanced Usage
83
84
### Complete API Documentation Setup
85
86
```python
87
from minos.networks import (
88
OpenAPIService, AsyncAPIService, HttpPort,
89
enroute, Request, Response
90
)
91
from minos.common import Config
92
93
class DocumentedAPI:
94
# REST endpoints for OpenAPI
95
@enroute.rest.query("/users", method="GET")
96
async def list_users(self, request: Request) -> Response:
97
"""List all users"""
98
return Response({"users": []})
99
100
@enroute.rest.command("/users", method="POST")
101
async def create_user(self, request: Request) -> Response:
102
"""Create a new user"""
103
user_data = await request.content()
104
return Response({"id": "123", **user_data}, status=201)
105
106
@enroute.rest.query("/users/{user_id}", method="GET")
107
async def get_user(self, request: Request) -> Response:
108
"""Get user by ID"""
109
params = await request.params()
110
return Response({"id": params["user_id"], "name": "John"})
111
112
# Broker events for AsyncAPI
113
@enroute.broker.event("user.created")
114
async def on_user_created(self, request: Request) -> Response:
115
"""Handle user creation events"""
116
return Response({"processed": True})
117
118
@enroute.broker.event("user.updated")
119
async def on_user_updated(self, request: Request) -> Response:
120
"""Handle user update events"""
121
return Response({"processed": True})
122
123
@enroute.broker.command("user.validate")
124
async def validate_user(self, request: Request) -> Response:
125
"""Validate user data"""
126
return Response({"valid": True})
127
128
# Configuration
129
config = Config({
130
"service": {
131
"name": "user-service",
132
"version": "1.0.0",
133
"description": "User management microservice"
134
}
135
})
136
137
# Setup specification services
138
openapi_service = OpenAPIService(config)
139
asyncapi_service = AsyncAPIService(config)
140
141
# Setup HTTP port
142
http_port = HttpPort._from_config(config)
143
144
# Start services
145
await http_port.start()
146
147
print("API running with auto-generated documentation:")
148
print("- OpenAPI spec: http://localhost:8080/spec/openapi")
149
print("- AsyncAPI spec: http://localhost:8080/spec/asyncapi")
150
```
151
152
### Custom Specification Enhancement
153
154
```python
155
class EnhancedOpenAPIService(OpenAPIService):
156
def __init__(self, config: Config):
157
super().__init__(config)
158
159
# Enhance base specification
160
self.spec.update({
161
"info": {
162
"title": config.service.name,
163
"version": config.service.version,
164
"description": config.service.description,
165
"contact": {
166
"name": "API Support",
167
"email": "support@example.com"
168
}
169
},
170
"servers": [
171
{
172
"url": f"http://localhost:{config.service.port}",
173
"description": "Development server"
174
},
175
{
176
"url": f"https://api.example.com",
177
"description": "Production server"
178
}
179
]
180
})
181
182
@enroute.rest.command("/spec/openapi", "GET")
183
async def generate_specification(self, request: Request) -> Response:
184
"""Generate enhanced OpenAPI specification"""
185
# Get base specification
186
base_response = await super().generate_specification(request)
187
base_spec = await base_response.content()
188
189
# Add custom extensions
190
base_spec["x-custom-version"] = "1.0"
191
base_spec["x-generator"] = "minos-networks"
192
193
# Add security schemes
194
base_spec["components"] = {
195
"securitySchemes": {
196
"bearerAuth": {
197
"type": "http",
198
"scheme": "bearer",
199
"bearerFormat": "JWT"
200
},
201
"apiKey": {
202
"type": "apiKey",
203
"in": "header",
204
"name": "X-API-Key"
205
}
206
}
207
}
208
209
return Response(base_spec)
210
211
# Usage
212
enhanced_openapi = EnhancedOpenAPIService(config)
213
```
214
215
### Specification Validation and Export
216
217
```python
218
import json
219
import yaml
220
from pathlib import Path
221
222
class SpecificationManager:
223
def __init__(self, config: Config):
224
self.config = config
225
self.openapi_service = OpenAPIService(config)
226
self.asyncapi_service = AsyncAPIService(config)
227
228
async def export_specifications(self, output_dir: str = "./specs"):
229
"""Export specifications to files"""
230
output_path = Path(output_dir)
231
output_path.mkdir(exist_ok=True)
232
233
# Generate OpenAPI spec
234
openapi_response = await self.openapi_service.generate_specification(None)
235
openapi_spec = await openapi_response.content()
236
237
# Generate AsyncAPI spec
238
asyncapi_response = await self.asyncapi_service.generate_specification(None)
239
asyncapi_spec = await asyncapi_response.content()
240
241
# Export as JSON
242
with open(output_path / "openapi.json", "w") as f:
243
json.dump(openapi_spec, f, indent=2)
244
245
with open(output_path / "asyncapi.json", "w") as f:
246
json.dump(asyncapi_spec, f, indent=2)
247
248
# Export as YAML
249
with open(output_path / "openapi.yaml", "w") as f:
250
yaml.dump(openapi_spec, f, default_flow_style=False)
251
252
with open(output_path / "asyncapi.yaml", "w") as f:
253
yaml.dump(asyncapi_spec, f, default_flow_style=False)
254
255
print(f"Specifications exported to {output_path}")
256
257
def validate_openapi_spec(self, spec: dict) -> bool:
258
"""Validate OpenAPI specification"""
259
required_fields = ["openapi", "info", "paths"]
260
return all(field in spec for field in required_fields)
261
262
def validate_asyncapi_spec(self, spec: dict) -> bool:
263
"""Validate AsyncAPI specification"""
264
required_fields = ["asyncapi", "info", "channels"]
265
return all(field in spec for field in required_fields)
266
267
# Usage
268
spec_manager = SpecificationManager(config)
269
270
# Export specifications
271
await spec_manager.export_specifications("./api-docs")
272
273
# Validate specifications
274
openapi_response = await spec_manager.openapi_service.generate_specification(None)
275
openapi_spec = await openapi_response.content()
276
is_valid = spec_manager.validate_openapi_spec(openapi_spec)
277
print(f"OpenAPI spec valid: {is_valid}")
278
```
279
280
### Integration with Documentation Tools
281
282
```python
283
class DocumentationIntegration:
284
def __init__(self, config: Config):
285
self.config = config
286
self.openapi_service = OpenAPIService(config)
287
self.asyncapi_service = AsyncAPIService(config)
288
289
@enroute.rest.query("/docs", method="GET")
290
async def swagger_ui(self, request: Request) -> Response:
291
"""Serve Swagger UI for OpenAPI documentation"""
292
swagger_html = f"""
293
<!DOCTYPE html>
294
<html>
295
<head>
296
<title>API Documentation</title>
297
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3.52.5/swagger-ui.css" />
298
</head>
299
<body>
300
<div id="swagger-ui"></div>
301
<script src="https://unpkg.com/swagger-ui-dist@3.52.5/swagger-ui-bundle.js"></script>
302
<script>
303
SwaggerUIBundle({{
304
url: '/spec/openapi',
305
dom_id: '#swagger-ui',
306
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.presets.standalone]
307
}});
308
</script>
309
</body>
310
</html>
311
"""
312
return Response(swagger_html, content_type="text/html")
313
314
@enroute.rest.query("/async-docs", method="GET")
315
async def asyncapi_docs(self, request: Request) -> Response:
316
"""Serve AsyncAPI documentation"""
317
asyncapi_html = f"""
318
<!DOCTYPE html>
319
<html>
320
<head>
321
<title>AsyncAPI Documentation</title>
322
</head>
323
<body>
324
<div id="asyncapi"></div>
325
<script src="https://unpkg.com/@asyncapi/web-component@1.0.0-next.39/lib/asyncapi-web-component.js" defer></script>
326
<asyncapi-component
327
schemaUrl="/spec/asyncapi"
328
config='{{"show": {{"sidebar": true}}}}'
329
></asyncapi-component>
330
</body>
331
</html>
332
"""
333
return Response(asyncapi_html, content_type="text/html")
334
335
@enroute.rest.query("/", method="GET")
336
async def api_index(self, request: Request) -> Response:
337
"""API documentation index page"""
338
index_html = """
339
<!DOCTYPE html>
340
<html>
341
<head>
342
<title>API Documentation</title>
343
</head>
344
<body>
345
<h1>API Documentation</h1>
346
<ul>
347
<li><a href="/docs">REST API (Swagger UI)</a></li>
348
<li><a href="/async-docs">Async API Documentation</a></li>
349
<li><a href="/spec/openapi">OpenAPI Specification (JSON)</a></li>
350
<li><a href="/spec/asyncapi">AsyncAPI Specification (JSON)</a></li>
351
</ul>
352
</body>
353
</html>
354
"""
355
return Response(index_html, content_type="text/html")
356
357
# Usage - automatically provides documentation endpoints
358
doc_integration = DocumentationIntegration(config)
359
360
# Access documentation at:
361
# http://localhost:8080/ - Documentation index
362
# http://localhost:8080/docs - Swagger UI
363
# http://localhost:8080/async-docs - AsyncAPI docs
364
```