This commit is contained in:
2026-02-07 09:23:49 +00:00
parent 96eabe3db6
commit 36552903e7
85 changed files with 9820870 additions and 1767 deletions

View File

@@ -0,0 +1,302 @@
---
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