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:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user