Add frontend integration checklist and docs-drift guard
PLUGIN-QUICKSTART Step 7 is now a concrete 7-item checklist (view conventions, route auto-discovery + ADR-009 gating meta, api client shape, nav/report hooks, settings auto-nesting, verification). New tests/test_docs_contract.py introspects BasePlugin and fails CI when a public hook is missing from PLUGIN-HOOKS.md or the documented contract version drifts - it immediately caught two undocumented hooks (get_provisioning_note, get_config_schema), now documented. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,50 @@ Consumed by `GET /api/reports`, which merges plugin cards after the static core
|
||||
reports sorted into category groups by the frontend (disabled plugins are
|
||||
skipped; a broken plugin is isolated in prod, re-raised in dev/test).
|
||||
|
||||
### `get_provisioning_note() -> Optional[Dict]`
|
||||
|
||||
Transparency note the setup wizard shows the moment a site checks this plugin
|
||||
during setup. Return `None` (the default) for plugins that need no special
|
||||
setup. Plugins that create extra tables beyond their asset-extension table
|
||||
(e.g. a self-hosted directory) return:
|
||||
|
||||
```python
|
||||
class EmployeesPlugin(BasePlugin):
|
||||
def get_provisioning_note(self):
|
||||
return {
|
||||
'tables': ['directoryemployees'],
|
||||
'note': 'Creates a local employee directory table in the shopdb database.',
|
||||
'docs': 'plugins/employees/README.md',
|
||||
}
|
||||
```
|
||||
|
||||
### `get_config_schema() -> List[Dict]`
|
||||
|
||||
Declares the config fields this plugin needs, so the setup wizard can prompt
|
||||
for them. Return `[]` (the default) if the plugin needs no configuration.
|
||||
Each field is a dict:
|
||||
|
||||
| Key | Meaning |
|
||||
|-----|---------|
|
||||
| `key` | the Setting key (non-secret) it maps to |
|
||||
| `label` | human label shown in the wizard |
|
||||
| `type` | `'text'` / `'number'` / `'password'` |
|
||||
| `secret` | `True` for credentials; NOT stored in the DB - the wizard emits an `.env` line for the operator instead |
|
||||
| `envvar` | (secret only) the `.env` variable name to emit |
|
||||
| `default` | optional placeholder |
|
||||
| `help` | optional hint |
|
||||
|
||||
```python
|
||||
class PrintersPlugin(BasePlugin):
|
||||
def get_config_schema(self):
|
||||
return [
|
||||
{'key': 'zabbix_url', 'label': 'Zabbix URL', 'type': 'text',
|
||||
'help': 'Base URL of the Zabbix server for supply lookups'},
|
||||
{'key': 'zabbix_token', 'label': 'Zabbix API token', 'type': 'password',
|
||||
'secret': True, 'envvar': 'ZABBIX_TOKEN'},
|
||||
]
|
||||
```
|
||||
|
||||
### `get_collector_schema() -> Optional[Dict]`
|
||||
|
||||
Declares the JSON Schema for an external collector pushing to `/api/collector/<pluginname>`. See [ADR-006](../docs/adr/ADR-006-collector-contract.md) for the contract.
|
||||
|
||||
@@ -126,15 +126,40 @@ Override hooks on the plugin class as needed. See [PLUGIN-HOOKS.md](PLUGIN-HOOKS
|
||||
|
||||
Each hook has a default that does nothing. Override only what your plugin needs.
|
||||
|
||||
## Step 7: Frontend (manual for now)
|
||||
## Step 7: Frontend (manual checklist)
|
||||
|
||||
Backend scaffolding is automated. Frontend is manual until the frontend scaffolding skill ships. Convention:
|
||||
Backend scaffolding is automated. The frontend is a manual checklist until a frontend scaffold ships. Copy from the closest bundled plugin (`network` is the cleanest) and work through these in order:
|
||||
|
||||
- `frontend/src/views/cameras/CamerasList.vue`
|
||||
- `frontend/src/views/cameras/CameraDetail.vue`
|
||||
- `frontend/src/views/cameras/CameraForm.vue`
|
||||
1. **View files** - create `frontend/src/views/cameras/CamerasList.vue`, `CameraDetail.vue`, `CameraForm.vue`. Copy from `frontend/src/views/network/` and rename. Use the global `.filters` / `.form-control` / `.card` styles; do not invent per-page input styling.
|
||||
|
||||
Copy from an existing plugin's view files (e.g., `frontend/src/views/network/`) as a starting point. Update the API client in `frontend/src/api/index.js` to add cameras endpoints.
|
||||
2. **Route file** - create `frontend/src/router/routes/cameras.js` exporting a route array. The router auto-discovers every file in `routes/` via `import.meta.glob`, so no registration edit is needed. Tag EVERY route with `meta: { plugin: 'cameras' }` - the ADR-009 guard redirects to the dashboard when the backend plugin is disabled. Add `requiresAuth: true` on form routes:
|
||||
|
||||
```js
|
||||
export default [
|
||||
{
|
||||
path: 'cameras',
|
||||
name: 'cameras',
|
||||
component: () => import('../../views/cameras/CamerasList.vue'),
|
||||
meta: { plugin: 'cameras' }
|
||||
},
|
||||
{
|
||||
path: 'cameras/:id/edit',
|
||||
name: 'camera-edit',
|
||||
component: () => import('../../views/cameras/CameraForm.vue'),
|
||||
meta: { requiresAuth: true, plugin: 'cameras' }
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
3. **API client** - add a `camerasApi` block to `frontend/src/api/index.js` wrapping your endpoints. Match an existing block's shape (`list(params)`, `get(id)`, `create(data)`, `update(id, data)`).
|
||||
|
||||
4. **Sidebar entry** - implement `get_navigation_items` on the plugin class. No frontend edit: the sidebar builds itself from `/api/dashboard/navigation`.
|
||||
|
||||
5. **Report cards** (if any) - implement `get_reports` on the plugin class. No frontend edit: the Reports hub builds itself from `/api/reports`. Use `route` for a dedicated page (add it to your route file), or `endpoint` for inline rendering.
|
||||
|
||||
6. **Settings page** (if the plugin has subtypes) - add a route whose path starts with `settings/` (e.g. `settings/cameratypes`) to your route file; the router automatically nests it under the settings shell. Copy a types-list view from `frontend/src/views/settings/`.
|
||||
|
||||
7. **Verify** - `npm run build` must pass, then screenshot your pages against the dev servers: `venv/bin/python tools/shot.py /cameras`.
|
||||
|
||||
## Common errors
|
||||
|
||||
|
||||
Reference in New Issue
Block a user