Files
shopdb-flask/plugins/employees/plugin.py
cproudlock 087ece0f8c Setup wizard P2: per-plugin config schema + settings-first creds
- Plugin contract gains get_config_schema(); the plugins list API returns it.
  Employees plugin declares its directory-DB fields (host/name/user + password).
- employee_connection reads host/name/user settings-first (env fallback); the
  password stays env-only.
- Setup wizard Features step renders each enabled plugin's config: non-secret
  fields save to settings; secrets are never stored - the wizard emits .env
  lines to paste. Fixed the plugins-list data path (data.plugins).
- Settings PUT now upserts (creates the row on first write) so plugin-config
  keys can be saved without pre-seeding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 08:10:48 -04:00

79 lines
3.0 KiB
Python

"""Employees plugin main class.
Blueprint-only plugin: a read-only employee directory lookup over a separate HR
database. No model (the directory is an external DB) and no AssetType. The
connection helper (employee_connection) stays core infrastructure in shopdb.api,
shared with search and the notifications shopfloor feed.
"""
import json
import logging
from pathlib import Path
from typing import List, Dict, Optional, Type
from flask import Flask, Blueprint
from shopdb.plugins.base import BasePlugin, PluginMeta
from .api import employees_bp
logger = logging.getLogger(__name__)
class EmployeesPlugin(BasePlugin):
"""Employees plugin - read-only HR directory lookup."""
def __init__(self):
self._manifest = self._load_manifest()
def _load_manifest(self) -> Dict:
"""Load plugin manifest from JSON file."""
manifest_path = Path(__file__).parent / 'manifest.json'
if manifest_path.exists():
with open(manifest_path, 'r') as f:
return json.load(f)
return {}
@property
def meta(self) -> PluginMeta:
"""Return plugin metadata."""
return PluginMeta(
name=self._manifest.get('name', 'employees'),
version=self._manifest.get('version', '1.0.0'),
description=self._manifest.get(
'description',
'Read-only employee directory lookup'
),
author=self._manifest.get('author', 'ShopDB Team'),
dependencies=self._manifest.get('dependencies', []),
core_version=self._manifest.get('core_version', '>=0.1.0,<1.0.0'),
api_prefix=self._manifest.get('api_prefix', '/api/employees'),
)
def get_blueprint(self) -> Optional[Blueprint]:
"""Return Flask Blueprint with API routes."""
return employees_bp
def get_models(self) -> List[Type]:
"""No models - the directory is an external database."""
return []
def get_config_schema(self) -> List[Dict]:
"""Employee directory DB connection. Host/name/user are settings the
wizard can edit; the password stays in .env (emitted, not stored)."""
return [
{'key': 'employee_db_host', 'label': 'Employee DB host', 'type': 'text',
'secret': False, 'default': 'localhost'},
{'key': 'employee_db_name', 'label': 'Employee DB name', 'type': 'text',
'secret': False, 'default': 'wjf_employees'},
{'key': 'employee_db_user', 'label': 'Employee DB user', 'type': 'text',
'secret': False},
{'key': 'employee_db_password', 'label': 'Employee DB password', 'type': 'password',
'secret': True, 'envvar': 'EMPLOYEE_DB_PASSWORD',
'help': 'Stored in .env, not the database. The wizard shows the line to paste.'},
]
def init_app(self, app: Flask, db_instance) -> None:
"""Initialize plugin with Flask app."""
logger.info(f"Employees plugin initialized (v{self.meta.version})")