fix: resolve all architecture validation errors (62 -> 0)
Major refactoring to achieve zero architecture violations: API Layer: - vendor/settings.py: Move validation to Pydantic field validators (tax rate, delivery method, boost sort, preorder days, languages, locales) - admin/email_templates.py: Add Pydantic response models (TemplateListResponse, CategoriesResponse) - shop/auth.py: Move password reset logic to CustomerService Service Layer: - customer_service.py: Add password reset methods (get_customer_for_password_reset, validate_and_reset_password) Exception Layer: - customer.py: Add InvalidPasswordResetTokenException, PasswordTooShortException Frontend: - admin/email-templates.js: Use apiClient, Utils.showToast() - vendor/email-templates.js: Use apiClient, parent init pattern Templates: - admin/email-templates.html: Fix block name to extra_scripts - shop/base.html: Add language default filter Tooling: - validate_architecture.py: Fix LANG-001 false positive for SUPPORTED_LANGUAGES and SUPPORTED_LOCALES blocks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3228,7 +3228,12 @@ class ArchitectureValidator:
|
||||
"""Validate template patterns"""
|
||||
print("📄 Validating templates...")
|
||||
|
||||
template_files = list(target_path.glob("app/templates/admin/**/*.html"))
|
||||
# Include admin, vendor, and shop templates
|
||||
template_files = (
|
||||
list(target_path.glob("app/templates/admin/**/*.html")) +
|
||||
list(target_path.glob("app/templates/vendor/**/*.html")) +
|
||||
list(target_path.glob("app/templates/shop/**/*.html"))
|
||||
)
|
||||
self.result.files_checked += len(template_files)
|
||||
|
||||
# TPL-001 exclusion patterns
|
||||
@@ -3254,6 +3259,11 @@ class ArchitectureValidator:
|
||||
# Skip components showcase page
|
||||
is_components_page = "components.html" in file_path.name
|
||||
|
||||
# Determine template type
|
||||
is_admin = "/admin/" in file_path_str or "\\admin\\" in file_path_str
|
||||
is_vendor = "/vendor/" in file_path_str or "\\vendor\\" in file_path_str
|
||||
is_shop = "/shop/" in file_path_str or "\\shop\\" in file_path_str
|
||||
|
||||
content = file_path.read_text()
|
||||
lines = content.split("\n")
|
||||
|
||||
@@ -3298,13 +3308,24 @@ class ArchitectureValidator:
|
||||
|
||||
# TPL-010: Check Alpine variables are defined in JS
|
||||
if not is_base_or_partial and not is_macro and not is_components_page:
|
||||
# Try to find corresponding JS file
|
||||
# Try to find corresponding JS file based on template type
|
||||
# Template: app/templates/admin/messages.html -> JS: static/admin/js/messages.js
|
||||
# Template: app/templates/vendor/analytics.html -> JS: static/vendor/js/analytics.js
|
||||
template_name = file_path.stem # e.g., "messages"
|
||||
js_file = target_path / f"static/admin/js/{template_name}.js"
|
||||
if js_file.exists():
|
||||
js_content = js_file.read_text()
|
||||
self._check_alpine_template_vars(file_path, content, lines, js_content)
|
||||
if is_admin:
|
||||
js_dir = "admin"
|
||||
elif is_vendor:
|
||||
js_dir = "vendor"
|
||||
elif is_shop:
|
||||
js_dir = "shop"
|
||||
else:
|
||||
js_dir = None
|
||||
|
||||
if js_dir:
|
||||
js_file = target_path / f"static/{js_dir}/js/{template_name}.js"
|
||||
if js_file.exists():
|
||||
js_content = js_file.read_text()
|
||||
self._check_alpine_template_vars(file_path, content, lines, js_content)
|
||||
|
||||
# TPL-011: Check for deprecated macros
|
||||
self._check_deprecated_macros(file_path, content, lines)
|
||||
@@ -3339,21 +3360,34 @@ class ArchitectureValidator:
|
||||
if "standalone" in first_lines or "noqa: tpl-001" in first_lines:
|
||||
continue
|
||||
|
||||
# TPL-001: Check for extends
|
||||
# TPL-001: Check for extends (template-type specific)
|
||||
if is_admin:
|
||||
expected_base = "admin/base.html"
|
||||
rule_id = "TPL-001"
|
||||
elif is_vendor:
|
||||
expected_base = "vendor/base.html"
|
||||
rule_id = "TPL-001"
|
||||
elif is_shop:
|
||||
expected_base = "shop/base.html"
|
||||
rule_id = "TPL-001"
|
||||
else:
|
||||
continue # Skip unknown template types
|
||||
|
||||
has_extends = any(
|
||||
"{% extends" in line and "admin/base.html" in line for line in lines
|
||||
"{% extends" in line and expected_base in line for line in lines
|
||||
)
|
||||
|
||||
if not has_extends:
|
||||
template_type = "Admin" if is_admin else "Vendor" if is_vendor else "Shop"
|
||||
self._add_violation(
|
||||
rule_id="TPL-001",
|
||||
rule_id=rule_id,
|
||||
rule_name="Templates must extend base",
|
||||
severity=Severity.ERROR,
|
||||
file_path=file_path,
|
||||
line_number=1,
|
||||
message="Admin template does not extend admin/base.html",
|
||||
message=f"{template_type} template does not extend {expected_base}",
|
||||
context=file_path.name,
|
||||
suggestion="Add {% extends 'admin/base.html' %} at the top, or add {# standalone #} if intentional",
|
||||
suggestion=f"Add {{% extends '{expected_base}' %}} at the top, or add {{# standalone #}} if intentional",
|
||||
)
|
||||
|
||||
# =========================================================================
|
||||
@@ -3408,8 +3442,9 @@ class ArchitectureValidator:
|
||||
content = file_path.read_text()
|
||||
lines = content.split("\n")
|
||||
|
||||
# Track if we're inside LANGUAGE_NAMES dicts (allowed to use language names)
|
||||
in_language_names_dict = False
|
||||
# Track if we're inside LANGUAGE_NAMES dicts or SUPPORTED_LANGUAGES lists
|
||||
# (allowed to use language names as display values)
|
||||
in_language_names_block = False
|
||||
|
||||
for i, line in enumerate(lines, 1):
|
||||
# Skip comments
|
||||
@@ -3417,15 +3452,22 @@ class ArchitectureValidator:
|
||||
if stripped.startswith("#"):
|
||||
continue
|
||||
|
||||
# Track LANGUAGE_NAMES/LANGUAGE_NAMES_EN blocks - name values are allowed
|
||||
if (
|
||||
"LANGUAGE_NAMES" in line or "LANGUAGE_NAMES_EN" in line
|
||||
# Track LANGUAGE_NAMES, LANGUAGE_NAMES_EN, or SUPPORTED_LANGUAGES/LOCALES blocks
|
||||
# - name values are allowed in these structures
|
||||
if any(
|
||||
name in line
|
||||
for name in [
|
||||
"LANGUAGE_NAMES",
|
||||
"LANGUAGE_NAMES_EN",
|
||||
"SUPPORTED_LANGUAGES",
|
||||
"SUPPORTED_LOCALES",
|
||||
]
|
||||
) and "=" in line:
|
||||
in_language_names_dict = True
|
||||
if in_language_names_dict and stripped == "}":
|
||||
in_language_names_dict = False
|
||||
in_language_names_block = True
|
||||
if in_language_names_block and stripped in ("}", "]"):
|
||||
in_language_names_block = False
|
||||
continue
|
||||
if in_language_names_dict:
|
||||
if in_language_names_block:
|
||||
continue
|
||||
|
||||
for wrong, correct in invalid_codes:
|
||||
|
||||
Reference in New Issue
Block a user