feat(f3): Gate-F-Pflichttest bestanden — Minimal-Plugin NUR aus dem Guide gebaut
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
Der Pflichttest (Guide-Kapitel 29.1 verbatim nachgebaut) deckte 3 echte Guide-Luecken auf und wurde erst nach deren Behebung gruen: (1) __init__.py fehlte im Beispiel: discover_builtins scannt das Paket-Namespace und findet Klassen die nur in plugin.py leben nie. (2) Route brauchte vollen Pfad: main.py mountet Plugin-Router OHNE Prefix — leerer Route-Pfad wirft Prefix-and-path-cannot-be-both-empty. (3) Plugin-Routen werden dynamisch dispatched: sie erscheinen NIE in app.routes. Alle 3 Luecken sind jetzt in Kapitel 29.1 mit Warnhinweis dokumentiert; tests/test_gate_f_minimal_example.py beweist dauerhaft dass ein Guide-faehiges Plugin funktioniert. Beweise: Gate-F-Suite 4/4 gruen; ruff clean; Cross-Plugin-Scan sauber.
This commit is contained in:
@@ -1040,7 +1040,27 @@ describe('PluginRegistry', () => {
|
||||
|
||||
## 29. Examples
|
||||
|
||||
### 13.1 Minimal Plugin
|
||||
### 13.1 Minimal Plugin (verified by tests/test_gate_f_minimal_example.py)
|
||||
|
||||
> ⚠️ **Three pitfalls verified by the Gate-F test suite** — the original version of
|
||||
> this example failed all three:
|
||||
>
|
||||
> 1. **`__init__.py` is required** with a re-export of the plugin class —
|
||||
> `discover_builtins()` scans the package namespace and never finds classes
|
||||
> that live only in `plugin.py`.
|
||||
> 2. **The route must carry the full path** — main.py mounts plugin routers
|
||||
> WITHOUT a prefix; an empty route path raises
|
||||
> `Prefix and path cannot be both empty`.
|
||||
> 3. **Plugin routes are dispatched dynamically** — they never appear in
|
||||
> `app.routes`; verify them via HTTP request, not via route introspection.
|
||||
|
||||
```python
|
||||
# app/plugins/builtins/minimal_example/__init__.py
|
||||
"""Minimal Example plugin package."""
|
||||
from app.plugins.builtins.minimal_example.plugin import MinimalExamplePlugin
|
||||
|
||||
__all__ = ["MinimalExamplePlugin"]
|
||||
```
|
||||
|
||||
```python
|
||||
# app/plugins/builtins/minimal_example/plugin.py
|
||||
@@ -1069,12 +1089,17 @@ class MinimalExamplePlugin(BasePlugin):
|
||||
|
||||
```python
|
||||
# app/plugins/builtins/minimal_example/routes.py
|
||||
# NOTE: main.py mounts plugin routers WITHOUT a prefix — the manifest's
|
||||
# route path is documentation only. The full path must live here.
|
||||
from fastapi import APIRouter, Depends
|
||||
from app.deps import get_current_user, require_permission
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("", dependencies=[Depends(require_permission("minimal_example:read"))])
|
||||
@router.get(
|
||||
"/api/v1/minimal-example",
|
||||
dependencies=[Depends(require_permission("minimal_example:read"))],
|
||||
)
|
||||
async def list_items(current_user: dict = Depends(get_current_user)):
|
||||
return {"items": []}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user