chore(version): update version and changelog for release 0.8.123
All checks were successful
Deploy Development / deploy (push) Successful in 41s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m2s
Test Suite / pytest-backend (pull_request) Successful in 34s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 12s
Test Suite / k6 /health Baseline (pull_request) Successful in 33s
Test Suite / playwright-tests (pull_request) Successful in 1m1s

- Bumped APP_VERSION to 0.8.123 and updated the changelog to reflect recent changes.
- Fixed internal calls in GET /api/dashboard/kpis to use unwrap_query_default, preventing 500 errors due to FastAPI query defaults.
- Enhanced list_exercises and list_training_units functions to utilize unwrap_query_default for improved query handling.
- Added unit tests for unwrap_query_default to ensure correct behavior in various scenarios.
This commit is contained in:
Lars 2026-05-14 11:48:11 +02:00
parent 4235246cd7
commit 2e105a99b8
5 changed files with 55 additions and 2 deletions

View File

@ -0,0 +1,16 @@
"""Hilfen für direkte Python-Aufrufe von FastAPI-Route-Handlern (ohne Request-Kontext)."""
from __future__ import annotations
from typing import Any
def unwrap_query_default(value: Any) -> Any:
"""
Parameter mit Annotation ``= Query(default=)`` sind im Funktionskörper ``fastapi.params.Query``-Instanzen,
solange FastAPI sie nicht durch echte Werte ersetzt hat (interne Aufrufe, Aggregat-Endpunkte).
"""
try:
from fastapi.params import Query
except ImportError:
return value
return value.default if isinstance(value, Query) else value

View File

@ -19,6 +19,8 @@ from fastapi.responses import FileResponse, Response, StreamingResponse
from pydantic import BaseModel, Field, model_validator from pydantic import BaseModel, Field, model_validator
from psycopg2.extras import Json from psycopg2.extras import Json
from fastapi_param_unwrap import unwrap_query_default
from db import get_db, get_cursor, r2d from db import get_db, get_cursor, r2d
from club_tenancy import ( from club_tenancy import (
assert_valid_governance_visibility, assert_valid_governance_visibility,
@ -1773,6 +1775,11 @@ def list_exercises(
Optional include_variants für Variantenauswahl in der Trainingsplanung. Optional include_variants für Variantenauswahl in der Trainingsplanung.
Keyset: cursor_updated_at + cursor_id ersetzt große OFFSET-Werte (Sortierung: updated_at DESC, id DESC). Keyset: cursor_updated_at + cursor_id ersetzt große OFFSET-Werte (Sortierung: updated_at DESC, id DESC).
""" """
cursor_updated_at = unwrap_query_default(cursor_updated_at)
cursor_id = unwrap_query_default(cursor_id)
limit = unwrap_query_default(limit)
offset = unwrap_query_default(offset)
profile_id = tenant.profile_id profile_id = tenant.profile_id
c_ts_raw = (cursor_updated_at or "").strip() or None c_ts_raw = (cursor_updated_at or "").strip() or None

View File

@ -10,6 +10,8 @@ from typing import Any, Dict, List, Optional, Tuple
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from psycopg2.extras import Json as PsycopgJson from psycopg2.extras import Json as PsycopgJson
from fastapi_param_unwrap import unwrap_query_default
from db import get_db, get_cursor, r2d from db import get_db, get_cursor, r2d
from tenant_context import TenantContext, get_tenant_context, library_content_visibility_sql from tenant_context import TenantContext, get_tenant_context, library_content_visibility_sql
from club_tenancy import ( from club_tenancy import (
@ -1341,6 +1343,19 @@ def list_training_units(
), ),
tenant: TenantContext = Depends(get_tenant_context), tenant: TenantContext = Depends(get_tenant_context),
): ):
group_id = unwrap_query_default(group_id)
club_id = unwrap_query_default(club_id)
start_date = unwrap_query_default(start_date)
end_date = unwrap_query_default(end_date)
status = unwrap_query_default(status)
assigned_to_me = unwrap_query_default(assigned_to_me)
debrief_pending = unwrap_query_default(debrief_pending)
sort = unwrap_query_default(sort)
limit = unwrap_query_default(limit)
cursor_planned_date = unwrap_query_default(cursor_planned_date)
cursor_planned_time = unwrap_query_default(cursor_planned_time)
cursor_id = unwrap_query_default(cursor_id)
profile_id = tenant.profile_id profile_id = tenant.profile_id
role = tenant.global_role role = tenant.global_role

View File

@ -1,13 +1,15 @@
"""GET /api/dashboard/kpis: Auth (kein DB nötig).""" """GET /api/dashboard/kpis: Auth + interne Aufruf-Hilfen."""
from __future__ import annotations from __future__ import annotations
import os import os
import pytest import pytest
from fastapi import Query
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
os.environ.setdefault("SKIP_DB_MIGRATE", "1") os.environ.setdefault("SKIP_DB_MIGRATE", "1")
from fastapi_param_unwrap import unwrap_query_default
from main import app from main import app
@ -16,6 +18,12 @@ def client() -> TestClient:
return TestClient(app) return TestClient(app)
def test_unwrap_query_default_for_direct_route_calls() -> None:
assert unwrap_query_default(Query(default=None)) is None
assert unwrap_query_default("2026-01-01") == "2026-01-01"
assert unwrap_query_default(7) == 7
def test_dashboard_kpis_unauthenticated_401(client: TestClient) -> None: def test_dashboard_kpis_unauthenticated_401(client: TestClient) -> None:
r = client.get("/api/dashboard/kpis") r = client.get("/api/dashboard/kpis")
assert r.status_code == 401 assert r.status_code == 401

View File

@ -1,6 +1,6 @@
# Shinkan Jinkendo Version Information # Shinkan Jinkendo Version Information
APP_VERSION = "0.8.122" APP_VERSION = "0.8.123"
BUILD_DATE = "2026-05-12" BUILD_DATE = "2026-05-12"
DB_SCHEMA_VERSION = "20260514062" DB_SCHEMA_VERSION = "20260514062"
@ -36,6 +36,13 @@ MODULE_VERSIONS = {
} }
CHANGELOG = [ CHANGELOG = [
{
"version": "0.8.123",
"date": "2026-05-13",
"changes": [
"Fix: GET /api/dashboard/kpis — interne Aufrufe von list_exercises / list_training_units erhielten FastAPI-Query-Defaults statt None; .strip() auf Query-Objekt → 500. unwrap_query_default in beiden Handlern (Hilfsmodul fastapi_param_unwrap.py).",
],
},
{ {
"version": "0.8.122", "version": "0.8.122",
"date": "2026-05-13", "date": "2026-05-13",