From d9aed519f26011836a6132ff74785a00fcd82e45 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 24 Aug 2026 21:12:58 +0200 Subject: [PATCH] =?UTF-8?q?fix(i-c):=20BUG-024=20behoben=20=E2=80=94=20Plu?= =?UTF-8?q?gin-Detail-Endpoint=20GET=20/api/v1/plugins/{name}=20implementi?= =?UTF-8?q?ert=20(Manifest-Metadaten=20+=20DB-Status,=20404=20f=C3=BCr=20u?= =?UTF-8?q?nbekannte);=20Beweistest=20test=5Fplugin=5Fdetail.py=202/2=20gr?= =?UTF-8?q?=C3=BCn;=20Existenzpr=C3=BCfung=20vorher:=20Route=20fehlte=20ko?= =?UTF-8?q?mplett=20(bewiesen),=20Frontend-Nutzung=20niedrig=20aber=20API-?= =?UTF-8?q?Vollst=C3=A4ndigkeit=20hergestellt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/plugins.py | 45 ++++++++++++++++++++++++++++++++++++- docs/test-bugs.md | 2 +- tests/test_plugin_detail.py | 36 +++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 tests/test_plugin_detail.py diff --git a/app/routes/plugins.py b/app/routes/plugins.py index 97adf7b..219772c 100644 --- a/app/routes/plugins.py +++ b/app/routes/plugins.py @@ -5,13 +5,14 @@ from __future__ import annotations import logging from typing import Any -from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile +from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import get_db from app.deps import get_current_user, require_admin, require_permission from app.plugins.migration_runner import MigrationValidationError +from app.plugins.registry import get_registry from app.services.plugin_service import get_plugin_service logger = logging.getLogger(__name__) @@ -43,6 +44,48 @@ async def list_plugins( return {"plugins": plugins, "total": len(plugins)} +@router.get("/{name}") +async def get_plugin_detail( + name: str, + db: AsyncSession = Depends(get_db), + current_user: dict = Depends(require_permission("plugins:read")), +): + """Get detail for a single plugin: manifest metadata + DB status. + + BUG-024 fix: this endpoint was missing entirely (404). + """ + registry = get_registry() + plugin = registry.get_plugin(name) + if plugin is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail={"detail": f"Plugin '{name}' not found", "code": "not_found"}, + ) + + m = plugin.manifest + record = registry.get_db_status(name) + return { + "name": m.name, + "version": m.version, + "display_name": getattr(m, "display_name", None) or m.name, + "description": getattr(m, "description", None), + "author": getattr(m, "author", None), + "is_core": bool(getattr(m, "is_core", False)), + "permissions": list(m.permissions), + "depends_on": list(getattr(m, "depends_on", []) or []), + "status": { + "installed": record is not None, + "active": bool(record.active) if record is not None else False, + "version_installed": getattr(record, "version", None), + }, + "menu_items": len(getattr(m, "menu_items", []) or []), + "page_routes": len(getattr(m, "page_routes", []) or []), + "detail_tabs": len(getattr(m, "detail_tabs", []) or []), + "settings_pages": len(getattr(m, "settings_pages", []) or []), + "dashboard_widgets": len(getattr(m, "dashboard_widgets", []) or []), + } + + @router.get("/manifest") async def get_manifest_schema( current_user: dict = Depends(require_permission("plugins:read")), diff --git a/docs/test-bugs.md b/docs/test-bugs.md index d376148..a77aa1c 100644 --- a/docs/test-bugs.md +++ b/docs/test-bugs.md @@ -251,7 +251,7 @@ Jeder Bug wird wie folgt dokumentiert: - **Tatsächlich:** 404 Not Found für alle Plugins - **Schweregrad:** Medium - **Ursache:** Es gibt `/{name}/config`, `/{name}/activate`, `/{name}/deactivate` aber keine reine `GET /{name}` Route -- **Status:** ⏳ Nicht gefixt +- **Status:** ✅ Gefixt 2026-08-24 (Block E/I-C): GET /api/v1/plugins/{name} implementiert — Manifest-Metadaten + DB-Status kombiniert, 404 für unbekannte Plugins; Beweistest tests/test_plugin_detail.py 2/2 grün (200 für calendar inkl. status-Objekt, 404 für unknown); ruff clean. Nutzungsrelevanz war niedrig (Frontend ruft kein Detail auf), aber API-Vollständigkeit hergestellt. ### BUG-025: Workflow Execute und Instances API-Pfade falsch - **Kategorie:** API diff --git a/tests/test_plugin_detail.py b/tests/test_plugin_detail.py new file mode 100644 index 0000000..1486323 --- /dev/null +++ b/tests/test_plugin_detail.py @@ -0,0 +1,36 @@ +"""Tests for the plugin detail endpoint (BUG-024 fix). + +Proves GET /api/v1/plugins/{name} returns manifest+status detail for known +plugins and 404 for unknown ones. +""" + +from __future__ import annotations + +import pytest + +from tests.conftest import ORIGIN_HEADER, login_client, seed_tenant_and_users + + +@pytest.mark.asyncio +async def test_plugin_detail_returns_manifest_and_status( + client, db_session +): + """GET /api/v1/plugins/calendar -> 200 with manifest metadata + status.""" + await seed_tenant_and_users(db_session) + await login_client(client, "admin@tenanta.com") + + resp = await client.get("/api/v1/plugins/calendar", headers=ORIGIN_HEADER) + assert resp.status_code == 200, resp.text + data = resp.json() + assert data["name"] == "calendar" + assert "status" in data and "installed" in data["status"] + + +@pytest.mark.asyncio +async def test_plugin_detail_unknown_plugin_returns_404(client, db_session): + """GET /api/v1/plugins/__unknown__ -> 404 not_found.""" + await seed_tenant_and_users(db_session) + await login_client(client, "admin@tenanta.com") + + resp = await client.get("/api/v1/plugins/__unknown_plugin_xyz__", headers=ORIGIN_HEADER) + assert resp.status_code == 404, resp.text