feat(sdk): API-Referenz Frontend + Backend-Konsolidierung (Shared Utilities, CRUD Factory)
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 30s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 18s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 32s
CI / test-python-backend-compliance (push) Successful in 30s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 18s
- API-Referenz Seite (/sdk/api-docs) mit ~690 Endpoints, Suche, Filter, Modul-Index - Shared db_utils.py (row_to_dict) + tenant_utils Integration in 6 Route-Dateien - CRUD Factory (crud_factory.py) fuer zukuenftige Module - Version-Route Auto-Registration in versioning_utils.py - 1338 Tests bestanden, -232 Zeilen Duplikat-Code Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,6 @@ Endpoints:
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Any, Dict
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Header
|
||||
from pydantic import BaseModel
|
||||
@@ -22,12 +21,12 @@ from sqlalchemy import text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from classroom_engine.database import get_db
|
||||
from .tenant_utils import get_tenant_id as _get_tenant_id
|
||||
from .db_utils import row_to_dict as _row_to_dict
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/obligations", tags=["obligations"])
|
||||
|
||||
DEFAULT_TENANT_ID = "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Pydantic Schemas
|
||||
@@ -65,25 +64,6 @@ class ObligationStatusUpdate(BaseModel):
|
||||
status: str
|
||||
|
||||
|
||||
def _row_to_dict(row) -> Dict[str, Any]:
|
||||
result = dict(row._mapping)
|
||||
for key, val in result.items():
|
||||
if isinstance(val, datetime):
|
||||
result[key] = val.isoformat()
|
||||
elif hasattr(val, '__str__') and not isinstance(val, (str, int, float, bool, list, dict, type(None))):
|
||||
result[key] = str(val)
|
||||
return result
|
||||
|
||||
|
||||
def _get_tenant_id(x_tenant_id: Optional[str] = Header(None)) -> str:
|
||||
if x_tenant_id:
|
||||
try:
|
||||
UUID(x_tenant_id)
|
||||
return x_tenant_id
|
||||
except ValueError:
|
||||
pass
|
||||
return DEFAULT_TENANT_ID
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Routes
|
||||
@@ -98,10 +78,9 @@ async def list_obligations(
|
||||
limit: int = Query(100, ge=1, le=500),
|
||||
offset: int = Query(0, ge=0),
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
):
|
||||
"""List obligations with optional filters."""
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
|
||||
where_clauses = ["tenant_id = :tenant_id"]
|
||||
params: Dict[str, Any] = {"tenant_id": tenant_id, "limit": limit, "offset": offset}
|
||||
@@ -159,10 +138,9 @@ async def list_obligations(
|
||||
@router.get("/stats")
|
||||
async def get_obligation_stats(
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
):
|
||||
"""Return obligation counts per status and priority."""
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
|
||||
rows = db.execute(text("""
|
||||
SELECT
|
||||
@@ -187,11 +165,10 @@ async def get_obligation_stats(
|
||||
async def create_obligation(
|
||||
payload: ObligationCreate,
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
x_user_id: Optional[str] = Header(None),
|
||||
):
|
||||
"""Create a new compliance obligation."""
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
logger.info("create_obligation user_id=%s tenant_id=%s title=%s", x_user_id, tenant_id, payload.title)
|
||||
|
||||
import json
|
||||
@@ -228,9 +205,8 @@ async def create_obligation(
|
||||
async def get_obligation(
|
||||
obligation_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
):
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
row = db.execute(text("""
|
||||
SELECT * FROM compliance_obligations
|
||||
WHERE id = :id AND tenant_id = :tenant_id
|
||||
@@ -245,11 +221,10 @@ async def update_obligation(
|
||||
obligation_id: str,
|
||||
payload: ObligationUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
x_user_id: Optional[str] = Header(None),
|
||||
):
|
||||
"""Update an obligation's fields."""
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
logger.info("update_obligation user_id=%s tenant_id=%s id=%s", x_user_id, tenant_id, obligation_id)
|
||||
import json
|
||||
|
||||
@@ -285,11 +260,10 @@ async def update_obligation_status(
|
||||
obligation_id: str,
|
||||
payload: ObligationStatusUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
x_user_id: Optional[str] = Header(None),
|
||||
):
|
||||
"""Quick status update for an obligation."""
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
logger.info("update_obligation_status user_id=%s tenant_id=%s id=%s status=%s", x_user_id, tenant_id, obligation_id, payload.status)
|
||||
valid_statuses = {"pending", "in-progress", "completed", "overdue"}
|
||||
if payload.status not in valid_statuses:
|
||||
@@ -312,10 +286,9 @@ async def update_obligation_status(
|
||||
async def delete_obligation(
|
||||
obligation_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
x_user_id: Optional[str] = Header(None),
|
||||
):
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
logger.info("delete_obligation user_id=%s tenant_id=%s id=%s", x_user_id, tenant_id, obligation_id)
|
||||
result = db.execute(text("""
|
||||
DELETE FROM compliance_obligations
|
||||
@@ -334,11 +307,10 @@ async def delete_obligation(
|
||||
async def list_obligation_versions(
|
||||
obligation_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
):
|
||||
"""List all versions for an Obligation."""
|
||||
from .versioning_utils import list_versions
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
return list_versions(db, "obligation", obligation_id, tenant_id)
|
||||
|
||||
|
||||
@@ -347,11 +319,10 @@ async def get_obligation_version(
|
||||
obligation_id: str,
|
||||
version_number: int,
|
||||
db: Session = Depends(get_db),
|
||||
x_tenant_id: Optional[str] = Header(None),
|
||||
tenant_id: str = Depends(_get_tenant_id),
|
||||
):
|
||||
"""Get a specific Obligation version with full snapshot."""
|
||||
from .versioning_utils import get_version
|
||||
tenant_id = _get_tenant_id(x_tenant_id)
|
||||
v = get_version(db, "obligation", obligation_id, version_number, tenant_id)
|
||||
if not v:
|
||||
raise HTTPException(status_code=404, detail=f"Version {version_number} not found")
|
||||
|
||||
Reference in New Issue
Block a user