gateway/docs/real-estate-feature-integration-guide/05-routes.md

8.1 KiB

Schritt 4: Routen erstellen

← Zurück: Feature-Logik implementieren | Weiter: Router registrieren →

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:

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

{
    "success": true,
    "intent": "CREATE",
    "entity": "Projekt",
    "result": {
        "operation": "CREATE",
        "entity": "Projekt",
        "result": {
            "id": "projekt_123",
            "label": "Hauptstrasse 42",
            ...
        }
    }
}

Query-Endpunkt:

{
    "status": "success",
    "rows": [
        {"id": "...", "label": "...", ...}
    ],
    "columns": ["id", "label", ...],
    "rowCount": 15,
    "executionTime": 0.123
}

Beispiel-Requests

Command-Endpunkt

# 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

# 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 | Weiter: Router registrieren →