feat: add module definition completeness validation and permissions

Add new validation rules MOD-020 to MOD-023 for module definition
completeness and standardize permissions across all modules.

Changes:
- Add MOD-020: Module definitions must have required attributes
- Add MOD-021: Modules with menus should have features
- Add MOD-022: Feature modules should have permissions
- Add MOD-023: Modules with routers should use get_*_with_routers pattern

Module permissions added:
- analytics: view, export, manage_dashboards
- billing: view_tiers, manage_tiers, view_subscriptions, manage_subscriptions, view_invoices
- cart: view, manage
- checkout: view_settings, manage_settings
- cms: view_pages, manage_pages, view_media, manage_media, manage_themes
- loyalty: view_programs, manage_programs, view_rewards, manage_rewards
- marketplace: view_integration, manage_integration, sync_products
- messaging: view_messages, send_messages, manage_templates
- payments: view_gateways, manage_gateways, view_transactions

Module improvements:
- Complete cart module with features and permissions
- Complete checkout module with features and permissions
- Add features to catalog module
- Add version to cms module
- Fix loyalty platform_router attachment
- Add path definitions to payments module
- Remove empty scheduled_tasks from dev_tools module

Documentation:
- Update module-system.md with new validation rules
- Update architecture-rules.md with MOD-020 to MOD-023

Tests:
- Add unit tests for module definition completeness
- Add tests for permission structure validation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-02 18:23:04 +01:00
parent 30a5c75e74
commit 967f08e4ba
50 changed files with 1014 additions and 66 deletions

View File

@@ -4351,6 +4351,117 @@ class ArchitectureValidator:
suggestion="Create 'exceptions.py' or 'exceptions/__init__.py'",
)
# MOD-020: Check module definition has required attributes
self._validate_module_definition_completeness(module_dir, module_name, definition_file, definition_content)
def _validate_module_definition_completeness(
self,
module_dir: Path,
module_name: str,
definition_file: Path,
definition_content: str
):
"""
Validate module definition completeness (MOD-020 to MOD-023).
Checks:
- MOD-020: Required attributes (code, name, description, version, features, permissions)
- MOD-021: Modules with menus should have features
- MOD-022: Feature modules should have permissions
- MOD-023: Modules with routers should use get_*_with_routers pattern
"""
# Detect module characteristics
is_internal = "is_internal=True" in definition_content or "is_internal = True" in definition_content
is_core_infrastructure = (
("is_core=True" in definition_content or "is_core = True" in definition_content) and
("menu_items={}" in definition_content or "menu_items = {}" in definition_content or "menu_items" not in definition_content)
)
has_features = "features=[" in definition_content or "features = [" in definition_content
has_permissions = "permissions=[" in definition_content or "permissions = [" in definition_content
has_menus = "menus={" in definition_content or "menus = {" in definition_content
has_menu_items = (
"menu_items={" in definition_content and
not re.search(r"menu_items\s*=\s*\{\s*\}", definition_content)
)
# Check for router lazy import pattern
has_router_imports = "_get_admin_router" in definition_content or "_get_vendor_router" in definition_content
has_get_with_routers = re.search(r"def get_\w+_module_with_routers\s*\(", definition_content)
# MOD-020: Check required attributes
# Basic required attributes
required_attrs = ["code=", "name=", "description=", "version="]
for attr in required_attrs:
if attr not in definition_content:
attr_name = attr.replace("=", "")
self._add_violation(
rule_id="MOD-020",
rule_name="Module definition must have required attributes",
severity=Severity.WARNING,
file_path=definition_file,
line_number=1,
message=f"Module '{module_name}' missing required attribute '{attr_name}'",
context="ModuleDefinition()",
suggestion=f"Add '{attr_name}' to ModuleDefinition",
)
# Check features (unless infrastructure module)
if not has_features and not is_core_infrastructure:
self._add_violation(
rule_id="MOD-020",
rule_name="Module definition must have required attributes",
severity=Severity.WARNING,
file_path=definition_file,
line_number=1,
message=f"Module '{module_name}' missing 'features' list",
context="ModuleDefinition() without features",
suggestion="Add 'features=[...]' to describe module capabilities",
)
# MOD-021: Modules with menus should have features
if (has_menus or has_menu_items) and not has_features:
self._add_violation(
rule_id="MOD-021",
rule_name="Modules with menus should have features",
severity=Severity.WARNING,
file_path=definition_file,
line_number=1,
message=f"Module '{module_name}' has menus but no features defined",
context="menus={...} without features=[...]",
suggestion="Add 'features=[...]' to describe what the module provides",
)
# MOD-022: Feature modules should have permissions
if has_features and not has_permissions and not is_internal:
# Check if it's a storefront-only module (cart, checkout)
is_storefront_only = module_name in ["cart", "checkout"]
if not is_storefront_only:
self._add_violation(
rule_id="MOD-022",
rule_name="Feature modules should have permissions",
severity=Severity.INFO,
file_path=definition_file,
line_number=1,
message=f"Module '{module_name}' has features but no permissions defined",
context="features=[...] without permissions=[...]",
suggestion="Add 'permissions=[PermissionDefinition(...), ...]' for RBAC",
)
# MOD-023: Modules with routers should use get_*_with_routers pattern
if has_router_imports and not has_get_with_routers:
self._add_violation(
rule_id="MOD-023",
rule_name="Modules with routers should use get_*_with_routers pattern",
severity=Severity.INFO,
file_path=definition_file,
line_number=1,
message=f"Module '{module_name}' has router imports but no get_*_with_routers() function",
context="_get_admin_router() or _get_vendor_router() without wrapper",
suggestion=f"Add 'def get_{module_name}_module_with_routers()' function",
)
def _validate_legacy_locations(self, target_path: Path):
"""
Validate that code is not in legacy locations (MOD-016 to MOD-019).