Phase 1 - Vendor Router Integration: - Wire up vendor module routers in app/api/v1/vendor/__init__.py - Use lazy imports via __getattr__ to avoid circular dependencies Phase 2 - Extract Remaining Modules: - Create 6 new module directories: customers, cms, analytics, messaging, dev_tools, monitoring - Each module has definition.py and route wrappers - Update registry to import from extracted modules Phase 3 - Database Table Migration: - Add PlatformModule junction table for auditable module tracking - Add migration zc2m3n4o5p6q7_add_platform_modules_table.py - Add modules relationship to Platform model - Update ModuleService with JSON-to-junction-table migration Phase 4 - Module-Specific Configuration UI: - Add /api/v1/admin/module-config/* endpoints - Add module-config.html template and JS Phase 5 - Integration Tests: - Add tests/fixtures/module_fixtures.py - Add tests/integration/api/v1/admin/test_modules.py - Add tests/integration/api/v1/modules/test_module_access.py Architecture fixes: - Fix JS-003 errors: use ...data() directly in Alpine components - Fix JS-005 warnings: add init() guards to prevent duplicate init - Fix API-001 errors: add MenuActionResponse Pydantic model - Add FE-008 noqa for dynamic number input in template Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
792 lines
25 KiB
Python
792 lines
25 KiB
Python
# app/modules/service.py
|
|
"""
|
|
Module service for platform module operations.
|
|
|
|
Provides methods to check module enablement, get enabled modules,
|
|
and filter menu items based on module configuration.
|
|
|
|
Module configuration can be stored in two places:
|
|
1. PlatformModule junction table (preferred, auditable)
|
|
2. Platform.settings["enabled_modules"] (fallback, legacy)
|
|
|
|
If neither is configured, all modules are enabled (backwards compatibility).
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime, timezone
|
|
from functools import lru_cache
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.modules.base import ModuleDefinition
|
|
from app.modules.registry import (
|
|
MODULES,
|
|
get_core_module_codes,
|
|
get_menu_item_module,
|
|
get_module,
|
|
)
|
|
from models.database.admin_menu_config import FrontendType
|
|
from models.database.platform import Platform
|
|
from models.database.platform_module import PlatformModule
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ModuleService:
|
|
"""
|
|
Service for platform module operations.
|
|
|
|
Handles module enablement checking, module listing, and menu item filtering
|
|
based on enabled modules.
|
|
|
|
Module configuration is stored in two places (with fallback):
|
|
1. PlatformModule junction table (preferred, auditable)
|
|
2. Platform.settings["enabled_modules"] (legacy fallback)
|
|
|
|
The service checks the junction table first. If no records exist,
|
|
it falls back to the JSON settings for backwards compatibility.
|
|
|
|
If neither is configured, all modules are enabled (backwards compatibility).
|
|
|
|
Example PlatformModule records:
|
|
PlatformModule(platform_id=1, module_code="billing", is_enabled=True, config={"stripe_mode": "live"})
|
|
PlatformModule(platform_id=1, module_code="inventory", is_enabled=True, config={"low_stock_threshold": 10})
|
|
|
|
Legacy Platform.settings (fallback):
|
|
{
|
|
"enabled_modules": ["core", "billing", "inventory", "orders"],
|
|
"module_config": {
|
|
"billing": {"stripe_mode": "live"},
|
|
"inventory": {"low_stock_threshold": 10}
|
|
}
|
|
}
|
|
"""
|
|
|
|
# =========================================================================
|
|
# Module Enablement
|
|
# =========================================================================
|
|
|
|
def get_platform_modules(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
) -> list[ModuleDefinition]:
|
|
"""
|
|
Get all enabled modules for a platform.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
|
|
Returns:
|
|
List of enabled ModuleDefinition objects (always includes core)
|
|
"""
|
|
enabled_codes = self._get_enabled_module_codes(db, platform_id)
|
|
return [MODULES[code] for code in enabled_codes if code in MODULES]
|
|
|
|
def get_enabled_module_codes(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
) -> set[str]:
|
|
"""
|
|
Get set of enabled module codes for a platform.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
|
|
Returns:
|
|
Set of enabled module codes (always includes core modules)
|
|
"""
|
|
return self._get_enabled_module_codes(db, platform_id)
|
|
|
|
def is_module_enabled(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
module_code: str,
|
|
) -> bool:
|
|
"""
|
|
Check if a specific module is enabled for a platform.
|
|
|
|
Core modules are always enabled.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
module_code: Module code to check
|
|
|
|
Returns:
|
|
True if module is enabled
|
|
"""
|
|
# Core modules are always enabled
|
|
if module_code in get_core_module_codes():
|
|
return True
|
|
|
|
enabled_codes = self._get_enabled_module_codes(db, platform_id)
|
|
return module_code in enabled_codes
|
|
|
|
def _get_enabled_module_codes(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
) -> set[str]:
|
|
"""
|
|
Get enabled module codes for a platform.
|
|
|
|
Checks two sources with fallback:
|
|
1. PlatformModule junction table (preferred, auditable)
|
|
2. Platform.settings["enabled_modules"] (legacy fallback)
|
|
|
|
If neither is configured, returns all module codes (backwards compatibility).
|
|
Always includes core modules.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
|
|
Returns:
|
|
Set of enabled module codes
|
|
"""
|
|
platform = db.query(Platform).filter(Platform.id == platform_id).first()
|
|
if not platform:
|
|
logger.warning(f"Platform {platform_id} not found, returning all modules")
|
|
return set(MODULES.keys())
|
|
|
|
# Try junction table first (preferred)
|
|
platform_modules = (
|
|
db.query(PlatformModule)
|
|
.filter(PlatformModule.platform_id == platform_id)
|
|
.all()
|
|
)
|
|
|
|
if platform_modules:
|
|
# Use junction table data
|
|
enabled_set = {pm.module_code for pm in platform_modules if pm.is_enabled}
|
|
else:
|
|
# Fallback to JSON settings (legacy)
|
|
settings = platform.settings or {}
|
|
enabled_modules = settings.get("enabled_modules")
|
|
|
|
# If not configured, enable all modules (backwards compatibility)
|
|
if enabled_modules is None:
|
|
return set(MODULES.keys())
|
|
|
|
enabled_set = set(enabled_modules)
|
|
|
|
# Always include core modules
|
|
core_codes = get_core_module_codes()
|
|
enabled_set = enabled_set | core_codes
|
|
|
|
# Resolve dependencies - add required modules
|
|
enabled_set = self._resolve_dependencies(enabled_set)
|
|
|
|
return enabled_set
|
|
|
|
def _migrate_json_to_junction_table(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
user_id: int | None = None,
|
|
) -> None:
|
|
"""
|
|
Migrate JSON settings to junction table records.
|
|
|
|
Called when first creating a junction table record for a platform
|
|
that previously used JSON settings. This ensures consistency when
|
|
mixing junction table and JSON approaches.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
user_id: ID of user performing the migration (for audit)
|
|
"""
|
|
# Check if any junction table records exist
|
|
existing_count = (
|
|
db.query(PlatformModule)
|
|
.filter(PlatformModule.platform_id == platform_id)
|
|
.count()
|
|
)
|
|
|
|
if existing_count > 0:
|
|
# Already using junction table
|
|
return
|
|
|
|
platform = db.query(Platform).filter(Platform.id == platform_id).first()
|
|
if not platform:
|
|
return
|
|
|
|
settings = platform.settings or {}
|
|
enabled_modules = settings.get("enabled_modules")
|
|
|
|
if enabled_modules is None:
|
|
# No JSON settings, start fresh with all modules enabled
|
|
enabled_codes = set(MODULES.keys())
|
|
else:
|
|
enabled_codes = set(enabled_modules) | get_core_module_codes()
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
# Create junction table records for all known modules
|
|
for code in MODULES.keys():
|
|
is_enabled = code in enabled_codes
|
|
pm = PlatformModule(
|
|
platform_id=platform_id,
|
|
module_code=code,
|
|
is_enabled=is_enabled,
|
|
enabled_at=now if is_enabled else None,
|
|
enabled_by_user_id=user_id if is_enabled else None,
|
|
disabled_at=None if is_enabled else now,
|
|
disabled_by_user_id=None if is_enabled else user_id,
|
|
config={},
|
|
)
|
|
db.add(pm)
|
|
|
|
# Flush to ensure records are visible to subsequent queries
|
|
db.flush()
|
|
|
|
logger.info(
|
|
f"Migrated platform {platform_id} from JSON settings to junction table"
|
|
)
|
|
|
|
def _resolve_dependencies(self, enabled_codes: set[str]) -> set[str]:
|
|
"""
|
|
Resolve module dependencies by adding required modules.
|
|
|
|
If module A requires module B, and A is enabled, B must also be enabled.
|
|
|
|
Args:
|
|
enabled_codes: Set of explicitly enabled module codes
|
|
|
|
Returns:
|
|
Set of enabled module codes including dependencies
|
|
"""
|
|
resolved = set(enabled_codes)
|
|
changed = True
|
|
|
|
while changed:
|
|
changed = False
|
|
for code in list(resolved):
|
|
module = get_module(code)
|
|
if module:
|
|
for required in module.requires:
|
|
if required not in resolved:
|
|
resolved.add(required)
|
|
changed = True
|
|
logger.debug(
|
|
f"Module '{code}' requires '{required}', auto-enabling"
|
|
)
|
|
|
|
return resolved
|
|
|
|
# =========================================================================
|
|
# Menu Item Filtering
|
|
# =========================================================================
|
|
|
|
def get_module_menu_items(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
frontend_type: FrontendType,
|
|
) -> set[str]:
|
|
"""
|
|
Get all menu item IDs available for enabled modules.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
frontend_type: Which frontend (admin or vendor)
|
|
|
|
Returns:
|
|
Set of menu item IDs from enabled modules
|
|
"""
|
|
enabled_modules = self.get_platform_modules(db, platform_id)
|
|
menu_items = set()
|
|
|
|
for module in enabled_modules:
|
|
menu_items.update(module.get_menu_items(frontend_type))
|
|
|
|
return menu_items
|
|
|
|
def is_menu_item_module_enabled(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
menu_item_id: str,
|
|
frontend_type: FrontendType,
|
|
) -> bool:
|
|
"""
|
|
Check if the module providing a menu item is enabled.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
menu_item_id: Menu item ID
|
|
frontend_type: Which frontend (admin or vendor)
|
|
|
|
Returns:
|
|
True if the module providing this menu item is enabled,
|
|
or if menu item is not associated with any module.
|
|
"""
|
|
module_code = get_menu_item_module(menu_item_id, frontend_type)
|
|
|
|
# If menu item isn't associated with any module, allow it
|
|
if module_code is None:
|
|
return True
|
|
|
|
return self.is_module_enabled(db, platform_id, module_code)
|
|
|
|
def filter_menu_items_by_modules(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
menu_item_ids: set[str],
|
|
frontend_type: FrontendType,
|
|
) -> set[str]:
|
|
"""
|
|
Filter menu items to only those from enabled modules.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
menu_item_ids: Set of menu item IDs to filter
|
|
frontend_type: Which frontend (admin or vendor)
|
|
|
|
Returns:
|
|
Filtered set of menu item IDs
|
|
"""
|
|
available_items = self.get_module_menu_items(db, platform_id, frontend_type)
|
|
|
|
# Items that are in available_items, OR items not associated with any module
|
|
filtered = set()
|
|
for item_id in menu_item_ids:
|
|
module_code = get_menu_item_module(item_id, frontend_type)
|
|
if module_code is None or item_id in available_items:
|
|
filtered.add(item_id)
|
|
|
|
return filtered
|
|
|
|
# =========================================================================
|
|
# Module Configuration
|
|
# =========================================================================
|
|
|
|
def get_module_config(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
module_code: str,
|
|
) -> dict:
|
|
"""
|
|
Get module-specific configuration for a platform.
|
|
|
|
Checks two sources with fallback:
|
|
1. PlatformModule.config (preferred, auditable)
|
|
2. Platform.settings["module_config"] (legacy fallback)
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
module_code: Module code
|
|
|
|
Returns:
|
|
Module configuration dict (empty if not configured)
|
|
"""
|
|
# Try junction table first (preferred)
|
|
platform_module = (
|
|
db.query(PlatformModule)
|
|
.filter(
|
|
PlatformModule.platform_id == platform_id,
|
|
PlatformModule.module_code == module_code,
|
|
)
|
|
.first()
|
|
)
|
|
|
|
if platform_module:
|
|
return platform_module.config or {}
|
|
|
|
# Fallback to JSON settings (legacy)
|
|
platform = db.query(Platform).filter(Platform.id == platform_id).first()
|
|
if not platform:
|
|
return {}
|
|
|
|
settings = platform.settings or {}
|
|
module_configs = settings.get("module_config", {})
|
|
return module_configs.get(module_code, {})
|
|
|
|
def set_module_config(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
module_code: str,
|
|
config: dict,
|
|
) -> bool:
|
|
"""
|
|
Set module-specific configuration for a platform.
|
|
|
|
Uses junction table for persistence. Creates record if doesn't exist.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
module_code: Module code
|
|
config: Configuration dict to set
|
|
|
|
Returns:
|
|
True if successful
|
|
"""
|
|
if module_code not in MODULES:
|
|
logger.error(f"Unknown module: {module_code}")
|
|
return False
|
|
|
|
platform = db.query(Platform).filter(Platform.id == platform_id).first()
|
|
if not platform:
|
|
logger.error(f"Platform {platform_id} not found")
|
|
return False
|
|
|
|
# Get or create junction table record
|
|
platform_module = (
|
|
db.query(PlatformModule)
|
|
.filter(
|
|
PlatformModule.platform_id == platform_id,
|
|
PlatformModule.module_code == module_code,
|
|
)
|
|
.first()
|
|
)
|
|
|
|
if platform_module:
|
|
platform_module.config = config
|
|
else:
|
|
# Create new record with config
|
|
platform_module = PlatformModule(
|
|
platform_id=platform_id,
|
|
module_code=module_code,
|
|
is_enabled=True, # Default to enabled
|
|
config=config,
|
|
)
|
|
db.add(platform_module)
|
|
|
|
logger.info(f"Updated config for module '{module_code}' on platform {platform_id}")
|
|
return True
|
|
|
|
def set_enabled_modules(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
module_codes: list[str],
|
|
user_id: int | None = None,
|
|
) -> bool:
|
|
"""
|
|
Set the enabled modules for a platform.
|
|
|
|
Core modules are automatically included.
|
|
Dependencies are automatically resolved.
|
|
Uses junction table for auditability.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
module_codes: List of module codes to enable
|
|
user_id: ID of user making the change (for audit)
|
|
|
|
Returns:
|
|
True if successful, False if platform not found
|
|
"""
|
|
platform = db.query(Platform).filter(Platform.id == platform_id).first()
|
|
if not platform:
|
|
logger.error(f"Platform {platform_id} not found")
|
|
return False
|
|
|
|
# Validate module codes
|
|
valid_codes = set(MODULES.keys())
|
|
invalid = [code for code in module_codes if code not in valid_codes]
|
|
if invalid:
|
|
logger.warning(f"Invalid module codes ignored: {invalid}")
|
|
module_codes = [code for code in module_codes if code in valid_codes]
|
|
|
|
# Always include core modules
|
|
core_codes = get_core_module_codes()
|
|
enabled_set = set(module_codes) | core_codes
|
|
|
|
# Resolve dependencies
|
|
enabled_set = self._resolve_dependencies(enabled_set)
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
# Update junction table for all modules
|
|
for code in MODULES.keys():
|
|
platform_module = (
|
|
db.query(PlatformModule)
|
|
.filter(
|
|
PlatformModule.platform_id == platform_id,
|
|
PlatformModule.module_code == code,
|
|
)
|
|
.first()
|
|
)
|
|
|
|
should_enable = code in enabled_set
|
|
|
|
if platform_module:
|
|
# Update existing record
|
|
if should_enable and not platform_module.is_enabled:
|
|
platform_module.is_enabled = True
|
|
platform_module.enabled_at = now
|
|
platform_module.enabled_by_user_id = user_id
|
|
elif not should_enable and platform_module.is_enabled:
|
|
platform_module.is_enabled = False
|
|
platform_module.disabled_at = now
|
|
platform_module.disabled_by_user_id = user_id
|
|
else:
|
|
# Create new record
|
|
platform_module = PlatformModule(
|
|
platform_id=platform_id,
|
|
module_code=code,
|
|
is_enabled=should_enable,
|
|
enabled_at=now if should_enable else None,
|
|
enabled_by_user_id=user_id if should_enable else None,
|
|
disabled_at=None if should_enable else now,
|
|
disabled_by_user_id=None if should_enable else user_id,
|
|
config={},
|
|
)
|
|
db.add(platform_module)
|
|
|
|
logger.info(
|
|
f"Updated enabled modules for platform {platform_id}: {sorted(enabled_set)}"
|
|
)
|
|
return True
|
|
|
|
def enable_module(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
module_code: str,
|
|
user_id: int | None = None,
|
|
) -> bool:
|
|
"""
|
|
Enable a single module for a platform.
|
|
|
|
Also enables required dependencies.
|
|
Uses junction table for auditability when available.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
module_code: Module code to enable
|
|
user_id: ID of user enabling the module (for audit)
|
|
|
|
Returns:
|
|
True if successful
|
|
"""
|
|
if module_code not in MODULES:
|
|
logger.error(f"Unknown module: {module_code}")
|
|
return False
|
|
|
|
platform = db.query(Platform).filter(Platform.id == platform_id).first()
|
|
if not platform:
|
|
logger.error(f"Platform {platform_id} not found")
|
|
return False
|
|
|
|
# Migrate JSON settings to junction table if needed
|
|
self._migrate_json_to_junction_table(db, platform_id, user_id)
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
# Enable this module and its dependencies
|
|
modules_to_enable = {module_code}
|
|
module = get_module(module_code)
|
|
if module:
|
|
for required in module.requires:
|
|
modules_to_enable.add(required)
|
|
|
|
for code in modules_to_enable:
|
|
# Check if junction table record exists
|
|
platform_module = (
|
|
db.query(PlatformModule)
|
|
.filter(
|
|
PlatformModule.platform_id == platform_id,
|
|
PlatformModule.module_code == code,
|
|
)
|
|
.first()
|
|
)
|
|
|
|
if platform_module:
|
|
# Update existing record
|
|
platform_module.is_enabled = True
|
|
platform_module.enabled_at = now
|
|
platform_module.enabled_by_user_id = user_id
|
|
else:
|
|
# Create new record
|
|
platform_module = PlatformModule(
|
|
platform_id=platform_id,
|
|
module_code=code,
|
|
is_enabled=True,
|
|
enabled_at=now,
|
|
enabled_by_user_id=user_id,
|
|
config={},
|
|
)
|
|
db.add(platform_module)
|
|
|
|
logger.info(f"Enabled module '{module_code}' for platform {platform_id}")
|
|
return True
|
|
|
|
def disable_module(
|
|
self,
|
|
db: Session,
|
|
platform_id: int,
|
|
module_code: str,
|
|
user_id: int | None = None,
|
|
) -> bool:
|
|
"""
|
|
Disable a single module for a platform.
|
|
|
|
Core modules cannot be disabled.
|
|
Also disables modules that depend on this one.
|
|
Uses junction table for auditability when available.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_id: Platform ID
|
|
module_code: Module code to disable
|
|
user_id: ID of user disabling the module (for audit)
|
|
|
|
Returns:
|
|
True if successful, False if core or not found
|
|
"""
|
|
if module_code not in MODULES:
|
|
logger.error(f"Unknown module: {module_code}")
|
|
return False
|
|
|
|
module = MODULES[module_code]
|
|
if module.is_core:
|
|
logger.warning(f"Cannot disable core module: {module_code}")
|
|
return False
|
|
|
|
platform = db.query(Platform).filter(Platform.id == platform_id).first()
|
|
if not platform:
|
|
logger.error(f"Platform {platform_id} not found")
|
|
return False
|
|
|
|
# Migrate JSON settings to junction table if needed
|
|
self._migrate_json_to_junction_table(db, platform_id, user_id)
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
# Get modules to disable (this one + dependents)
|
|
modules_to_disable = {module_code}
|
|
dependents = self._get_dependent_modules(module_code)
|
|
modules_to_disable.update(dependents)
|
|
|
|
for code in modules_to_disable:
|
|
# Check if junction table record exists
|
|
platform_module = (
|
|
db.query(PlatformModule)
|
|
.filter(
|
|
PlatformModule.platform_id == platform_id,
|
|
PlatformModule.module_code == code,
|
|
)
|
|
.first()
|
|
)
|
|
|
|
if platform_module:
|
|
# Update existing record
|
|
platform_module.is_enabled = False
|
|
platform_module.disabled_at = now
|
|
platform_module.disabled_by_user_id = user_id
|
|
else:
|
|
# Create disabled record for tracking
|
|
platform_module = PlatformModule(
|
|
platform_id=platform_id,
|
|
module_code=code,
|
|
is_enabled=False,
|
|
disabled_at=now,
|
|
disabled_by_user_id=user_id,
|
|
config={},
|
|
)
|
|
db.add(platform_module)
|
|
|
|
if code != module_code:
|
|
logger.info(
|
|
f"Also disabled '{code}' (depends on '{module_code}')"
|
|
)
|
|
|
|
logger.info(f"Disabled module '{module_code}' for platform {platform_id}")
|
|
return True
|
|
|
|
def _get_dependent_modules(self, module_code: str) -> set[str]:
|
|
"""
|
|
Get modules that depend on a given module.
|
|
|
|
Args:
|
|
module_code: Module code to find dependents for
|
|
|
|
Returns:
|
|
Set of module codes that require the given module
|
|
"""
|
|
dependents = set()
|
|
for code, module in MODULES.items():
|
|
if module_code in module.requires:
|
|
dependents.add(code)
|
|
# Recursively find dependents of dependents
|
|
dependents.update(self._get_dependent_modules(code))
|
|
return dependents
|
|
|
|
# =========================================================================
|
|
# Platform Code Helpers
|
|
# =========================================================================
|
|
|
|
def get_platform_modules_by_code(
|
|
self,
|
|
db: Session,
|
|
platform_code: str,
|
|
) -> list[ModuleDefinition]:
|
|
"""
|
|
Get enabled modules for a platform by code.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_code: Platform code (e.g., "oms", "loyalty")
|
|
|
|
Returns:
|
|
List of enabled ModuleDefinition objects
|
|
"""
|
|
platform = db.query(Platform).filter(Platform.code == platform_code).first()
|
|
if not platform:
|
|
logger.warning(f"Platform '{platform_code}' not found, returning all modules")
|
|
return list(MODULES.values())
|
|
|
|
return self.get_platform_modules(db, platform.id)
|
|
|
|
def is_module_enabled_by_code(
|
|
self,
|
|
db: Session,
|
|
platform_code: str,
|
|
module_code: str,
|
|
) -> bool:
|
|
"""
|
|
Check if a module is enabled for a platform by code.
|
|
|
|
Args:
|
|
db: Database session
|
|
platform_code: Platform code (e.g., "oms", "loyalty")
|
|
module_code: Module code to check
|
|
|
|
Returns:
|
|
True if module is enabled
|
|
"""
|
|
platform = db.query(Platform).filter(Platform.code == platform_code).first()
|
|
if not platform:
|
|
logger.warning(f"Platform '{platform_code}' not found, assuming enabled")
|
|
return True
|
|
|
|
return self.is_module_enabled(db, platform.id, module_code)
|
|
|
|
|
|
# Singleton instance
|
|
module_service = ModuleService()
|
|
|
|
|
|
__all__ = [
|
|
"ModuleService",
|
|
"module_service",
|
|
]
|