# API Documentation Strategy ## Best Practices für IoT Bridge ↔ Odoo Schnittstellen **Stand:** 2026-02-15 **Status:** ✅ Beide APIs funktionstüchtig, Dokumentation verbesserungswürdig --- ## 📋 Übersicht der Schnittstellen ``` ┌──────────────┐ ┌──────────────────┐ │ Odoo │ POST /config │ IoT Bridge │ │ │ ────────────────────────> │ │ │ │ (Device Config Push) │ │ │ │ │ │ │ │ GET /health │ │ │ │ ────────────────────────> │ │ │ │ (Health Check) │ │ └──────────────┘ └──────────────────┘ ┌──────────────┐ ┌──────────────────┐ │ IoT Bridge │ POST /ows/iot/event │ Odoo │ │ │ ────────────────────────> │ │ │ │ (Event Submission) │ │ │ │ JSON-RPC 2.0 │ │ └──────────────┘ └──────────────────┘ ``` ### API 1: **IoT Bridge Config API** (Odoo → Bridge) - **Technologie:** FastAPI (Python) - **Dokumentation:** ✅ Automatisch via OpenAPI/Swagger - **Endpunkte:** `POST /config`, `GET /config`, `GET /health` - **Zugriff:** http://localhost:8080/docs ### API 2: **Odoo IoT Event API** (Bridge → Odoo) - **Technologie:** Odoo HTTP Controller (JSON-RPC) - **Dokumentation:** ⚠️ Manuell zu erstellen - **Endpunkt:** `POST /ows/iot/event` - **Format:** JSON-RPC 2.0 --- ## 🎯 Empfohlene Dokumentationsstrategie ### Prinzipien 1. **Single Source of Truth** - Code ist die Quelle, Dokumentation wird daraus generiert 2. **Living Documentation** - Tests als ausführbare Beispiele 3. **Developer-First** - Fokus auf Nutzbarkeit für Entwickler 4. **Automatisierung** - Swagger/OpenAPI wo möglich 5. **Versionierung** - API-Versionen klar kennzeichnen ### Werkzeuge | Aspekt | Bridge API | Odoo API | Tool | |--------|-----------|----------|------| | Schema Definition | ✅ Automatisch | ⚠️ Manuell | Pydantic / Python Docstrings | | Interactive Docs | ✅ Swagger UI | ❌ Fehlt | FastAPI built-in / Postman Collection | | Code Examples | ✅ Tests | ✅ Tests | pytest / test_e2e_push_architecture.py | | Changelog | ⚠️ Git only | ⚠️ Git only | CHANGELOG.md | | Type Safety | ✅ Pydantic | ⚠️ Basic | -/- | --- ## 🔧 Implementierungsplan ### 1. IoT Bridge API (FastAPI) - ✅ Gut, kleinere Verbesserungen **Bereits vorhanden:** - ✅ OpenAPI Schema automatisch generiert - ✅ Swagger UI: http://localhost:8080/docs - ✅ ReDoc: http://localhost:8080/redoc - ✅ Pydantic Models für Validation - ✅ API.md mit Beispielen **Verbesserungen:** #### a) Erweitere FastAPI Endpoint-Dokumentation mit response_model und Examples ```python # In config_server.py @self.app.post( "/config", response_model=ConfigResponse, summary="Receive device configuration from Odoo", description=""" Receives full device configuration push from Odoo. **Triggered automatically by Odoo when:** - Device is created - Device is updated (relevant fields changed) - Device is deleted - Manual push button clicked **Side effects:** - Validates config schema - Computes device diff (added/updated/removed) - Updates MQTT subscriptions - Persists config to /data/config-active.yaml """, responses={ 200: { "description": "Configuration successfully applied", "content": { "application/json": { "example": { "status": "success", "message": "Configuration applied", "devices_configured": 2, "added": ["device-123"], "updated": ["device-456"], "removed": [], "timestamp": "2026-02-15T10:30:45.123456" } } } }, 422: { "description": "Validation error", "content": { "application/json": { "example": { "detail": [ { "loc": ["body", "devices", 0, "session_config", "working_threshold_w"], "msg": "working_threshold_w must be greater than standby_threshold_w", "type": "value_error" } ] } } } } }, tags=["Configuration"] ) async def receive_config(...): ... ``` #### b) Füge OpenAPI Metadata hinzu ```python # In config_server.py __init__ self.app = FastAPI( title="IoT Bridge Config API", description=""" **Autonomous MQTT-to-Odoo Integration Service** This API receives device configuration from Odoo and manages MQTT subscriptions. ## Features - Push-based configuration (no polling) - Dynamic MQTT subscription management - Config persistence to /data/config-active.yaml - Pydantic validation for type safety ## Workflow 1. Odoo pushes config via POST /config 2. Bridge validates and applies config 3. Bridge subscribes to MQTT topics 4. Bridge persists config for restart survival ## Authentication Optional Bearer token via `BRIDGE_API_TOKEN` environment variable. """, version="1.0.0", contact={ "name": "Open Workshop Team", "email": "lotz.matthias@gmail.com" }, license_info={ "name": "MIT", "url": "https://opensource.org/licenses/MIT" }, openapi_tags=[ { "name": "Configuration", "description": "Device configuration management" }, { "name": "Health", "description": "Service health and monitoring" } ] ) ``` #### c) Exportiere OpenAPI Schema in CI/CD ```bash # In iot_bridge/Makefile oder CI-Pipeline docs-export: python3 -c "from config_server import ConfigServer; import json; \ server = ConfigServer(lambda x: None); \ print(json.dumps(server.app.openapi(), indent=2))" \ > docs/openapi.json docs-serve: cd docs && python3 -m http.server 8888 ``` --- ### 2. Odoo IoT Event API (JSON-RPC) - ⚠️ Braucht Dokumentation **Status:** Funktioniert, aber keine strukturierte Dokumentation außer Docstrings **Empfohlener Ansatz:** #### a) Erstelle OpenAPI-ähnliches Dokument ```markdown # Odoo IoT Event API Reference **Base URL:** `http://localhost:9018` **Endpoint:** `POST /ows/iot/event` **Protocol:** JSON-RPC 2.0 **Authentication:** Odoo Session (Cookie) oder API Key ## Request Format (JSON-RPC 2.0) All requests must use JSON-RPC 2.0 format: { "jsonrpc": "2.0", "params": { "event_uid": "550e8400-e29b-41d4-a716-446655440000", "event_type": "session_started", "device_id": "shellypmminig3-48f6eeb73a1c", "session_id": "optional-session-uuid", "timestamp": "2026-02-15T10:30:45.123456Z", "payload": { "power_w": 125.5, "state": "working" } } } ## Parameters | Field | Type | Required | Description | |-------|------|----------|-------------| | event_uid | String (UUID) | ✅ Yes | Unique event identifier (prevents duplicates) | | event_type | String (Enum) | ✅ Yes | Event type (see below) | | device_id | String | ✅ Yes | Device identifier (must exist in mqtt.device) | | session_id | String | ❌ Optional | Session identifier (for session events) | | timestamp | String (ISO 8601) | ✅ Yes | Event timestamp (UTC) | | payload | Object | ❌ Optional | Device-specific data | ## Event Types ### device_online Device came online (MQTT connection established). **Effect:** Sets device.state = 'idle' **Example:** { "jsonrpc": "2.0", "params": { "event_uid": "a1b2c3d4-...", "event_type": "device_online", "device_id": "shelly-pm-001", "timestamp": "2026-02-15T10:00:00Z", "payload": {} } } ### device_offline Device went offline (MQTT timeout or disconnect). **Effect:** Sets device.state = 'offline' **Example:** { "jsonrpc": "2.0", "params": { "event_uid": "e5f6g7h8-...", "event_type": "device_offline", "device_id": "shelly-pm-001", "timestamp": "2026-02-15T10:05:00Z", "payload": {} } } ### session_started Work session started (power crossed working threshold). **Effect:** - Sets device.state = 'active' - Creates mqtt.session record **Example:** { "jsonrpc": "2.0", "params": { "event_uid": "i9j0k1l2-...", "event_type": "session_started", "device_id": "shelly-pm-001", "session_id": "session-abc123", "timestamp": "2026-02-15T10:15:00Z", "payload": { "power_w": 125.5, "state": "working" } } } ### session_stopped Work session ended (power dropped below standby threshold). **Effect:** - Sets device.state = 'idle' - Updates mqtt.session.end_time - Calculates session.duration **Example:** { "jsonrpc": "2.0", "params": { "event_uid": "m3n4o5p6-...", "event_type": "session_stopped", "device_id": "shelly-pm-001", "session_id": "session-abc123", "timestamp": "2026-02-15T11:30:00Z", "payload": { "power_w": 3.2, "total_duration_s": 4500 } } } ### session_heartbeat Periodic update during active session. **Effect:** Updates session statistics (avg_power, peak_power, etc.) **Example:** { "jsonrpc": "2.0", "params": { "event_uid": "q7r8s9t0-...", "event_type": "session_heartbeat", "device_id": "shelly-pm-001", "session_id": "session-abc123", "timestamp": "2026-02-15T10:20:00Z", "payload": { "power_w": 110.3, "avg_power_w": 115.2, "peak_power_w": 130.1, "state": "working" } } } ## Response Format ### Success (201 Created) { "status": "success", "event_id": 123, "event_uid": "550e8400-e29b-41d4-a716-446655440000", "timestamp": "2026-02-15T10:30:45.234567" } ### Duplicate Event (409 Conflict) { "status": "duplicate", "event_id": 122, "event_uid": "550e8400-e29b-41d4-a716-446655440000", "message": "Event already processed" } ### Validation Error (400 Bad Request) { "status": "error", "error": "Missing required fields: event_uid, device_id", "code": 400 } ### Device Not Found (404 Not Found) { "status": "error", "error": "Device not found: unknown-device-123", "code": 404 } ## Idempotency The API is **idempotent** via `event_uid`: - Same `event_uid` submitted twice → 409 Conflict (second request ignored) - Prevents duplicate event processing - Bridge can safely retry failed submissions ## Rate Limiting Currently **no rate limiting** implemented. ## Testing ### Manual Test (curl) ```bash curl -X POST http://localhost:9018/ows/iot/event \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "params": { "event_uid": "550e8400-e29b-41d4-a716-446655440000", "event_type": "device_online", "device_id": "testshelly-simulator", "timestamp": "2026-02-15T10:30:45Z", "payload": {} } }' ``` ### Automated Tests ```bash cd iot_bridge/tests python3 test_e2e_push_architecture.py ``` ## Changelog | Version | Date | Changes | |---------|------|---------| | 1.0.0 | 2026-02-15 | Initial implementation with JSON-RPC 2.0, event_uid for idempotency | ``` #### b) Füge Docstrings für IDEs hinzu ```python # In extra-addons/open_workshop/open_workshop_mqtt/controllers/iot_api.py @route('/ows/iot/event', type='json', auth='none', methods=['POST'], csrf=False, save_session=False) def receive_iot_event(self, event_uid=None, event_type=None, device_id=None, session_id=None, timestamp=None, payload=None, **kw): """ Receive IoT event from Bridge (JSON-RPC 2.0). Args: event_uid (str): Unique event identifier (UUID, required for idempotency) event_type (str): Event type - one of: - 'device_online': Device came online → state='idle' - 'device_offline': Device went offline → state='offline' - 'session_started': Session started → state='active', creates mqtt.session - 'session_stopped': Session ended → state='idle', finalizes mqtt.session - 'session_heartbeat': Periodic update during session device_id (str): Device identifier (must exist in mqtt.device, required) session_id (str, optional): Session identifier (required for session events) timestamp (str): ISO 8601 timestamp (required) payload (dict, optional): Device-specific data Returns: dict: Response with status, event_id, timestamp Example Request (JSON-RPC 2.0): { "jsonrpc": "2.0", "params": { "event_uid": "550e8400-e29b-41d4-a716-446655440000", "event_type": "session_started", "device_id": "shellypmminig3-48f6eeb73a1c", "session_id": "abc-123", "timestamp": "2026-02-15T10:30:45.123456Z", "payload": {"power_w": 125.5} } } Example Response (Success): { "status": "success", "event_id": 123, "event_uid": "550e8400-e29b-41d4-a716-446655440000", "timestamp": "2026-02-15T10:30:45.234567" } Example Response (Duplicate): { "status": "duplicate", "event_id": 122, "event_uid": "550e8400-e29b-41d4-a716-446655440000", "message": "Event already processed" } HTTP Status Codes: 201: Event created successfully 409: Duplicate event_uid (idempotent retry) 400: Validation error (missing fields, invalid data) 404: Device not found 500: Internal server error Side Effects: - Creates iot.event record - May update mqtt.device.state (online/offline/idle/active) - May create/update mqtt.session record (for session events) - Idempotent via event_uid (duplicate submissions return 409) """ # ... implementation ``` --- ### 3. Postman Collection - Für beide APIs **Vorteil:** Interaktive Dokumentation + Testing Tool Erstelle Postman Collection mit: - ✅ Alle Endpoints (Bridge + Odoo) - ✅ Beispiel-Requests mit Variablen - ✅ Pre-request Scripts (UUID Generation für event_uid) - ✅ Tests (Assertions für Status Codes) ```json { "info": { "name": "IoT Bridge Odoo Integration", "description": "Complete API collection for IoT Bridge ↔ Odoo communication", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "variable": [ { "key": "bridge_url", "value": "http://localhost:8080" }, { "key": "odoo_url", "value": "http://localhost:9018" } ], "item": [ { "name": "Bridge API", "item": [ { "name": "Health Check", "request": { "method": "GET", "url": "{{bridge_url}}/health" } }, { "name": "Get Config", "request": { "method": "GET", "url": "{{bridge_url}}/config" } }, { "name": "Push Config (from Odoo)", "request": { "method": "POST", "url": "{{bridge_url}}/config", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"devices\": [\n {\n \"device_id\": \"testshelly-simulator\",\n \"machine_name\": \"Test Machine\",\n \"mqtt_topic\": \"testshelly-simulator/status/pm1:0\",\n \"parser_type\": \"shelly_pm\",\n \"session_config\": {\n \"standby_threshold_w\": 5.0,\n \"working_threshold_w\": 50.0,\n \"start_debounce_s\": 3.0,\n \"stop_debounce_s\": 15.0,\n \"message_timeout_s\": 20.0,\n \"heartbeat_interval_s\": 300.0\n }\n }\n ]\n}" } } } ] }, { "name": "Odoo API", "item": [ { "name": "Submit Device Online Event", "request": { "method": "POST", "url": "{{odoo_url}}/ows/iot/event", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"jsonrpc\": \"2.0\",\n \"params\": {\n \"event_uid\": \"{{$guid}}\",\n \"event_type\": \"device_online\",\n \"device_id\": \"testshelly-simulator\",\n \"timestamp\": \"{{$isoTimestamp}}\",\n \"payload\": {}\n }\n}" } } }, { "name": "Submit Session Started Event", "request": { "method": "POST", "url": "{{odoo_url}}/ows/iot/event", "header": [ { "key": "Content-Type", "value": "application/json" } ], "body": { "mode": "raw", "raw": "{\n \"jsonrpc\": \"2.0\",\n \"params\": {\n \"event_uid\": \"{{$guid}}\",\n \"event_type\": \"session_started\",\n \"device_id\": \"testshelly-simulator\",\n \"session_id\": \"session-{{$timestamp}}\",\n \"timestamp\": \"{{$isoTimestamp}}\",\n \"payload\": {\n \"power_w\": 125.5,\n \"state\": \"working\"\n }\n }\n}" } } } ] } ] } ``` **Nutzung:** 1. Importiere in Postman 2. Setze Environment Variables (bridge_url, odoo_url) 3. Führe Requests aus 4. Teile Collection mit Team (Export als JSON) --- ### 4. Tests als Living Documentation **Bereits vorhanden:** `test_e2e_push_architecture.py` **Verbesserungen:** #### a) Füge docstrings zu Test-Funktionen hinzu ```python def test_device_status_monitoring(odoo: OdooAPI, bridge: BridgeAPI): """ Test: Device Status Lifecycle (offline → idle → active → idle → offline) Scenario: 1. Send device_online event → Device state = 'idle' 2. Send session_started event → Device state = 'active' 3. Send session_stopped event → Device state = 'idle' 4. Send device_offline event → Device state = 'offline' Validates: - JSON-RPC 2.0 format for Odoo event API - event_uid requirement for idempotency - State transitions via device.write() in controllers/iot_api.py - Proper timestamp handling (ISO 8601) Related: - controllers/iot_api.py::receive_iot_event() - models/mqtt_device.py::state field - DOCUMENTATION_STRATEGY.md::Odoo IoT Event API """ # ... test implementation ``` #### b) Generiere HTML Test Report mit Beispielen ```bash # In iot_bridge/tests/ pytest test_e2e_push_architecture.py \ --html=report.html \ --self-contained-html \ --capture=no ``` Zeigt: - ✅ Welche Tests liefen - ✅ Request/Response Examples aus den Tests - ✅ Failure Details mit Stacktraces --- ## 📦 Deliverables (Was konkret erstellen?) ### Sofort (heute): 1. **✅ DOCUMENTATION_STRATEGY.md** (dieses Dokument) - Übersicht der APIs - Werkzeuge & Best Practices - Implementierungsplan 2. **🔧 Erweitere config_server.py** (15 min) - Füge OpenAPI Metadata hinzu (contact, license, tags) - Erweitere endpoint docstrings mit examples - Siehe Code-Snippet oben 3. **📝 ODOO_EVENT_API.md** erstellen (30 min) - Vollständige Referenz für /ows/iot/event - Alle Event Types dokumentiert - Beispiele für jeden Event-Typ - Siehe Template oben ### Kurzfristig (diese Woche): 4. **🧪 Test Docstrings erweitern** (20 min) - Alle test_*() Funktionen dokumentieren - Verlinke zu relevanten Code-Stellen - Siehe Beispiel oben 5. **📮 Postman Collection erstellen** (45 min) - Alle Endpoints beider APIs - Environment Variables - Pre-request Scripts für UUID/Timestamp - Export als JSON im Repo 6. **📊 OpenAPI Export im CI/CD** (15 min) - Script zum Exportieren von openapi.json - Commit in Repo für Versionskontrolle - Siehe Makefile-Snippet oben ### Mittelfristig (nächster Sprint): 7. **🔄 API Changelog einführen** - CHANGELOG.md für Breaking Changes - Semantic Versioning (1.0.0 → 1.1.0 → 2.0.0) - Git Tags für API-Versionen 8. **🏷️ API Versioning Strategy** - Entscheide: URL-basiert (/v1/event) oder Header-basiert? - Implementiere Version Header: `X-API-Version: 1.0` - Plane Deprecation Policy (wie lange Support für alte Version?) 9. **📈 API Monitoring Dashboard** - Prometheus Metrics für beide APIs - Grafana Dashboard mit: - Request Rate (req/s) - Error Rate (4xx/5xx) - Latency (p50, p95, p99) - Active Devices --- ## 🎨 Empfohlene Verzeichnisstruktur ``` iot_bridge/ ├── API.md # ✅ Bereits vorhanden (Bridge API) ├── docs/ │ ├── openapi.json # 🆕 Exportiertes OpenAPI Schema │ └── postman/ │ └── collection.json # 🆕 Postman Collection └── tests/ └── test_e2e_push_architecture.py # ✅ Mit erweiterten Docstrings extra-addons/open_workshop/open_workshop_mqtt/ ├── API.md # 🆕 Odoo Event API Referenz ├── controllers/ │ └── iot_api.py # 🔧 Erweiterte Docstrings └── docs/ └── postman/ └── collection.json # 🆕 Odoo API Beispiele ``` --- ## ✅ Vorteile dieser Strategie ### Für Entwickler: - ✅ **Automatische Swagger Docs** - kein manuelles Update nötig - ✅ **Type Safety** - Pydantic Models verhindern Fehler - ✅ **Executable Examples** - Tests zeigen echte Nutzung - ✅ **IDE Support** - Docstrings → Autocomplete ### Für DevOps: - ✅ **OpenAPI Schema** - kann in API Gateway integriert werden - ✅ **Health Endpoint** - für Load Balancer / Monitoring - ✅ **Postman Collection** - für Smoke Tests nach Deployment ### Für QA: - ✅ **E2E Tests** - validieren komplette Workflows - ✅ **Postman Collection** - für manuelle Explorative Tests - ✅ **Test Reports** - HTML Report mit Beispielen ### Für neue Teammitglieder: - ✅ **Swagger UI** - interaktiv API ausprobieren - ✅ **Markdown Docs** - leicht zu lesen - ✅ **Tests** - zeigen "How to use" Patterns --- ## 🚀 Quick Wins (Start hier!) ### 30-Minuten-Sprint: ```bash # 1. Erstelle Odoo API Dokumentation cd extra-addons/open_workshop/open_workshop_mqtt cat > API.md << 'EOF' [Inhalt von oben: "# Odoo IoT Event API Reference" ...] EOF # 2. Teste Swagger UI curl http://localhost:8080/docs # 3. Exportiere OpenAPI Schema curl http://localhost:8080/openapi.json > iot_bridge/docs/openapi.json # 4. Commit git add . git commit -m "docs: Add comprehensive API documentation for Bridge and Odoo APIs" ``` --- ## 📚 Weiterführende Ressourcen - [FastAPI Docs - Metadata and Doc URLs](https://fastapi.tiangolo.com/tutorial/metadata/) - [OpenAPI Specification](https://swagger.io/specification/) - [Postman Collections](https://learning.postman.com/docs/getting-started/creating-your-first-collection/) - [Semantic Versioning](https://semver.org/) - [API Versioning Best Practices](https://www.freecodecamp.org/news/rest-api-best-practices-rest-endpoint-design-examples/) --- **Autor:** Open Workshop Team **Letzte Aktualisierung:** 2026-02-15 **Status:** 🟢 Aktiv implementiert