cleanup
This commit is contained in:
302
bitbylaw/steps/advoware_proxy/advoware_api_proxy_get_step.md
Normal file
302
bitbylaw/steps/advoware_proxy/advoware_api_proxy_get_step.md
Normal 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
|
||||
Reference in New Issue
Block a user