--- type: step category: api name: Advoware Proxy GET version: 1.0.0 status: active tags: [advoware, proxy, api, rest] dependencies: - services/advoware.py - redis (for token caching) emits: [] subscribes: [] --- # Advoware Proxy GET Step ## Zweck Universeller REST-API-Proxy für GET-Requests an die Advoware API mit automatischer Authentifizierung und Token-Management. ## Kontext Die Advoware API verwendet HMAC-512 Authentifizierung, die komplex und fehleranfällig ist. Dieser Proxy abstrahiert die Authentifizierung und bietet einen einfachen HTTP-Endpunkt für GET-Requests. Clients müssen sich nicht um Token-Management, Signatur-Generierung oder Error-Handling kümmern. ## Technische Spezifikation ### Config ```python { 'type': 'api', 'name': 'Advoware Proxy GET', 'description': 'Universal proxy for Advoware API (GET)', 'path': '/advoware/proxy', 'method': 'GET', 'emits': [], 'flows': ['advoware'] } ``` ### Input - **HTTP Method**: GET - **Path**: `/advoware/proxy` - **Query Parameters**: - `endpoint` (required, string): Advoware API endpoint path (ohne Base-URL) - Alle weiteren Parameter werden an Advoware weitergeleitet **Beispiel**: ``` GET /advoware/proxy?endpoint=employees&limit=10&offset=0 ``` ### Output **Success Response (200)**: ```json { "status": 200, "body": { "result": { // Advoware API Response } } } ``` **Error Response (400)**: ```json { "status": 400, "body": { "error": "Endpoint required as query param" } } ``` **Error Response (500)**: ```json { "status": 500, "body": { "error": "Internal server error", "details": "Error message from Advoware or network" } } ``` ### Events - **Emits**: Keine - **Subscribes**: Keine ## Verhalten ### Ablauf 1. Extrahiere `endpoint` Parameter aus Query-String 2. Validiere dass `endpoint` vorhanden ist 3. Extrahiere alle anderen Query-Parameter (außer `endpoint`) 4. Erstelle AdvowareAPI-Instanz 5. Rufe `api_call()` mit GET-Methode auf - Intern: Token wird aus Redis geladen oder neu geholt - Intern: HMAC-Signatur wird generiert - Intern: Request wird an Advoware gesendet 6. Gebe Response als JSON zurück ### Fehlerbehandlung **Fehlender `endpoint` Parameter**: - HTTP 400 mit Fehlermeldung - Request wird nicht an Advoware weitergeleitet **Advoware API Error**: - HTTP 500 mit Details - Exception wird geloggt mit Stack-Trace - Keine Retry-Logik (fail-fast) **Token Expired (401)**: - Automatisch behandelt durch AdvowareAPI Service - Neuer Token wird geholt und Request wiederholt - Transparent für Client **Network Error**: - HTTP 500 mit Details - Exception wird geloggt - Timeout nach `ADVOWARE_API_TIMEOUT_SECONDS` (default: 30s) ### Side Effects - **Keine Writes**: GET-Request modifiziert keine Daten - **Token Cache**: Liest aus Redis DB 1 (`advoware_access_token`) - **Logging**: Schreibt INFO und ERROR logs in Motia Workbench ## Abhängigkeiten ### Services - **AdvowareAPI** (`services/advoware.py`): API-Client - `api_call(endpoint, method='GET', params, json_data=None)` - Handhabt Authentifizierung, Token-Caching, Error-Handling ### Redis Keys (gelesen via AdvowareAPI) - **DB 1**: - `advoware_access_token` (string, TTL: 53min): Bearer Token - `advoware_token_timestamp` (string, TTL: 53min): Token Creation Time ### Environment Variables ```bash ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/ ADVOWARE_API_KEY=base64_encoded_key ADVOWARE_APP_ID=your_app_id ADVOWARE_USER=api_user ADVOWARE_PASSWORD=secure_password ADVOWARE_API_TIMEOUT_SECONDS=30 REDIS_HOST=localhost REDIS_PORT=6379 REDIS_DB_ADVOWARE_CACHE=1 ``` ### External APIs - **Advoware API**: Alle GET-fähigen Endpoints - **Rate Limits**: Unknown (keine offizielle Dokumentation) ## Testing ### Manual Test ```bash # Test employee list curl -X GET "http://localhost:3000/advoware/proxy?endpoint=employees&limit=5" # Test appointments curl -X GET "http://localhost:3000/advoware/proxy?endpoint=appointments?datum=2026-02-07" # Test with error (missing endpoint) curl -X GET "http://localhost:3000/advoware/proxy" # Expected: 400 Bad Request ``` ### Expected Behavior 1. **Success Case**: - Status: 200 - Body enthält `result` mit Advoware-Daten - Logs zeigen "Proxying request to Advoware: GET {endpoint}" 2. **Error Case (missing endpoint)**: - Status: 400 - Body: `{"error": "Endpoint required as query param"}` 3. **Error Case (Advoware down)**: - Status: 500 - Body: `{"error": "Internal server error", "details": "..."}` - Logs zeigen Error mit Stack-Trace ## Monitoring ### Logs ``` [INFO] Proxying request to Advoware: GET employees [INFO] Using cached token [ERROR] Proxy error: ConnectionTimeout ``` ### Metrics (potentiell) - Request Count - Response Time (avg, p95, p99) - Error Rate - Cache Hit Rate (Token) ### Alerts - Error Rate > 10% über 5 Minuten - Response Time > 30s (Timeout-Grenze) - Redis Connection Failed ## Performance ### Response Time - **Cached Token**: 200-500ms (typisch) - **New Token**: 1-2s (Token-Fetch + API-Call) - **Timeout**: 30s (konfigurierbar) ### Throughput - **No rate limit** auf Motia-Seite - **Advoware API**: Unknown rate limits - **Bottleneck**: Advoware API Response-Zeit ## Security ### Secrets - ❌ Keine Secrets im Code - ✅ API Key über Environment Variable - ✅ Token in Redis (lokaler Zugriff nur) ### Authentication - Client → Motia: Keine (TODO: API Key oder OAuth) - Motia → Advoware: HMAC-512 + Bearer Token ### Data Exposure - GET-Requests lesen nur Daten - Keine PII in Logs (nur Endpoint-Pfade) - Response enthält alle Advoware-Daten (keine Filterung) ## Änderungshistorie | Version | Datum | Änderung | |---------|-------|----------| | 1.0.0 | 2024-10-24 | Initiale Implementierung | ## KI-Assistant Guidance ### Typische Änderungen **1. Timeout erhöhen**: ```python # In services/advoware.py, nicht im Step Config.ADVOWARE_API_TIMEOUT_SECONDS = 60 ``` **2. Request-Parameter anpassen**: ```python # Query-Parameter werden automatisch weitergeleitet # Keine Code-Änderung nötig ``` **3. Response-Transformation**: ```python # Vor return: result = await advoware.api_call(...) transformed = transform_response(result) # Neue Funktion return {'status': 200, 'body': {'result': transformed}} ``` **4. Caching hinzufügen**: ```python # Vor api_call: cache_key = f'cache:{endpoint}:{params}' cached = redis_client.get(cache_key) if cached: return {'status': 200, 'body': {'result': json.loads(cached)}} # ... api_call ... redis_client.set(cache_key, json.dumps(result), ex=300) ``` ### Don'ts - ❌ **Keine synchronen Blocking-Calls**: Immer `await` verwenden - ❌ **Keine Hardcoded Credentials**: Nur Environment Variables - ❌ **Keine unbehandelten Exceptions**: Immer try-catch - ❌ **Kein Logging von Secrets**: Keine Passwörter/Tokens loggen ### Testing-Tipps ```bash # Test mit verschiedenen Endpoints curl "http://localhost:3000/advoware/proxy?endpoint=employees" curl "http://localhost:3000/advoware/proxy?endpoint=appointments" curl "http://localhost:3000/advoware/proxy?endpoint=cases" # Test Error-Handling curl "http://localhost:3000/advoware/proxy" # Missing endpoint # Test mit vielen Parametern curl "http://localhost:3000/advoware/proxy?endpoint=employees&limit=100&offset=0&sortBy=name" ``` ### Related Steps - [advoware_api_proxy_post_step.md](advoware_api_proxy_post_step.md) - POST-Requests - [advoware_api_proxy_put_step.md](advoware_api_proxy_put_step.md) - PUT-Requests - [advoware_api_proxy_delete_step.md](advoware_api_proxy_delete_step.md) - DELETE-Requests ### Related Services - [services/advoware.py](../../services/ADVOWARE_SERVICE.md) - API-Client Implementierung