---
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
