332 lines
8.1 KiB
Markdown
332 lines
8.1 KiB
Markdown
# Schritt 4: Routen erstellen
|
|
|
|
[← Zurück: Feature-Logik implementieren](04-feature-logic.md) | [Weiter: Router registrieren →](06-router-registration.md)
|
|
|
|
**Datei:** `modules/routes/routeRealEstate.py`
|
|
|
|
Die Routen definieren die REST-API-Endpunkte für das Feature. Das Feature arbeitet **stateless** ohne Session-Management.
|
|
|
|
## Route-Struktur
|
|
|
|
```
|
|
/api/realestate/
|
|
├── POST /command → Natürliche Sprache → CRUD-Operation
|
|
└── POST /query → Direkte SQL-Query
|
|
```
|
|
|
|
## Beispiel-Implementierung:
|
|
|
|
```python
|
|
"""
|
|
Real Estate routes for the backend API.
|
|
Implements stateless endpoints for real estate database operations with AI-powered natural language processing.
|
|
"""
|
|
|
|
import logging
|
|
from typing import Optional, Dict, Any
|
|
from fastapi import APIRouter, HTTPException, Depends, Body, Request
|
|
from modules.security.auth import limiter, getCurrentUser
|
|
from modules.datamodels.datamodelUam import User
|
|
from modules.features.realEstate.mainRealEstate import (
|
|
processNaturalLanguageCommand,
|
|
executeDirectQuery,
|
|
)
|
|
|
|
# Configure logger
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Create router for real estate endpoints
|
|
router = APIRouter(
|
|
prefix="/api/realestate",
|
|
tags=["Real Estate"],
|
|
responses={404: {"description": "Not found"}}
|
|
)
|
|
|
|
|
|
# ===== Stateless Command Endpoint =====
|
|
|
|
@router.post("/command", response_model=Dict[str, Any])
|
|
@limiter.limit("120/minute")
|
|
async def process_command(
|
|
request: Request,
|
|
userInput: str = Body(..., embed=True, description="Natural language command"),
|
|
currentUser: User = Depends(getCurrentUser)
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Process natural language command and execute corresponding CRUD operation.
|
|
|
|
Uses AI to analyze user intent and extract parameters, then executes the appropriate
|
|
CRUD operation. Works stateless without session management.
|
|
|
|
Example user inputs:
|
|
- "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
|
- "Zeige mir alle Projekte in Zürich"
|
|
- "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
|
- "Lösche Parzelle ABC"
|
|
- "SELECT * FROM Projekt WHERE plz = '8000'"
|
|
|
|
Returns:
|
|
{
|
|
"success": true,
|
|
"intent": "CREATE|READ|UPDATE|DELETE|QUERY",
|
|
"entity": "Projekt|Parzelle|...|null",
|
|
"result": {...}
|
|
}
|
|
"""
|
|
try:
|
|
result = await processNaturalLanguageCommand(
|
|
currentUser=currentUser,
|
|
userInput=userInput
|
|
)
|
|
return result
|
|
except ValueError as e:
|
|
logger.error(f"Validation error: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error processing command: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=str(e)
|
|
)
|
|
|
|
|
|
# ===== Stateless Query Endpoint =====
|
|
|
|
@router.post("/query", response_model=Dict[str, Any])
|
|
@limiter.limit("120/minute")
|
|
async def execute_query(
|
|
request: Request,
|
|
queryText: str = Body(..., embed=True, description="SQL query text"),
|
|
parameters: Optional[Dict[str, Any]] = Body(None, embed=True, description="Optional query parameters for parameterized queries"),
|
|
currentUser: User = Depends(getCurrentUser)
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Execute a direct SQL query without session management.
|
|
|
|
Executes the query directly and returns the result. No query history is saved.
|
|
|
|
WARNING: This endpoint executes raw SQL queries. Ensure proper validation
|
|
and sanitization on the frontend. Consider implementing query whitelisting
|
|
or only allowing SELECT statements for production use.
|
|
|
|
Returns:
|
|
{
|
|
"status": "success",
|
|
"rows": [...],
|
|
"columns": [...],
|
|
"rowCount": 15,
|
|
"executionTime": 0.123
|
|
}
|
|
"""
|
|
try:
|
|
result = await executeDirectQuery(
|
|
currentUser=currentUser,
|
|
queryText=queryText,
|
|
parameters=parameters,
|
|
)
|
|
return result
|
|
except ValueError as e:
|
|
logger.error(f"Validation error: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error executing query: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=str(e)
|
|
)
|
|
```
|
|
|
|
## Wichtige Punkte:
|
|
|
|
### 1. Stateless Design
|
|
|
|
- **Keine Session-Management**: Alle Endpunkte arbeiten stateless
|
|
- **Direkte Verarbeitung**: User-Input wird direkt verarbeitet und Ergebnis zurückgegeben
|
|
- **Keine History**: Queries werden nicht gespeichert (kann optional später hinzugefügt werden)
|
|
|
|
### 2. API-Endpunkte
|
|
|
|
**`POST /api/realestate/command`**
|
|
- Verarbeitet natürliche Sprache
|
|
- Nutzt AI für Intent-Analyse
|
|
- Führt CRUD-Operationen aus
|
|
- Gibt Ergebnis direkt zurück
|
|
|
|
**`POST /api/realestate/query`**
|
|
- Führt direkte SQL-Queries aus
|
|
- Keine Session notwendig
|
|
- Gibt Query-Ergebnis direkt zurück
|
|
|
|
### 3. Sicherheit
|
|
|
|
- **Rate Limiting**: `@limiter.limit("120/minute")` für API-Schutz
|
|
- **Authentication**: `Depends(getCurrentUser)` für alle Endpunkte
|
|
- **Query-Validierung**: WICHTIG - Validieren Sie SQL-Queries vor Ausführung
|
|
- **MandateId-Filter**: Wird automatisch durch Interfaces angewendet
|
|
|
|
### 4. Error Handling
|
|
|
|
- Umfassendes Error Handling mit HTTPException
|
|
- Unterschiedliche Status-Codes: 400 (Validation), 404 (Not Found), 500 (Server Error)
|
|
- Detaillierte Fehlermeldungen für Debugging
|
|
|
|
### 5. Response-Struktur
|
|
|
|
**Command-Endpunkt:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"intent": "CREATE",
|
|
"entity": "Projekt",
|
|
"result": {
|
|
"operation": "CREATE",
|
|
"entity": "Projekt",
|
|
"result": {
|
|
"id": "projekt_123",
|
|
"label": "Hauptstrasse 42",
|
|
...
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Query-Endpunkt:**
|
|
```json
|
|
{
|
|
"status": "success",
|
|
"rows": [
|
|
{"id": "...", "label": "...", ...}
|
|
],
|
|
"columns": ["id", "label", ...],
|
|
"rowCount": 15,
|
|
"executionTime": 0.123
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Beispiel-Requests
|
|
|
|
### Command-Endpunkt
|
|
|
|
```bash
|
|
# CREATE Operation
|
|
POST /api/realestate/command
|
|
Content-Type: application/json
|
|
Authorization: Bearer <token>
|
|
|
|
{
|
|
"userInput": "Erstelle ein neues Projekt namens 'Hauptstrasse 42'"
|
|
}
|
|
|
|
# READ Operation
|
|
POST /api/realestate/command
|
|
{
|
|
"userInput": "Zeige mir alle Projekte in Zürich"
|
|
}
|
|
|
|
# UPDATE Operation
|
|
POST /api/realestate/command
|
|
{
|
|
"userInput": "Aktualisiere Projekt XYZ mit Status 'Planung'"
|
|
}
|
|
|
|
# DELETE Operation
|
|
POST /api/realestate/command
|
|
{
|
|
"userInput": "Lösche Parzelle ABC"
|
|
}
|
|
|
|
# QUERY Operation (SQL wird erkannt)
|
|
POST /api/realestate/command
|
|
{
|
|
"userInput": "SELECT * FROM Projekt WHERE plz = '8000'"
|
|
}
|
|
```
|
|
|
|
### Query-Endpunkt
|
|
|
|
```bash
|
|
# Direkte SQL-Query
|
|
POST /api/realestate/query
|
|
Content-Type: application/json
|
|
Authorization: Bearer <token>
|
|
|
|
{
|
|
"queryText": "SELECT * FROM Projekt WHERE plz = '8000'"
|
|
}
|
|
|
|
# Parameterized Query
|
|
POST /api/realestate/query
|
|
{
|
|
"queryText": "SELECT * FROM Projekt WHERE plz = $1",
|
|
"parameters": {"$1": "8000"}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Flow: Route → Feature-Logik
|
|
|
|
### Command-Endpunkt Flow
|
|
|
|
```
|
|
POST /api/realestate/command
|
|
↓
|
|
routeRealEstate.process_command()
|
|
↓
|
|
getCurrentUser() # Auth
|
|
↓
|
|
processNaturalLanguageCommand(currentUser, userInput)
|
|
↓
|
|
mainRealEstate.processNaturalLanguageCommand()
|
|
↓
|
|
analyzeUserIntent() → executeIntentBasedOperation()
|
|
↓
|
|
return Dict mit Ergebnis
|
|
```
|
|
|
|
### Query-Endpunkt Flow
|
|
|
|
```
|
|
POST /api/realestate/query
|
|
↓
|
|
routeRealEstate.execute_query()
|
|
↓
|
|
getCurrentUser() # Auth
|
|
↓
|
|
executeDirectQuery(currentUser, queryText, parameters)
|
|
↓
|
|
mainRealEstate.executeDirectQuery()
|
|
↓
|
|
getChatInterface(currentUser)
|
|
↓
|
|
RealEstateChatObjects.executeQuery(queryText)
|
|
↓
|
|
DatabaseConnector.executeQuery(sql)
|
|
↓
|
|
return Dict mit rows, columns, rowCount
|
|
```
|
|
|
|
---
|
|
|
|
## Vorteile des stateless Ansatzes
|
|
|
|
- **Einfachheit**: Kein Session-Management notwendig
|
|
- **Performance**: Weniger Datenbank-Operationen pro Request
|
|
- **Skalierbarkeit**: Stateless Requests sind einfacher zu skalieren
|
|
- **Flexibilität**: Jeder Request ist unabhängig
|
|
- **Schnell**: Direkte Verarbeitung ohne Overhead
|
|
|
|
---
|
|
|
|
[← Zurück: Feature-Logik implementieren](04-feature-logic.md) | [Weiter: Router registrieren →](06-router-registration.md)
|
|
|
|
|
|
|