or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

brokers.mddecorators.mddiscovery.mdhttp.mdindex.mdrequests.mdrouters.mdscheduling.mdspecs.mdsystem-utils.md

specs.mddocs/

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

```